Skip to content

List persisted keys that contain a tilde - #43

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/persisted-tilde-keys-listed
Sep 26, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/persisted-tilde-keys-listed

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #41

What changed

PersistenceProviderUtilities.GetKeyFromFileName returned null for any filename that contained ~, because it treated every such name as truncated. As a result, entries stored under keys like backup~1, ~draft or a~b could be retrieved and cleared, but GetAllKeysAsync never listed them.

A name is now treated as truncated only if it matches the whole shape that Truncate produces. The new IsTruncatedName check requires all of the following:

  • the name is 98–100 characters long (MaxEncodedLength minus the up-to-two characters Truncate backs off to avoid splitting a percent-escape)
  • the name ends with ~ followed by 16 lowercase hex digits

Why the encoding was not changed

The issue also suggested adding ~ to ReservedCharacters so a literal tilde would encode as %7E. I left that out on purpose. It would move every existing tilde key to a new filename. StoreAsync, RetrieveAsync and ExistsAsync look files up by encoded name, so entries already on disk under a raw ~ would stop being found by key, and the listing would then report keys that RetrieveAsync can't open. Checking the shape fixes the listing and leaves the filenames of existing data unchanged.

One ambiguity is not new: a key of 98–100 characters that happens to end in ~ plus 16 hex digits looks exactly like a truncated name. It stays hidden from the listing, as it was before this change.

Tests

  • Keys_Containing_A_Tilde_Are_Listed stores a~b, backup~1 and ~draft through FileSystemPersistenceProvider and asserts that GetAllKeysAsync returns all three.
  • SafeFileName_Roundtrips_Through_Decoding now also covers tilde keys.
  • SafeFileName_Bounds_Length_And_Stays_Distinct still passes, so truncated names are still reported as unrecoverable.
  • With the fix reverted, both the new test and the extended round-trip test fail.
  • Full suite passes: 905/905.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NHprKRwyYopkJnUyhF3KSr


Generated by Claude Code

GetKeyFromFileName treated any filename containing '~' as truncated and
returned null, so an entry stored under a key such as "backup~1" could be
retrieved and cleared but never appeared in GetAllKeysAsync. Truncated
names are now recognised by their full shape: at or just under the
maximum encoded length, ending in the marker and a 16-digit lowercase
hex hash.

Fixes #41

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHprKRwyYopkJnUyhF3KSr
Comment thread Essentials.Tests/PersistenceNamingTests.cs Fixed
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persisted keys containing ~ are stored and retrievable but missing from GetAllKeysAsync

2 participants