Ask HN: Why don't file systems and OS's provide file system transactions?
This is inspired by [1].
Quite frankly, I don't know why filesystems don't provide these things.
I have read that Windows has a transactional API, but they've actually deprecated it! [2] They say it's because few programs use it.
I mean, sure, that might be true, but I bet it's really important for those programs that do use it.
Bonus question: why does Windows hide its equivalent of `openat()` in the NT API? [3] Rust code seems to claim its fundamental to the NT kernel [4], so why is it not exposed?
[1]: https://news.ycombinator.com/item?id=32190032
[2]: https://docs.microsoft.com/en-us/windows/win32/fileio/deprec...
[3]: https://docs.microsoft.com/en-us/windows/win32/api/winternl/...
[4]: https://github.com/rust-lang/rust/blob/1c63ec48b8cbf553d291a...
31 comments
[ 2.8 ms ] story [ 76.7 ms ] threadhttps://pubs.opengroup.org/onlinepubs/9699919799/functions/r...
ISO C 9899:2018 (actually the 2017 draft) says in 7.21.4.2.3 that on a failure return the file is available by its original name, but that's a much weaker guarantee as that doesn't say anything about failing to return (e.g. hard system crash)…
It certainly can be a convenient and productive abstraction but the model itself has very clear natural limits.
IMHO you can probably ignore their deprecation and keep using it. The set of things MS deprecates and the set of things they actually remove from the OS are quite different. IIRC their own components depend on FS transactions and I don't see them rewriting their own components anytime soon. However, note that even without deprecation, transactions can fail for a variety of reasons (not just conflicts), so you'll need fallbacks anyway.
> why does Windows hide its equivalent of `openat()` in the NT API?
I don't know for certain but I've always imagined it's because (a) Win32 programmers (or for that matter, most programmers) are used to the path-based API, and (b) it would be much slower to perform manual traversal level-by-level, and (c) I think in practice there aren't that many common scenarios where the race condition can realistically turn into a security vulnerability.
what sort of semantics would you want out of the transaction? atomicity? isolation? durability? how should concurrency behave? should there be a mvcc implementation?
linux provides atomic writes up to 4k. moves on the same fs are also atomic.
fsync ensures writes are durable and written to disk (allegedly[0])
file advisory locks can be used to ensure mutual exclusion. or memory mapping in the file to shared memory and allocating a mutex in it (libapr provides a few options for interprocess mutual exclusion)
...but in reality, if you need transactional semantics, you're really just better off using a database. because the database developers will have a much better idea of the nuances that applications need from transactional semantics than the kernel devs will.
and if you want your program that requires transactional semantics to be portable, major database vendors have already dealt with inconsistencies across multiple major operating systems. because of that the database gives one system to handle transactions, versus pushing the portability concerns onto each individual application.
[0] https://news.ycombinator.com/item?id=19119991
they've put a lot of effort into making it work well even in likely-to-crash situations (e.g., running on a phone with a user who doesn't tend to the battery)
https://www.sqlite.org/fasterthanfs.html
https://www.sqlite.org/atomiccommit.html
https://www.sqlite.org/hirely.html
It was very easy to program. I saw colleagues with no computer science education writing complex queries on "Pick Basic", what would have been impossible for them on classical computers of the time (or of today as well).
https://en.wikipedia.org/wiki/Pick_operating_system
Consider a process P1 using a fictional transactional API in an OS and is accessing the path `/a/b/c` and is creating some files under directory `c`.
Consider another process P2 executing a `mv /a/b /x`.
P1 uses the transaction API, but P2 is not. So, under the covers the system calls will all have to use the new transactional API to ensure global correctness. That is asking a lot of the kernel and possibly makes a lot of legacy programs slower.
The other question to answer is: do we want ACID properties or eventual consistency to be guaranteed by the transaction? What to do when some processes want ACID guarantees and some processes are okay with eventual consistency? How does the kernel handle concurrent running of these processes in contention with the same resources under two different transaction semantics.
These are some of the reasons why the transaction management is better handled in userspace.
There’s a lot that these APIs hide from you in order to make it so you can pretend that this is the 1970s and your file is essentially a piece of tape. We’ve then built filesystems and directory structures on top of this piecemeal. The abstraction covers all these fancy journaling structures and a whole page cache.
For something that dives into the consequences, the classic paper “Don’t stack your log on my log”
https://www.usenix.org/system/files/conference/inflow14/infl...
Even if that is the case, lets not make it worse :).
Transactional operations bring the chance of deadlocks. Deadlocks cause performance and denial of service implications. Deadlocks are far more easier to detect vs prevent vs avoid. Detected deadlocks are resolved by killing one of the requests, which must be handled by the cooperating and highly coupled processes.
The filesystem is an abstraction of convenience and very loose rules. Instead of all the structure and rigor a database brings a program just gives a string to the os and gets bytes back. The cost of this ease of use is that you must keep your program's demands and expectations low.
In a fully controlled environment you could do it, just like you can do cooperative scheduling, but outside of such an environment you just can’t do it in any way sanely.
They do. F2FS [1] does. There were attempts to add them to xfs/ext4 too, but they petered out, probably because of lack of interest.
[1] https://www.kernel.org/doc/html/latest/filesystems/f2fs.html
Would love to hear more details if anyone is knowledgeable of this arcane history.
So I think it depends on the context and what you wanna use it for. I don't see the transactional features of a file system as useful for actual users that are directly interact with files on their file system. It seems more useful in the context of applications that maintain files and where you likely do not want the user to directly interact with those files.
Take a look at https://vldb.org/pvldb/vol15/p21-skiadopoulos.pdf