Follow CultLib Cut 3: opening hydrates, PullOnOpen is gone

CultLib's CultCacheMessagePack.Create and OpenAsync now always read the store,
and CultCacheOpenOptions.PullOnOpen no longer exists.

- CultCachePatchDocumentStore.WriteDefaultAsync and
  AquaSynthDaemonService.WriteReceiptAsync write single-document files. They
  open (which hydrates), remove every record other than the one they write,
  upsert, and flush; the flush replaces the file atomically. Before this,
  Create never pulled, so their PullOnOpen = false was already a no-op.
- IpaTrialResults and SpeechDistributedTraining passed File.Exists-style
  PullOnOpen values; hydrating a missing store is empty, so the option goes.
- AquaSynthDaemonServiceTests drops PullOnOpen = true.

Globals: nothing in CultLib invents a [CultGlobal] record any more. AquaSynth
never relied on that: its three global types are positional records with no
parameterless constructor, which the old invention skipped, and every one is
written explicitly and read back by explicit key or GetAll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-13 17:59:48 +02:00
commit aed65255cc
5 changed files with 22 additions and 18 deletions

View file

@ -87,10 +87,13 @@ public static class CultCachePatchDocumentStore
{
public static async Task WriteDefaultAsync(string filePath)
{
using var cache = CultCacheMessagePack.Create(filePath, new CultCacheOpenOptions
// Opening hydrates; this file holds exactly the default document, so everything else goes before the write.
using var cache = CultCacheMessagePack.Create(filePath);
foreach (var stale in cache.AllStoredDocuments.Where(stored => stored.Key.Value != CultCachePatchDocumentCatalog.Key).ToArray())
{
PullOnOpen = false
});
cache.Remove(stale.Key);
}
await cache.UpsertAsync(
CultCachePatchDocumentCatalog.CreateDefault(),
new CultRecordHandle<CultCachePatchDocument>(new CultRecordKey(CultCachePatchDocumentCatalog.Key)))

View file

@ -115,7 +115,7 @@ public static class IpaTrialResultCultCacheStore
return [];
}
using var cache = await OpenTrialStoreAsync(filePath, pullOnOpen: true).ConfigureAwait(false);
using var cache = await OpenTrialStoreAsync(filePath).ConfigureAwait(false);
return cache.GetAll<IpaTrialResult>()
.OrderBy(result => result.TrialId, StringComparer.Ordinal)
.ToArray();
@ -128,7 +128,7 @@ public static class IpaTrialResultCultCacheStore
return [];
}
using var cache = await OpenTrialStoreAsync(filePath, pullOnOpen: true).ConfigureAwait(false);
using var cache = await OpenTrialStoreAsync(filePath).ConfigureAwait(false);
return cache.GetAll<SongChallengeEvidenceDocument>()
.OrderBy(document => document.EvidenceId, StringComparer.Ordinal)
.ToArray();
@ -141,7 +141,7 @@ public static class IpaTrialResultCultCacheStore
return [];
}
using var cache = await OpenTrialStoreAsync(filePath, pullOnOpen: true).ConfigureAwait(false);
using var cache = await OpenTrialStoreAsync(filePath).ConfigureAwait(false);
return cache.GetAll<SongTrialDistillationDocument>()
.OrderBy(document => document.DistillationId, StringComparer.Ordinal)
.ToArray();
@ -154,7 +154,7 @@ public static class IpaTrialResultCultCacheStore
return [];
}
using var cache = await OpenTrialStoreAsync(filePath, pullOnOpen: true).ConfigureAwait(false);
using var cache = await OpenTrialStoreAsync(filePath).ConfigureAwait(false);
return cache.GetAll<MusicProductionKnowledgeDocument>()
.OrderBy(document => document.KnowledgeId, StringComparer.Ordinal)
.ToArray();
@ -172,7 +172,7 @@ public static class IpaTrialResultCultCacheStore
Directory.CreateDirectory(directory);
}
using var cache = await OpenTrialStoreAsync(filePath, pullOnOpen: StoreExists(filePath)).ConfigureAwait(false);
using var cache = await OpenTrialStoreAsync(filePath).ConfigureAwait(false);
foreach (var document in documents)
{
await cache.UpsertAsync(document, new CultRecordHandle<T>(new CultRecordKey(key(document)))).ConfigureAwait(false);
@ -181,12 +181,11 @@ public static class IpaTrialResultCultCacheStore
await cache.FlushAsync().ConfigureAwait(false);
}
private static Task<CultCache> OpenTrialStoreAsync(string filePath, bool pullOnOpen) =>
private static Task<CultCache> OpenTrialStoreAsync(string filePath) =>
CultCacheMessagePack.OpenAsync(
filePath,
new CultCacheOpenOptions
{
PullOnOpen = pullOnOpen,
UseDirectoryStore = true
});

View file

@ -257,11 +257,8 @@ public static class SpeechDistributedTrainingCultCacheStore
return cache.GetAll<T>().OrderBy(orderKey, StringComparer.Ordinal).ToArray();
}
private static async Task<CultCache> OpenForMutationAsync(string filePath)
{
var options = new CultCacheOpenOptions { PullOnOpen = File.Exists(filePath) };
return await CultCacheMessagePack.OpenAsync(filePath, options).ConfigureAwait(false);
}
private static Task<CultCache> OpenForMutationAsync(string filePath) =>
CultCacheMessagePack.OpenAsync(filePath);
}
public static class SpeechDistributedTrainingCoordinator

View file

@ -920,8 +920,14 @@ public sealed class AquaSynthDaemonService : IDisposable
{
var filePath = Path.Combine(options.StoreRoot, family, $"{SafeFileName(id)}.cc");
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
using var cache = CultCacheMessagePack.Create(filePath, new CultCacheOpenOptions { PullOnOpen = false });
// Opening hydrates; a receipt file holds exactly one document, so everything else goes before the write.
using var cache = CultCacheMessagePack.Create(filePath);
var key = global ? new CultRecordKey("global") : new CultRecordKey(id);
foreach (var stale in cache.AllStoredDocuments.Where(stored => !stored.Key.Equals(key)).ToArray())
{
cache.Remove(stale.Key);
}
await cache.UpsertAsync(document, new CultRecordHandle<T>(key)).ConfigureAwait(false);
await cache.FlushAsync().ConfigureAwait(false);
}