文件系统天生就是不平等的 - 实现崩溃一致性应用的复杂性

最近看到一篇 Paper,觉得很有意思,Paper 的主题是 “All File Systems Are Not Created Equal”,看来文件系统也跟人一样,天生就是不平等的。这篇 Paper 主要是讨论如何找出应用程序对文件操作的有问题的地方,保证在发生崩溃的时候,也能保证数据的一致性,从而正常的恢复,论文里面作者叫做 “crash consistent”,也就是『崩溃一致性』。

要让应用在崩溃之后,能正常恢复,说起来容易,实际还是比较困难的,因为一方面应用程序会依赖底层的文件系统的实现,不同文件系统对一些保证是不一样的,譬如是否能支持原子 rename 这些。同时,很多文件系统,会提供丰富的配置供用户调优,譬如 etx4 就有 write back,ordered 这些挂载属性,这些就更加难判定文件系统在崩溃时的具体行为。

而另一方面,则是应用程序自己对数据一致性的处理,我们都知道,每次文件操作之后,都进行强制 sync,这样就是非常安全的(当然,这里排除了硬件故障),但大家知道这个性能就是非常的差了,所以为了性能,我们势必在文件操作上面做很多优化,这些就在崩溃的时候引入了不确定性了。

为了解决这两个问题,论文作者实现了两个工具,一个就是 Block Order Breaker (BOB),而另一个就是 Application-Level Intelligent Crash Explorer(ALICE)。

BOB

BOB 主要是为了测试文件系统的 Persistence Properties。对于一个给定的文件系统,当在发生崩溃之后,有哪些可能的状态,这个就是用 Persistence Properties 来确定的。

那这个 BOB 是怎么做的呢?首先 BOB 会一个简单的场景去压力测试 Persistence Properties(譬如,一批特定 size 的持续写入来验证 overwrite 的原子性)。BOB 会收集这个场景产生的 block I/O,然后重新排序这些收集的 blocks,选择性的将一些写入到磁盘去产生一个合法的磁盘 state。用这种方式,BOB 能产生一批在崩溃之后,多个对应不同 disk states 的唯一的磁盘 images 。然后 BOB 会在各自的 image 上面执行文件系统的 recovery ,并检查 Persistence Properites 是否继续满足(譬如 Write 是原子的)。

ALICE

ALICE 的原理其实比较简单,就是通过 trace 应用程序的 system call 来直接构建文件的 state。

使用 ALICE 的时候,用户首先需要提供一个初始化的文件 snapshot,譬如一个完整的文件目录,一个 workload 脚本(譬如执行一次事务),一个跟 workload 对应的 checker 脚本,用来检查 workload 的不变性(譬如事务是否是原子的)。Alice 会在不同的 crash states 上面去执行 checker。在执行 workload 的时候,Alice 会生成 update protocol 的逻辑表示,记录 protocol 里面的脆弱的地方以及对应的代码,以及 persistence properties。

ALICE 会将 trace 的 system call 转换成逻辑操作。逻辑操作会将当前的 system call,I/O 操作抽象成更精简的文件系统操作。譬如,write,pwrite,writev,pwritev,mmap 这些操作会转成 overwrite 或者 append。

对于不同的文件系统来说,crash state 是不一样的,ALICE 使用一个 Abstract Persistence Model (APM) 来抽象出不同文件系统的 crash state,如果想测试特定文件系统里面的易脆性,可以写一个特定的 APM。对于指定的文件系统,APM 会指定逻辑操作里面原子性和顺序性的所有约束,这样就能定义 crash state。APM 通过两个逻辑实体 - file inodes 包括 data 和 size,directories 包括 directory 的 entries。每个逻辑操作会操作一个或者多个这些实体。为了更好的抓住中间的 crash state,APM 会将逻辑操作拆分成多个微操作 - 也就是能用到逻辑实体的最小原子更新。主要包括:

  • write_block:也就是写到文件的一个 size 的 block。包含两个特定的参数,zeroesrandomzero 意味着文件系统初始化了一个新分配的全为 0 的 block,而 random 则是表示一个未初始化的 block。如果文件不是追加写,那么就不会更新文件的 size。
  • change_file_size:更新 file inode 的大小
  • create_dir_entry:在一个目录里面创建一个目录 entry,并且关联一个文件 inode 或者目录到它上面。
  • delete_dir_entry:删掉一个目录 entry。
  • stdout:在终端输出添加消息。

APM 定义了逻辑操作如何转换成微操作,譬如上面说的 overwrite 操作,就会转换成一个或者多个 write_block 操作。APM 同样也会定义不同微操作之间的顺序,譬如所有在 sync 之后的操作都必须排在 sync 的后面。

