Skip to content

Commit 32aea11

Browse files
KSemenenkoclaude
andcommitted
Repair the Linux benchmark and CI contracts the first complete runs exposed
Benchmarks run 40046956549 was the first to get past the compiled-bundle import; every remaining failure was a Linux- or CI-only contract defect: - KeyLoad cells: the workload client re-verified each server process by an exact Process.StartTime match against the bootstrap's observation. Linux .NET caches a boot-time estimate once per observing process, so two processes disagree by the coarse clock granularity. Identity now pairs the PID, executable path and compiled-file hashes with a start time inside a configurable observer window (TestExecutionOptions.ProcessStartTimeObserverWindow, one second by default); a focused unit test exercises both sides. - Neo4j cells: the runtime-import session-id pattern rejected ids with digits. - PostgreSQL, Redis, Qdrant, RabbitMQ and KurrentDB finalization: the resource-evidence validator expected runtimeMaxOutputBytes while the C# observation policy writes maxOutputBytes; the validator follows the producer. - Redis three nodes: replicas name the primary by its container-network address, so the client-visible primary endpoint can never match. Both replica proofs now follow the primary's replication id. - SurrealDB: the image runs as nonroot and its declared /data volume root is root-owned; the session volume now lives under the image user's home. - OpenSearch cells: a synchronous _update_by_query over one million documents returns one TASK_RESOURCE_USAGE response header per scroll batch (about 230 KB), above the .NET 64 KB default; the document HTTP client declares DocumentClientMaxResponseHeadersKilobytes (1024) as recorded policy. - MongoDB: ubuntu-latest moved to Ubuntu 26.04 with a 7.0 kernel that the pinned server refuses; Benchmarks jobs declare ubuntu-24.04 like Build and Tests, and the serialization job identity follows that label. Build and Tests ran the unit suite with unlimited parallelism on four processors (234 of 3718 failed, about 105 of them load cancellations) and stopped at the first failing suite. Each suite is now its own step that runs after a failing one, unit parallelism is bounded to the runner's processors, and the job timeout covers all four suites. Docs, the closed unit test inventory and status.json record the current contracts; no figures are published. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 0613351 commit 32aea11

25 files changed

Lines changed: 185 additions & 72 deletions

File tree

