Post

Estimator core in evaluator

Estimator core in evaluator

记录另外一次 Estimator 的 core 排查。

背景

我们用户使用 Estimator 都是使用 chief + worker + ps + evaluator 的形式。在线上偶发的 evaluator 这个角色会 core 掉。

1
2
NotFoundError: ** open file failed **
***Error in `python`: malloc: smallbin double linked list corrupted***

打开文件失败,打开的是 CheckpointSaverHook 写出 checkpoint 时写出的 checkpoint state 文件。这个 checkpoint state 文件存储最新的模型版本和文件的路径信息。每次保存模型时,都会去更新它。 为了保证正确性,tensorflow 是会先写临时文件,再写 rename 成目标文件。

这里存在一种可能是:chief 保存模型时在 rename 一瞬间,evaluator 先读,然后 close 失败。

由于我们写的是一个分布式系统(类似于 HDFS)。rename 不是原子的,会先删除,然后在 rename。线下尝试复现这个问题,一直没有复现成功。

今天另外一个同事也遇到了这个问题,问题的严重性凸显。因为 core 的日志还显示不全。

复现代码

写了两个脚本

模拟 evaluator 的功能,不断地去读 checkpoint 文件,以获取 state。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
import random

import tensorflow as tf
from tensorflow.python import file_io

path = "/path/to/file.txt"

while True:
    try:
        with tf.gfile.Open(path) as fd:
            print(fd.read())
        seconds = random.random() * 3
        print("sleep: ", seconds,  " s")
    except Exception as e:
        print(e)
    time.sleep(seconds)

模拟 chief 的行为,不停地去写 checkpoint 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
import time
import random

import tensorflow as tf
from tensorflow.python import file_io

path = "/path/to/file.txt"

while True:
    file_io.atomic_write_string_to_file(path, contents)
    seconds = random.random() * 3
    print("sleep: ", seconds,  " s")
    time.sleep(seconds)

一段时间后就发现 core 了。很开心,终于可以稳定复现。

1
2
3
ulimit -c unlimited  # enable core file
cat /proc/sys/kernel/core_pattern  # core file location
gdb python -c /path/to/core_file

通过允许 core,以及 core 的位置。将 core 给相关存储 RD 排查,怀疑是文件句柄被重复 close。

仔细 Review 之前的 code,发现之前在修复 tensorflow 的一个问题时,读到 bytes 为 0,但是文件没有结束时, 会关闭文件,然后重新打开文件。在这个期间如果文件被删除,打开会失败。会走到 File 类的析构函数,在析构 函数中会再次关闭文件,由于第一次关闭文件时并没有置空句柄,导致重复关闭时 core。

Lessons learned

  • 出现 core 时,需要尽量知道 core 发生的路径,例如特殊数据,特殊逻辑。然后在线下复现
  • C++ 中对于句柄的管理,析构很重要。还是最好交由智能指针来做

参考

This post is licensed under CC BY 4.0 by the author.