Microkernel filesystem은 failure boundary를 바꿉니다. Monolithic kernel filesystem이 실패하면 machine 전체가 보통 crash하고 application도 volatile progress를 함께 잃습니다. User-space filesystem server가 실패하면 operating system과 client는 살아 있을 수 있습니다. Server만 restart하면 availability가 좋아져야 하지만 replacement는 surviving application이 존재한다고 믿는 state를 그대로 재현해야 합니다.

이 state는 durable disk image보다 큽니다. Client는 buffered write의 success를 이미 받았을 수 있고 advanced offset을 가진 open file descriptor를 유지할 수 있으며 storage에 아직 도달하지 않은 rename을 관찰했을 수 있습니다. 일반 journal은 system crash 뒤 persisted state의 consistency를 복구합니다. Application-visible memory와 disk 사이의 gap은 재구성하지 못합니다. 모든 update를 durable하게 만들면 gap은 닫히지만 write-heavy software가 몇 배 느려질 수 있습니다.

Ananke는 uFS user-space filesystem에 recovery plane을 추가합니다[1]. Completed call의 semantic effect를 protected memory에 기록하고 failure 전에 replacement process를 준비하며 host kernel이 handoff를 조정합니다. Recovery는 damaged process address space를 신뢰하지 않고 disk journal과 volatile log를 결합합니다. Rare failure를 permanent I/O synchronization cost로 바꾸지 않으면서 application continuity를 얻는 구조입니다.

다른 evidence가 필요한 process crash와 system crash

Full-system crash는 모든 volatile state를 없애므로 recovery가 durable media에서 시작해야 합니다. Filesystem process crash는 kernel, application, shared-memory IPC ring, machine memory 대부분을 남깁니다. 두 event를 똑같이 처리하면 surviving evidence를 버리고 나머지 execution이 이어지는데도 successful operation이 사라졌다고 client에 알리게 됩니다.

State gap은 세 형태입니다. File descriptor는 existence와 offset을 복원해야 하는 ephemeral object입니다. Inode에는 일부만 durable한 buffered 데이터와 metadata가 있습니다. Path-to-inode mapping은 create, unlink, rename으로 달라집니다. Background writeback은 이 effect를 program order와 다르게 persist할 수 있으므로 모든 prior call을 순서대로 replay하면 durable work를 반복하거나 다른 precondition을 만날 수 있습니다.

Ananke는 process-crash recovery를 기존 system-crash journal과 분리합니다. Disk log는 durable consistency의 authoritative record로 남습니다. Process-crash log(p-log)는 call effect 중 memory에만 남을 수 있는 부분을 기록합니다. Restart 때 서로 다른 time domain을 다루는 두 log를 합칩니다.

Ananke는 durable crash consistency와 application-visible process recovery를 분리합니다. Running filesystem은 completed call과 영향을 받은 descriptor, inode, pathname mapping을 replicated·checksummed p-log에 기록합니다. Process crash 뒤 host kernel은 rescued page, client ring, p-log를 미리 initialize한 clean process에 넘깁니다. Disk-journal recovery가 durable state를 복원한 뒤 Act-Ignore-Modify replay가 남은 volatile state gap만 재구성합니다. 이 글을 위해 새로 만든 도판.

Cache 전체가 아니라 effect를 기록하는 p-log

Worker core마다 circular p-log 하나를 가집니다. Entry는 system call, argument, return value, 관련 file descriptor 및 inode target, written page reference, completion time, checksum을 담습니다. Bitmap은 어느 target이 아직 gap에 기여하는지 표시합니다. Inode가 durable해지면 Ananke는 earlier entry에서 해당 bit를 clear합니다. Descriptor가 close되면 ephemeral contribution도 사라질 수 있습니다.

Log는 dirty byte 전체를 복제하지 않습니다. Page-cache 데이터의 logical reference를 보관하고 kernel이 failure handling 중 live entry가 가리키는 page를 rescue합니다. 이 선택은 common-path memory와 copy cost를 제한하지만 rescue procedure를 trusted recovery boundary에 넣습니다. Implementation은 replicated, pointer-free p-log와 checksum을 사용하므로 fresh process가 failed heap의 possibly corrupted pointer를 따라가지 않습니다.