‎.github/workflows/benchmarks.yml‎

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
comparison-runtime:
2020
name: Prepare comparison runtime
2121
if: github.repository == 'managedcode/KeyLoad' && github.ref == 'refs/heads/main'
22-
runs-on: ubuntu-latest
22+
runs-on: ubuntu-24.04
2323
timeout-minutes: 150
2424
outputs:
2525
databases: ${{ steps.plan.outputs.database_matrices }}
@@ -109,7 +109,7 @@ jobs:
109109
name: ${{ matrix.jobName }}
110110
if: (github.event_name == 'push' || inputs.mode == 'full')
111111
needs: [comparison-runtime]
112-
runs-on: ubuntu-latest
112+
runs-on: ubuntu-24.04
113113
timeout-minutes: 240
114114
strategy:
115115
fail-fast: false
@@ -156,7 +156,7 @@ jobs:
156156
name: ${{ matrix.jobName }}
157157
if: (github.event_name == 'push' || inputs.mode == 'full')
158158
needs: [comparison-runtime]
159-
runs-on: ubuntu-latest
159+
runs-on: ubuntu-24.04
160160
timeout-minutes: 240
161161
strategy:
162162
fail-fast: false
@@ -167,7 +167,7 @@ jobs:
167167
name: ${{ matrix.jobName }}
168168
if: (github.event_name == 'push' || inputs.mode == 'full')
169169
needs: [comparison-runtime]
170-
runs-on: ubuntu-latest
170+
runs-on: ubuntu-24.04
171171
timeout-minutes: 240
172172
strategy:
173173
fail-fast: false
@@ -178,7 +178,7 @@ jobs:
178178
name: ${{ matrix.jobName }}
179179
if: (github.event_name == 'push' || inputs.mode == 'full')
180180
needs: [comparison-runtime]
181-
runs-on: ubuntu-latest
181+
runs-on: ubuntu-24.04
182182
timeout-minutes: 240
183183
strategy:
184184
fail-fast: false
@@ -189,7 +189,7 @@ jobs:
189189
name: ${{ matrix.jobName }}
190190
if: (github.event_name == 'push' || inputs.mode == 'full')
191191
needs: [comparison-runtime]
192-
runs-on: ubuntu-latest
192+
runs-on: ubuntu-24.04
193193
timeout-minutes: 240
194194
strategy:
195195
fail-fast: false
@@ -200,7 +200,7 @@ jobs:
200200
name: ${{ matrix.jobName }}
201201
if: (github.event_name == 'push' || inputs.mode == 'full')
202202
needs: [comparison-runtime]
203-
runs-on: ubuntu-latest
203+
runs-on: ubuntu-24.04
204204
timeout-minutes: 240
205205
strategy:
206206
fail-fast: false
@@ -211,7 +211,7 @@ jobs:
211211
name: ${{ matrix.jobName }}
212212
if: (github.event_name == 'push' || inputs.mode == 'full')
213213
needs: [comparison-runtime]
214-
runs-on: ubuntu-latest
214+
runs-on: ubuntu-24.04
215215
timeout-minutes: 240
216216
strategy:
217217
fail-fast: false
@@ -222,7 +222,7 @@ jobs:
222222
name: ${{ matrix.jobName }}
223223
if: (github.event_name == 'push' || inputs.mode == 'full')
224224
needs: [comparison-runtime]
225-
runs-on: ubuntu-latest
225+
runs-on: ubuntu-24.04
226226
timeout-minutes: 240
227227
strategy:
228228
fail-fast: false
@@ -233,7 +233,7 @@ jobs:
233233
name: ${{ matrix.jobName }}
234234
if: (github.event_name == 'push' || inputs.mode == 'full')
235235
needs: [comparison-runtime]
236-
runs-on: ubuntu-latest
236+
runs-on: ubuntu-24.04
237237
timeout-minutes: 240
238238
strategy:
239239
fail-fast: false
@@ -244,7 +244,7 @@ jobs:
244244
name: ${{ matrix.jobName }}
245245
if: (github.event_name == 'push' || inputs.mode == 'full')
246246
needs: [comparison-runtime]
247-
runs-on: ubuntu-latest
247+
runs-on: ubuntu-24.04
248248
timeout-minutes: 240
249249
strategy:
250250
fail-fast: false
@@ -255,7 +255,7 @@ jobs:
255255
name: ${{ matrix.jobName }}
256256
if: (github.event_name == 'push' || inputs.mode == 'full')
257257
needs: [comparison-runtime]
258-
runs-on: ubuntu-latest
258+
runs-on: ubuntu-24.04
259259
timeout-minutes: 240
260260
strategy:
261261
fail-fast: false
@@ -266,7 +266,7 @@ jobs:
266266
name: KeyLoad functional CLUSTER million-record ingestion
267267
if: (github.event_name == 'push' || inputs.mode == 'full')
268268
needs: [comparison-runtime]
269-
runs-on: ubuntu-latest
269+
runs-on: ubuntu-24.04
270270
timeout-minutes: 180
271271
steps:
272272
- name: Download source code
@@ -292,7 +292,7 @@ jobs:
292292
name: Combine benchmark results
293293
needs: [comparison-runtime, comparison-keyload, comparison-postgresql, comparison-qdrant, comparison-rabbitmq, comparison-redis, comparison-neo4j, comparison-mongodb, comparison-opensearch, comparison-kurrentdb, comparison-surrealdb, comparison-helixdb, keyload-functional-heavy-load]
294294
if: ${{ always() && !cancelled() && (github.event_name == 'push' || inputs.mode == 'full') && needs.comparison-runtime.result == 'success' }}
295-
runs-on: ubuntu-latest
295+
runs-on: ubuntu-24.04
296296
timeout-minutes: 150
297297
env:
298298
GH_TOKEN: ${{ github.token }}
@@ -333,7 +333,7 @@ jobs:
333333
!contains(needs.*.result, 'skipped') &&
334334
github.repository == 'managedcode/KeyLoad' && github.event.repository.id == 477801965 &&
335335
github.ref == 'refs/heads/main'
336-
runs-on: ubuntu-latest
336+
runs-on: ubuntu-24.04
337337
timeout-minutes: 5
338338
permissions: {actions: write}
339339
env:

