动作类型详解

一致性保证(Consistency guarantees)

当多个用户同时运行动作(action)修改同一个本体(Ontology)时,Foundry 如何保证数据不乱、不丢、不串?这一篇讲清事务、写模式与隔离级别。

原文 Palantir Foundry · Action types 预计阅读 11 分钟 互动演示 + 6 道测验
本文来源 · Source 内容整理自 Palantir Foundry 官方文档:
https://www.palantir.com/docs/foundry/action-types/consistency-guarantees/
原始标题:Consistency and isolation

一句话速览

一致性保证(Consistency guarantees):当多个用户同时运行动作(action)修改同一个本体(Ontology)时,Foundry 如何保证数据不乱、不丢、不串?这一篇讲清事务、写模式与隔离级别。

1 一个动作就是一笔事务(transaction)
在 Foundry 里,一个动作(action)通过单一事务对本体(Ontology)进行读取、应用你定义的逻辑、并把编辑写入一个或…
2 批量写 vs 暂存写
区别在于:同一个动作在写之后再读,能不能看到自己刚写的内容
3 对象级的写-写冲突
当两个动作并发写入同一个对象时(即使是不同的属性),其中一个会失败
4 写偏斜(Write skew)陷阱
设想一个值班(on-call)场景:动作 A 和动作 B 都读取了"当前谁在值班"这一重叠数据,然后各自写入不同的对象
1

一个动作就是一笔事务(transaction)

先建立直觉:动作不是零散的几次修改,而是一笔"全有或全无"的账。

在 Foundry 里,一个动作(action)通过单一事务对本体(Ontology)进行读取、应用你定义的逻辑、并把编辑写入一个或多个对象(object)和链接(link)。简单来说,它像数据库的一笔事务,提供标准的 ACID 属性。

"An action applies edits to the Ontology through a single transaction that can read from the Ontology, apply user-defined logic, and write edits to one or more objects and links."动作通过单一事务对本体进行编辑:它可以读取本体、应用用户定义的逻辑,并将编辑写入一个或多个对象和链接。
  • 原子性(Atomicity):编辑作为单一、全有或全无的批次应用;但它扩展到通知、webhook、函数外部调用这类"副作用(side effects)"。
  • 一致性(Consistency):只有当本体约束(如属性类型、是否可空)被满足时才提交。
  • 隔离性(Isolation):控制在并发执行时,动作之间如何互相影响(见下文隔离级别)。
  • 持久性(Durability):提交之后,编辑被永久持久化。
提示:提交完成后,之后启动的动作或查询能看到这次编辑;但同一个动作执行期间自身的读,是否"看到自己的写入",取决于写模式。
2

两种写模式:批量写 vs 暂存写

两种模式都会把编辑收集起来,最后作为"单一原子批次"一次性提交,中途不会被别人看到半截结果。

区别在于:同一个动作在写之后再读,能不能看到自己刚写的内容。

BATCHED WRITES · 默认
批量写(Batched) 自身后续读 "看不到" 已收集到的编辑。 例:更新 status 后再读,返回的是原值。
STAGED WRITES
暂存写(Staged) 自身后续读 "能看到" 自己的编辑(含搜索/聚合)。 当前支持 TypeScript v2、Python、AIP Logic 的函数后端动作。
互动实验:读你自己的写入
点击上方按钮,模拟"先写入 status = Closed,再读取该对象"。
3

三种隔离级别(Isolation levels)

隔离级别决定两件事:你读到的是哪个版本的数据,以及哪些并发变更会让你这个动作失败。点击展开看区别。

默认快照隔离(Snapshot isolation)
新批量写动作默认采用,提供单时间点的稳定读视图。
读:所有直接对本体的读,都观察"动作开始时"的一个单时间点快照;同一对象或搜索重复查询都返回相同结果。
写:批量写,自身后续读不含早前的编辑。
冲突检查:若另一个动作改了你正写入的对象,则本动作失败;仅读取的对象不会导致冲突。
注意:函数调用的 source 在时间点视图之外,可能读到不同时间的数据,不受原子/冲突检查覆盖。
旧版Legacy
为早期动作类型保留;新批量写默认已改为快照隔离。
读:通常来自单时间点,但不保证;可能因存储刷新导致不同对象看到不同状态。
写:两种写模式都支持(批量不含自身编辑、暂存含)。
冲突检查:若在对象加载到提交之间,有其他动作改了你写入的对象,则失败;但读的不一致可能让你基于"多时间点"做决定。
慎用读已提交(Read committed)
即将提供;每读返回读时的最新提交值,无固定快照。
读:每次读都返回读那一刻的最新提交值,没有固定快照。
写:批量写。
冲突检查:无;"最后提交者赢",早提交者的编辑会被丢弃(并发改同对象时要特别小心)。
提示:隔离级别在 Ontology Manager 的 Capabilities 标签中设置,创建后仍可更改。默认新批量写动作是快照隔离;暂存写动作目前仅支持 Legacy。
4

