datainfra前沿动态 · Vol.81

Apache Iceberg 1.12.0 三次候选后落地,代价是 Spark 3.4 与 Flink 2.0 出局;delta-rs 的 Rust 实现走到 1.0.0,把 S3 并发提交的 DynamoDB 外部锁换成条件写;Databricks 把 ABAC 从表扩到视图与时间旅行;Gravitino 1.3.1 让 Iceberg 目录默认改道 REST

本期聚焦 9 月 29 至 30 日的四件事:Iceberg 1.12.0 正式发布,代价是移除 Spark 3.4、Flink 2.0 与带行数据的位置删除写入;delta-rs 撤掉 DynamoDB 外部锁、改用 S3 条件写并升到 1.0.0;Databricks 把 ABAC 的行列策略从表推到视图与历史版本;Gravitino 1.3.1 把所有 Iceberg 目录访问收敛到自己的 REST 服务。

DataInfra Apache Iceberg Iceberg REST Catalog Delta Lake delta-rs Apache Gravitino Databricks Unity Catalog ABAC Lakehouse Apache Flink 数据治理

9 月 29 到 30 日这两天,数据基建的动静落在同一个动作上:把一件原本靠外部组件或多方约定的事,收进离数据最近的那一层,并明确写下不再支持什么。Apache Iceberg 1.12.0 在连切三个候选、前两个倒在许可证缺口之后终于落地,代价是 Spark 3.4 模块、Flink 2.0 支持和带行数据的位置删除写入一起被移除。delta-rs 的 Rust 实现从 0.32.4 直接跳到 1.0.0,把 S3 上的并发提交从 DynamoDB 外部锁换成对象存储自己的条件写,副作用是与 Spark 侧通过 log store 做的协同提交从此失效。Databricks 把 ABAC 的行过滤与列掩码从表推到视图,并在 9 月 23 日让时间旅行查询也强制策略。Apache Gravitino 1.3.1 是个补丁版,却把非 REST 的 Iceberg 目录默认改道到自己的 Iceberg REST 服务,且漏配凭据时不会在升级当下报错。四件事里有三件都留了一句「升级后看起来健康,之后才炸」的警告。


Apache Iceberg 1.12.0:三次候选后落地,先把不再支持什么写清楚

事件概述

9 月 30 日,Apache Iceberg 发布 1.12.0 正式版,标签 apache-iceberg-1.12.0 于 UTC 时间 9 月 30 日凌晨 1 点 52 分推送。这是 1.12.0 的第三个候选,也是这个项目第一次出现同一版本连切三次候选的情况。前两次的失败都不在代码功能上:RC0 阶段在文档站点资源里发现一个 Bootstrap 样式表和一段 Lottie JavaScript 保留了版权行却缺少 MIT 许可声明,根 LICENSE 也没计入;RC1 于 9 月 22 日开投后被投出 non-binding -1,原因是 iceberg-gcp-bundle 捆绑了 Caffeine 缓存库而 LICENSE 里没有对应条目。功能早已冻结,卡住发布的是许可证卫生。

正式版本身包含超过 800 条合并变更。官方发布页的说明尚未同步到 1.12.0 条目,目前可读的一手材料是 GitHub 上的 release tag 与 Dremio 在 9 月 30 日发的升级指南。后者用最终发布标签与合并源码为准,把这次发布概括成一句话:让 v3 的几项特性在更多引擎里可用,收紧 REST catalog 契约,修掉一批流式场景的正确性问题,并落地大量 v4 格式的铺路代码。