‎.github/workflows/build-and-tests.yml‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ permissions:
99
jobs:
1010
build-and-test:
1111
runs-on: ubuntu-24.04
12-
timeout-minutes: 120
12+
timeout-minutes: 180
1313
steps:
1414
- name: Download source code
1515
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -26,9 +26,14 @@ jobs:
2626
- name: Install administration browser
2727
shell: pwsh
2828
run: tests/KeyLoad.IntegrationTests/bin/Debug/net10.0/playwright.ps1 install --with-deps chromium
29-
- name: Test
30-
run: |
31-
dotnet test --project tests/KeyLoad.Analyzers.Tests/KeyLoad.Analyzers.Tests.csproj --no-build --output Detailed
32-
dotnet test --project tests/KeyLoad.UnitTests/KeyLoad.UnitTests.csproj --no-build --output Detailed
33-
dotnet test --project tests/KeyLoad.RecoveryTests/KeyLoad.RecoveryTests.csproj --no-build --output Detailed
34-
dotnet test --project tests/KeyLoad.IntegrationTests/KeyLoad.IntegrationTests.csproj --no-build --output Detailed --treenode-filter '/*/*/*/*[Category!=HeavyLoad]'
29+
- name: Test code analyzers
30+
run: dotnet test --project tests/KeyLoad.Analyzers.Tests/KeyLoad.Analyzers.Tests.csproj --no-build --output Detailed
31+
- name: Test units
32+
if: ${{ !cancelled() }}
33+
run: dotnet test --project tests/KeyLoad.UnitTests/KeyLoad.UnitTests.csproj --no-build --output Detailed --maximum-parallel-tests "$(nproc)"
34+
- name: Test process recovery
35+
if: ${{ !cancelled() }}
36+
run: dotnet test --project tests/KeyLoad.RecoveryTests/KeyLoad.RecoveryTests.csproj --no-build --output Detailed
37+
- name: Test Aspire topology
38+
if: ${{ !cancelled() }}
39+
run: dotnet test --project tests/KeyLoad.IntegrationTests/KeyLoad.IntegrationTests.csproj --no-build --output Detailed --treenode-filter '/*/*/*/*[Category!=HeavyLoad]'

‎benchmarks/KeyLoad.ComparisonHost/Configuration/execution.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"OpenSearchHealthWaitTimeoutSeconds": 60,
1515
"CleanupTimeout": "00:00:30",
1616
"DocumentClientMaxConnections": 1,
17+
"DocumentClientMaxResponseHeadersKilobytes": 1024,
1718
"DocumentVerificationConnectionReserve": 1,
1819
"DocumentCleanupTimeout": "00:20:00",
1920
"IndexBuildTimeout": "00:20:00",

‎benchmarks/KeyLoad.ComparisonHost/Session/Configuration/BenchmarkSessionProcessEvidence.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Immutable;
22
using System.Text.Json;
3+
using KeyLoad.AppHost.Hosting;
34
using KeyLoad.AppHost.TestInfrastructure;
45
using KeyLoad.Comparisons;
56
using KeyLoad.Diagnostics.ResourceExecution;
@@ -42,9 +43,10 @@ internal static BenchmarkSessionProcesses Read(IConfiguration configuration, Ben
4243
{
4344
throw new InvalidDataException(ServerTopologyMismatch);
4445
}
46+
var execution = AppHostOptionsRegistration.BindTestExecution(configuration);
4547
foreach (var server in servers)
4648
{
47-
ProjectProcessEvidence.Verify(server);
49+
ProjectProcessEvidence.Verify(server, execution);
4850
}
4951
return new(client, servers);
5052
}

