filesystem 동기화 작업은 현재 journaling 기술의 compound transaction 방식으로 지연 될 수 있다. fsync()는 특정 file에 대해 durability를 보장하는 시스템 콜 이다. 현재 JBD2 동작은 특정 file에 대해 fsync가 호출하면 compound transaction에 대해 commit 요청을 보낸다. compound transaction은 fsync가 호출된 파일의 데이터 및 메타 데이터 뿐만 아니라 다른 파일의 메타데이터와 데이터도 포함한다. 따라서, JBD의 fsync()는 durablilty를 요청받지 않은 데이터와 메타데이터까지 저널 영역으로 commit을 완료해야 하므로 latency가 길다. 따라서, 기존 JBD2의 동작은 fsync-intensive 워크로드에 대하여 확장성이 좋지 않다. 본 논문에서, 저자는 compound transaction기법으로 인해 fsync latency가 확장성이 좋지 않은 문제를 iJournaling으로 해결하고자 한다.
본 논문이 제안한 iJournaling 기법은 fsync의 요청을 처리하기 위해 file-level의 정보를 담고 있는 i-transaction을 commit 한다. iJournaling 기법은 스토리지 공간에 journal 영역과 ijournal 영역을 할당한다. 기존 JBD 스레드가 주기적으로 journal transaction commit 동작을 할때 transaction은 journal 영역으로 기록된다. 주기적으로 발생하는 journal transaction의 동작은 기존 JBD2의 동작과 동일하며 transaction 자료구조도 바뀌지 않는다.
iJournaling의 주요 자료구조는 iJournal transaction (i-transaction) 이다. i-transaction은 file i-transaction 과 directory i-transaction 두가지 타입을 가지고 있다. File i-transaction은 하나의 head block, 여러개의 external extent[1] block들 (존재한다면), 그리고 하나의 commit block으로 구성되어있다. Head block은 journal head을 가지고 있는데 이 journal head는 일반 JBD2에서 사용되는 journal head 자료구조와 동일한 자료구조로 magic number와 transaction ID를 가지고 있다. Journal head의 transaction ID는 JBD2가 사용하는 일반 running transaction의 tid값이다. 그 running transaction은 i-transaction의 file-level 변경사항과 해당 file-level 변경사항과 함께 수정된 모든 메타데이터 수정사항을 가지고 있다. iJournal head의 inode 번호와 inode 자료구조 정보는 inode table, inode bitmap, 그리고 GDT를 복구하는데 사용되어진다. iJournal head의 block tag는 external extent block의 실제 file-system block 번호가 기록되어있다.
Directory i-transaction은 directory의 수정사항과 관련된 정보를 기록한다. File을 생성하고 fsync를 하였을때, 해당 file의 부모 directory entry가 system crash 이전에 commit 되어지지 않았다면 해당 file은 복구가 되더라도 접근하지 못한다. iJournaling은 fsync되 file과 관련있는 모든 directory들을 식별하기 위해 inode 자료구조에 uncommitted_DE flag를 추가하였다. 새로운 file이 생겨날때, 생성된 파일의 inode에서 uncommitted_DE flag는 아직 directory entry가 기록되지 않았다는 것을 의미한다. 유저 프로세스가 파일에 fsync를 호출하면 먼저 uncommitted_DE 플래그를 확인한다. uncommitted_DE 플래그가 있으면, uncommitted_DE 플래그가 없는 가장 최상위 부모 디렉토리부터 순서대로 directory i-transaction을 commit해야 한다.
Figure 1: structure of i-transaction.
Directory i-transaction과 file i-transaction 두가지 모두 특정 file에 대한 수정 metadata정보만 담을 수 있으며 공통적으로 inode 정보를 가지고 있다. 두 i-transaction의 차이는 file i-transaction는 external extent block이 들어가고, directory i-transaction은 directory entry가 들어간다는 점이다. inode structure, external extent , 다른 file도 수정하는 metadata block (superblock, block bitmap, GDT, inode bitmap)은 i-transaction에 들어가지 않는다.
iJournaling 기법은 특정 file에 대한 durable을 보장하기 위해 스토리지 iJournal 영역으로 i-transaction commit을 한다. 유저 프로그램이 특정 파일에 대해 fsync를 호출하면 i-transaction을 iJournal 영역으로 commit한다.
Figure 2: Block bitmap recovery with iJournal.
Commit된 (committed) i-transaction으로 file system 복구하는 것은 global metadata block을 재생하는 것이 핵심이다. Global metadata는 여러 file들의 수정사항에 대해 공통적으로 수정되는 block (superblock, block bitmap, GDT, inode bitmap)들을 의미한다. Global metadata block 복구는 old global metadata block으로 부터 i-transaction에 기록된 변경 사항을 재생하여 복구한다. 예를 들어, File A에 8KB append write해서 30번 block 31번 블록이 새로 할당되었고 fsync요청을 완료한 상태를 가정하자 (Figure 2). 스토리지에 저장된 block bitmap과 해당 file에대한 inode 정보를 읽으면, block bitmap의 30번과 31의 비트가 사용중이지 않았지만 새로 할당되었다는 정보를 알 수 있다. 이런 방식으로 global metadata block인 block bitmap이 간단히 복구 될 수 있다.
Figure 3: file system recovery.
Crash 복구는 오직 valid i-transaction을 이용한다. i-transaction은 현재 running transaction id와 i-transaction commit 요청 순서, 두개의 정보를 쌍으로 가지고 있는 식별자를 받는다. (TxID, sub-TXID)의 식별자를 갖는 i-transaction은 일반 transaction의 일부 수정사항을 담고있다 의미를 갖는다. n-1의 id를 가지는 일반 transaction이 commit 되면 TxID가 n-1인 모든 i-transaction은 invalid 상태가 된다.이미 n-1 transaction 모든 수정사항을 적용했기에 합당한 동작이다. Figure 3을 예로들면, 복구 모듈은 가장 먼저 일반 journal 영역의 아직 checkpoint가 되지 않은 transaction들을 읽는다. 가장 마지막으로 commit이 완료된 transaction의 id가 n이라면 TxID가 n이하인 모든 i-transaction은 invalid가 된다. 오직 n+1을 TxID로 갖는 (n+1,0), (n+1, 1) i-transaction들이 Tx 과 함께 복구를 위해 쓰인다.
iJournaling 기법은 fsync가 병렬적으로 동작할 수 있도록 구현하였다. 유저 프로그램이 fsync를 호출하면 특정 스레드에게 commit 요청을 보내지 않고 system call을 호출하여 fsync 관련 i-transaction commit을 한다. 또한, 각 코어에는 자체 ijournal 영역이 있으므로 여러 fsync 호출을 여러 프로세서 코어에서 동시에 처리 할 수 있다. 병렬적으로 fsync 호출이 가능하기 때문에 manycore에서 확장성을 가진다. per-core iJournal 영역에 있는 i-transaction 사이에는 dependency가 존재한다. 앞서 언급하였듯, i-transaction은 (TxID, sub-TXID) 식별자로 가지고 있다. TxID는 해당 i-transaction의 모든 변경사항이 기록되어있는 Running transaction 이다. sub-TxID는 i-transaction에게 fsync를 호출한 순서를 global counter로 부터 받아 저장한다. TxID가 같은 i-transaction들은 sub-TxID가 낮은 것 부터 변경사항이 먼저 적용되어야 한다.
추가 설명:
[1] EXT4는 extent자료구조 기반의 data block mapping을 한다. 하나의 extent는 다수의 연속된(logical) data block를 담고 있다. inode entry는 총 4개의 extent를 가질 수 있다. 만약 inode entry가 extent를 4개 이상 필요로 한다면, 추가적인 extent가 data block 영역에 할당 된다. 이 추가적인 extent를 external extent라고 한다.
장점 :
-
각각의 fsync를 처리하기 위해 disk에 기록되는 데이터가 기존 JBD 방식보다 작다. 따라서 fsync latency가 짧다.
-
ijournal 영역을 per-core로 분리시키고 i-transaction commit을 여러 스레드가 처리할 수 있게 하여, fsync가 병렬적으로 동작할 수 있다.
단점 :
-
fsync가 자주 발생할 수록 file-level metadata (Inode entry, external extent, directory entry)가 주기적으로 발생하는 journal commit에 의해 쓰기가 증폭된다.
-
하나의 per-core ijournal 영역이 가득차면 강제로 checkpoint를 발생시키면서 fsync 동작이 직렬화 될 수 있다. (추정)
-
uncommitted_DE 플래그가 있으면, uncommitted_DE 플래그가 없는 가장 최상위 부모 디렉토리부터 순서대로 directory i-transaction을 commit해야 한다.
소스코드 :
현재 Linux kernel에서 ijournaling의 단순화 된 버전 인 “fast commit”에 대하여 패치가 진행되고 있다.현재 Linux kernel에서 ijournaling의 단순화 된 버전 인 “fast commit”에 대하여 패치가 진행되고 있다.
https://lwn.net/Articles/800976/