技术解析

  • 兼容性边界先划清:Spark 3.4 模块被移除,支持线更新为 3.5.9、4.0.4、4.1.3,源码发布中带 Spark 4.2。仍加载 iceberg-spark-runtime-3.4 的作业在 1.12 里没有对应产物可替换。Flink 侧增加 2.2 与 2.3、保留 1.20 与 2.1、移除 2.0,支持线变成 1.20、2.1、2.2、2.3 四条。有状态流作业需要在准备部署的那组 Flink 与 Iceberg 版本对上单独测 savepoint、checkpoint、序列化与提交行为,编译通过不等于能升。
  • 带行数据的位置删除变成只读遗留物:v2 允许位置删除记录里带上被删的那一行,这个特性通常写作 PDWR,在 1.11 被弃用,1.12 从 Java writer 里移除。旧文件仍然可读,position_deletes 元数据表保留 row 列供检查;新 writer 不再产出这类文件。两个维护动作遇到 PDWR 会直接失败而不是静默处理:rewrite_position_delete_files 与 rewrite_table_path。升级前的动作是扫一遍 position_deletes 里 row 非空的行,要么在保持 v2 的前提下把这些删除压实进数据文件,要么走 v3 路径把它们改写成 deletion vector。
  • 一批弃用 API 真的删掉了:涉及 Core、Data、Spark、Flink、Kafka Connect、BigQuery 与 AWS 集成,包括 SparkFilters、旧 SparkTableUtil 方法、GenericAppenderFactory、BaseFileWriterFactory、DataReader、旧命名空间编码 helper、旧 HadoopFileIO 构造器,以及一批旧 S3 signer 类与属性。自建 catalog、FileIO、writer 或 Spark 扩展代码的团队必须先拿 1.12 编译一遍,只跑运行时冒烟会漏掉仅在维护或失败处理路径上加载的代码。
  • v3 特性从规范页走进引擎:Variant 在 Spark 上有了向量化 Parquet 读,Flink 能走 Avro 读写并支持 Parquet shredding,Kafka Connect 与 generic record writer 也补上 shredding,精度层面修掉高精度 decimal shredding、字符串边界的 UTF-8 排序、值列无统计时的 metrics 行为、畸形二进制解析与 Variant 列上 ORC 过滤下推。geometry 与 geography 映射到 Parquet 逻辑类型,支持 WKB 读写、Avro 承载、API 二进制序列化、几何包围盒 metrics 与 writer metrics 里的 CRS 保留,Spark 4.1 拿到 Parquet 读写与 DML 覆盖并在无法向量化读时有回退。行级 lineage 修掉 last-updated-sequence 继承与 Spark manifest rewrite 时的 first-row-ID 携带问题,Arrow 向量化读的直接内存泄漏也一并修掉。加密方向,manifest-list 的加密密钥随使用它的快照一起提交,Spark 的 manifest rewrite 会对新写出的 manifest 加密,merge 路径保留 deletion vector 的加密元数据,并新增一个 delegate 形态的 EncryptingFileIO。
  • REST catalog 往控制面走:新增 unregister table 端点,删除的是目录注册而不是表文件;新增 function 的 list 与 load 端点,把 SQL 函数元数据纳入 REST 契约;Spark 增加 rest.catalog-purge,让 DROP TABLE PURGE 把删除委托给 REST 服务;另加 CatalogObjectIdentifier、labels、REST 模型里的 Variant 支持,以及 REST 路径分段改用 RFC 3986 百分号编码。其中 catalog 侧 purge 的操作影响最大——文件删除需要存储凭据、表上下文和清晰的审计边界,委托给 catalog 意味着删文件的能力不再发给每个 Spark 客户端,失败域也随之改变:一次失败后表是仍注册、已注销、部分删除还是排队异步清理,需要先想清楚。unregister 同理,它不是 drop,文件是故意留下的,恢复手册里得先记下最后的 metadata location 再操作。
  • Spark 与 Flink 的日常改动:rewrite_data_files 增加 Hilbert 曲线聚类策略,与 bin packing、Z-order 并列,官方提醒要拿自己真实的过滤组合去压测,并把重写成本、文件大小分布和新写入多久会把聚类磨掉。migrate 与 snapshot 过程加 ignore_missing_files,用于老表的 metastore 引用了已不存在文件的情况,官方明确标注不能当默认值用——忽略缺失文件把一个明显的失败变成了有意为之的数据遗漏。Flink 侧修掉 job ID 变更后重启导致重复提交的 case,新增一条从 equality delete 到 deletion vector 的转换 pipeline,DynamicSink 支持设置 slot sharing group 做更细的资源控制,并按 schema identifier fields 路由记录。
  • Kafka Connect 不再吞掉提交失败:提交失败现在会上报,新增 partial commit failure 指标,对瞬时提交异常做有界重试,顺带修掉 rebalance 期间可能提交上一次 attempt 文件的问题,并把控制 topic 的 offset 作为高水位跟踪。官方建议升级后做三个故障演练:让 catalog 提交失败、在提交挂起时触发一次 task rebalance、从已知控制 topic offset 重启。
  • v4 只是铺路:1.12 里带了大量标注 v4 的代码——相对路径与把绝对位置转成相对位置的工具、Parquet 或 Avro 写的 manifest、可选的 v4 表 metadata location、tracked file 的 builder 与 adapter、v4 manifest reader、content statistics 结构与过滤器、first-row-ID 分配,以及一份明确标为 draft 的 Mumbling Bitmap 规范与只读实现。项目与 Dremio 都写明:这不等于 v4 可以上生产,也不等于设置 format-version=4 是一条受支持的迁移路径。