‎benchmarks/KeyLoad.Comparisons/Configuration/ComparisonExecutionOptions.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ namespace KeyLoad.Comparisons;
33
public sealed partial class ComparisonExecutionOptions
44
{
55
internal const int RequiredDocumentClientConnections = 1;
6+
internal const int RequiredDocumentClientResponseHeadersKilobytes = 1024;
67
private const int RequiredDocumentCleanupMinutes = 20;
78
internal static readonly TimeSpan RequiredDocumentCleanupTimeout = TimeSpan.FromMinutes(RequiredDocumentCleanupMinutes);
89
/// <summary>Additional execution primary connection reserved for untimed final verification.</summary>
910
public int DocumentVerificationConnectionReserve { get; set; } = RequiredDocumentClientConnections;
1011
/// <summary>One actual connection per independently admitted HTTP document client.</summary>
1112
public int DocumentClientMaxConnections { get; set; } = RequiredDocumentClientConnections;
13+
/// <summary>Response header bound of the document HTTP client; OpenSearch answers a synchronous update-by-query with one TASK_RESOURCE_USAGE header per scroll batch, about 230 KB for one million documents.</summary>
14+
public int DocumentClientMaxResponseHeadersKilobytes { get; set; } = RequiredDocumentClientResponseHeadersKilobytes;
1215
/// <summary>Joined cleanup bound for a complete million-record execution document namespace.</summary>
1316
public TimeSpan DocumentCleanupTimeout { get; set; } = RequiredDocumentCleanupTimeout;
1417
}

