Skip to content

Commit 49b9085

Browse files
committed
v0.42.9: HP-31 registration rate window
Signed-off-by: Eli Ma <eli@patch.sh>
1 parent 0075d0b commit 49b9085

6 files changed

Lines changed: 783 additions & 38 deletions

File tree

‎Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mega2"
3-
version = "0.42.8"
3+
version = "0.42.9"
44
edition = "2024"
55
license = "MIT OR Apache-2.0"
66
description = "Monorepo engine and Git hosting server for AI agents: trunk-based storage, Git Smart HTTP, Git LFS, OCI registry, and Agent Session Capture"

‎docs/plan/plan-20261002.md‎

Lines changed: 22 additions & 18 deletions
Large diffs are not rendered by default.

‎docs/refactoring/history-projection.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,7 @@ fn mark_ready_if_covered(F, tip) -> bool: # 只在持
12661266
5. **名额。** 需要冷启动时,统计 `warming_since IS NOT NULL` 的行数,达到 `max_concurrent_cold_starts` 即拒绝。
12671267
6. **写入。** 插入新过滤器行(`warming_since = now()`,`ON CONFLICT (filter_id) DO NOTHING`),或者对已回收的行置 `warming_since = now()`(P1 起,在同一语句中置 `last_access_at = now()`,见 4.6);需要时插入 `mega_view` 新 version;计入速率时,插入一行 `mega_view_register_log`,并删除本 requester 窗口外的旧行。提交后发 4.4 的进程内信号。
12681268
由读者访问触发的重新预热(4.6)只执行第 1、2、4、5、6 步,不计速率。
1269-
- **拒绝应答。** 第 3、4、5 步任一项不满足时,事务回滚,不写任何行,返回 429 加 `Retry-After`。速率超限时,取窗口内最早一行还要多久离开窗口;其余情形固定为 30 秒。【代码】`ApiError` 不携带响应头,注册接口要自行构造响应,参照 `lfs_media.rs::map_media_error`(约 L79–85)。
1269+
- **拒绝应答。** 第 3、4、5 步任一项不满足时,事务回滚,不写任何行,返回 429 加 `Retry-After`。速率超限时,取窗口内最早一行还要多久离开窗口,向上取整到秒,上限 3600 秒;其余情形固定为 30 秒。【代码】`ApiError` 不携带响应头,注册接口要自行构造响应,参照 `lfs_media.rs::map_media_error`(约 L79–85)。
12701270
- **名额的释放与崩溃回收。** 名额记在数据库状态中,不是进程持有的租约,所以不需要心跳或 TTL。
12711271
- 释放只有两处:视图首次就绪的那个 catch_up 批事务内清空 `warming_since`(4.4);回收与 rebuild 时清空(4.6)。
12721272
- 处理冷启动的副本崩溃时,名额与 `projected_seq` 都已持久化。任一副本的补偿任务都把 `warming_since` 非空的过滤器视为活跃视图,从已提交的水位接着做(4.4 触发方式第 2 条)。水位已到链尾、尚未就绪的过滤器同样是补偿候选(6.9),由下一次 catch_up 置就绪并释放名额。
@@ -1468,7 +1468,7 @@ P1 的清扫周期,以及闲置阈值 `idle_recycle_secs`(下界为 2T = 720
14681468

14691469
**注册准入(P0):**
14701470
- 用两个连接模拟两个副本。在活跃过滤器数为 `max_filters − 1`、且名额只剩一个时,并发注册两个不同的新过滤器:恰好一个成功,另一个得到 429,且没有留下过滤器行、视图行或速率行。同一个新过滤器并发注册两次:只建一行,只占一个名额;
1471-
- 同一 token 在窗口内第 `register_rate_per_token + 1` 次产生新定义的注册得到 429,`Retry-After` 不超过窗口的剩余时间;幂等的重复注册不计数;不同 token 互不影响;
1471+
- 同一 token 在窗口内第 `register_rate_per_token + 1` 次产生新定义的注册得到 429,`Retry-After` 等于最早一行离开窗口的剩余时间向上取整到秒,不超过 3600 秒;幂等的重复注册不计数;不同 token 互不影响;
14721472
- 冷启动进行中丢弃处理它的 worker,当前批不提交;另一个 worker 实例在同一 schema 上续做到就绪,名额在就绪的同一事务内释放;
14731473
- 冷启动的最后一批:如果根链已覆盖 `main@/`,`projected_seq`、`ready_seq` 与 `warming_since` 在同一事务内更新,提交后不存在‘水位到链尾、`ready_seq` 为 NULL’的中间状态。如果最后一批时 `main@/` 已前进到根链之外,则不置就绪;根链扩展后,视图续追到就绪。另在测试中直接写出以下状态:`projected_seq` 等于链尾、`ready_seq` 为 NULL、`warming_since` 非空、`main@/` 等于链尾,此后不再推进根。只运行另一个 worker 实例的补偿任务,断言视图在一个周期内就绪并释放名额。
14741474
- 4.2 前提校验失败的冷启动仍占用名额,并能由指标观测到;

‎docs/refactoring/integration.md‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,9 @@ Try/Blocking/取消等锁;以及全部锁键与同 schema 的两两隔离。
225225

226226
`AdmitRequest`、`AdmitMode`、`FilterDefinition`、`AdmitLimits`、
227227
`AdmitOutcome` 与 `RejectReason` 都限定在 storage crate 内。限额仅来自调用方传入的
228-
`AdmitLimits`,其 `From<&ViewsConfig>` 实现读取 `max_filters` 与
229-
`max_concurrent_cold_starts`;准入自身不读取 `Storage` 的配置快照。拒绝返回
228+
`AdmitLimits`:HP-13 的 `max_filters` 与 `max_concurrent_cold_starts`,以及 HP-31
229+
追加的 `register_rate_per_token` 都由 `From<&ViewsConfig>` 读取;准入自身不读取
230+
`Storage` 的配置快照。总数与名额拒绝返回
230231
`Rejected { reason, retry_after: 30 秒 }` 并回滚;幂等命中没有写入。新过滤器以
231232
`ON CONFLICT (filter_id) DO NOTHING RETURNING` 写入,若 L_R 以外的写入方在查询后
232233
抢先提交,SeaORM 的零返回行会规范为
@@ -250,6 +251,28 @@ L_R,并轮询确认两个准入正在等待再释放;轮询失败分支会
250251
未提交的同键插入与准入经 `tokio::join!` 在 30 秒内运行,在观测到 transactionid 等待后
251252
提交;观测失败先显式回滚写入事务,验证零行冲突回滚。
252253

254+
## DB 模块覆盖:视图注册速率窗口(HP-31)
255+
256+
`AdmitLimits` 从调用方的配置快照额外携带
257+
`register_rate_per_token`,`RejectReason::Rate` 表示按 requester 的速率拒绝。
258+
`ViewStorage::admit` 在 Register 的幂等判定之后、总数和冷启动名额之前,以同一短事务
259+
读取 requester 在 `REGISTER_RATE_WINDOW_SECS`(3600 秒)内的注册行数和最早一行的
260+
离窗秒数。达到阈值时显式回滚并返回向上取整、最多 3600 秒的 `retry_after`;成功的
261+
计速率 Register 在提交前插入一条默认时间戳的日志,并仅删除该 requester 的过期行。
262+
`Idempotent` 与 `Rewarm` 不读写 `mega_view_register_log`。
263+
264+
六个 lib 用例覆盖:`rate_rejects_iff_requester_window_full` 的三类计速率 Register、
265+
过期行、requester 隔离和热加载阈值;`rate_rejection_writes_nothing` 的速率、总数和
266+
名额拒绝逐行三表快照;`rate_retry_after_from_oldest_row` 的 SQL 时间边界和 3600 秒上限;
267+
`rate_admitted_appends_and_trims` 的插入与 requester 范围内清理;
268+
`rate_idempotent_and_rewarm_not_counted` 的不计数路径;以及
269+
`rate_concurrent_last_quota` 的跨副本最后一个速率配额。窗口状态由直接插入的
270+
`created_at = localtimestamp - k 秒` 行构造,并在各状态之间清空运行态表;重试秒数由
271+
调用前后的 SQL 值夹定,Rust 不直接比较时间。并发用例用 `test_db_config` 和
272+
`database_connection` 共享 schema,先预热三个连接池,再由第三个连接以
273+
`ViewLockMode::Blocking` 持有 L_R;只有观察到两个等待者才释放锁,10 秒内未观察到则
274+
显式回滚,整个 `tokio::join!` 另有 30 秒上限。
275+
253276
## DB 模块覆盖:根链停追与恢复(HP-28)
254277

255278
`ROOT_CHAIN_HALTED_SQL` 是不带绑定参数、不引用外层别名的完整布尔表达式。读者可把它

0 commit comments

Comments
 (0)