Garbage collection은 effect가 모두 durable하거나 visible하지 않은 entry를 제거합니다. Log threshold가 너무 작으면 frequent collection 또는 background sync가 발생해 throughput이 줄어듭니다. 평가는 copy당 4MiB threshold를 사용했고 replication을 포함하면 core당 8MiB였습니다. 이는 complete shadow filesystem이 아니라 bounded metadata capacity입니다.

Replay 전 Act, Ignore, Modify 판정

한 call이 durable effect와 volatile effect를 함께 가질 수 있어 naive replay는 틀립니다. Write가 inode에는 도달했지만 descriptor offset은 live application state에 남을 수 있습니다. Create의 pathname은 durable해도 open descriptor는 다시 만들어야 할 수 있습니다. Original operation을 반복하면 이미 correct storage를 바꾸고, skip하면 client를 깨뜨립니다.

Act-Ignore-Modify algorithm은 remaining target bit로 logged operation을 분류합니다. Relevant effect가 durable하지 않으면 Act가 original operation을 반복합니다. 모든 effect가 durable하거나 obsolete면 Ignore가 버립니다. 일부 semantic portion만 필요하면 Modify가 related call로 바꿉니다. 예를 들어 persisted write는 offset adjustment가 되고 durable create는 existing inode를 여는 operation이 될 수 있습니다.

이 approach는 데이터 structure별 second reconstructor를 만들지 않고 filesystem의 own API implementation을 재사용합니다. Applicability는 state change를 descriptor, inode, pathname semantic으로 표현할 수 있는지에 달려 있습니다. Memory-mapped update, proprietary transactional interface, device-specific side effect가 있는 filesystem은 semantic inventory를 명시적으로 확장해야 합니다.

Crash 전에 기다리는 clean process

Recovery가 fault를 포함할 수 있는 heap을 재사용해서는 안 됩니다. Ananke는 primary와 함께 passive secondary process를 시작하고 expensive storage-device initialization을 미리 수행합니다. Secondary는 host kernel이 primary exit를 관찰할 때까지 block합니다. 이후 protected log와 client message ring을 받고 pinned storage memory를 다시 연결하며 disk recovery 및 semantic replay 뒤 service를 재개합니다.

Preinitialization은 다른 방식으로 seconds가 걸릴 수 있는 device connection setup을 숨깁니다. Kernel coordination은 exit detect, IPC resource transfer, failed process discard를 하나의 authority가 처리하게 합니다. Fresh address space는 recovery가 신뢰하는 memory를 제한하지만 prototype의 모든 단계가 kernel 안에 있지는 않습니다. Failed process의 signal handler가 page rescue에 참여해 stack 또는 heap이 손상된 소수 case에서 데이터를 잃었습니다. 저자들은 rescue를 read-only kernel code로 옮길 것을 제안합니다.

Important in-memory semantic structure의 checksum은 corrupted state가 return 또는 persist되기 전에 failure를 일으킵니다. 논문에서 broad checksum alone의 performance overhead는 2.85%였습니다. P-log replication과 결합한 full protection은 대부분 workload에서 2% 미만이지만 가장 write-intensive한 copy와 LevelDB load에서는 약 7%였습니다. Reliability feature별 cost를 따로 측정해야 하는 이유입니다.

Restart가 아니라 transparency를 시험한 fault injection

평가는 sort, copy, unzip utility와 SQLite, LevelDB를 포함합니다. 30,000회가 넘는 fail-stop injection point는 call 도중과 사이, background synchronization이 있는 run을 포함했습니다. 모든 Ananke run에서 application은 no-crash case와 같은 데이터로 완료했습니다. Concurrent client application 세 개도 300개 무작위 point에서 회복했습니다.

