Use one CultMesh node in Eve onboarding

This commit is contained in:
Meta Vens 2026-08-18 06:23:26 +02:00
commit 22bb9afaa4
3 changed files with 43 additions and 42 deletions

View file

@ -72,29 +72,32 @@ static async Task RunOdinAsync(Args arguments)
static async Task RunProviderAsync(Args arguments)
{
var registry = CultDocumentRegistry.ForTypes([typeof(CounterState), typeof(EveSurfaceDocument)]);
using var cache = await CultCacheMessagePack.OpenAsync(arguments.StatePath, new CultCacheOpenOptions
var documentTypes = new[] { typeof(CounterState), typeof(EveSurfaceDocument) };
var registry = CultMesh.CreateCultCacheDocumentRegistry(documentTypes);
var documents = CultMesh.CreateCultNetDocumentRegistry(documentTypes, registry);
using var node = await CultMesh.CreateNodeAsync(arguments.StatePath, new CultMeshNodeOptions
{
Registry = registry,
FlushOnDispose = true,
StoreFlushOnDispose = true
StartServer = false,
CacheOptions = new CultCacheOpenOptions
{
Registry = registry,
FlushOnDispose = true,
StoreFlushOnDispose = true
},
DatabaseOptions = new CultNetDatabaseOptions
{
RuntimeId = "sample.counter-provider",
DocumentRegistry = documents
}
});
var documents = new CultNetDocumentRegistry(registry)
.Register(CultNetDocumentBinding.ForDocument<CounterState>(registry, "sample.counter_state.v1"))
.Register(CultNetDocumentBinding.ForDocument<EveSurfaceDocument>(registry, EveSurfaceDocument.SchemaId));
using var database = new CultNetDatabase(cache, new CultNetDatabaseOptions
{
RuntimeId = "sample.counter-provider",
DocumentRegistry = documents
});
if (cache.Get<CounterState>(new CultRecordKey(counterKey)) == null)
await database.PutAsync(new CultRecordKey(counterKey), CounterState.Initial(counterKey));
if (cache.Get<EveSurfaceDocument>(new CultRecordKey(surfaceKey)) == null)
await database.PutAsync(new CultRecordKey(surfaceKey), CreateCounterSurface());
await cache.FlushAsync();
if (node.Cache.Get<CounterState>(new CultRecordKey(counterKey)) == null)
await node.Database.PutAsync(new CultRecordKey(counterKey), CounterState.Initial(counterKey));
if (node.Cache.Get<EveSurfaceDocument>(new CultRecordKey(surfaceKey)) == null)
await node.Database.PutAsync(new CultRecordKey(surfaceKey), CreateCounterSurface());
await node.FlushAsync();
await using var schemaServer = new CultNetWebSocketSchemaServer();
using var subscriptions = new CultNetDatabaseSubscriptionServer(schemaServer, database);
using var subscriptions = new CultNetDatabaseSubscriptionServer(schemaServer, node.Database);
using var operationGate = new SemaphoreSlim(1, 1);
using var operations = new CultNetOperationServer(schemaServer, "sample.counter-provider")
.Register<PingRequest, PingReceipt>(
@ -113,7 +116,7 @@ static async Task RunProviderAsync(Args arguments)
await operationGate.WaitAsync();
try
{
var current = cache.Get<CounterState>(new CultRecordKey(counterKey))
var current = node.Cache.Get<CounterState>(new CultRecordKey(counterKey))
?? throw new InvalidOperationException("Counter state is unavailable.");
if (current.Receipts.TryGetValue(context.IdempotencyKey, out var existing))
return existing;
@ -125,13 +128,13 @@ static async Task RunProviderAsync(Args arguments)
{
[context.IdempotencyKey] = receipt
};
await database.PutAsync(new CultRecordKey(counterKey), new CounterState
await node.Database.PutAsync(new CultRecordKey(counterKey), new CounterState
{
CounterId = counterKey,
Count = receipt.Count,
Receipts = receipts
});
await cache.FlushAsync();
await node.FlushAsync();
return receipt;
}
finally

View file

@ -19,7 +19,8 @@ The provider registers typed commands through `CultNetOperationServer`.
Application code receives `CultNetOperationContext<TRequest>` and returns a
typed receipt; it does not parse base64, switch on wire envelopes, or construct
correlation responses. The counter's durable transaction still owns
idempotency and state mutation.
idempotency and state mutation. The provider opens one public `CultMeshNode`;
it does not assemble a parallel CultCache/CultNet database host by hand.
Run the proof from the CultLib repository:

View file

@ -7,6 +7,7 @@ client. Start with the state the provider actually owns.
```csharp
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Mesh;
using GameCult.Networking;
using MessagePack;
@ -19,35 +20,31 @@ public sealed class CounterState
}
```
Open a `.cc` store, register the document once, and write through the CultMesh
database boundary:
Open a `.cc` node, register the document once, and write through its database
boundary:
```csharp
var key = new CultRecordKey("counter:main");
var registry = CultDocumentRegistry.ForTypes([typeof(CounterState)]);
using var cache = await CultCacheMessagePack.OpenAsync("counter.cc", new CultCacheOpenOptions
var documentTypes = new[] { typeof(CounterState) };
var registry = CultMesh.CreateCultCacheDocumentRegistry(documentTypes);
var documents = CultMesh.CreateCultNetDocumentRegistry(documentTypes, registry);
using var node = await CultMesh.CreateNodeAsync("counter.cc", new CultMeshNodeOptions
{
Registry = registry,
FlushOnDispose = true,
StoreFlushOnDispose = true
});
var documents = new CultNetDocumentRegistry(registry)
.Register(CultNetDocumentBinding.ForDocument<CounterState>(
registry,
"sample.counter_state.v1"));
using var database = new CultNetDatabase(cache, new CultNetDatabaseOptions
{
RuntimeId = "sample.counter-provider",
DocumentRegistry = documents
StartServer = false,
CacheOptions = new CultCacheOpenOptions { Registry = registry },
DatabaseOptions = new CultNetDatabaseOptions
{
RuntimeId = "sample.counter-provider",
DocumentRegistry = documents
}
});
await database.PutAsync(key, new CounterState
await node.Database.PutAsync(key, new CounterState
{
CounterId = key.Value,
Count = 0
});
await cache.FlushAsync();
await node.FlushAsync();
```
CultCache owns durable local truth. CultNet owns the portable schema mapping.