问题背景
在一个客户环境下,升级时做了元数据导入导出,但在运行几天之后,又重新回滚了所有元数据,此时读一些 ao/orc 表,得到报错 ERROR: Failed to open layout file xxx
,例如:
问题排查
以上述问题为例:
- 先查出这张表的 oid
select oid from pg_class where relfilenode = 36059;
- 再查看这张表的 aoxr
select * from oushu_aoxr(36059) t (rootrelid oid, relfilenode oid, state int2, version int8);
- 再对比这张表的实际数据文件:
hdfs dfs -ls /oushudb/default_filespace/16385/16655/36059
NOTE(关于 AOXR):aoxr 是一张元数据表,记录了相应用户表路径下有哪些子文件夹,各字段表示:
- rootrelid 和 relfilenode 字段是和 pg_class 中相应字段相同,一般没有问题
- state 记录了子文件夹的前缀,version 记录了子文件夹的后缀
举几个例子:
- state = 1, version = xxx, 表示有一个子文件夹:d_xxx
- state = 2, version = xxx, 表示有一个子文件夹:i_xxx
- state = 3, version = xxx, 表示有两个子文件夹:i_xxx 和 d_xxx
- state = 6, version = xxx, 表示有一个子文件:bi_xxx
具体的转换逻辑参考代码 oushudb/src/backend/access/appendonly/aoxr.c:84 函数 GetTransactionDirPrefix
问题分析
从上述结果可以看到,db 是按照 aoxr 里的记录读取数据文件的,但这张表的 aoxr 记录和它的实际物理文件不匹配,所以读的时候会报错:找不到 layout 文件。
这个场景产生的原因是,db 在元数据回滚前的那几天触发了 auto compact,更新了 aoxr,这样元数据回滚之后,就有问题。
从 db 那几天的日志应该可以找到 compact 记录,不凑巧客户环境的日志丢失了,但可以看到 hdfs 文件夹的日期恰好是元数据回滚前的那天,从而印证上述的分析。
问题修复
修复手段就是把 aoxr 搞成和 hdfs 物理文件一致,在上述例子中,就是要把 aoxr 的记录全部删除,然后插入 (36059, 36059, 6, 122031176)
,所以执行的 SQL 为
drop function if exists insert_oushu_aoxr(oid, oid, int2, int8);
create function insert_oushu_aoxr(oid, oid, int2, int8) returns void as '$libdir/hornet', 'insert_oushu_aoxr' language c volatile;
drop function if exists delete_oushu_aoxr(oid, oid, int2, int8);
create function delete_oushu_aoxr(oid, oid, int2, int8) returns void as '$libdir/hornet', 'delete_oushu_aoxr' language c volatile;
-- 用 delete_oushu_aoxr 依次删除每一行
select delete_oushu_aoxr('36059'::oid, '36059'::oid, '6'::int2, '117695917'::int8);
...
-- 用 insert_oushu_aoxr 依次插入每一行
select insert_oushu_aoxr('36059'::oid, '36059'::oid, '6'::int2, '122031176'::int8);
...
-- 此时表应该能正常读取
select '36059'::regclass;
select count(*) from ${table_name};
问题延伸
目前没有好的办法确定:整个数据库中有哪些表的 aoxr 是不一致需要修复的。实际上,如果 db 相关日志还在的话,过滤 compact 记录很容易就能找到,但现在好像只能客户遇到一张问题表就修一次。(可能通过 hdfs -ls 再筛选日期也能大致得到有问题的表,不确定实际可行性)