Single-file and directory snapshot reads judge the format stamp before parsing the body; a flush heals an empty or truncated file

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-22 17:30:57 +02:00
commit 39e8ab5efd
5 changed files with 157 additions and 31 deletions

View file

@ -448,22 +448,25 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore
};
}
var snapshot = CultDocumentMessagePackSerialization.DeserializeSnapshot(manifestBytes);
if (CultPersistedStoreSnapshot.FormatNumber(snapshot.FormatVersion) is int number && number > 4)
// The stamp is judged before the manifest body is parsed, so a store stamped newer is refused whatever its body.
return CultDocumentMessagePackSerialization.DeserializeSnapshot(manifestBytes, RequireReadableStamp);
}
private void RequireReadableStamp(string stamp)
{
if (CultPersistedStoreSnapshot.FormatNumber(stamp) is int number && number > 4)
{
// Every write path parses the manifest under its commit lease before it writes a page or the manifest, so a
// store another process has since stamped newer is refused here, untouched.
throw new NotSupportedException(
$"Directory store {_manifestFile.FullName} is {snapshot.FormatVersion}, a newer format than this runtime reads ({IndexedFormatVersion}).");
$"Directory store {_manifestFile.FullName} is {stamp}, a newer format than this runtime reads ({IndexedFormatVersion}).");
}
if (!string.Equals(snapshot.FormatVersion, IndexedFormatVersion, StringComparison.Ordinal))
if (!string.Equals(stamp, IndexedFormatVersion, StringComparison.Ordinal))
{
throw new InvalidOperationException(
$"Directory store {_manifestFile.FullName} is {snapshot.FormatVersion}; only {IndexedFormatVersion} is readable.");
$"Directory store {_manifestFile.FullName} is {stamp}; only {IndexedFormatVersion} is readable.");
}
return snapshot;
}
private string ContentAddressedRecordPath(CultPersistedRecord metadata)