行业影响

Iceberg 1.12.0 是一条明确的兼容性边界,升级动作应当被当成一次兼容性决策而不是改个依赖版本号。官方与 Dremio 都建议把三件事分开做:升 Java 库、升引擎运行时、升表的格式版本,一次只动一件,回滚面才可理解。更值得注意的方向性变化是 REST catalog 的定位——它从「表元数据 API」长成了 catalog 的控制面,注册、注销、purge、函数、标签、远程签名这些策略正逐个搬到服务端。客户端不应当再根据服务端品牌或版本字符串推断能力,而是去发现端点或能力、发合法请求、按文档化的错误模型处理。这个方向与本期 Gravitino 的那条改动是同一件事在两个项目上的落地。

相关链接


delta-rs 走到 Rust 1.0.0:撤掉 DynamoDB 外部锁,S3 并发提交改用条件写

事件概述

9 月 28 日,delta-io/delta-rs 打出 rust-v1.0.0 标签,这是 deltalake Rust crate 的第一个稳定版本。上一个 Rust 标签 rust-v0.32.4 发布于 6 月 7 日,中间隔了三个多月。Python 绑定走的是独立版本号,最新为 python-v1.6.6(9 月 24 日),两者不同步。

这个版本顶部只有一条警告,但它是一次真正的破坏性变更:S3DynamoDbLogStore 被移除。项目在讨论区说明,移除之后 S3 上的写入默认改用基于 S3 的条件写语义。这条改动的直接后果写在讨论里——S3DynamoDbLogStore 原本让 delta-rs 能与同样配置了 DynamoDB log store 的 Delta 或 Spark 写入端在同一张表上协同提交,自 1.0.0 起这种协同不再工作。

