Files
inkwatch/tests/dump-rust-frame.py
jkooe 7c9c2f0605 refactor(rename): pmon → inkwatch 数据层迁移(阶段 B:库名/角色/卷名/连接串)
## 做了什么(顺序有意义)

1. **备份**:`pg_dump -Fc` → 152MB dump,落在仓库外
   `.backup-rename-20261108/`,并留了改名前后的表行数文件。
2. **停应用**(nginx/business/gateway),保留 PG/Redis。
3. `ALTER DATABASE pmon RENAME TO inkwatch` + `ALTER ROLE pmon RENAME TO inkwatch`
   + `ALTER ROLE inkwatch WITH PASSWORD 'inkwatch_dev_only'`。
4. **复制卷**(不是改名):`pmon_pg_data → inkwatch_pg_data`(3.807GB)、
   Redis、nginx 日志三个卷。**旧卷全部保留**,作回滚点。
5. 改 compose 的卷名与环境变量默认值、`deploy/` 下全部脚本/配置、测试默认连接串、
   `.env.example`、`docs/`。
6. 用新 compose 起栈,删旧容器(`pmon-*` 共 6 个,含 9 天前的 migrate 容器)。

## 数据校验:这是本次唯一不能靠"应该没事"的部分

第一次我拿 `pg_stat_user_tables.n_live_tup` 当基线,**那是错的** —— 它是估算值,
实测 `devices` 估 58、实际 14559,`device_metrics` 估 0、实际 257 万。用它做校验
等于没有校验。

改成的办法:把 dump **恢复到一个临时库**,然后两张库**逐表精确 `count(*)` 对比**
(排除 `_hyper_*` chunk,父表计数已覆盖)。结果:

```
唯一差异:device_metrics  当前 2572652 / 备份 2572456  → 当前多 196 行
```

多出来而不是少了 —— 因为 dump 是 20:04:57 取的,而停服在 20:07:53,期间设备仍在
正常上报。最新数据时间戳 `12:07:53 UTC` 佐证了这一点。**196 行是正常写入,不是差异。**
逐表其它 40+ 张表行数完全一致。

校验库随后删除,容器内临时 dump 也删了。

## 两个踩到的坑(都已记录在流程里)

**① `docker exec` 不加 `-i` 时 stdin 不转发。** 我第一次用 heredoc 把四条 SQL 喂给
`psql`,结果**一条都没执行、也不报错** —— 看起来像"执行了但没生效"。改成 `-c` 逐条。

**② `ALTER ROLE` 不能重命名当前会话用户**(`session user cannot be renamed`)。
解法是建一个临时超级用户来执行,用完立刻删(已验证 `pg_roles` 里查不到它)。
注意 `DROP ROLE` 同样不能删当前用户,必须换回原身份再删。

## 有意没做的事

- **旧卷没删**:`pmon_pg_data`(3.807GB) / `pmon_redis_data` / `pmon_nginx_logs` 都留着。
  这是唯一的整体回滚点。确认新栈稳定运行一段时间后,可自行
  `docker volume rm pmon_pg_data pmon_redis_data pmon_nginx_logs`。
- **`pmon_dbg1` / `pmon_claim_verify` 两个遗留库没动**:它们是历史测试/调试残留,
  不是产品库。要清可以直接 `DROP DATABASE`,但删数据这种事我不代做决定。

## 验证

新栈 5 个容器全 `healthy`,`/health` business 与 gateway 均 **200**;
库内 `users=128 devices=14559`,与迁移前一致。

服务端套件(全部自起源码实例或纯离线):
认领 **57/0**、埋点 **29/0**、配对码来源 **16/0**、权限 **60/0**、schema **116/0**
—— 与改名前的数字**完全一致**。

客户端上一提交已验证 **595 通过 / 2 失败**(两条为 `libinkwatch_core.dylib` 未编译)。
2026-11-08 20:12:25 +08:00

72 lines
2.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
从 Rust 核心产出一帧**真实采集数据**(JSON,打印到 stdout)
用途:给 tests/rust-frame-pipeline-test.js 喂真实帧。
存在的理由见该测试文件顶部说明 —— 只有"真实帧穿透全链路"才能证明
采集层字段名与协议白名单、DB 列是同一套契约。
用法:python3 tests/dump-rust-frame.py [dylib路径]
"""
import ctypes
import json
import os
import subprocess
import sys
import time
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def dylib_path():
if sys.platform == "darwin":
return os.path.join(ROOT, "rust-core", "target", "release", "libinkwatch_core.dylib")
if sys.platform.startswith("linux"):
return os.path.join(ROOT, "rust-core", "target", "release", "libinkwatch_core.so")
if sys.platform.startswith("win"):
return os.path.join(ROOT, "rust-core", "target", "release", "inkwatch_core.dll")
return ""
def main():
path = sys.argv[1] if len(sys.argv) > 1 else dylib_path()
if not os.path.exists(path):
# 产物缺失则尝试编译,避免调用方拿到空结果后误判
subprocess.run(
["cargo", "build", "--release"],
cwd=os.path.join(ROOT, "rust-core"),
capture_output=True, text=True, timeout=900,
)
if not os.path.exists(path):
print(f"找不到编译产物: {path}", file=sys.stderr)
return 1
lib = ctypes.CDLL(path)
lib.inkwatch_collector_new.argtypes = [ctypes.c_size_t]
lib.inkwatch_collector_new.restype = ctypes.c_void_p
lib.inkwatch_collect_frame.argtypes = [ctypes.c_void_p]
lib.inkwatch_collect_frame.restype = ctypes.c_void_p
lib.inkwatch_collector_free.argtypes = [ctypes.c_void_p]
lib.inkwatch_collector_free.restype = None
lib.inkwatch_string_free.argtypes = [ctypes.c_void_p]
lib.inkwatch_string_free.restype = None
h = lib.inkwatch_collector_new(4)
# 采两帧:第一帧的时间差极小,网络速率会失真;第二帧才是稳态真实值
first = lib.inkwatch_collect_frame(h)
lib.inkwatch_string_free(first)
time.sleep(1.0)
ptr = lib.inkwatch_collect_frame(h)
raw = ctypes.cast(ptr, ctypes.c_char_p).value.decode("utf-8")
lib.inkwatch_string_free(ptr)
lib.inkwatch_collector_free(h)
json.loads(raw) # 快速自检:不是合法 JSON 就非零退出
print(raw)
return 0
if __name__ == "__main__":
sys.exit(main())