Skip to content

feat(storage): persist any storage that promises a restart finds its data - #658

Open
zond wants to merge 1 commit into
ikatson:mainfrom
zond:storage-persistable-promise
Open

zond wants to merge 1 commit into
ikatson:mainfrom
zond:storage-persistable-promise

Conversation

@zond

@zond zond commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Session persistence refuses to serialize a torrent whose storage factory isn't a FilesystemStorageFactory, and persistence is on by default - so a session configured with any other storage fails every add outright.

The check is about a type, but what it is protecting is a promise. The persisted record is the torrent, an output folder, a file selection and a paused flag, with a have-bitfield beside it; it names no storage at all. A restart replays the record through add_torrent, which builds the session's default storage. So a storage is persistable when two things hold. One: the storage the restart builds reaches the same data - free for the filesystem one, since the record carries the output folder and the bytes are laid out as the torrent's own files. Two: the data is still there when the next process asks, because the have-bitfield outlives the process and the restart takes it at its word - it hashes one piece per file plus at most 64 sampled ones, of a torrent however large. Neither of those is a property of a type, and both are things only the storage knows.

StorageFactory::ensure_persistable() is that promise. It defaults to no and names the factory that didn't make it, so a storage persistence can't handle is refused when the torrent is added rather than at the next restart, and it forwards through boxing and through the middlewares that forward is_type_id - losing it there would refuse a perfectly persistable storage, or worse, invent a promise nobody made. FilesystemStorageFactory promises it, so the default path is unchanged.

The tests drive all three cases end to end: a storage that keeps the torrent as one flat blob of its own, persisted and restored across a session restart with a factory rebuilt from scratch; a storage that doesn't promise, refused at add time with a message naming it; and the filesystem storage, asserted rather than assumed.

Also created by my Claude Fable. This one is a bit of a design change, but I think it makes sense, other storages than the default FS storage are able to restart, right?

…data

Session persistence refuses to serialize a torrent whose storage factory isn't a
FilesystemStorageFactory, and persistence is on by default - so a session
configured with any other storage fails every add outright.

The check is about a type, but what it is protecting is a promise. The persisted
record is the torrent, an output folder, a file selection and a paused flag, with
a have-bitfield beside it; it names no storage at all. A restart replays the
record through add_torrent, which builds the session's default storage. So a
storage is persistable when two things hold. One: the storage the restart builds
reaches the same data - free for the filesystem one, since the record carries the
output folder and the bytes are laid out as the torrent's own files. Two: the
data is still there when the next process asks, because the have-bitfield
outlives the process and the restart takes it at its word - it hashes one piece
per file plus at most 64 sampled ones, of a torrent however large. Neither of
those is a property of a type, and both are things only the storage knows.

StorageFactory::ensure_persistable() is that promise. It defaults to no and names
the factory that didn't make it, so a storage persistence can't handle is refused
when the torrent is added rather than at the next restart, and it forwards
through boxing and through the middlewares that forward is_type_id - losing it
there would refuse a perfectly persistable storage, or worse, invent a promise
nobody made. FilesystemStorageFactory promises it, so the default path is
unchanged.

The tests drive all three cases end to end: a storage that keeps the torrent as
one flat blob of its own, persisted and restored across a session restart with a
factory rebuilt from scratch; a storage that doesn't promise, refused at add time
with a message naming it; and the filesystem storage, asserted rather than
assumed.

Co-Authored-By: Claude Opus 5 <[email protected]>
@zond
zond force-pushed the storage-persistable-promise branch from 007ac0b to b5b5188 Compare September 7, 2026 13:46
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.

1 participant