From 60cbfdde6df59548be8e82ed119bf0643e3ea26b Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 13 Sep 2026 16:54:43 +0200 Subject: [PATCH] Strip doc comments from the Caching projects Every /// line in GameCult.Caching and GameCult.Caching.MessagePack goes; they restated member names. Three blocks that state a rule stay as // comments: MaterializeMissingGlobals runs after hydration so persisted globals win, ExecuteTransactionAsync commits durably before the cache and observers advance, and CommitBatch implementations restore their staged view on failure. Both projects add NoWarn CS1591, since GenerateDocumentationFile is on in src/Directory.Build.props. Co-Authored-By: Claude Opus 5 --- .../CultCacheMessagePack.cs | 27 -- .../CultDocumentMessagePackSerialization.cs | 63 --- .../CultRecordRefFormatter.cs | 9 - .../DirectoryMessagePackBackingStore.cs | 15 - .../GameCult.Caching.MessagePack.csproj | 1 + src/GameCult.Caching/CultCache.cs | 398 +----------------- src/GameCult.Caching/CultDocumentContracts.cs | 87 ---- .../CultGeneratedDocumentMetadata.cs | 93 ---- src/GameCult.Caching/CultManagedDocument.cs | 30 -- src/GameCult.Caching/GameCult.Caching.csproj | 1 + 10 files changed, 7 insertions(+), 717 deletions(-) diff --git a/src/GameCult.Caching.MessagePack/CultCacheMessagePack.cs b/src/GameCult.Caching.MessagePack/CultCacheMessagePack.cs index 02ee32d5..e464e5e6 100644 --- a/src/GameCult.Caching.MessagePack/CultCacheMessagePack.cs +++ b/src/GameCult.Caching.MessagePack/CultCacheMessagePack.cs @@ -4,54 +4,27 @@ using System.Threading.Tasks; namespace GameCult.Caching.MessagePack { - /// - /// Options for opening a CultCache over the canonical single-file MessagePack backing store. - /// public sealed class CultCacheOpenOptions { - /// - /// Gets or sets the document registry to use. When omitted, the shared registry is used. - /// public CultDocumentRegistry? Registry { get; set; } - /// - /// Gets or sets whether the cache should pull the existing snapshot during open. - /// public bool PullOnOpen { get; set; } = true; - /// - /// Gets or sets whether the cache should flush attached stores when disposed. - /// public bool FlushOnDispose { get; set; } - /// - /// Gets or sets whether the backing store should flush when disposed. - /// public bool StoreFlushOnDispose { get; set; } - /// - /// Gets or sets whether the cache should use a paged directory store instead of one whole-file snapshot. - /// public bool UseDirectoryStore { get; set; } } - /// - /// Friendly MessagePack entrypoints for opening a CultCache with the canonical backing store. - /// public static class CultCacheMessagePack { - /// - /// Creates a cache with the canonical single-file MessagePack backing store attached. - /// public static CultCache Create(string filePath, CultCacheOpenOptions? options = null) { options ??= new CultCacheOpenOptions(); return CreateCore(filePath, options, initializeGlobals: true); } - /// - /// Creates a cache with the canonical single-file MessagePack backing store attached and optionally pulls the on-disk snapshot. - /// public static async Task OpenAsync(string filePath, CultCacheOpenOptions? options = null) { options ??= new CultCacheOpenOptions(); diff --git a/src/GameCult.Caching.MessagePack/CultDocumentMessagePackSerialization.cs b/src/GameCult.Caching.MessagePack/CultDocumentMessagePackSerialization.cs index fe9519b9..b5a621fb 100644 --- a/src/GameCult.Caching.MessagePack/CultDocumentMessagePackSerialization.cs +++ b/src/GameCult.Caching.MessagePack/CultDocumentMessagePackSerialization.cs @@ -7,20 +7,11 @@ using MessagePack.Resolvers; namespace GameCult.Caching.MessagePack; -/// -/// MessagePack resolver for CultCache-specific value types. -/// public sealed class CultDocumentResolver : IFormatterResolver { - /// - /// Gets the shared resolver instance. - /// public static readonly CultDocumentResolver Instance = new(); private CultDocumentResolver() { } - /// - /// Gets a formatter for the requested type, when this resolver owns it. - /// public IMessagePackFormatter? GetFormatter() { var type = typeof(T); @@ -34,9 +25,6 @@ public sealed class CultDocumentResolver : IFormatterResolver } } -/// -/// MessagePack serialization helpers for CultCache documents and backing stores. -/// public static class CultDocumentMessagePackSerialization { private const int PersistedRecordFieldCount = 4; @@ -44,9 +32,6 @@ public static class CultDocumentMessagePackSerialization private const int SchemaCatalogMemberFieldCount = 8; private const int StoreSnapshotFieldCount = 3; - /// - /// Gets the shared MessagePack serializer options for CultCache payloads. - /// public static readonly MessagePackSerializerOptions Options = MessagePackSerializerOptions.Standard .WithResolver(CompositeResolver.Create( @@ -54,33 +39,21 @@ public static class CultDocumentMessagePackSerialization StandardResolver.Instance)) .WithSecurity(MessagePackSecurity.UntrustedData); - /// - /// Serializes a typed value with the CultCache MessagePack options. - /// public static byte[] Serialize(T value) { return MessagePackSerializer.Serialize(value, Options); } - /// - /// Deserializes a typed value with the CultCache MessagePack options. - /// public static T Deserialize(byte[] payload) { return MessagePackSerializer.Deserialize(payload, Options); } - /// - /// Serializes a value whose document type is known at runtime. - /// public static byte[] SerializeUntyped(object value, Type type) { return SerializeUntyped(value, type, CultDocumentRegistry.Shared); } - /// - /// Serializes a value through an explicitly owned document registry. - /// public static byte[] SerializeUntyped(object value, Type type, CultDocumentRegistry registry) { if (registry == null) throw new ArgumentNullException(nameof(registry)); @@ -96,17 +69,11 @@ public static class CultDocumentMessagePackSerialization return MessagePackSerializer.Serialize(type, value, Options); } - /// - /// Deserializes a value whose document type is known at runtime. - /// public static object DeserializeUntyped(Type type, byte[] payload) { return DeserializeUntyped(type, payload, CultDocumentRegistry.Shared); } - /// - /// Deserializes a value through an explicitly owned document registry. - /// public static object DeserializeUntyped(Type type, byte[] payload, CultDocumentRegistry registry) { if (registry == null) throw new ArgumentNullException(nameof(registry)); @@ -120,9 +87,6 @@ public static class CultDocumentMessagePackSerialization ?? throw new InvalidOperationException($"MessagePack returned null for Cult document type {type.FullName}."); } - /// - /// Serializes one persisted store record. - /// public static byte[] SerializePersistedRecord(CultPersistedRecord record) { var buffer = new global::System.Buffers.ArrayBufferWriter(); @@ -132,18 +96,12 @@ public static class CultDocumentMessagePackSerialization return buffer.WrittenSpan.ToArray(); } - /// - /// Deserializes one persisted store record. - /// public static CultPersistedRecord DeserializePersistedRecord(byte[] payload) { var reader = new MessagePackReader(payload); return ReadPersistedRecord(ref reader); } - /// - /// Serializes a complete persisted store snapshot. - /// public static byte[] SerializeSnapshot(CultPersistedStoreSnapshot snapshot) { var buffer = new global::System.Buffers.ArrayBufferWriter(); @@ -166,9 +124,6 @@ public static class CultDocumentMessagePackSerialization return buffer.WrittenSpan.ToArray(); } - /// - /// Deserializes a complete persisted store snapshot. - /// public static CultPersistedStoreSnapshot DeserializeSnapshot(byte[] payload) { var reader = new MessagePackReader(payload); @@ -396,45 +351,27 @@ public static class CultDocumentMessagePackSerialization } } -/// -/// Single-file CultCache backing store that persists snapshots as MessagePack. -/// public class SingleFileMessagePackBackingStore : SingleFileBackingStore { - /// - /// Creates a MessagePack single-file backing store. - /// public SingleFileMessagePackBackingStore(string filePath) : base(filePath) { } - /// - /// Serializes a store snapshot. - /// protected override byte[] SerializeSnapshot(CultPersistedStoreSnapshot snapshot) { return CultDocumentMessagePackSerialization.SerializeSnapshot(snapshot); } - /// - /// Deserializes a store snapshot. - /// protected override CultPersistedStoreSnapshot DeserializeSnapshot(byte[] data) { return CultDocumentMessagePackSerialization.DeserializeSnapshot(data); } - /// - /// Serializes one document payload. - /// protected override byte[] SerializePayload(object document) { return CultDocumentMessagePackSerialization.SerializeUntyped(document, document.GetType(), Registry); } - /// - /// Deserializes one document payload. - /// protected override object DeserializePayload(Type documentType, byte[] payload) { return CultDocumentMessagePackSerialization.DeserializeUntyped(documentType, payload, Registry); diff --git a/src/GameCult.Caching.MessagePack/CultRecordRefFormatter.cs b/src/GameCult.Caching.MessagePack/CultRecordRefFormatter.cs index 3348c1c3..a5f984fc 100644 --- a/src/GameCult.Caching.MessagePack/CultRecordRefFormatter.cs +++ b/src/GameCult.Caching.MessagePack/CultRecordRefFormatter.cs @@ -3,22 +3,13 @@ using MessagePack.Formatters; namespace GameCult.Caching; -/// -/// Serializes typed CultCache record references as their persisted key strings. -/// public sealed class CultRecordRefFormatter : IMessagePackFormatter> { - /// - /// Writes a typed record reference to MessagePack. - /// public void Serialize(ref MessagePackWriter writer, CultRecordRef value, MessagePackSerializerOptions options) { options.Resolver.GetFormatterWithVerify().Serialize(ref writer, value.Key.Value, options); } - /// - /// Reads a typed record reference from MessagePack. - /// public CultRecordRef Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { var key = options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options) ?? string.Empty; diff --git a/src/GameCult.Caching.MessagePack/DirectoryMessagePackBackingStore.cs b/src/GameCult.Caching.MessagePack/DirectoryMessagePackBackingStore.cs index 0e6ab7bb..fd8d3c26 100644 --- a/src/GameCult.Caching.MessagePack/DirectoryMessagePackBackingStore.cs +++ b/src/GameCult.Caching.MessagePack/DirectoryMessagePackBackingStore.cs @@ -11,9 +11,6 @@ using MessagePack; namespace GameCult.Caching.MessagePack; -/// -/// MessagePack backing store that keeps a small schema manifest hot and stores each record as its own cold file. -/// public sealed class DirectoryMessagePackBackingStore : CacheBackingStore { private const string IndexedFormatVersion = "cultcache.store.v4.directory-content-addressed-pages"; @@ -26,9 +23,6 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore private Dictionary _durableIndex = new(StringComparer.Ordinal); private CultSchemaCatalogEntry[] _durableCatalog = Array.Empty(); - /// - /// Creates a paged MessagePack backing store. - /// public DirectoryMessagePackBackingStore(string manifestPath, string? recordDirectoryPath = null) { if (string.IsNullOrWhiteSpace(manifestPath)) @@ -40,12 +34,8 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore _recordDirectory = new DirectoryInfo(recordDirectoryPath ?? DefaultRecordDirectoryPath(manifestPath)); } - /// - /// Gets the default record directory path for a manifest path. - /// public static string DefaultRecordDirectoryPath(string manifestPath) => manifestPath + ".records"; - /// public override void PullAll() { lock (_mutationGate) @@ -120,14 +110,12 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore Trace("publish"); } - /// public override bool ContainsDurableRecord(CultRecordKey key) { lock (_mutationGate) return _durableIndex.ContainsKey(key.Value) || base.ContainsDurableRecord(key); } - /// public override void Push(CultStoredDocument entry) { lock (_mutationGate) @@ -139,7 +127,6 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore } } - /// public override void Delete(CultStoredDocument entry) { lock (_mutationGate) @@ -151,7 +138,6 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore } } - /// public override void CommitBatch( IReadOnlyCollection upserts, IReadOnlyCollection deletes) @@ -196,7 +182,6 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore } } - /// public override void PushAll() { lock (_mutationGate) diff --git a/src/GameCult.Caching.MessagePack/GameCult.Caching.MessagePack.csproj b/src/GameCult.Caching.MessagePack/GameCult.Caching.MessagePack.csproj index 71fcc969..24e7a48e 100644 --- a/src/GameCult.Caching.MessagePack/GameCult.Caching.MessagePack.csproj +++ b/src/GameCult.Caching.MessagePack/GameCult.Caching.MessagePack.csproj @@ -1,6 +1,7 @@ File-based MessagePack backing store for CultCache + $(NoWarn);CS1591 diff --git a/src/GameCult.Caching/CultCache.cs b/src/GameCult.Caching/CultCache.cs index 84b5e869..8cf59d40 100644 --- a/src/GameCult.Caching/CultCache.cs +++ b/src/GameCult.Caching/CultCache.cs @@ -13,205 +13,82 @@ using R3; namespace GameCult.Caching { - /// - /// Persisted schema metadata embedded in a CultCache backing store. - /// public sealed class CultSchemaCatalogEntry { - /// - /// Gets or sets the content-derived schema identifier. - /// public string SchemaId { get; set; } = string.Empty; - /// - /// Gets or sets the stable schema name. - /// public string SchemaName { get; set; } = string.Empty; - /// - /// Gets or sets the schema version string. - /// public string SchemaVersion { get; set; } = string.Empty; - /// - /// Gets or sets the canonical schema content hash. - /// public string ContentHash { get; set; } = string.Empty; - /// - /// Gets or sets the canonical schema description. - /// public string CanonicalSchemaJson { get; set; } = string.Empty; - /// - /// Gets or sets schema identifiers compatible with this entry. - /// public string[] CompatibleSchemaIds { get; set; } = Array.Empty(); - /// - /// Gets or sets the persisted member descriptors used for compatibility checks. - /// public CultSchemaMemberCatalogEntry[] Members { get; set; } = Array.Empty(); } - /// - /// Persisted catalog entry describing one schema member. - /// public sealed class CultSchemaMemberCatalogEntry { - /// - /// Gets or sets the persisted slot number. - /// public int Slot { get; set; } - /// - /// Gets or sets the member name. - /// public string MemberName { get; set; } = string.Empty; - /// - /// Gets or sets the persisted type name. - /// public string TypeName { get; set; } = string.Empty; - /// - /// Gets or sets whether the member is a reference. - /// public bool IsReference { get; set; } - /// - /// Gets or sets whether the member stores many values. - /// public bool IsMany { get; set; } - /// - /// Gets or sets the referenced schema name, when the member is a reference. - /// public string? TargetSchemaName { get; set; } - /// - /// Gets or sets whether the member provides the name lookup. - /// public bool IsName { get; set; } - /// - /// Gets or sets the index alias, when the member participates in lookups. - /// public string? IndexAlias { get; set; } } - /// - /// Persisted serialized document record. - /// public sealed class CultPersistedRecord { - /// - /// Gets or sets the persisted record key. - /// public string Key { get; set; } = string.Empty; - /// - /// Gets or sets the schema identifier used to deserialize the payload. - /// public string SchemaId { get; set; } = string.Empty; - /// - /// Gets or sets the storage timestamp. - /// public string StoredAt { get; set; } = string.Empty; - /// - /// Gets or sets the serialized document payload. - /// public byte[] Payload { get; set; } = Array.Empty(); } - /// - /// Complete snapshot persisted by a single-file CultCache backing store. - /// public sealed class CultPersistedStoreSnapshot { - /// - /// Gets or sets the backing store format version. - /// public string FormatVersion { get; set; } = "cultcache.store.v1"; - /// - /// Gets or sets the embedded schema catalog. - /// public CultSchemaCatalogEntry[] SchemaCatalog { get; set; } = Array.Empty(); - /// - /// Gets or sets the persisted records. - /// public CultPersistedRecord[] Records { get; set; } = Array.Empty(); } - /// - /// Classifies how a persisted schema mapped onto the local document registry. - /// public enum CultSchemaMigrationKind { - /// - /// The persisted schema matched the local schema exactly. - /// Exact, - /// - /// The persisted schema required a compatible soft-migration path. - /// CompatibleDrift } - /// - /// Describes one schema-resolution warning emitted during load. - /// public sealed class CultSchemaMigrationWarning { - /// - /// Gets or sets the stable warning code. - /// public string Code { get; set; } = string.Empty; - /// - /// Gets or sets the human-readable warning text. - /// public string Message { get; set; } = string.Empty; } - /// - /// Reports how one persisted schema entry resolved against the local schema catalog. - /// public sealed class CultSchemaMigrationReport { - /// - /// Gets or sets the persisted schema identifier. - /// public string PersistedSchemaId { get; set; } = string.Empty; - /// - /// Gets or sets the local schema identifier selected for reading. - /// public string LocalSchemaId { get; set; } = string.Empty; - /// - /// Gets or sets the persisted schema name. - /// public string PersistedSchemaName { get; set; } = string.Empty; - /// - /// Gets or sets the local schema name. - /// public string LocalSchemaName { get; set; } = string.Empty; - /// - /// Gets or sets how the persisted schema matched locally. - /// public CultSchemaMigrationKind Kind { get; set; } - /// - /// Gets or sets the persisted member slots ignored by the local reader. - /// public int[] IgnoredExtraSlots { get; set; } = Array.Empty(); - /// - /// Gets or sets the local member slots defaulted because the persisted record did not contain them. - /// public int[] DefaultedMissingSlots { get; set; } = Array.Empty(); - /// - /// Gets or sets emitted migration warnings. - /// public CultSchemaMigrationWarning[] Warnings { get; set; } = Array.Empty(); } @@ -228,9 +105,6 @@ namespace GameCult.Caching public CultSchemaMigrationReport Report { get; } } - /// - /// Runtime descriptor for a CultCache document type. - /// public sealed class CultDocumentDescriptor { internal CultDocumentDescriptor( @@ -263,53 +137,20 @@ namespace GameCult.Caching Members = members; } - /// - /// Gets the CLR document type. - /// public Type DocumentType { get; } - /// - /// Gets the stable schema name. - /// public string SchemaName { get; } - /// - /// Gets the schema version string. - /// public string SchemaVersion { get; } - /// - /// Gets the content-derived schema identifier. - /// public string SchemaId { get; } - /// - /// Gets the canonical schema content hash. - /// public string ContentHash { get; } - /// - /// Gets the canonical schema description. - /// public string CanonicalSchemaJson { get; } - /// - /// Gets whether this document type stores one global record. - /// public bool IsGlobal { get; } - /// - /// Gets the document name member, if any. - /// public string? NameMember { get; } internal Func? NameAccessor { get; } - /// - /// Gets the generated payload serializer, if one is available. - /// public Func? GeneratedPayloadSerializer { get; } - /// - /// Gets the generated payload deserializer, if one is available. - /// public Func? GeneratedPayloadDeserializer { get; } internal IReadOnlyDictionary> IndexAccessors { get; } internal IReadOnlyList Members { get; } - /// - /// Converts this descriptor into a persisted schema catalog entry. - /// public CultSchemaCatalogEntry ToCatalogEntry() { return new CultSchemaCatalogEntry @@ -358,14 +199,8 @@ namespace GameCult.Caching public string? IndexAlias { get; set; } } - /// - /// Stores a resolved document together with its descriptor and key. - /// public sealed class CultStoredDocument { - /// - /// Creates a stored document wrapper. - /// public CultStoredDocument( CultRecordKey key, string storedAt, @@ -378,27 +213,12 @@ namespace GameCult.Caching Document = document; } - /// - /// Gets the record key. - /// public CultRecordKey Key { get; } - /// - /// Gets the storage timestamp. - /// public string StoredAt { get; } - /// - /// Gets the document descriptor. - /// public CultDocumentDescriptor Descriptor { get; } - /// - /// Gets the document instance. - /// public object Document { get; } } - /// - /// Discovers, indexes, and resolves CultCache document descriptors. - /// public sealed class CultDocumentRegistry { private static readonly Lazy SharedRegistry = @@ -407,14 +227,8 @@ namespace GameCult.Caching private volatile RegistryIndexes _indexes = new(); private readonly object _registrationGate = new(); - /// - /// Gets the shared process-wide document registry. - /// public static CultDocumentRegistry Shared => SharedRegistry.Value; - /// - /// Creates a registry and discovers currently loaded document metadata. - /// public CultDocumentRegistry() { Refresh(); @@ -426,9 +240,6 @@ namespace GameCult.Caching Refresh(); } - /// - /// Creates a registry containing exactly the requested document types without scanning loaded assemblies. - /// public static CultDocumentRegistry ForTypes(IEnumerable documentTypes) { if (documentTypes == null) throw new ArgumentNullException(nameof(documentTypes)); @@ -442,15 +253,9 @@ namespace GameCult.Caching return registry; } - /// - /// Gets all known document descriptors. - /// public IEnumerable AllDescriptors => _indexes.ByType.Values.OrderBy(d => d.SchemaName, StringComparer.Ordinal); - /// - /// Rebuilds the registry from generated metadata and reflected document attributes. - /// public void Refresh() { lock (_registrationGate) @@ -473,9 +278,6 @@ namespace GameCult.Caching } } - /// - /// Gets the descriptor for a document type, building it when needed. - /// public CultDocumentDescriptor GetRequired(Type type) { if (_indexes.ByType.TryGetValue(type, out var descriptor)) @@ -500,17 +302,11 @@ namespace GameCult.Caching return RegisterDescriptor(descriptor); } - /// - /// Gets the descriptor for a document type. - /// public CultDocumentDescriptor GetRequired() where T : class { return GetRequired(typeof(T)); } - /// - /// Gets a descriptor by its schema identifier. - /// public CultDocumentDescriptor GetRequiredBySchemaId(string schemaId) { if (_indexes.BySchemaId.TryGetValue(schemaId, out var descriptor)) @@ -521,17 +317,11 @@ namespace GameCult.Caching throw new InvalidOperationException($"Unknown CultCache schema id '{schemaId}'."); } - /// - /// Resolves a persisted schema identifier against the local registry and embedded catalog. - /// public CultDocumentDescriptor ResolvePersistedSchema(string schemaId, IReadOnlyCollection catalog) { return ResolvePersistedSchemaDetailed(schemaId, catalog).Descriptor; } - /// - /// Resolves a persisted schema identifier and returns the selected descriptor with migration diagnostics. - /// public CultSchemaMigrationReport ResolvePersistedSchemaReport(string schemaId, IReadOnlyCollection catalog) { return ResolvePersistedSchemaDetailed(schemaId, catalog).Report; @@ -1263,9 +1053,6 @@ namespace GameCult.Caching } } - /// - /// In-memory document cache with pluggable persisted backing stores. - /// public sealed class CultCache : IDisposable { private readonly CultDocumentRegistry _registry; @@ -1282,18 +1069,11 @@ namespace GameCult.Caching private readonly object _stateGate = new(); private bool _hasUnflushedMutations; - /// - /// Creates a cache using the supplied document registry or the shared registry. - /// public CultCache(CultDocumentRegistry? registry = null) : this(registry, initializeGlobals: true) { } - /// - /// Creates a cache using the supplied document registry, optionally deferring global defaults - /// until a durable backing store has been hydrated. - /// public CultCache(CultDocumentRegistry? registry, bool initializeGlobals) { _registry = registry ?? CultDocumentRegistry.Shared; @@ -1303,38 +1083,20 @@ namespace GameCult.Caching } } - /// - /// Creates default instances for global document types that do not already have a record. - /// Durable open paths call this after hydration so persisted globals remain authoritative. - /// + // Durable open paths call this after hydration so persisted globals remain authoritative. public void MaterializeMissingGlobals() { InitializeGlobals(); } - /// - /// Gets whether the cache currently holds unflushed mutations in any attached backing store or only in memory. - /// public bool IsDirty => _hasUnflushedMutations || _backingStores.Any(store => store.IsDirty); - /// - /// Gets or sets whether disposing the cache should flush attached dirty backing stores first. - /// public bool FlushAttachedStoresOnDispose { get; set; } - /// - /// Raised when a backing store adds, updates, or removes a document. - /// public event Action? OnUpdate; - /// - /// Gets attached backing stores in registration order. - /// public IReadOnlyList BackingStores => _backingStores; - /// - /// Gets all document instances currently held by the cache. - /// public IEnumerable AllEntries { get @@ -1344,9 +1106,6 @@ namespace GameCult.Caching } } - /// - /// Gets all stored document records currently held by the cache. - /// public IEnumerable AllStoredDocuments { get @@ -1359,14 +1118,8 @@ namespace GameCult.Caching } } - /// - /// Gets the document registry used by this cache. - /// public CultDocumentRegistry Registry => _registry; - /// - /// Watches all local cache changes assignable to the requested document type. - /// public Observable> Watch() where T : class { return _changes @@ -1374,17 +1127,11 @@ namespace GameCult.Caching .Select(change => (CultCacheDocumentChange)change); } - /// - /// Watches one local cache record. - /// public Observable> WatchRecord(CultRecordKey key) where T : class { return Watch().Where(change => change.Key.Equals(key)); } - /// - /// Attaches a backing store to this cache. - /// public void AddBackingStore(CacheBackingStore store) { if (store == null) throw new ArgumentNullException(nameof(store)); @@ -1400,9 +1147,6 @@ namespace GameCult.Caching _backingStores.Add(store); } - /// - /// Pulls all documents from every attached backing store. - /// public async Task PullAllBackingStoresAsync() { if (_ambientTransaction.Value != null) @@ -1423,9 +1167,6 @@ namespace GameCult.Caching } } - /// - /// Flushes all attached backing stores. - /// public void FlushAllBackingStores() { if (_ambientTransaction.Value != null) @@ -1453,11 +1194,8 @@ namespace GameCult.Caching return Task.CompletedTask; } - /// - /// Runs a buffered cache mutation. Staged records are visible only to the executing - /// async flow. The durable backing store is committed before the live cache and its - /// observers advance; an exception discards the entire staged batch. - /// + // Staged records are visible only to the executing async flow. The durable backing store + // commits before the live cache and its observers advance; an exception discards the whole batch. public async Task ExecuteTransactionAsync(Func stageAsync) { if (stageAsync == null) throw new ArgumentNullException(nameof(stageAsync)); @@ -1488,7 +1226,6 @@ namespace GameCult.Caching PublishChange(change.Stored, change.Previous, change.Removed); } - /// Runs a value-producing buffered cache mutation. public async Task ExecuteTransactionAsync(Func> stageAsync) { if (stageAsync == null) throw new ArgumentNullException(nameof(stageAsync)); @@ -1500,9 +1237,6 @@ namespace GameCult.Caching return result; } - /// - /// Adds or replaces a typed document and returns its record handle. - /// public async Task> AddAsync(T document, CultRecordHandle? handle = null) { if (document == null) throw new ArgumentNullException(nameof(document)); @@ -1526,17 +1260,11 @@ namespace GameCult.Caching } } - /// - /// Adds or replaces a typed document and returns its record handle. - /// public Task> UpsertAsync(T document, CultRecordHandle? handle = null) { return AddAsync(document, handle); } - /// - /// Adds or replaces a document whose concrete type is known at runtime. - /// public async Task UpsertAsync(Type documentType, object document, CultRecordKey? key = null) { if (documentType == null) throw new ArgumentNullException(nameof(documentType)); @@ -1568,9 +1296,6 @@ namespace GameCult.Caching } } - /// - /// Gets the record handle for a document instance, if it is tracked. - /// public CultRecordHandle? TryGetHandle(T document) { if (document == null) throw new ArgumentNullException(nameof(document)); @@ -1579,9 +1304,6 @@ namespace GameCult.Caching : null; } - /// - /// Gets a document by record key. - /// public object? Get(CultRecordKey key) { lock (_stateGate) @@ -1595,17 +1317,11 @@ namespace GameCult.Caching } } - /// - /// Gets a typed document by record key. - /// public T? Get(CultRecordKey key) where T : class { return Get(key) as T; } - /// - /// Gets all cached documents assignable to the requested type. - /// public IEnumerable GetAll() where T : class { lock (_stateGate) @@ -1618,9 +1334,6 @@ namespace GameCult.Caching } } - /// - /// Gets the global document for the requested type, if one exists. - /// public T? GetGlobal() where T : class { return _globalKeys.TryGetValue(typeof(T), out var key) @@ -1628,9 +1341,6 @@ namespace GameCult.Caching : null; } - /// - /// Gets a typed document by its CultName value. - /// public T? GetByName(string name) where T : class { var type = typeof(T); @@ -1643,9 +1353,6 @@ namespace GameCult.Caching return null; } - /// - /// Gets a typed document by an indexed value. - /// public T? GetByIndex(string alias, string value) where T : class { if (_indexMaps.TryGetValue((typeof(T), alias), out var map) && @@ -1657,9 +1364,6 @@ namespace GameCult.Caching return null; } - /// - /// Removes a document by typed handle. - /// public void Remove(CultRecordHandle handle) { if (_ambientTransaction.Value is { } transaction) @@ -1680,9 +1384,6 @@ namespace GameCult.Caching } } - /// - /// Removes a document by cache-owned record key. - /// public bool Remove(CultRecordKey key) { if (Get(key) == null) @@ -1709,18 +1410,12 @@ namespace GameCult.Caching return true; } - /// - /// Removes a document by typed handle. - /// public Task DeleteAsync(CultRecordHandle handle) { Remove(handle); return Task.CompletedTask; } - /// - /// Disposes attached disposable backing stores. - /// public void Dispose() { if (FlushAttachedStoresOnDispose && IsDirty) @@ -2124,44 +1819,26 @@ namespace GameCult.Caching } } - /// - /// Base class for CultCache persistence adapters. - /// public abstract class CacheBackingStore : IDisposable { private CultDocumentRegistry? _registry; private bool _isDirty; private CultSchemaMigrationReport[] _lastSchemaMigrationReports = Array.Empty(); - /// - /// Gets the attached document registry. - /// protected CultDocumentRegistry Registry => _registry ?? throw new InvalidOperationException("Backing store is not attached to a CultDocumentRegistry."); - /// - /// Gets the backing store entries by record key. - /// protected ConcurrentDictionary Entries { get; } = new(StringComparer.Ordinal); - /// - /// Gets whether the backing store holds staged mutations not yet durably flushed. - /// public bool IsDirty { get => _isDirty; protected set => _isDirty = value; } - /// - /// Gets or sets whether disposing the backing store should flush staged mutations first. - /// public bool FlushOnDispose { get; set; } - /// - /// Gets schema migration reports emitted during the most recent pull. - /// public IReadOnlyList LastSchemaMigrationReports => _lastSchemaMigrationReports; // Set by the cache at attach; a store calls them for records it loads or drops. @@ -2173,29 +1850,15 @@ namespace GameCult.Caching _registry = registry ?? throw new ArgumentNullException(nameof(registry)); } - /// - /// Pulls all persisted records into the backing store. - /// public abstract void PullAll(); - /// - /// Returns whether the backing store knows a record is durable, including records left cold by selective hydration. - /// public virtual bool ContainsDurableRecord(CultRecordKey key) { return Entries.ContainsKey(key.Value); } - /// - /// Pushes one stored document into the backing store. - /// public abstract void Push(CultStoredDocument entry); - /// - /// Deletes one stored document from the backing store. - /// public abstract void Delete(CultStoredDocument entry); - /// - /// Atomically stages and durably commits a buffered record batch for this store. - /// Implementations must restore their prior staged view when finality fails. - /// + // Stages and durably commits a batch as one step. Implementations must restore their prior + // staged view when finality fails. public virtual void CommitBatch( IReadOnlyCollection upserts, IReadOnlyCollection deletes) @@ -2219,13 +1882,7 @@ namespace GameCult.Caching throw; } } - /// - /// Persists all current backing store entries. - /// public abstract void PushAll(); - /// - /// Flushes staged mutations first when FlushOnDispose is set. - /// public virtual void Dispose() { if (FlushOnDispose && IsDirty) @@ -2234,9 +1891,6 @@ namespace GameCult.Caching } } - /// - /// Converts a stored document into a persisted record. - /// protected CultPersistedRecord ToPersistedRecord(CultStoredDocument entry, Func serializePayload) { return new CultPersistedRecord @@ -2248,9 +1902,6 @@ namespace GameCult.Caching }; } - /// - /// Converts a persisted record into a stored document. - /// protected CultStoredDocument ToStoredDocument( CultPersistedRecord record, IReadOnlyCollection catalog, @@ -2265,60 +1916,30 @@ namespace GameCult.Caching document); } - /// - /// Records schema migration reports captured during a pull. - /// protected void SetLastSchemaMigrationReports(IEnumerable reports) { _lastSchemaMigrationReports = reports?.ToArray() ?? Array.Empty(); } - /// - /// Marks the backing store as durably flushed. - /// protected void MarkFlushSucceeded() { IsDirty = false; } } - /// - /// Base class for backing stores that persist a complete snapshot to one file. - /// public abstract class SingleFileBackingStore : CacheBackingStore { - /// - /// Creates a single-file backing store. - /// protected SingleFileBackingStore(string filePath) { FileInfo = new FileInfo(filePath); } - /// - /// Gets the file used by this backing store. - /// protected FileInfo FileInfo { get; } - /// - /// Serializes a full store snapshot. - /// protected abstract byte[] SerializeSnapshot(CultPersistedStoreSnapshot snapshot); - /// - /// Deserializes a full store snapshot. - /// protected abstract CultPersistedStoreSnapshot DeserializeSnapshot(byte[] data); - /// - /// Serializes one document payload. - /// protected abstract byte[] SerializePayload(object document); - /// - /// Deserializes one document payload. - /// protected abstract object DeserializePayload(Type documentType, byte[] payload); - /// - /// Loads every persisted record from disk. - /// public override void PullAll() { FileInfo.Refresh(); @@ -2375,27 +1996,18 @@ namespace GameCult.Caching IsDirty = false; } - /// - /// Stages one stored document in memory. - /// public override void Push(CultStoredDocument entry) { Entries[entry.Key.Value] = entry; IsDirty = true; } - /// - /// Removes one staged document. - /// public override void Delete(CultStoredDocument entry) { Entries.TryRemove(entry.Key.Value, out _); IsDirty = true; } - /// - /// Writes the staged snapshot to disk. - /// public override void PushAll() { var snapshot = new CultPersistedStoreSnapshot diff --git a/src/GameCult.Caching/CultDocumentContracts.cs b/src/GameCult.Caching/CultDocumentContracts.cs index 46dca479..e601f0bc 100644 --- a/src/GameCult.Caching/CultDocumentContracts.cs +++ b/src/GameCult.Caching/CultDocumentContracts.cs @@ -2,15 +2,9 @@ using System; namespace GameCult.Caching { - /// - /// Marks a type as a CultCache document with a stable schema name and version. - /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class CultDocumentAttribute : Attribute { - /// - /// Creates a document marker for the supplied schema identity. - /// public CultDocumentAttribute(string schemaName, string schemaVersion) { SchemaName = string.IsNullOrWhiteSpace(schemaName) @@ -21,172 +15,91 @@ namespace GameCult.Caching : schemaVersion; } - /// - /// Gets the stable schema name used by CultCache and CultNet. - /// public string SchemaName { get; } - /// - /// Gets the semantic schema version string for this document shape. - /// public string SchemaVersion { get; } } - /// - /// Marks a document type as having one global record in a cache. - /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class CultGlobalAttribute : Attribute { } - /// - /// Marks the member that supplies the human-readable document name. - /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true)] public sealed class CultNameAttribute : Attribute { } - /// - /// Marks a member as an indexed lookup value. - /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public sealed class CultIndexAttribute : Attribute { - /// - /// Creates an index marker with an optional alias. - /// public CultIndexAttribute(string? alias = null) { Alias = alias; } - /// - /// Gets the external index alias, or null to use the member name. - /// public string? Alias { get; } } - /// - /// Marks a member as a reference to another CultCache document. - /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true)] public sealed class CultReferenceAttribute : Attribute { - /// - /// Creates a document reference marker. - /// public CultReferenceAttribute(Type? targetType = null, bool many = false) { TargetType = targetType; Many = many; } - /// - /// Gets the explicit referenced document type, when one is required. - /// public Type? TargetType { get; } - /// - /// Gets whether this member stores many references rather than one reference. - /// public bool Many { get; } } - /// - /// Identifies a record inside a CultCache backing store. - /// public readonly struct CultRecordKey : IEquatable { - /// - /// Creates a record key from its persisted string value. - /// public CultRecordKey(string value) { Value = value ?? string.Empty; } - /// - /// Gets the persisted key value. - /// public string Value { get; } - /// - /// Returns the persisted key value. - /// public override string ToString() => Value; - /// - /// Returns whether this key and another key contain the same persisted value. - /// public bool Equals(CultRecordKey other) => string.Equals(Value, other.Value, StringComparison.Ordinal); - /// - /// Returns whether this key and another object contain the same persisted value. - /// public override bool Equals(object? obj) => obj is CultRecordKey other && Equals(other); - /// - /// Returns a hash code for the persisted key value. - /// public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(Value); } - /// - /// Carries the persisted record key for a typed document. - /// public readonly struct CultRecordHandle { - /// - /// Creates a typed document handle. - /// public CultRecordHandle(CultRecordKey key) { Key = key; } - /// - /// Gets the persisted record key. - /// public CultRecordKey Key { get; } - /// - /// Returns the persisted key value. - /// public override string ToString() => Key.ToString(); } - /// - /// Stores a typed reference to a CultCache document. - /// public readonly struct CultRecordRef { - /// - /// Creates a typed document reference. - /// public CultRecordRef(CultRecordKey key) { Key = key; } - /// - /// Gets the referenced record key. - /// public CultRecordKey Key { get; } - /// - /// Resolves the referenced record against the provided cache. - /// public T? Resolve(CultCache cache) { if (cache == null) throw new ArgumentNullException(nameof(cache)); return cache.Get(Key) is T typed ? typed : default; } - /// - /// Returns the referenced key value. - /// public override string ToString() => Key.ToString(); } } diff --git a/src/GameCult.Caching/CultGeneratedDocumentMetadata.cs b/src/GameCult.Caching/CultGeneratedDocumentMetadata.cs index 7314c87f..c0bb601d 100644 --- a/src/GameCult.Caching/CultGeneratedDocumentMetadata.cs +++ b/src/GameCult.Caching/CultGeneratedDocumentMetadata.cs @@ -6,45 +6,24 @@ using System.Text; namespace GameCult.Caching { - /// - /// Registers a generated CultCache document metadata provider with an assembly. - /// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class CultGeneratedDocumentMetadataProviderAttribute : Attribute { - /// - /// Creates an assembly metadata provider registration. - /// public CultGeneratedDocumentMetadataProviderAttribute(Type providerType) { ProviderType = providerType ?? throw new ArgumentNullException(nameof(providerType)); } - /// - /// Gets the provider type that can create generated document definitions. - /// public Type ProviderType { get; } } - /// - /// Supplies generated CultCache document metadata for an assembly. - /// public interface ICultGeneratedDocumentMetadataProvider { - /// - /// Gets the generated document definitions exposed by the provider. - /// IEnumerable GetDocumentDefinitions(); } - /// - /// Describes one generated CultCache document contract. - /// public sealed class CultGeneratedDocumentDefinition { - /// - /// Creates a generated document contract definition. - /// public CultGeneratedDocumentDefinition( Type documentType, string schemaName, @@ -73,65 +52,29 @@ namespace GameCult.Caching Members = members ?? Array.Empty(); } - /// - /// Gets the CLR document type. - /// public Type DocumentType { get; } - /// - /// Gets the stable CultCache schema name. - /// public string SchemaName { get; } - /// - /// Gets the schema version string. - /// public string SchemaVersion { get; } - /// - /// Gets whether this document type stores one global record. - /// public bool IsGlobal { get; } - /// - /// Gets the member used as the document name, if any. - /// public string? NameMember { get; } - /// - /// Gets the generated name accessor, if any. - /// public Func? NameAccessor { get; } - /// - /// Gets the generated payload serializer, if one was emitted. - /// public Func? SerializePayload { get; } - /// - /// Gets the generated payload deserializer, if one was emitted. - /// public Func? DeserializePayload { get; } - /// - /// Gets the generated index accessors. - /// public IReadOnlyList IndexAccessors { get; } - /// - /// Gets the generated persisted member definitions. - /// public IReadOnlyList Members { get; } } - /// - /// Describes a generated accessor for one CultCache index. - /// public sealed class CultGeneratedDocumentIndexAccessor { - /// - /// Creates a generated index accessor. - /// public CultGeneratedDocumentIndexAccessor(string alias, Func accessor) { Alias = string.IsNullOrWhiteSpace(alias) @@ -140,25 +83,13 @@ namespace GameCult.Caching Accessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); } - /// - /// Gets the index alias. - /// public string Alias { get; } - /// - /// Gets the function that extracts the index value. - /// public Func Accessor { get; } } - /// - /// Describes one generated persisted document member. - /// public sealed class CultGeneratedDocumentMemberDefinition { - /// - /// Creates a generated persisted member definition. - /// public CultGeneratedDocumentMemberDefinition( string memberName, int slot, @@ -183,44 +114,20 @@ namespace GameCult.Caching IndexAlias = indexAlias; } - /// - /// Gets the CLR member name. - /// public string MemberName { get; } - /// - /// Gets the persisted slot number. - /// public int Slot { get; } - /// - /// Gets the persisted type name. - /// public string TypeName { get; } - /// - /// Gets whether this member stores a document reference. - /// public bool IsReference { get; } - /// - /// Gets whether this member stores many references. - /// public bool IsMany { get; } - /// - /// Gets the target schema name for reference members. - /// public string? TargetSchemaName { get; } - /// - /// Gets whether this member is the document name. - /// public bool IsName { get; } - /// - /// Gets the index alias for indexed members. - /// public string? IndexAlias { get; } } diff --git a/src/GameCult.Caching/CultManagedDocument.cs b/src/GameCult.Caching/CultManagedDocument.cs index ce86871b..fcfeaee8 100644 --- a/src/GameCult.Caching/CultManagedDocument.cs +++ b/src/GameCult.Caching/CultManagedDocument.cs @@ -7,35 +7,17 @@ using R3; namespace GameCult.Caching { - /// - /// Describes how a CultCache-managed document changed. - /// public enum CultCacheDocumentChangeKind { - /// - /// A document was added. - /// Added, - /// - /// A document was updated. - /// Updated, - /// - /// A document was removed. - /// Removed } - /// - /// One reactive CultCache document change. - /// public sealed class CultCacheDocumentChange where T : class { - /// - /// Creates a document change. - /// public CultCacheDocumentChange( CultCacheDocumentChangeKind kind, CultRecordKey key, @@ -48,24 +30,12 @@ namespace GameCult.Caching PreviousDocument = previousDocument; } - /// - /// Gets the change kind. - /// public CultCacheDocumentChangeKind Kind { get; } - /// - /// Gets the document record key. - /// public CultRecordKey Key { get; } - /// - /// Gets the current POCO presentation, when present. - /// public T? Document { get; } - /// - /// Gets the previous POCO presentation, when present. - /// public T? PreviousDocument { get; } } } diff --git a/src/GameCult.Caching/GameCult.Caching.csproj b/src/GameCult.Caching/GameCult.Caching.csproj index 194872bc..9188de14 100644 --- a/src/GameCult.Caching/GameCult.Caching.csproj +++ b/src/GameCult.Caching/GameCult.Caching.csproj @@ -1,6 +1,7 @@ CultCache: Concurrent polymorphic cache with modular backing stores for .NET + $(NoWarn);CS1591