冲突检测:对象级的写-写冲突

在支持冲突检查的隔离级别下,本体会在"对象级"检测写-写冲突。

当两个动作并发写入同一个对象时(即使是不同的属性),其中一个会失败。失败时,该动作的全部编辑被丢弃。此外,底层数据集(backing dataset)重建也可能引发更广泛的、瞬时的冲突——重建完成后重试通常就能成功。

写-写冲突(Write-write conflict)
两个动作同时改同一对象 → 其中一个失败,全部编辑被丢弃。 例:A、B 同时改 Alert #7 的不同属性,仍会冲突。
瞬时冲突(Transient conflict)
底层数据集重建引发 → 重试通常成功。 由平台自动重试机制兜底(见下一步)。
注意:冲突检查只在"写入的对象"上做,只读的对象不会引发冲突——这正是下一步"写偏斜"的根因。
5

写偏斜(Write skew)陷阱

快照隔离只检查"写"的对象,不检查"读"的对象,于是可能破坏你本想守护的业务不变式。

设想一个值班(on-call)场景:动作 A 和动作 B 都读取了"当前谁在值班"这一重叠数据,然后各自写入不同的对象。两个动作都能提交,结果导致一段时间内无人值班——这正是快照隔离无法捕捉的"写偏斜"。

"Snapshot isolation cannot catch this because the violated invariant lives on data the actions read [...] not on any single object either action wrote."快照隔离抓不到它,因为被破坏的不变式存在于动作"读取"的数据上,而不在任何一个动作"写入"的单一对象上。

点击揭晓:当发现这种隐患,该怎么重新设计?

点击这里揭晓解法 →

6

瞬时错误重试与隔离级别选型

平台能自动重试,但前提是你的动作没有"不可重复的副作用"。

当动作遇到瞬时错误(含冲突、对象加载失败)时,平台最多自动重试 5 次,并按配置的隔离级别重新执行。默认只在没有外部调用(如 webhook)时重试;若动作含外部调用,默认不重试,以免重复触发副作用。

把场景匹配到合适的隔离级别(点击每行右侧选项):

一页带走

① 动作即事务
一个动作是单事务、ACID;原子性不覆盖通知/webhook 等副作用。
② 两种写模式
批量写看不到自己的写入;暂存写能看到(read-your-own-writes)。
③ 三种隔离级别
默认快照隔离;Legacy 向后兼容;读已提交无冲突检查。
④ 冲突与重试
写-写冲突对象级检测;瞬时错误最多自动重试 5 次(无外部调用时)。

延伸阅读 · 相关页面

按主题横向跳转,不必顺着目录一篇篇读。

常见问题速答 · FAQ

关于「一致性保证(Consistency guarantees)」,读者最常问的几个问题。

两种写模式:批量写 vs 暂存写是什么?
区别在于:同一个动作在写之后再读,能不能看到自己刚写的内容。
冲突检测:对象级的写-写冲突是什么?
当两个动作并发写入同一个对象时(即使是不同的属性),其中一个会失败。失败时,该动作的全部编辑被丢弃。此外,底层数据集(backing dataset)重建也可能引发更广泛的、瞬时的冲突——重建完成后重试通常就能成功。
写偏斜(Write skew)陷阱是什么?
设想一个值班(on-call)场景:动作 A 和动作 B 都读取了"当前谁在值班"这一重叠数据,然后各自写入不同的对象。两个动作都能提交,结果导致一段时间内无人值班——这正是快照隔离无法捕捉的"写偏斜"。
瞬时错误重试与隔离级别选型是什么?
当动作遇到瞬时错误(含冲突、对象加载失败)时,平台最多自动重试 5 次,并按配置的隔离级别重新执行。默认只在没有外部调用(如 webhook)时重试;若动作含外部调用,默认不重试,以免重复触发副作用。