Skip to content

Commit fabff69

Browse files
committed
Align isolated Redis readiness with its native TCP bootstrap
1 parent 3aeb8cb commit fabff69

6 files changed

Lines changed: 168 additions & 5 deletions

File tree

‎docs/ADR/ADR-080-benchmark-failure-isolation.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ upload; cancellation/timeouts that prevent artifacts remain explicit blockers.
5151
hosts/URLs/environment overrides. Root integrates this port-isolation follow-up,
5252
reviews the unchanged production callers and reruns the10 original scenarios
5353
through freshly built Aspire/TUnit before the next scoped checkpoint.
54+
Under FAIL-PREP-REDIS (REQ/AC-BC-FAIL-009), preserve the existing isolated
55+
password-authenticated RESP/TCP bootstrap and native6379 replication. Use the
56+
official resource-local `WithoutHttpsCertificate()` call, with compiler opt-in
57+
ASPIRECERTIFICATES001 limited to the native API and direct annotation checks.
58+
This prevents the pinned Aspire13.6 BeforeStart callback from changing the
59+
endpoint/discovered connection to TLS while the owned bootstrap stays plain.
60+
Native-failures agent owns only IsolatedRedisResources.cs, the existing Redis
61+
resource-model regression and actual RedisNativeReadinessRegression transport
62+
checks. Root owns docs, integration, build/format/Aspire verification, scoped
63+
checkpoint and genuine new-source1/2/3-node preflight/publication evidence.
64+
Resource tests require explicit certificate opt-out, fixed native port/scheme
65+
and authenticated non-TLS client configuration; real startup tests also retain
66+
PONG/write/direct-copy/cancellation and AOF/ack contracts. Never remove health
67+
checks, alter shared TLS/trust settings, disable certificate validation or
68+
change another engine's resources. Rollback is the scoped call/check removal
69+
and honestly restores the known readiness mismatch; no storage migration.
5470
Under FAIL-PREP-KURRENT, `KurrentTarget.InitializeAsync` constructs the actual
5571
SDK writer only after `KurrentClusterVerifier.VerifyAsync` proves membership.
5672
Three SDK/resource-model tests belong to ComparisonTests and run in common

‎docs/Features/BenchmarkComparisons.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ KeyLoad engine repair and concurrent series-codec work are outside this task.
691691
| REQ-BC-FAIL-006 bounded registry readiness | AC-BC-FAIL-006 each native HTTP probe has at most2s within the unchanged30s total; only settled non-aborted HTTP200 succeeds; private no-follow diagnostics remain at most121 records/64KiB and evidence-write failures propagate. Actual fixture listeners use kernel-assigned loopback ports; optional numeric port accepts only integers1..65535 before HTTP/evidence, while both production callers keep the default5000 | `ImageRegistryReadinessTests`:10 actual loopback HTTP/error/bounds cases, including rejected port inputs before I/O, plus genuine pinned Docker image export/import in GitHub |
692692
| REQ-BC-FAIL-007 Kurrent writer starts after membership | AC-BC-FAIL-007 verify all native1/2/3-member views before constructing the SDK writer; retain native DNS seeds, TLS verification, leader preference, NoStream semantics, acknowledgements, replica-copy oracle and cleanup | `IsolatedKurrentDiscoverySettingsTests`:3 actual SDK/resource-model cases; genuine Aspire-owned StreamAppend preflights for1/2/3 nodes |
693693
| REQ-BC-FAIL-008 explicit cancellation stops owned work | AC-BC-FAIL-008 workload, finalization and result upload use `!cancelled()` so ordinary failure still finalizes while cancellation stops execution/publication; `always()` cleanup retains bounded diagnostics and safely removes owned registries whose setup was cancelled | `WorkflowBenchmarkFailureTests`, unchanged canceled-job/producer rejection regressions and actual GitHub lifecycle |
694+
| REQ-BC-FAIL-009 align native Redis transport | AC-BC-FAIL-009 each isolated Redis resource uses the documented native certificate opt-out for its existing authenticated RESP/TCP bootstrap; after actual Aspire startup, primary/replica endpoints retain scheme `redis` and native target port6379 with TLS disabled. Discovered mapped host ports remain dynamic; native client settings retain a password without `ssl=true`. Preserve health checks, wait dependencies, AOF `always`, native membership, direct-copy/cancellation checks and WAIT/WAITAOF acknowledgements | actual pinned resource-model regressions plus genuine Aspire-owned native Redis 1/2/3-node preflights; complete matrix and site qualification remain mandatory |
694695

