One owner for the foreign-record refusal: it reaches the cache, names every key, and refuses only what the cache wrote
CacheBackingStore.RefuseForeign now decides, for both store kinds, which written keys a foreign record refuses, drops the store's staged change and the cache's copy of each through the Loaded path a pull uses, and throws one conflict naming every refused key. The single-file store tracks the keys staged since its last load or write, as the directory store already did, so a clean copy of a record another writer made foreign is carried, not refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bc15dba81b
commit
86eea40bce
3 changed files with 178 additions and 47 deletions
|
|
@ -243,28 +243,19 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore
|
|||
}
|
||||
|
||||
// The manifest's other records stay as they are, foreign ones included; a write that would replace or remove a foreign
|
||||
// record is refused before any page is written. The refusal takes the refused keys with it: a staged change to a record that
|
||||
// is foreign now, and this store's copy of one, are not what the file holds, and kept they would refuse every later write.
|
||||
private void RefuseForeign(CultPersistedStoreSnapshot currentManifest, IEnumerable<string> written)
|
||||
{
|
||||
var foreign = currentManifest.Records
|
||||
.Where(record => Foreign(record, currentManifest.SchemaCatalog) != null)
|
||||
.Select(record => record.Key)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
var refused = written.Concat(_deletedKeys.Keys).Concat(_dirtyKeys.Keys).Where(foreign.Contains).Distinct().ToArray();
|
||||
if (refused.Length == 0)
|
||||
return;
|
||||
foreach (var key in refused)
|
||||
{
|
||||
Entries.TryRemove(key, out _);
|
||||
_dirtyKeys.TryRemove(key, out _);
|
||||
_deletedKeys.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
IsDirty = !_dirtyKeys.IsEmpty || !_deletedKeys.IsEmpty;
|
||||
var held = currentManifest.Records.First(record => record.Key == refused[0]);
|
||||
throw Overwrites(Foreign(held, currentManifest.SchemaCatalog)!);
|
||||
}
|
||||
// record is refused before any page is written, by the one rule every store shares (CacheBackingStore.RefuseForeign).
|
||||
private void RefuseForeign(CultPersistedStoreSnapshot currentManifest, IEnumerable<string> written) =>
|
||||
RefuseForeign(
|
||||
written.Concat(_deletedKeys.Keys).Concat(_dirtyKeys.Keys),
|
||||
key => currentManifest.Records.FirstOrDefault(record => record.Key == key) is { } held
|
||||
? Foreign(held, currentManifest.SchemaCatalog)
|
||||
: null,
|
||||
key =>
|
||||
{
|
||||
_dirtyKeys.TryRemove(key, out _);
|
||||
_deletedKeys.TryRemove(key, out _);
|
||||
IsDirty = !_dirtyKeys.IsEmpty || !_deletedKeys.IsEmpty;
|
||||
});
|
||||
|
||||
// Runs under the commit lease: pages first, then the manifest that names them.
|
||||
private void WriteGeneration(CultPersistedStoreSnapshot currentManifest)
|
||||
|
|
|
|||
Loading…
Reference in a new issue