简体中文

开发者工具 · UUID 生成器

Nil 和 Max UUID:两个特殊值以及何时使用它们

· 背景

uuid 开发人员工作流程 数据验证

数据库行指向显式全零 UUID 异常,而全 f 值在验证边界处停止
原始 ToolAcre 矢量图

自 2005 以来,全零 UUID 一直在标准中,全 F Max UUID 加入 2024 中。这篇文章解释了它们的用途、它们如何与验证器交互以及要避免的哨兵值错误。

ID 为 00000000-0000-0000-0000-000000000000 的行 — 占位符如何成为生产错误

标识符为 00000000-0000-0000-0000-000000000000 的行看起来可能是 UUID 形状,但其含义与生成的标识符不同。如果应用程序悄悄地使用该值表示“尚未分配”,则每个未完成的行共享相同的标记。假定任何接受的 UUID 名称是真实对象的代码可以请求、缓存或加入占位符,就像它是普通键一样。可见的格式并不传达业务规则;只有明确的哨兵合同才可以。

当一层知道占位符而另一层不知道时,生产错误就会开始。表单可以提交 Nil,API 可以接受它,持久层可以存储它,而下游工作程序将每个非空字符串视为可用的外键。失败并不在于 Nil 畸形。 ToolAcre 故意识别它。失败之处在于允许“有效文本”、“生成的标识符”和“分配的关系”陷入一种未经检查的条件。

Nil UUID — 它的定义,以及为什么每个版本和变体检查在技术上都失败

在检查的实现中,Nil 是规范的全零字符串。它在测试正常 UUID 模式之前接收专用分支,因此即使正则表达式需要从 1 到 8 的版本数字以及从 8 到 b 的 RFC 变体半字节,isValidUuid 也会返回 true。 spectUuid 遵循相同的异常:它报告有效值,分配版本 0,并表示 UUID 全部为零位且不是随机的。这是经过验证的应用程序行为,而不是每个验证者必须做出相同选择的一般主张。

该分支很重要,因为 Nil 不会传递用于生成标识符的普通版本和变体路由。 ToolAcre version-4 值在版本位置携带 4,在变体位置携带 8、9、a 或 b 之一; Nil 在这两个地方都带有零。称这些检查“失败”而不提及异常会产生误导。检查器首先识别特殊值,然后通过设计绕过普通模式。如果消费者接受的话,他们需要同样可见的订购。

Nil UUID 是 ToolAcre 中的显式有效异常,报告为版本 0

all-f 字符串 ffffffff-ffff-ffff-ffff-ffffffffffff 在此存储库中没有接收特殊分支。它也无法正常模式,因为 f 超出了可接受的版本范围并且超出了可接受的 RFC 变体半字节集。因此,ToolAcre 将其报告为非规范,而不是像 Nil 一样对待它。该工作手册将标准化历史和范围边界目的归因于 Max,但工具记录、实现和测试都没有验证这些声明,因此本文不再重复它们。

这种差异比不受支持的历史记录更有用:Nil 是具有测试行为的命名常量,而 Max 是检查器拒绝的输入。项目可以在自己的协议中定义额外的哨兵语义,但不得从 ToolAcre 推断出该选择。如果互操作性取决于接受全 f 值,请记录该规则并在所属系统中对其进行测试。不要假设每个库都会对 UUID 形状的字符串进行相同的分类。

所有-f Max 值被 ToolAcre 拒绝;未断言 RFC 历史或预期使用范围

仅当模式如此表示时,哨兵和空值才会回答不同的问题。 Null 可以直接表示不存在关系。哨兵保持列填充,当周围接口不能携带 null 时可能很有用,但它会创建一个看起来像数据的值,因此会遍历索引、连接、序列化器和缓存。明显的便利将责任转移到每个读者身上:每个读者都必须记住,一个接受的 UUID 并没有命名指定的实体。

当哨兵可以满足外键形状检查而不满足关系的含义时,该交易就会成为陷阱。它还可以模糊不同的状态,例如未知、故意未分配、已删除或尚未处理。如果这些状态影响行为,请明确地表示它们,而不是重载一个魔术标识符。如果为了兼容性而保留 Nil,则为状态赋予一种记录的含义,在其他地方拒绝它,并在明确拥有的边界处对其进行转换,而不是在整个业务代码中分散比较。

验证器和特殊值 - 为什么严格的版本 /variant 检查可能会拒绝 Nil 和 Max,以及如何决定您的版本是否应该

ToolAcre 在一个验证器中演示了两层。正常输入被修剪,可选的外大括号被删除,剩余的字符串根据规范的 8-4-4-4-12 布局以及接受的版本和变体位置进行检查。 Nil 在该模式之前被测试并被故意接受。 Max也不例外,失败了。这意味着调用者无法仅从正则表达式预测特殊值策略;围绕模式的控制流是验证契约的一部分。

通过分离三个问题来设计您自己的政策。首先,文本是否可以以您的边界允许的形式被识别?其次,该值是普通的 UUID 还是命名异常?第三,该字段和操作是否允许该类别?即使诊断解析器识别出 Nil,创建端点也可能会拒绝 Nil,而导入边界可能会将记录的遗留 Nil 标记转换为 null。单独返回这些结果可以防止“解析器接受它”成为存储它的意外授权。

ToolAcre 明确接受 Nil 并在其版本和变体模式下拒绝 Max

考虑一个带有受让人标识符的任务表,该标识符使用 Nil 表示“未分配”。编写为 WHERE allocateee_id IS NOT NULL 的查询似乎选择分配的任务,但它也选择每个 Nil 行,因为哨兵是一个具体字符串。如果没有用户拥有该键,则联接可能会删除这些行,从而产生第二个不太明显的结果。两个查询在本地都是合理的;他们不同意,因为模式将状态隐藏在一个普通的标识符内,而不是直接公开赋值。

持久修复是将分配模型化为分配:在存储协定允许时使用可空关系,或者在必须区分多个状态时添加显式状态。如果兼容性边界仍然发送 Nil,则在持久化之前将其转换一次,并仅针对该边界反转映射。然后测试生成的 version-4 值、Nil、Max、空输入和格式错误的文本作为单独的情况。应用程序应该决定每个结果,而不是继承通用格式检查恰好返回的任何答案。

要点:特殊值需要显式处理 — 使用 ToolAcre 生成器生成真实 ID,并将 Nil 和 Max 视为有意的异常

特殊值需要命名处理,因为它们的形状无法承载应用程序的意图。 ToolAcre 从 Web Crypto 生成普通版本 4 UUID,必要时从 randomUUID 回退到 getRandomValues,并拒绝使用不安全的随机源。然后,它的检查器可以将生成的值与显式识别的 Nil 异常区分开来。这使得该工具对于观察很有用,但它不会选择数据库哨兵策略或证明接受的标识符属于现有记录。

使用新标识符生成器,​​并将每个标记视为单独的协议决策。在当前检查器中,Nil 是有效的,版本 0,并且不是随机的;麦克斯被拒绝了。在测试页面时保留这种区别,然后将其与您的语言、数据库和 API 的规则进行比较,然后再接受任一值。安全要点故意狭窄:生成的 ID、解析器异常、缺失的关系和业务状态是不同的概念,强大的边界使它们保持不同。