cd4bd6acb194acd30a8670e11063efb980d0ea5e
writeRecomputeChanges' purchase_task/return_match rechecks (added in the
previous phase 3 commit) used plain SELECT COUNT(*). On MySQL 8.4 under
REPEATABLE-READ, a plain read inside a transaction reuses the snapshot
taken at that transaction's first read (recomputeChanges' own SELECT), so
a purchase_task or return_match row committed by another connection AFTER
that snapshot was invisible to the recheck even after the syb_product row's
FOR UPDATE lock was granted — the row lock only serializes writers against
each other, it doesn't force a later plain read to see newer committed
data. Confirmed against a real local MySQL 8.4 server with two connections:
after the other transaction committed a task, plain COUNT(*) returned 0
while COUNT(*) ... FOR SHARE correctly returned 1. Every existing test
passed anyway because this package's tests run on SQLite, which has no
multi-connection snapshot isolation to reproduce the race at all.
Fix: both rechecks now use clause.Locking{Strength: "SHARE"} (a locking/
"current" read is enough since they only need to observe committed rows,
not lock them further). Split the three per-row queries (row FOR UPDATE,
purchase_task FOR SHARE, return_match FOR SHARE) into small query-builder
helpers (sybProductRowLockQuery / purchaseTaskLockedQuery /
returnMatchLockedQuery) so writeRecomputeChanges consumes them and a test
can independently assert their generated SQL.
New test: TestRecheckQueriesUseLockingReads opens a DryRun gorm session
against the MySQL dialector (mysql.New with SkipInitializeWithVersion,
DisableAutomaticPing — no real network connection is ever made) and pins
the exact SQL shape via db.ToSQL:
SELECT * FROM `syb_product` WHERE id = 1 FOR UPDATE
SELECT count(*) FROM `purchase_task` WHERE syb_product_id = 1 FOR SHARE
SELECT count(*) FROM `return_match` WHERE syb_product_id = 1
AND active_syb_product_id IS NOT NULL FOR SHARE
The test's own comment documents why SQLite cannot reproduce this race
(gorm's SQLite driver drops clause.Locking entirely; SQLite also has no
multi-connection REPEATABLE-READ snapshot semantics to begin with).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
GoAuto 移动采集管理平台
GoAuto 使用 go-admin、go-admin-ui 和 Android Portal/Agent 跑通 PDD 商品采集闭环:人工添加 PDD 链接、创建采集规则、创建任务、由 Android 执行并在任务详情查看结构化结果。
当前 MVP 只包含 PDD 商品、采集规则、采集任务、Android 执行和任务详情结果。顺云宝、Shopee、采购、批量任务、规则发布流程、全局停机和实时屏幕均不在当前范围。
阅读入口
旧版 ADB/Portal 验证代码、日志和诊断资料已归档到本机 demo/,该目录不进入 Git。
当前状态
- 需求和技术边界已确认。
- Harness Coding 文档和工单已建立。
- 服务端与 Android 端为可交互 HTML 原型。
server/已固定导入 go-adminv2.3.0。web/已固定导入 go-admin-uiv3.0.0。android/已建立 Kotlin/SDK 34 Agent 骨架。- 最小闭环 8 张业务表、迁移和唯一约束测试已经落地。
- 设备首次自注册、Token 认证、停用和吊销接口已经落地。
- Token 心跳、在线/离线、空闲/忙碌和离线任务失败已经落地。
验证
.\scripts\verify.ps1 -Component all
也可以把 all 改为 server、web 或 android 单独验证。
Languages
HTML
49.6%
Go
27.2%
Kotlin
10.3%
Vue
7.7%
JavaScript
2.2%
Other
2.9%