有了微操作以及顺序,ALICE 会先将应用程序的初始化 snapshot 表示成逻辑实体,然后会选择满足顺序关系的不同集合的微操作对初始状态进行应用,这样就会构造出一个 crash state。对于每个 crash state,ALICE 会将逻辑实体重新转换成对应的实际文件,然后使用 checker 进行检查。

例子

这里说一下 ALICE 提供的一个 toy 例子,比较简单,去掉了 assert 判断:

int fd = open("tmp", O_CREAT | O_RDWR, 0666);
int ret = write(fd, "world", 5);
ret = close(fd);
ret = rename("tmp", "file1");
printf("Updated\n");
ret = link("file1", "link1");
ret = link("file1", "link2");

这个例子会做几件事情:

  1. 打开 tmp 文件,会写入 “world”,然后将 tmp 文件改名。当打印 “Updated” 之后,这个操作就完成,文件内容就必须是 “world”。
  2. 设置 link,link1 或者 link2 要不存在,要不不存在。

首先我们执行 workload,也就是 toy_workload.sh 脚本,该脚本会编译 toy 测试用例,创建两个目录,一个是 workload_dir,用来放置操作的文件,我们会创建一个 file1 文件,写入 "hello"。另一个就是 traces_dir,用来保存 ALICE trace 的信息。然后执行 alice-record --workload_dir . \ --traces_dir ../traces_dir \ ../a.out

然后我们就执行 check,对应的 check 主要判断逻辑如下:

if 'Updated' in stdout:
    # Check durability
    assert open('file1').read() == 'world'
else:
    # Check atomicity
    assert open('file1').read() in ['hello', 'world']

# Check whether link1 and link2 were created together as a single atomic unit
dirlist = os.listdir('.')
assert ('link1' in dirlist) == ('link2' in dirlist)

如果控制台打印出来 “Updated”,那么文件一定有 “world”,否则就可能是原来的文件内容 “hello”,或者新的 “world”。Link1 和 Link2 要不都存在,要不都不在。

然后我们执行 check,alice-check --traces_dir=traces_dir --checker=./toy_checker.py,首先会将应用对应的逻辑操作打印出来:

Parsing traces to determine logical operations ...
Logical operations:
0   creat("tmp", parent=200440197, mode='0666', inode=200446770)
1   append("tmp", offset=0, count=5, inode=200446770)
2   rename(dest="file1", source="tmp", source_hardlinks=1, source_parent=200440197, dest_size=5, dest_hardlinks=1, source_size=5, dest_parent=200440197, source_inode=200446770, dest_inode=200440198)
3   stdout("'Updated\n'")
4   link(dest="link1", source="file1", source_parent=200440197, dest_parent=200440197, source_inode=200446770)
5   link(dest="link2", source="file1", source_parent=200440197, dest_parent=200440197, source_inode=200446770)

然后根据 check 来检查易脆性:

Finding vulnerabilities...
(Dynamic vulnerability) Across-syscall atomicity, sometimes concerning durability: Operations 4 until 5 need to be atomically persisted
(Static vulnerability) Across-syscall atomicity: Operation toy.c:19[main] until toy.c:21[main]
(Dynamic vulnerability) Ordering: Operation 1 needs to be persisted before 2
(Dynamic vulnerability) Ordering: Operation 2 needs to be persisted before 3
(Static vulnerability) Ordering: Operation toy.c:12[main] needed before toy.c:16[main]
(Static vulnerability) Ordering: Operation toy.c:16[main] needed before toy.c:19[main]
(Dynamic vulnerability) Atomicity: Operation 2(destination unlinked fully, source untouched, destination and source unlinked fully, destination unlinking partial  semi-truncated (3 count splits), destination unlinking partial , destination unlinking partial fully truncated)
(Static vulnerability) Atomicity: Operation toy.c:16[main] (destination unlinked fully, source untouched,destination unlinking partial fully truncated,destination and source unlinked fully,destination unlinking partial ,destination unlinking partial  semi-truncated (3 count splits))
Done finding vulnerabilities.

可以看到,在第二个操作 rename 之前,操作 1 必须持久化。

小结

上面只是对 ALICE 的一个简单介绍,当然ALICE 还有很多限制,但对于一些常用的操作,还是能检查出来了。后面还是需要再抽时间详细研究一下。

下一步自然就是想到将 ALICE 应用到我们自己的系统中来,无论是 RocksDB,还是我们其他很多的文件操作,我们都可以使用 ALICE 来看系统的 crash consistent。如果你对这块感兴趣,欢迎联系我,tl@pingcap.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容