Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[test-only] Simulate dialogue clients against a DeterministicScheduler #348

Merged
merged 111 commits into from
Feb 17, 2020
Merged
Show file tree
Hide file tree
Changes from 101 commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
4bdc207
Statistics sink interface
iamdanfox Feb 11, 2020
6b57fd5
WIP
iamdanfox Feb 11, 2020
b38159d
Eliminate one cache for simplicity
iamdanfox Feb 12, 2020
187af81
Seems like it kinda works
iamdanfox Feb 12, 2020
2c0cc5c
One more class
iamdanfox Feb 12, 2020
56a9e6d
Cache the computation on the hot path
iamdanfox Feb 12, 2020
df76f65
Randomness
iamdanfox Feb 12, 2020
1f42a68
Ticker
iamdanfox Feb 12, 2020
14e395e
Wire in ticker and codahale clock
iamdanfox Feb 12, 2020
fdd9b49
Combined ticker and deterministic scheduler
iamdanfox Feb 12, 2020
38d06ea
We can schedule stuff
iamdanfox Feb 12, 2020
1ae2078
Spin up lots of instant tests
iamdanfox Feb 12, 2020
0e8e6f3
format
iamdanfox Feb 12, 2020
5785c19
Pull class out
iamdanfox Feb 12, 2020
6fb6ba3
Initial simulation server
iamdanfox Feb 12, 2020
94b7a78
server simulates response times
iamdanfox Feb 12, 2020
347d67a
Move clock
iamdanfox Feb 12, 2020
fdcc3b5
Can draw graphs
iamdanfox Feb 12, 2020
f6212e7
SimulationMetrics
iamdanfox Feb 12, 2020
fee44df
Dump metrics to csv
iamdanfox Feb 12, 2020
6d8bc0f
Dump png
iamdanfox Feb 12, 2020
448873a
Pull out simulation test
iamdanfox Feb 12, 2020
233c91a
Support composing simulation servers
iamdanfox Feb 13, 2020
95b533e
Can I pass static analysis
iamdanfox Feb 13, 2020
e1e0967
Merge remote-tracking branch 'origin/develop' into dfox/statistics
iamdanfox Feb 13, 2020
118cf8e
Switch all logging to slf4j
iamdanfox Feb 13, 2020
70b04dc
Rename to Simulation
iamdanfox Feb 13, 2020
5e41eee
PreferLowestUpstreamUtilization
iamdanfox Feb 13, 2020
1b39887
Allow making parallel requests in batches
iamdanfox Feb 13, 2020
a44444c
Active requests counter
iamdanfox Feb 13, 2020
ea9c18c
WIP
iamdanfox Feb 13, 2020
6e072d6
ConcurrencyLimitedChannel has a clock
iamdanfox Feb 14, 2020
257e703
Test against concurrency limiters
iamdanfox Feb 14, 2020
7dafc33
Print out the mean from the client-side perspective
iamdanfox Feb 14, 2020
b4d6a79
Move everything to new :simulation project
iamdanfox Feb 14, 2020
392461c
Uncaught exception handler
iamdanfox Feb 14, 2020
b4d987b
Allow filtering metrics to print
iamdanfox Feb 14, 2020
6880f42
PreferLowestUpstreamUtilization implements LimitedChannel
iamdanfox Feb 14, 2020
db4d4a3
PreferLowestUtilization is neater
iamdanfox Feb 14, 2020
7c5e843
Move the code
iamdanfox Feb 15, 2020
577e9da
More move instrumentation
iamdanfox Feb 15, 2020
d655eb8
SimulationTest is now parameterized
iamdanfox Feb 15, 2020
13af858
This is beautiful
iamdanfox Feb 15, 2020
12961a6
prefix names
iamdanfox Feb 15, 2020
e90a29a
track status codes
iamdanfox Feb 15, 2020
369aa85
Track percentage success
iamdanfox Feb 15, 2020
5dcd96a
Random tiebreaking
iamdanfox Feb 15, 2020
481d3e8
linearResponseTime
iamdanfox Feb 15, 2020
d9c94ce
Nicer png dumping
iamdanfox Feb 15, 2020
72f7e9e
refactor PreferLowest to take LimitedChannels
iamdanfox Feb 15, 2020
543e201
PreferLowest takes limited channels
iamdanfox Feb 15, 2020
84a56c5
Combo charts
iamdanfox Feb 15, 2020
264be81
Check in some pngs
iamdanfox Feb 15, 2020
b580e81
Return typed results from benchmark
iamdanfox Feb 15, 2020
6e0b7b2
Charts include som key stats
iamdanfox Feb 15, 2020
d1171c4
Blacklisting channel behaves weirdly when the floodgates re-open
iamdanfox Feb 15, 2020
7b256ca
Blacklisting channel is drastically better
iamdanfox Feb 16, 2020
a009747
emphasise three states
iamdanfox Feb 16, 2020
36b0974
update graphs & pass static analysis
iamdanfox Feb 16, 2020
3c949f0
fast_500s_then_revert initial graphs
iamdanfox Feb 16, 2020
c418523
Blacklist host after receiving a 500
iamdanfox Feb 16, 2020
4220edf
drastic_slowdown graphs
iamdanfox Feb 16, 2020
ff87fbc
Needless generic
iamdanfox Feb 16, 2020
2364460
RetryingChannel retries 503 and 500s
iamdanfox Feb 16, 2020
8cafb90
PreferLowestUtilization was counting wrong!
iamdanfox Feb 16, 2020
2ad087d
Maybe caffeine iteration isn't deterministic?
iamdanfox Feb 16, 2020
31f7a9e
new all_nodes_500 case
iamdanfox Feb 16, 2020
49cf31c
Try QueuedChannel on LOWEST_UTILIZATION
iamdanfox Feb 16, 2020
367bf60
Trivial randomness move
iamdanfox Feb 16, 2020
a9c0547
Lossless metrics
iamdanfox Feb 16, 2020
42f6339
Delete unnecessary abstraction
iamdanfox Feb 16, 2020
128051b
Benchmark has smarter finish conditions
iamdanfox Feb 16, 2020
c164cfa
No change
iamdanfox Feb 16, 2020
6cb43b3
Use stream internally
iamdanfox Feb 16, 2020
5ce1e84
Way nicer API
iamdanfox Feb 16, 2020
be42176
New black_hole scenario
iamdanfox Feb 16, 2020
67a7d75
Use time to bound simulations
iamdanfox Feb 16, 2020
7a18393
Dial up simplest
iamdanfox Feb 16, 2020
d1fa9ad
SimulationServer can have multiple endpoints
iamdanfox Feb 16, 2020
f3c94dd
New scenario: one_endpoint_dies_on_each_server
iamdanfox Feb 16, 2020
11d1162
Simpler API
iamdanfox Feb 16, 2020
32f77fe
Per-endpoint metrics
iamdanfox Feb 16, 2020
5571cc0
Randomize endpoint calls
iamdanfox Feb 16, 2020
821c637
static analysis
iamdanfox Feb 16, 2020
aeda3ed
Merge remote-tracking branch 'origin/develop' into dfox/simulation
iamdanfox Feb 16, 2020
1821e6d
Per-endpoint blacklisting!
iamdanfox Feb 16, 2020
c72ac47
Don't need composed thingy anymore
iamdanfox Feb 16, 2020
994340d
Track cumulative server time
iamdanfox Feb 17, 2020
941bc58
Move all pngs to src/test/resources
iamdanfox Feb 17, 2020
31eed09
Sweet aggregate txt report
iamdanfox Feb 17, 2020
2cabe21
cute markers
iamdanfox Feb 17, 2020
de5fa01
SimulationTest ready to live reload
iamdanfox Feb 17, 2020
dde229a
new scenario: live_reloading
iamdanfox Feb 17, 2020
199e585
Tidying
iamdanfox Feb 17, 2020
2565396
Extra fancy failure threshold
iamdanfox Feb 17, 2020
562f7b2
Debugging the spike
iamdanfox Feb 17, 2020
f323f14
Merge remote-tracking branch 'origin/develop' into dfox/simulation
iamdanfox Feb 17, 2020
a2955fa
static analsysi
iamdanfox Feb 17, 2020
2f954ff
Revert RetryingChannel
iamdanfox Feb 17, 2020
54c5646
Revert BlacklistingChannel
iamdanfox Feb 17, 2020
5ffefea
Delete LOWEST_UTILIZATION for now
iamdanfox Feb 17, 2020
dc43ad4
SimpleLimiter -> Limiter for now
iamdanfox Feb 17, 2020
12a24f1
Combine RefreshingChannel file
iamdanfox Feb 17, 2020
3468b5a
Merge remote-tracking branch 'origin/develop' into dfox/simulation
iamdanfox Feb 17, 2020
90889ee
Supplier<Long> -> Ticker
iamdanfox Feb 17, 2020
cf3acae
use longs everywhere
iamdanfox Feb 17, 2020
888242f
always debug log
iamdanfox Feb 17, 2020
765b6b5
Dead code
iamdanfox Feb 17, 2020
28d9b4b
Improve comment
iamdanfox Feb 17, 2020
c445cd5
Don't clobber X-B3-TraceId
iamdanfox Feb 17, 2020
19ee73a
Extra javadoc & less dead code
iamdanfox Feb 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,3 @@ __init__.pyc
.cache/
.ipynb_checkpoints/
.vscode/


