A microkernel filesystem changes the failure boundary. When a monolithic kernel filesystem fails, the machine normally crashes and applications lose their volatile progress with it. When a user-space filesystem server fails, the operating system and clients can remain alive. Restarting only the server should improve availability, but it creates a stricter correctness obligation: the new server must reproduce the state that surviving applications believe exists.
That state is larger than the durable disk image. A client may have received success for buffered writes, retained open file descriptors with advanced offsets, or observed a rename that has not yet reached storage. An ordinary journal restores a consistent persisted state after a system crash. It cannot reconstruct this gap between application-visible memory and disk. Forcing every update durable closes the gap but can make write-heavy software several times slower.
Ananke adds a recovery plane to the uFS user-space filesystem[1]. It logs the semantic effects of completed calls in protected memory, prepares a replacement process before failure, and lets the host kernel coordinate the handoff. Recovery combines the disk journal with the volatile log rather than trusting the damaged process address space. The design seeks application continuity without turning rare failures into permanent I/O synchronization cost.
Process crashes and system crashes need different evidence
A full-system crash destroys all volatile state, so recovery must begin from durable media. A filesystem process crash leaves the kernel, applications, shared-memory IPC rings, and most machine memory available. Treating both events identically wastes this surviving evidence and tells clients that successful operations vanished even though the rest of their execution continues.
The state gap has three forms. File descriptors are ephemeral objects whose existence and offsets must be restored. Inodes contain buffered data and metadata that may be only partly durable. Path-to-inode mappings change through create, unlink, and rename. Background writeback can persist these effects out of program order, so replaying every prior call in sequence can duplicate durable work or encounter different preconditions.
Ananke separates process-crash recovery from the existing system-crash journal. The disk log remains authoritative for durable consistency. A process-crash log, or p-log, records precisely which call effects may still exist only in memory. The two logs cover different time domains and are composed during restart.

