一、背景与问题
在实时数据湖建设中,Flink + Hudi 的组合被广泛用于频繁更新场景下的实时湖建设。通过 Flink 实时Upsert写入Hudi表(MOR),搭配合理的合并(Compaction)任务,能够兼顾实时性和查询性能。在小数据量的场景下,一般配置在线合并(compaction.async.enabled=true),即使用Flink资源进行parquet+log合并;而在大数据量的场景下,在线合并会影响实时处理性能,通常配置为离线合并(compaction.async.enabled=false),即Flink仅生产合并计划,通过配置Spark定时任务来执行合并计划,保障实时处理效率与查询性能。
在我们生产环境的Flink+Hudi Upsert MOR场景下,我们遇到了一起Flink实时写入+Spark离线合并相配合而引发的异常。问题现象表现为Flink实时更新写入的作业运行日志中不定期出现报错Error limiting instant archival on metadata table Caused by: java.lang.UnsupportedOperationException,导致Flink作业异常重启,影响下游使用。
随后发现受影响的不仅是这一个作业,而是所有采用 Flink upsert + Spark离线合并模式的表,波及范围涵盖多个项目流和数十个作业。
二、问题排查与分析
1.问题排查
作业报错日志指向metadata table相关异常,我们先排查Flink作业对于metadata table的相关配置,由于我们统一使用HMS作为元数据管理,在Flink作业中禁用了Hudi元数据表(metadata.enabled=false)。随后我们对Spark离线合并任务也做了检查,Spark任务配置中也禁用了Hudi元数据表(hoodie.metadata.enable=false)。既然两边配置一样、都禁用了Hudi metadata table,为啥出这个问题呢?
抱着试一试的心态继续查看Spark任务运行日志,结果发现了线索:Hudi元数据表配置关闭的,但是Spark任务启动初始化识别为non-spark config被过滤了(查阅官方文档spark-hudi集成下hoodie.metadata.enable默认是true)。我们联系Spark平台同事确认Hudi任务的传参方式,反馈结果是经过验证确认Hudi任务自定义参数(非spark)在平台上的传入方式有bug,无法在Spark生效。虽然心里闪过一千匹马,但问题点也基本明确方向了。不过为啥会导致这个异常报错呢?
为了确认引擎配置不一致引起的线索,我们转向去检查报错表对应的 Hudi 元数据目录和 hoodie.properties配置文件,Hudi表的metadata table存在表路径下。发现表的 metadata目录曾经存在过,但当前已被清理;hoodie.properties中原本包含 hoodie.table.metadata.partitions=files参数(表示元数据表已启用且包含文件列表分区),但该参数已消失;对比不同时间点的配置,发现 hoodie.table.metadata.partitions和 hoodie.table.metadata.partitions.inflight属性会时而出现或消失。
2.问题分析
经过对比排查,我们发现根本原因在于 Flink 和 Spark 对 Hudi 元数据表的配置不一致:
Flink 集成 Hudi:元数据表开关参数为 metadata.enabled,默认值为 false(禁用),且我们的实时写入任务显式保持了该默认值,未开启元数据表。
Spark 集成 Hudi:元数据表开关参数为 hoodie.metadata.enable,默认值为 true(启用),离线合并任务禁用配置未生效。
当实时写任务(Flink)执行时,Hudi 表处于元数据禁用状态;当离线合并任务(Spark)执行时,又会尝试启用元数据表,创建 metadata目录并写入元数据信息。下一次实时写任务运行时,由于元数据表已启用,但 Flink 配置仍为禁用,便会触发 Hudi 的内部逻辑去清理元数据目录,并删除 hoodie.properties中的相关配置项。如此反复切换,导致元数据表在启用和禁用之间震荡,最终在合并任务试图进行元数据归档(instant archival)时抛出 UnsupportedOperationException。
三、解决方案
1.临时修复
在Spark任务代码中通过.option()或set命令直接设置的方式禁用Hudi元数据,使参数生效。
2.根本解决
Spark平台侧通过公共属性文件的方式(--properties-file)传参,便于集中管理配置、面向用户屏蔽技术细节。
四、总结
本次生产故障是一次典型的多引擎配置不一致引发的血案。通过这次排障,我们总结了以下几点经验,以供举一反三:多引擎共存时必须统一关键配置、重视参数传递链路的可靠性、加强跨引擎场景的测试覆盖。技术没有银弹,但通过细致的排查、严谨的测试和规范的配置管理,我们能够最大程度地避免此类“配置不一致”引发的隐蔽故障。