## 做了什么(顺序有意义) 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` 未编译)。
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
#!/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())
|