Keep MemoryFileSystem append writes private until transaction commit - #2185
martindurant merged 4 commits into
Conversation
| f = self.store[path] | ||
| if self._intrans and "a" in mode: | ||
| for pending in reversed(self.transaction.files): | ||
| if pending.path == path: |
There was a problem hiding this comment.
You mean, the same file has been opened more than once within the transaction? I'm not sure that something we particularly account for in other places either.
There was a problem hiding this comment.
Yes, e.g. two appends to the same file in one transaction. Without the lookup, the second open starts again from the committed bytes, so appending -1 then -2 to orig would commit orig-2 rather than orig-1-2 as on master. 4255a87 extends it to a file first written earlier in the same transaction. You're right that this isn't handled anywhere else, so if you'd rather keep it minimal I can drop the lookup and only copy on append.
There was a problem hiding this comment.
the second open starts again from the committed bytes,
I'm not sure that's wrong. Let's not process this here.
There was a problem hiding this comment.
OK, dropped the lookup in 9ad4344, so this is now just the copy on append.
Look up the latest pending file for the path before checking the store,
so an append also builds on a file first written earlier in the same
transaction ("wb" then "ab", or two appends to a new path). Previously
the later file started empty and replaced the earlier one on commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mE9zdG8orox5svQ6swNSr
An append inside a transaction only copies the committed bytes; a second open of the same file starts from the store again, like any other open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Appending to an existing
MemoryFileSystemfile insidefs.transactionchanges the stored bytes immediately, so an exception leaves the appended data behind._open()returns the committed buffer forab/a+b; this change appends to a copy instead, which is committed or discarded with the transaction.Minimal reproduction on current
master:Before the fix, the assertion fails with
b'original-uncommitted'. The transaction guarantee says an uncaught exception discards the files and leaves target locations untouched.Validation: 63 memory-backend tests and 7 local transaction tests pass. The regression covers commit/rollback, both append modes and shared/independent stores; all eight cases fail before the fix.
Prepared with AI assistance.