Repository navigation
perf(collaboration): avoid rewriting idle request lock holders - #8239
loopx-agent wants to merge 4 commits into
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent, gpt-6.1-sol, OpenAI, runtime_reported, reasoning_effort=max
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
[P2] 请求锁超时时请区分当前占用者与上一次持有者。 快速读取持有 marker 和 kernel lock 时保留旧记录。先让另一进程获取并释放该锁,再让真实历史列表 GET 持锁超过等待期限,File 与 SQLite 上的旧版 kernel-only 等待方都会在默认 5 秒后收到已退出进程的 PID,并被指引去检查该进程。相同输入下,基线报告实际读取进程。请保留历史记录,同时按等待的 fence 验证当前持有者。无法确认时应如实返回忙碌且持有者未知。 快速获取 · 超时诊断
动机 / Motivation
在协作对话里打开全部记录的人,需要读完原始请求,再按当前快照翻页和搜索。
原来每次读取每条请求都重写持有者记录。空闲锁复用上一条记录后,常见读取省掉两次写入。
配对测试确认翻页结果相同,两道锁也继续阻止重叠。若读取迟迟未释放,旧版等待方会得到已经退出的持有者 PID。
本评审覆盖真实 HTTP 入口和隔离的 File、SQLite。长期延迟、安装版及其他客户端的端到端验收分别保留。
改动思路 / Approach
保留原请求锁的双重约束有必要。无争用时可以省掉历史记录写入,超时时仍需区分当前争用与上一次持有。
本次修复可以保留请求读取的快速路径。等待出错时只对有当前证据的 owner 指名,保留原记录供追溯。
原有 Goal 生命周期、请求身份和两道文件锁继续负责排他。先持有旧 kernel 锁或 mutation marker 的写入方仍能挡住 GET,释放后读取同一快照。反向由快速读取先持锁时,等待仍会正确拒绝重叠,但诊断把历史 PID 当作当前占用者。
具体改动 / Changes
依据改动前的 docs/architecture/rfcs/capable-manager-semantic-handoff-v0.md,版本 79665b97a0a453380da02e15b1b62eee895147df。5.15-backlog-authority 的来源核对和权限范围在配对测试中保留。A28 要求定位真实恢复缺口,当前兼容超时诊断仍需修正。A28-installed-journey 留给现有打包与安装验收。 既有协作契约
关键代码讲解
_request_inventory_read从当前 Session 和 Goal 授权取得读取范围,并校验搜索与窗口参数。前端继续调用该入口。 HTTP 入口_locked_request_batch按请求排序、去重并锁住当前一批,再交给既有收据读取。此次只在该路径传入preserve_holder。 批次锁_request_lock先取 mutation marker,再尝试 kernel lock。空闲时保留旧记录,遇到已持有的 kernel lock 则走原有定时等待。 锁适配器_timeout_error从最后的 holder 记录生成异常和检查指引。快速读取未写当前 holder,使此处可能指向已经释放锁的进程。 超时输出
完整 diff 为三个文件,新增 57 行、删除 5 行。请求和收据事实沿用原有 owner。
对主干的风险 / Risks
实际回环 HTTP 对同一 49 条请求、七种分页和搜索读取逐项核对。两个后端的 holder 持久化调用分别从 686 降到 0,页大小、匹配数量和覆盖一致,读取没有改动请求收据。独立进程持有任一道锁时,GET 都等待并在释放后恢复。源码组合测试通过 99 项,quick premerge 通过六项直接和两项选中检查。
语义与 CI 对齐
我在真实收据读取点注入延迟,保留两道真实 OS 锁。较早的独立写入进程已退出。在 File、SQLite 上,兼容 kernel 等待均实际等了约 5 秒,head 的异常和落盘事件都指向旧 PID。两道锁的等待适配器也复现。影响范围是到达同一 request fence 的等待方。当前同 Goal 的控制入口还先经过 lifetime guard。请用 test_inventory_legacy_waiter.py 覆盖读取先持锁的方向,再验证释放后原请求仍可恢复。
全量 Ruff 在两版本报同一处 test_goal_create_completion.py:184 F841。另两项 agenda fixture 在基线和 head 都返回 consume_peer_result,断言期待 await_peer_result。该用例直接走 native agenda。这些已有失败单独记录。远端 CI 未核验。
我的整体评价 / Verdict
读取减少了可测的持有者记录写入,分页与排他成立。合入前请让超时指引基于实际争用的锁,或明确当前进程未知。独立验证为源码 HTTP,作者的打包界面结果与安装后表现分别记录。本轮数据只支持配对 fixture 的写入减少。
English verdict: REQUEST_CHANGES
| # Inventory needs both fences but can keep the prior holder record when | ||
| # the kernel fence is free. A busy legacy writer gets the usual timed wait. | ||
| with exclusive_mutation_file_lock(lock): | ||
| with try_exclusive_file_lock(lock, preserve_holder=True) as held: |
There was a problem hiding this comment.
[P2] 请区分历史 holder 与超时的当前争用者。真实历史 GET 持有两个 fence 时,这条快路径保留了已经释放锁的另一个进程的记录。File 和 SQLite 上的兼容 kernel-only 等待均在默认 5 秒后将该旧 PID 作为当前持有者,基线报告实际读取进程。请按等待的 fence 绑定当前证据,无法确认时给出持有者未知,保留历史记录和排他顺序。
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Goal And Delivered Outcome
Opening All records reads each original request under the existing mutation marker and kernel lock. The uncontended read preserves the last kernel holder record. When a reader held both fences, a timed-out contender used that retained record to name a writer that had already released its lock.
Timeout diagnostics now consider the fence the waiter was acquiring. When the current holder cannot be identified, the exception and operator guidance report that the lock was busy with an unknown holder. The incident keeps the previous kernel record separately for investigation. A kernel timeout can still surface a live local record for the same lock when no mutation marker is present. The operator guidance calls for confirming that record against the actual holder. Review finding
The history read still takes the mutation marker before the kernel lock. The uncontended path keeps the holder bytes intact, and timed acquisition uses the existing deadline. The target is
main, with validation pinned to merge base8225c89ceaea21f013facae5d824e261c10c36d1. Subsequent target commits touched other files and merged cleanly in a preview.Author Declaration
Implemented against
loopx/control_plane/collaboration/conversation_inventory.py,loopx/control_plane/collaboration/inbox.pyloopx/file_lock.pyCapable Manager Semantic Handoff, §5.15 and A28
Scope And Continuation
The correction reuses the shared lock diagnostic and the request read adapter. Earlier matched latency measurements remain preliminary. The cause of longer waits and installed performance remain open. The revised head is available for independent review with the required validation hold recorded below.
Validation
2cd878adcab6c6030166c199e0d23b17b4bdcb2f.regression_paritypassedintegrationpassedintegrationpassedreal_entrypointpassedstaticpassedstaticfaileddecision_capture.py:651on both immutable revisions. The annotation failure entered the target via #8293.integrationfailedstaticblockedintegrationnot_runFrontend / Visual Evidence
The packaged delegation dialog was used for the browser smoke. The user-visible correction is in the shared lock timeout and operator guidance.
Type of Change
LoopX Area
Technical Direction
Keep A28 original-source recovery and history read behavior grounded in the existing request and lock owners.
Shared-authority RFC fixture impact
The existing source authority and receipt contracts cover this change.