From b838862b2fbafcbfdec9299c3b2b0be666cc047a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 20 Jul 2026 03:06:30 +0100 Subject: [PATCH] Serve Aetheria bundle chunks without cache republish --- Aetheria.State.Daemon/Program.cs | 77 +++++++++++++------------ docs/aetheria-current-codebase-model.md | 26 +++++++++ 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/Aetheria.State.Daemon/Program.cs b/Aetheria.State.Daemon/Program.cs index 06c8c619a..d71c51a79 100644 --- a/Aetheria.State.Daemon/Program.cs +++ b/Aetheria.State.Daemon/Program.cs @@ -775,17 +775,14 @@ static async Task StartClientCultMeshHostAsync( startupPhase.Restart(); } - var contentCache = new CultCache(CultMesh.CreateCultCacheDocumentRegistry( - typeof(CultMeshCdnArtifactManifest), - typeof(CultMeshCdnArtifactChunk))); - var bundleCdnDocuments = BuildBundleCdnDocuments(node, contentCache, unityBundles, options.DaemonId); + var bundleCdnManifests = BuildBundleCdnManifestIndex(unityBundles); Trace("cdn-documents"); var server = new TcpFramedCultNetSchemaServer(new TcpListener( ParseBindAddress(options.ClientCultMeshHost), options.ClientCultMeshPort)); var content = new CultMeshTcpContentServer(new TcpListener( ParseBindAddress(options.ClientCultMeshHost), - options.ClientCultMeshContentPort), contentCache); + options.ClientCultMeshContentPort), unityBundles.ResolveChunk); Trace("tcp-listeners"); var realtimeCertificate = LoadOrCreateRealtimeCertificate( options.ClientCultMeshAdvertiseHost, @@ -808,7 +805,6 @@ static async Task StartClientCultMeshHostAsync( realtimeCertificate.Dispose(); content.Dispose(); server.Dispose(); - contentCache.Dispose(); throw; } var advertisedEndpoint = $"cultnet+tcp://{options.ClientCultMeshAdvertiseHost}:{server.LocalEndPoint.Port}"; @@ -1242,7 +1238,12 @@ static async Task StartClientCultMeshHostAsync( } } - InjectCultMeshCdnManifestSnapshots(bundleCdnDocuments, request, response); + InjectCultMeshCdnManifestSnapshots( + node, + options.DaemonId, + bundleCdnManifests, + request, + response); peer.SendCultNet(response); } catch (Exception ex) @@ -1270,7 +1271,6 @@ static async Task StartClientCultMeshHostAsync( content, realtime, realtimeCertificate, - contentCache, advertisedEndpoint, advertisedContentEndpoint, advertisedRealtimeEndpoint); @@ -1347,7 +1347,9 @@ static X509Certificate2 LoadOrCreateRealtimeCertificate(string advertisedHost, s } static void InjectCultMeshCdnManifestSnapshots( - IReadOnlyDictionary bundleCdnDocuments, + AetheriaStateNode node, + string daemonId, + IReadOnlyDictionary bundleCdnManifests, CultNetSnapshotRequestMessage request, CultNetSnapshotResponseRawMessage response) { @@ -1358,8 +1360,18 @@ static void InjectCultMeshCdnManifestSnapshots( var documents = new List(); foreach (var recordKey in recordKeys.Distinct(StringComparer.Ordinal)) { - if (bundleCdnDocuments.TryGetValue(recordKey, out var bundleDocument)) + if (bundleCdnManifests.TryGetValue(recordKey, out var manifest)) { + var bundleDocument = node.Database.Documents.CreateRawDocumentPutMessage( + $"aetheria-cdn:{recordKey}", + new CultRecordHandle(new CultRecordKey(recordKey)), + manifest, + new CultNetDocumentMessageOptions + { + SourceRuntimeId = daemonId, + SourceRole = "aetheria-cultmesh-cdn", + Tags = ["aetheria", "cultmesh-cdn", "asset-bundle"] + }).Document; if (string.Equals(Environment.GetEnvironmentVariable("AETHERIA_TRACE_EVE_SNAPSHOTS"), "1", StringComparison.Ordinal)) Console.WriteLine($"Eve CDN snapshot schema={bundleDocument.SchemaId} record={recordKey}"); documents.Add(bundleDocument); @@ -1376,35 +1388,16 @@ static void InjectCultMeshCdnManifestSnapshots( .ToArray(); } -static IReadOnlyDictionary BuildBundleCdnDocuments( - AetheriaStateNode node, - CultCache contentCache, - AetheriaUnityBundleArtifactSet unityBundles, - string daemonId) +static IReadOnlyDictionary BuildBundleCdnManifestIndex( + AetheriaUnityBundleArtifactSet unityBundles) { - var documents = new Dictionary(StringComparer.Ordinal); + var manifests = new Dictionary(StringComparer.Ordinal); foreach (var bundle in unityBundles.Bundles) { var artifact = bundle.Artifact; - CultMeshCdn.PublishAsync(contentCache, artifact).GetAwaiter().GetResult(); - Add(artifact.ManifestKey, artifact.Manifest); - } - return documents; - - void Add(CultRecordKey recordKey, T document) where T : class - { - var put = node.Database.Documents.CreateRawDocumentPutMessage( - $"aetheria-cdn:{recordKey.Value}", - new CultRecordHandle(recordKey), - document, - new CultNetDocumentMessageOptions - { - SourceRuntimeId = daemonId, - SourceRole = "aetheria-cultmesh-cdn", - Tags = ["aetheria", "cultmesh-cdn", "asset-bundle"] - }); - documents[recordKey.Value] = put.Document; + manifests[artifact.ManifestKey.ToString()] = artifact.Manifest; } + return manifests; } static async Task InjectEveSurfaceSnapshotAsync( @@ -3827,12 +3820,24 @@ internal sealed class AetheriaDaemonIngressState internal sealed class AetheriaUnityBundleArtifactSet { + private readonly IReadOnlyDictionary _chunks; + public AetheriaUnityBundleArtifactSet(IReadOnlyList bundles) { Bundles = bundles ?? throw new ArgumentNullException(nameof(bundles)); + var chunks = new Dictionary(StringComparer.Ordinal); + foreach (var chunk in bundles.SelectMany(bundle => bundle.Artifact.Chunks)) + chunks.TryAdd(CultMeshCdnArtifactChunk.CreateRecordKey(chunk).ToString(), chunk); + _chunks = chunks; } public IReadOnlyList Bundles { get; } + + public CultMeshCdnArtifactChunk? ResolveChunk(string hash) + { + var key = CultMeshCdnArtifactChunk.CreateRecordKey(hash).ToString(); + return _chunks.TryGetValue(key, out var chunk) ? chunk : null; + } } internal sealed class AetheriaUnityBundleArtifact @@ -3868,7 +3873,6 @@ internal sealed class AetheriaClientCultMeshHost : IAsyncDisposable CultMeshTcpContentServer content, CultMeshQuicRealtimeServer realtime, X509Certificate2 realtimeCertificate, - CultCache contentCache, string controlEndpoint, string contentEndpoint, string realtimeEndpoint) @@ -3877,7 +3881,6 @@ internal sealed class AetheriaClientCultMeshHost : IAsyncDisposable Content = content ?? throw new ArgumentNullException(nameof(content)); Realtime = realtime ?? throw new ArgumentNullException(nameof(realtime)); RealtimeCertificate = realtimeCertificate ?? throw new ArgumentNullException(nameof(realtimeCertificate)); - ContentCache = contentCache ?? throw new ArgumentNullException(nameof(contentCache)); ControlEndpoint = controlEndpoint ?? throw new ArgumentNullException(nameof(controlEndpoint)); ContentEndpoint = contentEndpoint ?? throw new ArgumentNullException(nameof(contentEndpoint)); RealtimeEndpoint = realtimeEndpoint ?? throw new ArgumentNullException(nameof(realtimeEndpoint)); @@ -3887,7 +3890,6 @@ internal sealed class AetheriaClientCultMeshHost : IAsyncDisposable public CultMeshTcpContentServer Content { get; } public CultMeshQuicRealtimeServer Realtime { get; } private X509Certificate2 RealtimeCertificate { get; } - private CultCache ContentCache { get; } public string ControlEndpoint { get; } public string ContentEndpoint { get; } public string RealtimeEndpoint { get; } @@ -3897,7 +3899,6 @@ internal sealed class AetheriaClientCultMeshHost : IAsyncDisposable await Realtime.DisposeAsync().ConfigureAwait(false); RealtimeCertificate.Dispose(); Content.Dispose(); - ContentCache.Dispose(); Control.Dispose(); } } diff --git a/docs/aetheria-current-codebase-model.md b/docs/aetheria-current-codebase-model.md index 0cee63232..a1cf6d3ba 100644 --- a/docs/aetheria-current-codebase-model.md +++ b/docs/aetheria-current-codebase-model.md @@ -176,6 +176,32 @@ certificate setup fell from 153 ms of RSA generation to 27 ms of PFX loading, and total client-host startup fell from about 319 ms to 183 ms. The first boot still pays certificate creation once. +### Provider-owned CDN bodies + +- **Owner:** the Aetheria bundle artifact set owns the immutable packed chunks + and manifests for the lifetime of the daemon. The TCP content server owns + framing and byte delivery, not storage. +- **Inputs:** provider-authored bundle files are packed once into content-addressed + manifests and chunks. The control plane indexes manifests by exact record key; + the content plane resolves chunks by normalized SHA-256 hash. +- **Outputs:** requested manifests remain typed CultNet snapshot records, while + chunk payloads travel on the dedicated TCP byte stream outside snapshot + messages. +- **Derived state:** raw manifest snapshot records are serialized only when a + client requests that manifest. No second CultCache mirrors the packed chunk + set. +- **Forbidden writers:** client-host startup must not republish provider-owned + chunks into an in-memory cache merely to satisfy a transport constructor, and + asset bodies must not fall back into batched state snapshots. +- **Cut line:** `CultMeshTcpContentServer` accepts a provider-owned chunk resolver; + its CultCache constructor is now only a convenience adapter for providers that + genuinely keep content in CultCache. +- **Verification layer:** CultMesh content-session and transport-modularity tests + stream a 700 KB provider-owned body directly and retain warm-cache reuse. On + the Aetheria artifact, eager CDN setup fell from about 132 ms to 1.9 ms. The + 13 MB cold released-client transfer still requires a fresh live witness before + it can be claimed healthy. + ### Restored-frame client readiness - **Owner:** the latest hard-flushed daemon frame owns restart world truth. Its