695696
[ADR-080](../ADR/ADR-080-benchmark-failure-isolation.md) owns the boundary change.
696697
Ordered task graph: FAIL-CONTRACT (root, complete) -> FAIL-SITE (site worker),
@@ -800,6 +801,28 @@ Source/module/fixture/runner and original report hashes are retained in the loca
800801
development receipt and status. Complete delivered-source GitHub publication is
801802
pending; these development results do not qualify a full cohort or website.
802803

804+
The original source6ec Redis preflights now prove a separate preparation defect:
805+
jobs111319875391/111319875411 started native Redis8.4.0 on plain TCP, but the
806+
Aspire13.6 healthcheck attempted TLS and blocked `app.StartAsync` until the60-minute
807+
case deadline. Both failed jobs finalized and uploaded null-report availability
808+
envelopes, then completed owned cleanup successfully. No workload measurements
809+
started. FAIL-PREP-REDIS uses the documented resource-local native
810+
`WithoutHttpsCertificate()` API to preserve the existing authenticated RESP/TCP
811+
contract; it does not alter a connection string by hand or disable certificate
812+
validation. Its experimental compiler opt-in is limited to that API and direct
813+
annotation verification. Root integrates the bounded benchmark-only change after
814+
the native source review, runs focused regressions and full build/format checks,
815+
then verifies genuine new-source preflights and complete publication. No Redis
816+
measurement repair or full-cohort publication is claimed before that evidence.
817+
818+
The Redis transport candidate (archive of3aeb8cb plus its scoped benchmark patch)
819+
passed full Release build with0warnings/errors, formatter and governance. Its
820+
freshly built actual pinned resource-model regressions passed3/3 through the
821+
Aspire/TUnit comparison entry point, without skips/cancellation/timeouts. The
822+
experimental opt-in is confined to the resource API and actual annotation checks.
823+
These model results do not prove Docker startup or workload execution; new-source
824+
native1/2/3 preflights and complete aggregate/site/Pages acceptance remain pending.
825+
803826
```mermaid
804827
flowchart LR
805828
Plan[Complete native cell plan] --> Jobs[Independent Aspire workloads]

‎docs/implementation/status.json‎

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,7 +3139,7 @@
31393139
"status": "in_progress",
31403140
"feature": "docs/Features/BenchmarkComparisons.md",
31413141
"decision": "docs/ADR/ADR-080-benchmark-failure-isolation.md",
3142-
"sourceStage": "authenticated_failed_cell_publication_with_native_preparation_repair_and_explicit_cancellation_cleanup",
3142+
"sourceStage": "authenticated_failed_cell_publication_with_native_registry_kurrent_redis_transport_repairs_and_explicit_cancellation_cleanup",
31433143
"baselineRun": 37154664616,
31443144
"originalFailedJob": 111299652762,
31453145
"originalDiagnosticArtifact": 11286445994,
@@ -3190,7 +3190,7 @@
31903190
"scope": "preparation and native preflight only;4096-record development workload; no scale, full cohort, winner or power-loss qualification"
31913191
}
31923192
},
3193-
"localDevelopmentVerification": "Delivered6ec Linux clean build, formatter, workflow inventory, registry10/10 and SDK/resource settings3/3 passed. Clean88e2cec snapshot passed full Release build, formatter and governance; actual full unit2821/2867 passed with46 failures, no skips/cancellation (including a registry port conflict, macOS temp-link rejections, missing Git metadata for the chunk child and an allocation assertion). Workflow cancellation3/3 and failed producer/finalizer2/2 passed through Aspire. Benchmark-only ephemeral-port follow-up freshly built clean with0warnings/errors; formatter and governance passed; actual registry10/10 normal and10/10 scalar passed through Aspire/TUnit with canonical owned TMPDIR. Complete delivered-source native/site/Pages qualification remains pending; no KeyLoad engine repair in this stage.",
3193+
"localDevelopmentVerification": "Delivered6ec Linux clean build, formatter, workflow inventory, registry10/10 and SDK/resource settings3/3 passed. Clean88e2cec snapshot passed full Release build, formatter and governance; actual full unit2821/2867 passed with46 failures, no skips/cancellation (including a registry port conflict, macOS temp-link rejections, missing Git metadata for the chunk child and an allocation assertion). Workflow cancellation3/3 and failed producer/finalizer2/2 passed through Aspire. Benchmark-only ephemeral-port follow-up freshly built clean with0warnings/errors; formatter and governance passed; actual registry10/10 normal and10/10 scalar passed through Aspire/TUnit with canonical owned TMPDIR. Complete delivered-source native/site/Pages qualification remains pending; no KeyLoad engine repair in this stage. Redis transport follow-up: fresh benchmark-only snapshot full Release0warnings/errors, formatter and governance passed; actual pinned resource models3/3 passed through Aspire/TUnit. Genuine new-source Redis1/2/3 startup/workload and complete website publication remain pending.",
31943194
"runtimeQualified": false,
31953195
"publicationQualified": false,
31963196
"keyLoadEngineChanges": false,
@@ -3277,6 +3277,82 @@
32773277
}
32783278
},
32793279
"githubQualified": false
3280+
},
3281+
"redisTransportRepair": {
3282+
"requirement": "REQ-BC-FAIL-009",
3283+
"originalSourceRevision": "6ec9233a5de963efd85fda041bc5d6d3d4b9972b",
3284+
"originalRunId": 37161833119,
3285+
"originalJobs": {
3286+
"n1": 111319875391,
3287+
"n2": 111319875411,
3288+
"n3": 111319875402
3289+
},
3290+
"provenFailurePhase": "Native Aspire startup before seeding or workload. Redis server accepted TCP6379 while native healthchecks attempted TLS; comparisons runner waited primaryhealthy until actual60min testcasecancel.",
3291+
"originalArchives": [
3292+
{
3293+
"artifactId": 11289029479,
3294+
"name": "comparison-preflight-redis-n1-point-read",
3295+
"digest": "sha256:6e963b3cc15bf8a79b3328712636ec72147c2e3e1f46e4689ddaa793ef1ac1bc"
3296+
},
3297+
{
3298+
"artifactId": 11288519787,
3299+
"name": "comparison-preflight-qualification-redis-n1-point-read",
3300+
"digest": "sha256:9bb2fab3ea9c39e9eae39d45d6ae8b9fc1dbc789b7656110002b283037807ca0"
3301+
},
3302+
{
3303+
"artifactId": 11289538105,
3304+
"name": "comparison-preflight-redis-n2-point-read",
3305+
"digest": "sha256:16fe4c4fca48dd680d1efc0986337f8c0b78a20a63ff73be9a2420b96dc46b3b"
3306+
},
3307+
{
3308+
"artifactId": 11289572992,
3309+
"name": "comparison-preflight-qualification-redis-n2-point-read",
3310+
"digest": "sha256:96627401553c71807f98036a137bab1d64b6db02224deaae4fc546991e706ce0"
3311+
},
3312+
{
3313+
"artifactId": 11289009755,
3314+
"name": "comparison-preflight-redis-n3-point-read",
3315+
"digest": "sha256:53a7c302c24004dc5f964a372893b77a7b9df63d8f5916d9c6c1a9e86d222006"
3316+
},
3317+
{
3318+
"artifactId": 11289618006,
3319+
"name": "comparison-preflight-qualification-redis-n3-point-read",
3320+
"digest": "sha256:a552fbfaf70045b1b3ae6774586892fc2f5bb3553b257d7a6fc7132814575ea8"
3321+
}
3322+
],
3323+
"originalTeardown": "all3 succeeded;failedStages empty;actual failed/null availability envelopes uploaded",
3324+
"nativeApi": "resource-local WithoutHttpsCertificate;single-call experimental opt-in;native TCP target6379 and discovered mapped host ports",
3325+
"preserved": "password,healthcheck,WaitFor,AOFalways,native replication,direct-copy,cancellation,WAIT/WAITAOF acknowledgement",
3326+
"development": {
3327+
"scope": "local development; actual pinned native resource model, not Docker startup or GitHub qualification",
3328+
"sourceRevision": "3aeb8cb7201ade69682d3f7106abd80ef137155e",
3329+
"candidatePatchSha256": "67c6834fb7a390e59684b7d8516c54b0fe3ef76c0500d216ff1dd0a26b86ac62",
3330+
"sourceFiles": {
3331+
"src/KeyLoad.AppHost/Features/BenchmarkComparisons/IsolatedRedisResources.cs": "283621f15aeb23adc7b40319ab45a030458242e45542763c785908952f1519b9",
3332+
"tests/KeyLoad.ComparisonTests/Features/BenchmarkComparisons/IsolatedResourceTopologyRedisTests.cs": "9909e073cb2faba4ad330dc79e90623ab798c1eb328c0e41ff5cd268ffdc3e77",
3333+
"tests/KeyLoad.ComparisonTests/Features/BenchmarkComparisons/RedisNativeReadinessRegression.cs": "f3c10491d7c5f1377591039f2111893ae6505e85edc3fe98481e9c46af130e72"
3334+
},
3335+
"runnerDllSha256": "ba7cf6baa9f2e0360be0019ded6a2c2c5191834362fb85dfe4551f6fe79b9665",
3336+
"fullReleaseBuild": "passed;0warnings;0errors",
3337+
"formatter": "passed",
3338+
"governance": "passed",
3339+
"invoker": "freshly built Aspire/TUnit comparison runner with owned canonical TMPDIR",
3340+
"report": {
3341+
"summary": {
3342+
"total": 3,
3343+
"passed": 3,
3344+
"failed": 0,
3345+
"skipped": 0,
3346+
"cancelled": 0,
3347+
"timedOut": 0,
3348+
"flaky": 0
3349+
},
3350+
"sha256": "29566022b609fb9e8ee50614fc3bbe35c79532c27e3712cbe7720a8bd194ab10",
3351+
"operatingSystem": "macOS 27.0.1",
3352+
"runtimeVersion": ".NET 10.0.12"
3353+
}
3354+
},
3355+
"nativeGitHubQualified": false
32803356
}
32813357
}
32823358
}

‎src/KeyLoad.AppHost/Features/BenchmarkComparisons/IsolatedRedisResources.cs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private static IResourceBuilder<RedisResource> AddNode(IsolatedResourceContext c
6767
var name = NodeNames[index];
6868
var directory = context.DataDirectory(name);
6969
ClusterProfileStore.PrepareDirectory(directory);
70-
var node = context.Builder.AddRedis(name, password: password)
70+
var node = UseNativeTcp(context.Builder.AddRedis(name, password: password))
7171
.WithImageTag(Version).WithImageSHA256(BenchmarkResources.RedisDigest[7..])
7272
.WithContainerNetworkAlias(name).WithDataBindMount(directory)
7373
.WithBindMount(script, ScriptTarget, isReadOnly: true).WithEntrypoint(Shell)
@@ -83,4 +83,12 @@ private static IResourceBuilder<RedisResource> AddNode(IsolatedResourceContext c
8383
}
8484
return node;
8585
}
86+
87+
private static IResourceBuilder<RedisResource> UseNativeTcp(IResourceBuilder<RedisResource> node)
88+
{
89+
// ADR-080: the owned authenticated bootstrap and replica links use native TCP on port 6379.
90+
#pragma warning disable ASPIRECERTIFICATES001
91+
return node.WithoutHttpsCertificate();
92+
#pragma warning restore ASPIRECERTIFICATES001
93+
}
8694
}

‎tests/KeyLoad.ComparisonTests/Features/BenchmarkComparisons/IsolatedResourceTopologyRedisTests.cs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ internal sealed class IsolatedResourceTopologyRedisTests
1616
private const string ReplicationHostGuard = "[ \"$KEYLOAD_REDIS_PRIMARY\" = primary.dev.internal ] || exit 1";
1717
private const string ReplicationConfiguration = "'replicaof primary.dev.internal 6379'";
1818
private const string IncorrectReplicationConfiguration = "'replicaof primary 6379'";
19+
private const string NativeScheme = "redis";
20+
private const int NativePort = 6379;
1921

20-
/// <summary>AC-ISO-003/005: one native primary and direct replicas, authenticated and persisted independently.</summary>
22+
/// <summary>AC-ISO-003/005 and AC-BC-FAIL-009: native authenticated TCP resources with explicit certificate opt-out.</summary>
2123
[Test]
2224
[Arguments(1)]
2325
[Arguments(2)]
@@ -62,6 +64,7 @@ await Assert.That(runnerEnvironment[IsolatedResourceTopologyFixture.NativePrefix
6264

6365
private static async Task VerifyNodeAsync(IsolatedResourceTopologyFixture fixture, RedisResource node, RedisResource primary)
6466
{
67+
await VerifyNativeTransportAsync(node);
6568
await IsolatedResourceTopologyFixture.VerifyPrivateDataAsync(node, fixture.Context.Root);
6669
await IsolatedResourceTopologyFixture.VerifyUserAsync(node);
6770
await Assert.That(node.PasswordParameter).IsSameReferenceAs(primary.PasswordParameter);
@@ -90,6 +93,21 @@ await Assert.That(node.Annotations.OfType<ContainerImageAnnotation>().Single().S
9093
.IsEqualTo(BenchmarkResources.RedisDigest[7..]);
9194
}
9295

96+
private static async Task VerifyNativeTransportAsync(RedisResource node)
97+
{
98+
// Inspect the actual pinned Aspire annotation; a missing explicit opt-out must fail this regression.
99+
#pragma warning disable ASPIRECERTIFICATES001
100+
var certificate = node.Annotations.OfType<HttpsCertificateAnnotation>().Single();
101+
await Assert.That(certificate.Certificate).IsNull();
102+
await Assert.That(certificate.UseDeveloperCertificate).IsFalse();
103+
#pragma warning restore ASPIRECERTIFICATES001
104+
var endpoint = node.Annotations.OfType<EndpointAnnotation>().Single();
105+
await Assert.That(endpoint.UriScheme).IsEqualTo(NativeScheme);
106+
await Assert.That(endpoint.TargetPort).IsEqualTo(NativePort);
107+
await Assert.That(endpoint.TlsEnabled).IsFalse();
108+
await Assert.That(node.Annotations.OfType<HealthCheckAnnotation>().Any()).IsTrue();
109+
}
110+
93111
private static async Task VerifyBootstrapReplicationAsync(string path)
94112
{
95113
var source = await File.ReadAllTextAsync(path, TestContext.Current!.Execution.CancellationToken);

0 commit comments

Comments
 (0)