Memory-corruption experiment는 stack, heap, filesystem metadata, p-log region을 바꿨습니다. 18,100회 injection 중 workload가 이후 사용한 memory에 영향을 줘 error가 드러난 case는 3,373개였습니다. 모두 restart했고 correct metadata를 회복했습니다. Prototype의 user-space rescue procedure가 방해받은 9개 case는 file 데이터가 incorrect했습니다. 따라서 broad claim은 detected corruption에서 prompt recovery이며 final page-rescue mechanism에는 documented gap이 있습니다.

Recovery는 400ms 이내에 끝났습니다. LevelDB example에서 Ananke는 102~171ms였고 write-heavy workload는 rescued dirty page를 유지했으며 read-heavy workload는 clean cache page를 다시 데우는 동안 잠시 느려졌습니다. Application restart를 피할 수 있지만 모든 latency SLO에 invisible한 interval은 아닙니다. Client timeout과 retry policy가 pause를 견디고 duplicate higher-level operation을 만들지 않아야 합니다.

Isolation의 가치를 결정하는 common-path cost

Success마다 sync한 comparison은 transparent recovery를 제공했지만 uFS-Sync를 최대 6배 느리게 만들었습니다. Certain update 주변에서 full synchronization을 한 Membrane-style 방식은 file-creation 및 write-heavy workload를 최대 3.4배 느리게 했습니다. Ananke의 log-only path는 대부분 case에서 2% 미만이었고 intensive write와 full memory protection에서는 더 높았습니다.

CRC storage는 reported case에서 workload memory의 0.01% 미만이었고 replicated p-log는 core당 8MiB를 reserve했습니다. 이 숫자는 total application memory가 아니라 filesystem worker count에 따라 scale합니다. Operator는 recovery 중 pinned rescued page와 warm standby process도 budget해야 합니다. Bounded always-on memory reservation을 주고 I/O amplification과 restart time을 줄이는 구조입니다.

평가는 uFS, SPDK, controlled workload를 사용했습니다. 실제 deployment에는 repeated crash, device-reset failure, log exhaustion, mixed tenant, mmap semantic, direct I/O, application deadline 검증이 필요합니다. Gap size, oldest unreclaimed entry, rescued-page byte, recovery phase latency, checksum-trigger source의 observability도 있어야 합니다. 이런 계수기가 없으면 겉으로 빠른 restart가 growing volatile exposure를 숨길 수 있습니다.

Filesystem API에 포함되는 recovery semantics

Ananke가 보여 주는 더 큰 판단은 fault isolation만으로 continuity가 완성되지 않는다는 점입니다. Subsystem을 process로 옮기면 한 fault가 machine을 내리는 것은 막지만 client는 replacement가 지켜야 할 expectation과 함께 살아 있습니다. Recovery design은 어떤 volatile fact가 API boundary를 넘어갔는지 식별하고 정확히 그 fact를 보존해야 합니다.

이 principle은 user-space network stack, storage target, accelerator runtime에도 적용됩니다. Work를 acknowledge한 뒤 모든 state가 durable하지 않을 수 있습니다. Process-level restart에는 compact semantic record, clean replacement, surviving evidence를 client-visible state로 바꾸는 transformation이 필요합니다. System-crash protocol만 재사용하면 consistent하면서도 surviving caller에는 wrong일 수 있습니다.

Deployment는 server restart time만이 아니라 failure를 포함한 completed application work를 비교해야 합니다. Denominator에는 steady-state overhead, worker당 reserved memory, visible pause, unrecoverable injection class, client timeout이 duplicate work를 만들 probability가 들어갑니다. Ananke는 protected operation log가 every-call synchronous persistence 없이 microkernel isolation을 유용하게 만들 수 있음을 보여 줍니다. 단 page rescue path는 failed process 밖으로 완전히 옮겨야 합니다.

출처와 저작권 안내

이 글은 Silicon & Systems가 작성한 편집 분석으로 design, measurement, limitation을 우리 표현으로 다시 썼습니다. 원문의 문장, 표, 도판은 재수록하지 않았고 도판은 이 글을 위해 새로 만들었습니다. 전체 논문은 USENIX FAST 2025 발표 페이지에서 확인할 수 있습니다. 저작권은 저자에게 있습니다. 2025.