DaZuiZui commented on issue #18428: URL: https://github.com/apache/iotdb/issues/18428#issuecomment-5284464055
## 中文对照:范围受限的实现补充说明(对应英文 addendum) 以下内容仅为上一条英文 [“Scope-limited addendum: required implementation clarifications”](https://github.com/apache/iotdb/issues/18428#issuecomment-5278263388) 的中文对照,方便中文读者阅读。它不引入任何新的要求、语义或实现范围,也不单独构成规范;如中英文表述存在歧义或不一致,以上一条英文 addendum 为准。其适用范围、待 maintainer 确认状态及与此前方案的优先关系,均与英文原文完全相同:仅在发生冲突的具体点上替代此前表述,其余内容保持不变。 本补充说明只用于消除上述方案中的实现歧义。它仅限于 #18428 所要求的日历时长行为,并等待 maintainer 确认。如果本补充说明与之前的某一点冲突,则只取代发生冲突的那一点;方案的其余部分保持不变。 ### 1. CQ 时长单位别名 仅在 CQ 的 `EVERY` 和 `RANGE` 时长参数位置,支持以下大小写不敏感的日历单位: - 月:`mo`、`month` - 年:`y`、`year` `1y` 和 `1year` 统一归一化为 12 个日历月。复数形式的别名、小数、带正负号的输入分量以及时长内部的空白仍不受支持。CQ 专用的时长规则必须完整消费并校验整个值,因此在这里增加 `month` 和 `year` 不会改变 `GROUP BY TIME`、日期运算、`FILL`、`SESSION` 或一般标识符的词法切分方式。 这一点取代此前“本 issue 只支持缩写形式”的表述。 ### 2. 结构化请求与显式 BOUNDARY 使用一个数值有界的结构化值,形式例如: ```text CQDuration { int64 monthPart int64 fixedPart } ``` `TCreateCQReq` 为 `every`、`startOffset` 和 `endOffset` 增加 optional 结构化值,并增加 optional 的 `boundaryExplicit` 标志。对于新的结构化请求,三个最终生效的时长和 `boundaryExplicit` 必须全部存在;部分提供的结构化表示必须被拒绝。所有算术运算和窄化转换都必须检查溢出与越界。 为兼容 legacy fixed-only 请求,保留现有的 `i64` 时长字段及其 field ID。完全没有结构化字段的请求按 legacy fixed-only 请求解释。对于结构化请求,结构化值是权威值;新代码绝不能使用 legacy `i64` 字段近似任何非零的日历月分量。当所有结构化时长的 `monthPart` 均为 0 时,legacy fixed 值与 structured fixed 值必须一致,否则拒绝该请求。 `boundaryExplicit=false` 表示 ConfigNode 应用已经选定的“省略 BOUNDARY”规则。`boundaryExplicit=true` 则将用户显式写出的 `BOUNDARY 0` 保留为 Unix epoch instant。该标志、归一化后的 boundary、各时长、`ZoneId` 和进度索引必须完整贯穿 procedure/plan 序列化以及 CQ metadata snapshot。 ### 3. 混合版本下的创建策略 本 issue 不声称支持在滚动升级的混合版本窗口中创建日历型 CQ。只有当所有已注册的 ConfigNode,以及所有能够接收客户端 SQL 的 DataNode,都已经升级到能够理解结构化表示的代码版本后,才支持日历型 CQ 的 CREATE。之所以该条件必须覆盖 DataNode,是因为 `CREATE CQ` 会先在 DataNode 上解析,然后才构造 `TCreateCQReq`。 在转发结构化日历请求前,升级后的 SQL-ingress DataNode 必须使用现有的集群节点版本信息,明确验证上述条件已经满足;升级后的 ConfigNode 在接受请求前再次执行该检查。任何已注册节点的版本未知或不受支持,都必须导致清晰的拒绝。在升级期间,使用日历型 CREATE 之前,必须先将旧 DataNode 从客户端路由中移除。fixed-only CQ 的创建以及历史 CQ 继续保持 legacy 行为。 这只是针对 #18428 的狭义、fail-closed 兼容性检查,并不是新的 feature-activation、membership-attestation 或升级框架。 ### 4. 确定性的日历与 DST 解析 令 `B` 为使用持久化 timestamp precision tick 表示的精确锚点 instant,`Z` 为持久化的 CQ `ZoneId`。对于时长向量 `(M, F)`: ```text anchor = instant(B).atZone(Z) anchorLocal = anchor.toLocalDateTime() anchorOffset = anchor.getOffset() ``` - 如果 `M == 0`,则 `calendarApply(B, (M, F), Z)` 为经过检查的 elapsed-tick 加法 `B + F`。特别地,`calendarApply(B, ZERO, Z) == B`,并且 fixed-only CQ 的行为保持不变。 - 否则,计算 `targetLocal = anchorLocal.plusMonths(M)`,其中包括月末钳制。按照 `Z` 的规则解析该本地时间;当 `anchorOffset` 在 `targetLocal` 有效时优先使用它,之后再把 `F` 作为经过检查的 elapsed ticks 加上。 - 对于 DST gap,将本地时间按 transition duration 向前平移,并使用 transition 后的 offset。对于 overlap,如果 `anchorOffset` 有效则使用它,否则使用较早的有效 offset。这与 `ZonedDateTime.ofLocal(targetLocal, Z, anchorOffset)` 的行为一致,并且不得依赖宿主机的默认时区。 整数形式或显式带 offset 的 BOUNDARY,首先解析为精确的 instant `B`。写出的 offset 只用于选定该 instant,不会替换持久化的 CQ 时区 `Z`;recurrence 会在 `Z` 中观察并计算 `B`。不带 offset 的 BOUNDARY 使用 `Z` 解析:gap 使用同样的规则;overlap 在不存在 preferred anchor offset 时使用较早的 offset。 本 issue 持久化的是 `ZoneId`,而不是 TZDB 规则的私有副本。各调度节点应使用兼容的 TZDB 数据;安装较新的 TZDB 可能会合理地影响未来的 civil-time transition。 ### 5. 相对锚点的复合时长与进度 时长运算按分量执行,并且相对于原始锚点: ```text E = (monthPart, fixedPart) n * E = (n * monthPart, n * fixedPart) executionTime(n) = calendarApply(B, n * E, Z) ``` 乘法、向量加减以及 timestamp 转换都必须经过检查。复合时长不得反复作用于前一个 occurrence。例如,以 UTC 的 `2024-01-30 00:00` 为锚点,`EVERY 1mo2d` 时,`n=1` 为 `2024-03-02 00:00`,`n=2` 为 `2024-04-03 00:00`。RANGE 向量同样先进行按分量运算,然后只调用一次 `calendarApply`。 ConfigNode 只捕获一次 CREATE reference instant `C`。初始索引严格定义为: ```text min { n >= 0 | executionTime(n) >= C } ``` 相等时选择该 occurrence。CQ metadata 持久化 `nextOccurrenceIndex`,其定义为第一个尚未持久完成的 occurrence。执行失败时重试同一个索引;执行成功后,必须先持久化进度,再调度下一个 occurrence;恢复时从已存储的索引继续。`BLOCKED` 按顺序推进;`DISCARD` 在跳过错过的索引时,针对一次捕获的 callback time 使用同一个 lower-bound 规则。这样保持现有的 at-least-once 执行边界,并且不会引入 exactly-once 协议。 ### 6. Timeout 与定向测试 `TExecuteCQ.timeout` 仍以毫秒表示。对于 occurrence `n`,根据相邻两个实际计划执行 instant 之间的距离计算: ```text deltaTicks = executionTime(n + 1) - executionTime(n) timeoutMs = ceil(deltaTicks / ticksPerMillisecond) ``` 减法和转换都必须经过检查,任何正的亚毫秒间隔转换为 1 ms。不得将 timestamp-precision ticks 直接作为毫秒传递,也不得使用固定 30 天的近似值。 除此前已经列出的测试外,还将通过定向测试覆盖: - 四种受支持的别名,以及在 CQ `EVERY`/`RANGE` 之外使用这些别名时的拒绝行为; - 部分提供或相互冲突的结构化字段,以及 `boundaryExplicit` 的 round trip; - 在 CREATE ingress 和 ConfigNode acceptance 两处对混合版本/未知版本的拒绝; - `calendarApply(B, ZERO, Z) == B`、DST gap、overlap 的两种 anchor offset,以及显式带 offset 的 BOUNDARY 与不同 CQ `ZoneId` 的组合; - 复合 EVERY/RANGE 调度以及经过检查的向量溢出; - CREATE 恰好发生在 boundary 上、持久化的 `nextOccurrenceIndex`,以及恢复时不发生 off-by-one 偏移; - ms/us/ns timestamp precision 下的 timeout 转换。 本补充说明明确不包括:新的 V2 RPC families、PREPARE/ACTIVATE 命令、reader-floor markers、node attestation、通用 mutation exact-retry 协议、execution-admission redesign,以及自定义 ZoneRules snapshot 格式。 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