The p-log records effects rather than copying the cache
Each worker core owns a circular p-log. An entry contains the system call, arguments, return value, involved file descriptor and inode targets, references to written pages, completion time, and checksums. A bitmap states which targets still contribute to the gap. When an inode becomes durable, Ananke clears its bits in earlier entries. When a descriptor closes, its ephemeral contribution can also disappear.
The log does not duplicate every dirty byte. It keeps logical references to page-cache data, and the kernel rescues pages referenced by live entries during failure handling. This choice controls common-path memory and copy cost, but makes the rescue procedure part of the trusted recovery boundary. The implementation uses a replicated, pointer-free p-log and checksums so a fresh process can read it without following possibly corrupted pointers from the failed heap.
Garbage collection removes entries whose effects are fully durable or no longer visible. A log threshold that is too small forces frequent collection or background sync and reduces throughput. The evaluation used a 4 MiB threshold per copy, producing 8 MiB per core with replication. This is predictable metadata capacity, not a complete shadow filesystem.
Act, Ignore, or Modify before replay
Naive replay is incorrect because a single call can have both durable and volatile effects. A write may have reached its inode while its descriptor offset remains part of the live application state. A create may have a durable pathname but an open descriptor that still must exist. Repeating the original operation would alter already-correct storage; skipping it would break the client.
The Act-Ignore-Modify algorithm classifies each logged operation from its remaining target bits. Act repeats an operation when none of its relevant effects has become durable. Ignore drops it when every effect is already durable or obsolete. Modify substitutes a related call that recreates only the missing semantic portion. For example, a persisted write can become an offset adjustment, while a durable create can become an open of the existing inode.
This approach reuses the filesystem’s own API implementations rather than building a second state reconstructor for every data structure. Its applicability depends on expressing state changes through well-defined descriptor, inode, and pathname semantics. Filesystems with memory-mapped updates, proprietary transactional interfaces, or device-specific side effects need an explicit extension of that semantic inventory.
A clean process waits before the crash
Recovery should not reuse a heap that may contain the fault. Ananke starts a passive secondary process alongside the primary and performs expensive storage-device initialization in advance. The secondary blocks until the host kernel observes the primary exit. It then receives the protected log and client message rings, reattaches pinned storage memory, runs disk recovery and semantic replay, and resumes service.
Preinitialization hides device connection setup that can otherwise take seconds. Kernel coordination also gives one authority responsibility for detecting exit, transferring IPC resources, and discarding the failed process. The fresh address space limits what recovery trusts, but not everything is inside the kernel. In the prototype, a signal handler in the failed process participates in rescuing pages, which caused a small set of corruption cases to lose data when its stack or heap was damaged. The authors propose moving rescue into read-only kernel code.
Checksums over important in-memory semantic structures trigger failure before corrupted state is returned or persisted. The paper measures 2.85% performance overhead for these broad checks alone. Combined with p-log replication, full protection remained below 2% for most workloads but reached about 7% for the most write-intensive copy and LevelDB load cases. Reliability features therefore have distinct costs that should be measured separately.
Fault injection tests transparency, not only restart
The evaluation covers utilities such as sort, copy, and unzip plus SQLite and LevelDB. More than 30,000 fail-stop injection points included crashes during and between calls and runs with background synchronization. In every Ananke run, applications completed with the same data as the no-crash case. Three concurrent client applications also recovered at 300 random points.
Memory-corruption experiments changed stack, heap, filesystem metadata, and p-log regions. Of 18,100 injections, 3,373 affected memory that the workload later used and manifested an error. Every such case restarted and recovered correct metadata. Nine cases produced incorrect file data because the prototype’s user-space rescue procedure could itself be disrupted. Thus the broad claim is prompt recovery from detected corruption, with a documented gap in the final page-rescue mechanism.
Recovery completed within 400 ms. LevelDB examples ranged from 102 to 171 ms for Ananke, with write-heavy workloads preserving rescued dirty pages and read-heavy workloads temporarily slowing while clean cache pages warmed again. These intervals avoid an application restart, but they are not invisible to every latency SLO. Client timeout and retry policies must tolerate the pause without issuing duplicate higher-level operations.
Common-path cost determines whether isolation pays
Synchronizing before every success gave transparent recovery in a comparison but made uFS-Sync up to six times slower. A Membrane-style full synchronization around certain updates made file-creation and write-heavy workloads up to 3.4 times slower. Ananke’s log-only path was below 2% for most cases, with higher overhead under intensive writes and full memory protection.
In the reported cases, CRC storage consumed under one-hundredth of a percent of workload memory; the replicated p-log separately reserved 8 MiB per core. Those numbers scale with filesystem worker count rather than total application memory. Operators should also budget pinned rescued pages during recovery and the warm standby process. The design exchanges a bounded always-on memory reservation for lower I/O amplification and faster restart.
The evaluation uses uFS with SPDK and controlled workloads. Production deployment needs repeated-crash behavior, device-reset failures, log exhaustion, mixed tenants, mmap semantics, direct I/O, and application deadlines. It also needs observability for gap size, oldest unreclaimed entry, rescued-page bytes, recovery phase latency, and checksum-trigger source. Without these counters, an apparently fast restart can hide growing volatile exposure.
Recovery semantics become part of the filesystem API
Ananke’s larger lesson is that fault isolation is incomplete without continuity semantics. Moving a subsystem into a process prevents one fault from taking down the machine, but clients survive with expectations that the replacement must honor. A recovery design must identify which volatile facts have crossed an API boundary and preserve exactly those facts.
This principle extends beyond filesystems. A user-space network stack, storage target, or accelerator runtime may acknowledge work before all state is durable. A process-level restart needs a compact semantic record, a clean replacement, and a transformation from surviving evidence to client-visible state. Reusing a system-crash protocol can be consistent yet wrong for a surviving caller.
Deployment should therefore compare completed application work across failures, not server restart time alone. The relevant denominator includes steady-state overhead, memory reserved per worker, visible pause, unrecoverable injection classes, and the probability that client timeouts create duplicate work. Ananke shows that a protected operation log can make microkernel isolation useful without synchronous persistence on every call, provided the rescue path is moved fully outside the failed process.
Source and copyright notice
This article is an editorial analysis by Silicon & Systems. It restates the design, measurements, and limitations in our own words. No source sentence, table, or figure is reproduced; the figure was created for this article. The paper is available from the USENIX FAST 2025 presentation page. Copyright remains with the authors, 2025.