1 change: 1 addition & 0 deletions dialogue-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
testCompile 'org.mockito:mockito-core'
testRuntimeOnly 'org.slf4j:slf4j-simple'

annotationProcessor 'org.immutables:value'
compile 'org.immutables:value::annotations'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,24 @@
*/
final class ConcurrencyLimitedChannel implements LimitedChannel {
private static final Void NO_CONTEXT = null;
private static final Supplier<Long> SYSTEM_NANOTIME = System::nanoTime;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LongSupplier or a custom interface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out the netflix builder requires a Supplier<Long>, so I've used the same caffeine Ticker interface we use everywhere else.


private final Channel delegate;
private final LoadingCache<Endpoint, Limiter<Void>> limiters;
private final LoadingCache<Endpoint, SimpleLimiter<Void>> limiters;

@VisibleForTesting
ConcurrencyLimitedChannel(Channel delegate, Supplier<Limiter<Void>> limiterSupplier) {
ConcurrencyLimitedChannel(Channel delegate, Supplier<SimpleLimiter<Void>> limiterSupplier) {
iamdanfox marked this conversation as resolved.
Show resolved Hide resolved
this.delegate = delegate;
this.limiters =
Caffeine.newBuilder().expireAfterAccess(Duration.ofMinutes(5)).build(key -> limiterSupplier.get());
}

static ConcurrencyLimitedChannel create(Channel delegate) {
return new ConcurrencyLimitedChannel(delegate, ConcurrencyLimitedChannel::createLimiter);
return new ConcurrencyLimitedChannel(delegate, () -> ConcurrencyLimitedChannel.createLimiter(SYSTEM_NANOTIME));
}

private static Limiter<Void> createLimiter() {
@VisibleForTesting
static SimpleLimiter<Void> createLimiter(Supplier<Long> nanoTimeClock) {
AIMDLimit aimdLimit = AIMDLimit.newBuilder()
// Explicitly set values to prevent library changes from breaking us
.initialLimit(20)
Expand All @@ -68,13 +70,15 @@ private static Limiter<Void> createLimiter() {
.timeout(Long.MAX_VALUE, TimeUnit.DAYS)
.build();
return SimpleLimiter.newBuilder()
.clock(nanoTimeClock)
.limit(WindowedLimit.newBuilder().build(aimdLimit))
.build();
}

@Override
public Optional<ListenableFuture<Response>> maybeExecute(Endpoint endpoint, Request request) {
return limiters.get(endpoint).acquire(NO_CONTEXT).map(listener ->
SimpleLimiter<Void> limiter = limiters.get(endpoint);
return limiter.acquire(NO_CONTEXT).map(listener ->
DialogueFutures.addDirectCallback(delegate.execute(endpoint, request), new LimiterCallback(listener)));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* (c) Copyright 2020 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.dialogue.core;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.palantir.conjure.java.api.config.service.ServicesConfigBlock;
import com.palantir.dialogue.Channel;
import com.palantir.dialogue.Endpoint;
import com.palantir.dialogue.Request;
import com.palantir.dialogue.Response;
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
import java.util.Optional;
import java.util.function.Supplier;

final class GenericRefreshingChannel implements Channel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split this change to a separate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled out here: #352

private final Supplier<Channel> channelSupplier;

private GenericRefreshingChannel(Supplier<Channel> channelSupplier) {
this.channelSupplier = channelSupplier;
}

/**
* Returns a refreshing {@link Channel} for a service identified by {@code service} in
* {@link ServicesConfigBlock#services()}.
*/
public static <T> Channel create(Supplier<T> confSupplier, ChannelFactory<T> channelFactory) {
Supplier<Channel> channelSupplier = new MemoizingComposingSupplier<>(confSupplier, conf -> {
return channelFactory.create(conf).orElse(AlwaysThrowingChannel.INSTANCE);
});
return new GenericRefreshingChannel(channelSupplier);
}

@Override
public ListenableFuture<Response> execute(Endpoint endpoint, Request request) {
return channelSupplier.get().execute(endpoint, request);
}

private enum AlwaysThrowingChannel implements Channel {
INSTANCE;

@Override
public ListenableFuture<Response> execute(Endpoint _endpoint, Request _request) {
return Futures.immediateFailedFuture(new SafeIllegalStateException("Service not configured"));
}
}

public interface ChannelFactory<T> {
Optional<Channel> create(T conf);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.palantir.dialogue.Endpoint;
import com.palantir.dialogue.Request;
import com.palantir.dialogue.Response;
import com.palantir.logsafe.SafeArg;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
Expand All @@ -37,6 +38,8 @@
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import org.immutables.value.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A {@link Channel} that queues requests while the underlying {@link LimitedChannel} is unable to accept any new
Expand All @@ -57,7 +60,7 @@
* TODO(jellis): record metrics for queue sizes, num requests in flight, time spent in queue, etc.
*/
final class QueuedChannel implements Channel {

private static final Logger log = LoggerFactory.getLogger(QueuedChannel.class);
private static final Executor DIRECT = MoreExecutors.directExecutor();

private final BlockingDeque<DeferredCall> queuedCalls;
Expand Down Expand Up @@ -114,8 +117,12 @@ private void onCompletion() {
* Try to schedule as many tasks as possible. Called when requests are submitted and when they complete.
*/
private void schedule() {
int numScheduled = 0;
while (scheduleNextTask()) {
// Do nothing
numScheduled++;
}
if (numScheduled > 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (numScheduled > 1) {
if (log.isDebugEnabled()) {

zero and one are also helpful data points imo.

log.debug("Scheduled {} at the same time", SafeArg.of("numScheduled", numScheduled));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import com.google.common.util.concurrent.SettableFuture;
import com.netflix.concurrency.limits.Limiter;
import com.netflix.concurrency.limits.limiter.SimpleLimiter;
import com.palantir.dialogue.Channel;
import com.palantir.dialogue.Endpoint;
import com.palantir.dialogue.Request;
Expand All @@ -47,7 +48,7 @@ public class ConcurrencyLimitedChannelTest {
private Channel delegate;

@Mock
private Limiter<Void> limiter;
private SimpleLimiter<Void> limiter;

@Mock
private Limiter.Listener listener;
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ include 'dialogue-java-client'
include 'dialogue-okhttp-client'
include 'dialogue-serde'
include 'dialogue-target'
include 'simulation'
24 changes: 24 additions & 0 deletions simulation/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
versionsLock {
testProject()
}

dependencies {
implementation project(':dialogue-core')
implementation project(':dialogue-target')
implementation 'org.jmock:jmock'
implementation 'org.knowm.xchart:xchart'

testImplementation 'com.palantir.safe-logging:preconditions-assertj'
testImplementation 'junit:junit'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'

testRuntimeOnly 'org.slf4j:slf4j-simple'

annotationProcessor 'org.immutables:value'
compile 'org.immutables:value::annotations'
}

tasks.withType(JavaCompile) {
options.errorprone.errorproneArgs += '-Xep:Slf4jLogsafeArgs:OFF'
}
Loading