技术解析

  • 为什么要拆掉 DynamoDB:多写入端在对象存储上做提交,核心难题是「我要写的这个 commit 文件不能已经存在」。S3 长期没有原生的条件写,delta-rs 的兜底方案是用一张 DynamoDB 表当外部锁,所有写入端先去抢锁,代价是多一个必须部署、配置和付费的组件,且只有同样接入这张锁表的写入端才被这个协议约束住。S3 原生条件写(If-None-Match)落地之后,「文件不存在才写」变成一个原子操作,外部锁表就没有存在理由了。代价写在明处:还没切到条件写的其他 Delta 写入端,从此和 delta-rs 互不相认。
  • 升级路径上的取舍:这不是一个「多了一个选项」的改动,而是一次单向的互操作断裂。用 Spark 与 delta-rs 交错写同一张 S3 表的团队,在升级前必须确认 Spark 侧是否也已经切到 S3 条件写,否则两端会各自认为自己拿到了提交权。delta-rs 文档里仍保留了 AWS_S3_LOCKING_PROVIDER=dynamodb 与 AWS_S3_ALLOW_UNSAFE_RENAME 的说明,存储选项表里也已出现 aws_conditional_put(取值例如 etag,用于 S3 兼容存储),文档与实际行为之间存在过渡期的错位,动手前以代码与 release note 为准。
  • 版本里真正的增强:DataFusion 升级到 55(PR #4655),column mapping 支持在建表时启用(PR #4505),并新增 stream history API(PR #4763)。配套补上了 TableFeature::V2Checkpoint 的暴露、Python 侧可在同一事务里一起提交 AddAction 与 RemoveAction、Unity Catalog 托管表支持,以及自定义 CredentialProvider 与 TokenCredential 的接入点。
  • 一批影响面很具体的修复:慢对象存储上无上限的并发上传导致 OOM(#4701);大表上新的 DeltaTableProvider 吃内存过多(#4518);timestamp 统计未按 Delta 规范截断到毫秒(#4659);小文件多的表上 vacuum 扫得慢(#4634);MERGE 的 when_matched_update 把 list 类型列静默写成 NULL(#4578);write 与 read 跑在大量短生命周期线程上触发 SIGSEGV,先 import pyarrow 可规避(#4643);to_uri 在 add.path 含百分号编码的 .. 时会解析到表根目录之外(#4637);以及为移除 thrift 漏洞 CVE-2026-43868 升级 parquet 依赖(#4625)。剩下的修复覆盖 struct 列与其子字段同名导致 Duplicate delta.columnMapping.physicalName 的初始化失败、CDF scan 在启用 column mapping 时不报错、日期解析不了大年份、Windows 上 Conda Forge 包的 symlink 问题等。

行业影响

非 JVM 的 Delta 读写路径第一次有了稳定 API 承诺,这对把 Delta 表接进 Rust 数据栈(DataFusion、Polars、各类 Agent 与嵌入式分析)的人来说是个门槛事件:0.x 时代每次升级都可能碰到 API 变动,1.0 之后这条顾虑可以拿掉。更值得注意的仍然是那个被移除的组件——对象存储原生条件写正在把「外部锁表」这一类东西从湖仓提交协议里挤出去,Iceberg、Delta、Hudi 三条链路都在往这个方向走。收益是少运维一个组件、少一笔成本;代价是过渡期里跨引擎互操作会按「谁先切换」分批断裂,而这类断裂通常不会在写入时报出来。

相关链接


Databricks 把 ABAC 推到视图与时间旅行:治理对象从表扩到派生对象和历史版本

事件概述

9 月 29 日,Databricks 发布说明里新增一条:Unity Catalog 的 ABAC 行过滤与列掩码扩展到视图,覆盖范围从表扩到通过视图暴露的敏感数据。该项为预览功能,账户管理员需在账户控制台的 Previews 页面开启,且要求 Databricks Runtime 19 及以上。

这已经是 ABAC 在 9 月里第二次往外扩边界。9 月 23 日,符合条件的 Unity Catalog 托管表上的时间旅行查询开始强制 ABAC 的行过滤与列掩码策略(Beta,同样要求 DBR 19+);9 月 17 日,metastore 级 ABAC 策略进入 Beta,一条策略可以作用于 metastore 内所有 catalog,包括之后新建的 catalog;9 月 8 日 ABAC DENY 策略进入 Beta;9 月 16 日,单个 VARIANT 函数可用来掩码 ARRAY 与 MAP 类型的列,此前只覆盖 STRUCT 等类型。再往前,ABAC 本身在 2026 年 4 月 GA,覆盖表、物化视图与流式表,并在 GA 时把策略求值身份从视图或函数所有者改为会话用户——那是一次被官方标注为 breaking change 的改动。

技术解析

  • 视图为什么是难啃的一块:视图是派生对象,策略挂在哪一层、以谁的身份求值,决定了同一条 SQL 在不同入口下看到什么。ABAC GA 时已经把求值身份统一到会话用户,让通过视图与函数查询的行为与表级行级安全、列掩码对齐,查询者也仍然不需要对底层表的直接权限。把行过滤与列掩码本身再扩到视图,补上的是另一半:视图自己也可以成为策略的承载对象,而不只是策略的传递通道。对把敏感字段包进视图再授权给下游的团队来说,这意味着以前「视图之外还有一层表级策略」的假设不再成立,视图层也要参与标签与策略设计。
  • 时间旅行为什么必须补:历史版本是「数据在被打上敏感标签之前」的快照。如果策略只作用在当前版本上,TIMESTAMP AS OF 或 VERSION AS OF 就成了一条绕过掩码的旁路——数据没变,只是换了个读取入口。9 月 23 日那条改动把这个入口也收进策略求值范围,前提是表为符合条件的 Unity Catalog 托管表且运行在 DBR 19+。
  • 请求上下文也被纳入条件:8 月进入 Beta 的 context attributes 让策略的 WHEN 子句可以引用请求本身的上下文,例如 request.client_id(哪个 OAuth 应用在调用)与 request.is_on_behalf_of(是否代表用户执行)。典型用法是限制一个 Agent 通过已注册 OAuth 应用代用户读取时能看到什么,而同一用户直接在工作区里查询时仍看到全量。同期进入 Beta 的 identity attributes 让列掩码条件可以命中从 IdP 同步过来的用户属性(部门、国家等),而不只是组成员身份。
  • 配置与适用条件需要提前确认:视图策略与时间旅行策略都要求 DBR 19 及以上,视图策略还需账户级 Previews 开启。metastore 级策略目前是 Beta。对已经在用 ABAC 的账户,4 月的会话用户求值变更仍值得复查一遍——官方当时给了受影响的客户三个月适应期,未在名单上的新客户则已是默认行为。

行业影响

治理的对象正在从「表」这一层往上和往侧面扩:往上是 metastore 级的一条策略管住所有 catalog,往侧面是视图、函数这类派生对象,往下是历史版本,往里是请求上下文与用户属性。这条扩张线的驱动力很明确——读取方从人换成了 Agent。Agent 会按自己的权限去拼查询,会走时间旅行去取历史快照,会通过 OAuth 应用代用户读数据;每多一条这样的路径,就多一条绕开行列策略的旁路。Databricks 的做法是把这些入口逐个收进同一套标签驱动的策略求值里,代价是配置的复杂度上升:策略作用域、求值身份、运行时版本、预览开关,四者都要对齐才能生效。

相关链接


Apache Gravitino 1.3.1:补丁版里的行为变更——Iceberg 目录默认改道 REST

事件概述

9 月 30 日,Apache Gravitino 发布 1.3.1。官方把它定位为聚焦稳定性、正确性与安全加固的补丁版本,修复覆盖核心服务端、授权、Iceberg REST 服务(IRC),以及 Trino、Spark、Flink connector 和 ClickHouse、Doris、Glue、Hive、JDBC、Lance 等 catalog。

虽然是补丁版,Trino 与 Spark connector 里有几处需要在升级前就处理的行为变更。官方在 release note 开头把话说得很直白:下面这些改动会让集群在升级后立刻看起来健康,然后在之后某个时刻失败,所以要在升级之前读,而不是升级之后。

技术解析

  • 非 REST 的 Iceberg catalog 默认改走 IRC:Trino connector 里,catalog-backend=jdbc 或 hive 的 metalake 在升级后行为会变——connector 不再把 catalog-backend 翻译成 Trino 的 jdbc 或 hive_metastore catalog 类型,而是针对 Gravitino 的 IRC 构建一个内部 Iceberg REST catalog。保留旧行为的开关是 gravitino.iceberg.rest-routing-enabled=false。Spark connector 做了同样的默认切换,对应开关为 spark.sql.gravitino.iceberg.rest-routing-enabled=false;此外当找不到可用的 IRC endpoint 时,connector 现在会抛出可直接操作的错误,而不是静默回退到 Hive/JDBC 翻译。
  • IRC 凭据要单独配,且漏配不会立刻报错:如果 IRC 开了鉴权,它现在需要自己的凭据,且必须在 coordinator 和每一个 worker 上分别配置。这份凭据不从 Gravitino 的 catalog 属性派生,也不会随生成的 CREATE CATALOG 语句一起走,只能落在本地 catalog.properties 里(或通过 Trino 的 ${ENV:...} 密钥替换注入)。漏配任何一个节点都不会让 CREATE CATALOG 失败——catalog 照常注册、照常列出,直到该节点上第一次有查询触达 IRC 时才以鉴权错误的形式冒出来。
  • Spark 侧的 OAuth2 复用:当 Gravitino client 使用 OAuth2 时,connector 默认把同一份配置复用于 IRC 连接(spark.sql.gravitino.iceberg.reuseOAuth2=true),设为 false 可单独配置 IRC 凭据。
  • 内部 JDBC 密码不再外泄:trino.jdbc.password 不再传播进 connector 生成的 catalog,也不再进入 connector 日志。
  • 一个会改动查询结果的类型映射:Trino、Spark、Flink 三个 connector 上,PostgreSQL 中不带精度与刻度的 NUMERIC 列现在读成字符串,而不是 Decimal(38, 18)。对该列做算术、聚合或比较的查询在升级后需要显式 cast。
  • 其余改进:核心侧把建表、建 topic、建视图、建模型时的锁从 schema 粒度改为 entity node 粒度;IRC 支持配置 advertised URI 用于服务发现;允许 catalog 用户用已存配置测试现有连接;观测到 OOM 后上报 unhealthy;job 状态流转改用 entityStore.update();batchGetOwner 支持组所有者。客户端侧补齐了视图与函数的 tag 支持,Python client 新增 view API,并引入扩展 SPI 让下游项目挂自己的 catalog adapter。

行业影响

把非 REST 的 Iceberg catalog 默认改道 IRC,与 Iceberg 1.12.0 里 REST catalog 往控制面走是同一件事在两个项目上的落地:访问一张表的方式不再由客户端各自配置决定,而是收敛成服务端的一条路径,认证、鉴权、注册与注销都在那一条路径上做。收益是策略只有一个执行点;代价是升级后的故障模式变了——REST 路由、IRC 凭据、类型映射这三类问题都不会在升级当下暴露,而是在第一次查询、第一次涉及某个类型、某个特定 worker 被调度到时才出现。官方给的排查顺序值得照抄:按 catalog 逐个验能力协商、验特殊字符名称、验路径编码、验 purge 归属,并保留一个已知可用的 reader 在整个灰度期间不下线。

相关链接


编者评

本期四件事里,有三件都附了一句同类型的警告,而这三句警告说的其实是同一件事:收敛带来的代价不是当下的,而是延后的。Iceberg 1.12.0 把 PDWR 的写入能力和 Spark 3.4、Flink 2.0 一起砍掉,但旧文件照旧可读,问题要等到某次维护动作撞上去才暴露;Gravitino 1.3.1 把非 REST 的 Iceberg 目录默认改道 IRC,漏配凭据的 catalog 照样注册、照样列出,要到某个 worker 上第一次查询才报鉴权错;delta-rs 1.0.0 撤掉 DynamoDB 外部锁之后,与 Spark 侧的协同提交不会在写入时报冲突,而是两端各自以为自己拿到了提交权。三者都是「升级后监控全绿,然后在别处炸」。

对照着看,收益也很一致。被移除的东西都是外部组件或旁路:DynamoDB 锁表、客户端各自持有的删文件能力、catalog-backend 到 Trino catalog type 的翻译层、视图与历史版本这两条绕开行列策略的读取路径。收进去之后,执行点只剩一个,策略只需要在一个地方写对。这是过去两年数据基建最稳定的一条走向——先去中心化的元数据,再把策略收回到中心。

值得单独拎出来的是治理这条线的扩张速度。Databricks 的 ABAC 在半年里从表扩到物化视图、流式表、metastore 级、DENY 策略、ARRAY/MAP 掩码、上下文属性、身份属性、视图、时间旅行,每扩一格都是在补一条 Agent 会走到的旁路。当读取方从人换成程序,「谁能看到什么」的边界必须穷举所有入口才能成立,而穷举本身成了治理的主要工作量。本期没有找到可对等的中文源进展——阿里云、火山引擎的公开文档与开发者社区在 9 月 29 至 30 日这个窗口内没有匹配的数据基建类更新,本栏目将持续跟踪。另有几条线索留到下期:Iceberg 1.12.0 的官方 release notes 页面截至发稿尚未同步该版本条目;Amazon Redshift 的 Iceberg 写入系列第 3 篇(9 月 28 日)讲 schema 与分区演进、Lake Formation 资源链接;LanceDB 的 v0.40.0 正式版仍在 beta 线上迭代(最新为 9 月 30 日的 v0.40.0-beta.12)。Spark Connect 覆盖到 Amazon EMR on EKS 的 9 月 29 日公告,因本栏目已于 Vol.75 报道同一特性在 EMR on EC2 上的落地,不再单列。