用证据解释变更结果
一句话总结
变更报告要在同一个时点对照批准、执行审计和观测到的当前行,不能把没有确认的事实混进“完成”这句话里。
为什么需要它
收到了外星庆典的取消作业已经结束的消息。运维负责人说“处理了三笔”,客户问“三个人都恢复原样了吗?”同样是“三笔”这个数字,遮住了不同的问题。批准的三个 ID 对不对、当时变更是否已确认、之后是否被其他负责人改过、现在是否仍是那个状态,这些全都不同。
把报告写长,问题就能解决吗?把没有依据的“完成”这句话拉长到三页,只会变成更像样的误解。这次你不是重新修改数据库的人,而是调查已经执行的变更,并向客户说明、让客户能决定下一步行动的负责人。输入是批准列表、执行当时的审计、观测时点的当前行。输出是同时带有供机器检查的结构和供人阅读的说明的小型证据包。
前面的分块课程是接着执行作业。这个报告工具不交给执行权限。发现不一致时,写下搁置的理由,不为了符合报告而覆盖批准或当前值。为了让报告变成绿色而改变事实的那一刻,调查与执行的边界就消失了。
工作原理
1. 先对齐集合和含义,再看笔数
批准 ID 是 1、2、5,审计 ID 是 1、2、31,那么两边都是三笔,但并不完成。批准之外的 ID 31 是需要确认的事故线索,ID 5 是没有审计的对象。只比较两个集合的长度,发现不了这种差异。
只有 ID 也不够。审计的之前 revision、之后 revision、数量,必须与批准的内容一致,才算作该变更的确认依据。审计记录的之后 revision 必须是批准 revision+1。当前行的客户、数量、状态、revision,是用来确认此后是否又变化过的另一份材料。即使同样是 cancelled 状态,只要 revision 更大,之后就可能发生过作业,所以不能悄悄当作一致。
这次客户契约的分类如下。committed 是有与批准完全相符的审计的 ID,remaining 是其余的批准 ID。committed 中当前值也相符的是 matching,当前行不存在是 missing,其余是 drifted。批准之外的审计,或者前后版本、数量不同的审计,另外记为 invalid_audit。格式本身损坏或 ID 重复的材料,在分析之前就予以拒绝。
仅凭没有审计的 remaining 的当前状态,不能断定它没有执行。可能有其他路径的作业,也可能审计遗漏了。本课以“没有确认依据”来报告。要重新开始真正的执行,需要前面课程的当前条件和新批准校验。不要把报告当作执行许可。
2. 让多条 SELECT 看到同一个瞬间
读取批准之后,另一个连接把订单和审计一起确认了。如果报告工具在那之后再去读审计和订单,第一个查询可能是以前的时点,下一个查询可能是新的时点。仅仅加了 BEGIN,并不能让多次查询成为同一个瞬间。
这次采集使用 PostgreSQL 的 REPEATABLE READ READ ONLY 事务。在同一次观测内,把普通表的查询保持为一致的快照,同时让报告代码无法修改业务表。请在隔离级别官方文档中比较 Read Committed 和 Repeatable Read 的查询时点,并读一读 SET TRANSACTION 的 READ ONLY 限制。
实验不只看是否用字符串写了隔离级别。在 after-plan 或 after-audit 这个点,另一个连接会真实提交订单和审计。这次采集中不能混进新的变更,只有采集结束之后再次观测时才能看到。还要确认,在只读区间里尝试 UPDATE,真实的数据库是否会拒绝。
这个保证不等于所有业务数据都正确。如果原来的审计已经损坏,一致的快照里也会原样看到那种损坏。它也不会把多个系统的外部 API 绑在同一个瞬间。这次的对象是同一个 PostgreSQL 内的普通表,所采集材料的业务一致性,要在之后的步骤另行检查。
3. 归还借来的连接状态
psycopg 连接可能被重复使用。如果这次调用设置了只读、隔离级别、很短的时间限制,就不能原样留给下一次调用。本实验的契约是接收 autocommit=True、调用开始时没有打开的事务的连接。只在采集上下文内应用设置,成功和失败之后都回到 IDLE。
借来的连接不关闭,只关闭 publish 自己创建的连接。在写文件期间,也没有理由一直打开数据库快照。采集到材料之后,先关闭连接,再进行序列化和文件替换。请看 psycopg 事务管理,再次确认嵌套事务和真正提交的区别。与文档版本无关,实验是在已安装的 psycopg 3.2.3 上运行。
4. 给人看的说明也用同样的依据来生成
如果在机器用的结果里写了 hold,而给客户的说明里却写“已完成”,这份材料在内部就已经冲突了。这次的证据包同时放入 evidence、report、summary。summary 是在同一个 analyze 结果中,展示批准、确认、未确认、当前一致、后续差异、缺失、审计不一致的 ID 的纯文本。
“完成”这个词也要附上范围。complete 是“观测时点完成”,并不承诺到观测之后的状态仍然保持。有问题的审计、后续差异、缺失,就是 hold;没有这些问题,但还剩下没有确认依据的批准 ID,则是 incomplete。把搁置排在完成之前的原因,是在执行其余部分之前,必须先说明现有的异常。
报告不是 SQL 或 HTML。JSON 的字符串和数字作为数据对待,不执行。时间和快照的记法,是日后用来识别说的是哪次观测的元数据。不要忘了这些值是可以手工输入的,而把它说成像权威认证时间那样。
在现场相遇的样子
哈希对得上,也可能是虚假的报告
序列化时固定键顺序、空白、UTF-8 表示,同样的规范数据,字节和哈希就会相同。这是本次格式的本地规则,不主张所有 JSON 工具都会自动生成相同的字节。要区分数字、字符串、布尔值,并且拒绝重复键和 NaN 这类输入。请参考 Python JSON 文档中的序列化选项和 object_pairs_hook。
哈希是核对内容有没有变的手段,而不是认证出处的签名。如果有人只把判定改成 complete 再重新计算哈希,就可以通过简单的哈希检查。所以要从 evidence 重新计算 report 和 summary 并一起比较。反过来,如果把观测时间等整套证据一致地重新生成,再算出哈希,仅凭这项检查就无法识别出那是虚构的。实验也会展示这种情形能通过的反例。内部一致与真伪是不同的要求。
交给真实客户时,可能还需要可信的采集路径、访问控制、不可变记录和批准体系。不要因为做出了这种文件格式,就说实现了那个组织全部的证据保存策略。用户认证和按客户的数据库访问权限,也不是这个分析函数的职责。
如果报告文件只覆盖了一半就失败了
如果先以写入模式打开已有的 report.json,后面的错误可能把以前的完整版本也一并抹掉。要把完整的字节写入同一目录里单独的临时文件并关闭,再用 os.replace 换掉目标。替换之前出错,旧文件还在;替换之后只是丢了响应,新文件作为完成的状态保留下来。请把 os.replace 文档和失败发生的位置一起读。
实验还会检查,在普通异常时是否清理了自己的临时文件、是否没有碰其他文件。进程被强制终止时,finally 可能不会执行;断电还要加上文件系统的耐久性问题。这次验证的,是正常执行和注入异常时的文件替换保护,并不证明强制终止、磁盘损坏、断电的情形。
FDE 的报告是帮助下一个决定
Palantir FDSE 招聘启事谈到定制客户的实现,以及与多个利益相关方的协作。以此为依据,设计了一个同时制作客户阅读的说明和技术团队重新验证的结构的任务。这并不意味着复现了那家公司的内部表格或必需的技术模式。
下一项实验要做什么
一致地采集太空庆典的变更证据,把笔数相同但 ID 不同、审计版本错误、后续变更分开。让给人阅读的说明和机器判定基于同样的依据,并拒绝重新计算过哈希的错误结论以及重复的 JSON 键。最后,从只读的数据库采集,连到保留已有文件的发布。
实验只使用虚构数据和每个学员各自的一次性数据库。没有把报告发给外部客户或修改生产数据库的作业。