‎benchmarks/KeyLoad.Comparisons/Configuration/DocumentPolicy.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal static class DocumentPolicy
99
internal static void Validate(ComparisonExecutionOptions options)
1010
{
1111
if (options.DocumentClientMaxConnections != ComparisonExecutionOptions.RequiredDocumentClientConnections
12+
|| options.DocumentClientMaxResponseHeadersKilobytes != ComparisonExecutionOptions.RequiredDocumentClientResponseHeadersKilobytes
1213
|| options.DocumentVerificationConnectionReserve != ComparisonExecutionOptions.RequiredDocumentClientConnections
1314
|| options.DocumentCleanupTimeout != ComparisonExecutionOptions.RequiredDocumentCleanupTimeout)
1415
{
@@ -18,6 +19,7 @@ internal static void Validate(ComparisonExecutionOptions options)
1819
internal static void Record(ComparisonExecutionOptions options, IDictionary<string, string> parameters)
1920
{
2021
parameters[nameof(options.DocumentClientMaxConnections)] = options.DocumentClientMaxConnections.ToString(CultureInfo.InvariantCulture);
22+
parameters[nameof(options.DocumentClientMaxResponseHeadersKilobytes)] = options.DocumentClientMaxResponseHeadersKilobytes.ToString(CultureInfo.InvariantCulture);
2123
parameters[nameof(options.DocumentVerificationConnectionReserve)] = options.DocumentVerificationConnectionReserve.ToString(CultureInfo.InvariantCulture);
2224
parameters[nameof(options.DocumentCleanupTimeout)] = options.DocumentCleanupTimeout.ToString(DurationFormat, CultureInfo.InvariantCulture);
2325
}

‎benchmarks/KeyLoad.Comparisons/Documents/Lifetimes/DocumentHttpClient.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ internal HttpClient Client
1111
}
1212
internal DocumentHttpClient(HttpClient source, IOptions<ComparisonExecutionOptions> options)
1313
{
14+
var settings = ComparisonExecutionOptions.Require(options).Value;
1415
handler = new()
1516
{
16-
MaxConnectionsPerServer = ComparisonExecutionOptions.Require(options).Value.DocumentClientMaxConnections
17+
MaxConnectionsPerServer = settings.DocumentClientMaxConnections,
18+
MaxResponseHeadersLength = settings.DocumentClientMaxResponseHeadersKilobytes
1719
};
1820
try
1921
{

‎benchmarks/KeyLoad.Comparisons/Documents/Validation/RedisDocumentCopyProof.cs‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ internal static async Task<ClusterEvidence> VerifyAsync(ConnectionMultiplexer pr
3636
}
3737

3838
await RequireAofAsync(primaryServer, CommandFlags.DemandMaster, deadline.Token).ConfigureAwait(false);
39-
await JoinAsync(RedisReplicaProof.VerifyWorkerPrimaryAsync(primary, topology, CancellationToken.None), deadline.Token).ConfigureAwait(false);
39+
var replicationId = await JoinAsync(RedisReplicaProof.VerifyWorkerPrimaryAsync(primary, topology, CancellationToken.None), deadline.Token).ConfigureAwait(false);
4040
var database = primary.GetDatabase();
4141
var fenced = false;
4242
try
4343
{
44-
var identities = await ObserveReplicasAsync(primaryEndpoint, primaryIdentity, endpoints, replicas, deadline.Token).ConfigureAwait(false);
44+
var identities = await ObserveReplicasAsync(primaryEndpoint, replicationId, primaryIdentity, endpoints, replicas, deadline.Token).ConfigureAwait(false);
4545
RedisNodeIdentity.RequireUniqueVersionedSet(identities, primaryIdentity, copies);
4646
var evidence = replicas.Count == NoReplicas ? RedisNodeIdentity.SingleEvidence(primaryIdentity, SingleState)
4747
: RedisNodeIdentity.ReplicatedEvidence(primaryIdentity, identities.Skip(PrimaryIdentityCount).ToArray(), ReplicatedState);
@@ -57,9 +57,8 @@ internal static async Task<ClusterEvidence> VerifyAsync(ConnectionMultiplexer pr
5757
fenced = true;
5858
await RequireAllFsyncAsync(database, copies - PrimaryCopyCount, clientId, lifecycleOptions.Value.RedisReceiptTimeout, deadline.Token).ConfigureAwait(false);
5959
var observations = evidence.Observations.ToList();
60-
await VerifyReplicaReadbacksAsync(replicas, identities, primaryEndpoint, schedule, readReplica, observations, token).ConfigureAwait(false);
61-
await JoinAsync(RedisReplicaProof.VerifyWorkerPrimaryAsync(primary, topology, CancellationToken.None), token).ConfigureAwait(false);
62-
RedisNodeIdentity.RequireUnchanged(await JoinAsync(RedisNodeIdentity.ReadAsync(primaryServer, CommandFlags.DemandMaster, CancellationToken.None), token).ConfigureAwait(false), primaryIdentity);
60+
await VerifyReplicaReadbacksAsync(replicas, identities, replicationId, schedule, readReplica, observations, token).ConfigureAwait(false);
61+
await RequireFinalPrimaryAsync(primary, primaryServer, topology, replicationId, primaryIdentity, token).ConfigureAwait(false);
6362
return evidence with
6463
{
6564
Observations = observations.Append(string.Format(System.Globalization.CultureInfo.InvariantCulture, FinalFsyncObservationFormat, schedule.Final, copies - PrimaryCopyCount)).ToImmutableArray()
@@ -83,7 +82,16 @@ internal static async Task<ClusterEvidence> VerifyAsync(ConnectionMultiplexer pr
8382
}
8483
}
8584
}
86-
private static async Task<List<RedisNodeIdentity>> ObserveReplicasAsync(System.Net.EndPoint primaryEndpoint,
85+
private static async Task RequireFinalPrimaryAsync(ConnectionMultiplexer primary, IServer primaryServer, ComparisonTopology topology,
86+
string replicationId, RedisNodeIdentity primaryIdentity, CancellationToken token)
87+
{
88+
if (await JoinAsync(RedisReplicaProof.VerifyWorkerPrimaryAsync(primary, topology, CancellationToken.None), token).ConfigureAwait(false) != replicationId)
89+
{
90+
throw new ComparisonFailureException(Failure);
91+
}
92+
RedisNodeIdentity.RequireUnchanged(await JoinAsync(RedisNodeIdentity.ReadAsync(primaryServer, CommandFlags.DemandMaster, CancellationToken.None), token).ConfigureAwait(false), primaryIdentity);
93+
}
94+
private static async Task<List<RedisNodeIdentity>> ObserveReplicasAsync(System.Net.EndPoint primaryEndpoint, string replicationId,
8795
RedisNodeIdentity primaryIdentity, string[] endpoints, List<ConnectionMultiplexer> replicas, CancellationToken token)
8896
{
8997
var identities = new List<RedisNodeIdentity>(endpoints.Length + PrimaryIdentityCount) { primaryIdentity };
@@ -101,14 +109,14 @@ private static async Task<List<RedisNodeIdentity>> ObserveReplicasAsync(System.N
101109
}
102110

103111
var server = replica.GetServer(actualEndpoint);
104-
await RequireReplicaAsync(server, primaryEndpoint, token).ConfigureAwait(false);
112+
await RequireReplicaAsync(server, replicationId, token).ConfigureAwait(false);
105113
await RequireAofAsync(server, CommandFlags.DemandReplica, token).ConfigureAwait(false);
106114
identities.Add(await JoinAsync(RedisNodeIdentity.ReadAsync(server, CommandFlags.DemandReplica, CancellationToken.None), token).ConfigureAwait(false));
107115
}
108116
return identities;
109117
}
110118
private static async Task VerifyReplicaReadbacksAsync(List<ConnectionMultiplexer> replicas, List<RedisNodeIdentity> identities,
111-
System.Net.EndPoint primaryEndpoint, DocumentComparisonSchedule schedule,
119+
string replicationId, DocumentComparisonSchedule schedule,
112120
Func<ConnectionMultiplexer, CancellationToken, IAsyncEnumerable<FoundDocument>> readReplica,
113121
List<string> observations, CancellationToken token)
114122
{
@@ -117,7 +125,7 @@ private static async Task VerifyReplicaReadbacksAsync(List<ConnectionMultiplexer
117125
var readback = await DocumentComparisonOracle.VerifyAsync(readReplica(replicas[index], token), schedule.FinalDocuments(), token).ConfigureAwait(false);
118126
observations.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, FinalReplicaObservationFormat, identities[index + PrimaryIdentityCount].RunId, readback.Records, readback.ActualSha256));
119127
var server = replicas[index].GetServer(RedisProtocol.ConfiguredEndpoint(replicas[index], Failure));
120-
await RequireReplicaAsync(server, primaryEndpoint, token).ConfigureAwait(false);
128+
await RequireReplicaAsync(server, replicationId, token).ConfigureAwait(false);
121129
RedisNodeIdentity.RequireUnchanged(await JoinAsync(RedisNodeIdentity.ReadAsync(server, CommandFlags.DemandReplica, CancellationToken.None), token).ConfigureAwait(false), identities[index + PrimaryIdentityCount]);
122130
}
123131
}
@@ -144,15 +152,17 @@ private static async Task RequireAofAsync(IServer server, CommandFlags flags, Ca
144152
throw new ComparisonFailureException(Failure);
145153
}
146154
}
147-
private static async Task RequireReplicaAsync(IServer server, System.Net.EndPoint primary, CancellationToken token)
155+
// Replicas name the primary by its container-network address; the primary's replication id is the follow oracle.
156+
private static async Task RequireReplicaAsync(IServer server, string replicationId, CancellationToken token)
148157
{
149158
var info = await JoinAsync(RedisProtocol.ReadInfoAsync(server, RedisProtocol.ReplicationSection, CommandFlags.DemandReplica, CancellationToken.None), token).ConfigureAwait(false);
150159
var role = await JoinAsync(RedisProtocol.ReadRoleAsync(server, CommandFlags.DemandReplica, CancellationToken.None), token).ConfigureAwait(false);
151160
var host = info.GetValueOrDefault(RedisProtocol.MasterHostField);
152161
var port = RedisProtocol.ParseInteger(info, RedisProtocol.MasterPortField);
153162
if (info.GetValueOrDefault(RedisProtocol.RoleField) != RedisProtocol.ReplicaRole
154163
|| info.GetValueOrDefault(RedisProtocol.LinkField) != RedisProtocol.LinkUp || host is null
155-
|| !RedisProtocol.EndpointMatches(primary, host, port) || role.Length < RedisProtocol.RoleReplicaResponseLength
164+
|| !string.Equals(info.GetValueOrDefault(RedisProtocol.ReplicationIdField), replicationId, StringComparison.Ordinal)
165+
|| role.Length < RedisProtocol.RoleReplicaResponseLength
156166
|| role[RedisProtocol.RoleNameIndex].ToString() != RedisProtocol.ReplicaRole
157167
|| !StringComparer.OrdinalIgnoreCase.Equals(role[RedisProtocol.RoleHostIndex].ToString(), host) || role[RedisProtocol.RolePortIndex].ToString() != port.ToString(System.Globalization.CultureInfo.InvariantCulture))
158168
{
@@ -182,11 +192,7 @@ internal static void RequireFsyncReceipt(long[] counts, int replicas)
182192
throw new ComparisonFailureException(Failure);
183193
}
184194
}
185-
private static async Task JoinAsync(Task original, CancellationToken token)
186-
{
187-
await original.ConfigureAwait(false);
188-
token.ThrowIfCancellationRequested();
189-
}
195+
190196
private static async Task<T> JoinAsync<T>(Task<T> original, CancellationToken token)
191197
{
192198
var result = await original.ConfigureAwait(false);

0 commit comments

Comments
 (0)