@furongjun1999/dsh-memory 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +572 -465
- package/docs/README.md +1 -0
- package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225v2_/345/217/202/350/200/203dsh-TUI_v1.0.md +248 -0
- package/docs/eval/DSH/346/227/245/345/277/227/347/264/242/345/274/225/346/225/210/346/236/234/351/252/214/350/257/201_v1.0.md +209 -0
- package/docs/eval/DSH/347/253/257/347/274/272/351/231/267/344/270/223/351/241/271_v1.0.md +254 -0
- package/docs/eval/P1b2_/350/257/273/351/235/242/344/273/243/351/231/205/344/277/256/345/244/215_v1.0.md +124 -0
- package/docs/eval/bench_lingshu_self/bench_self.py +140 -0
- package/docs/eval/bench_lingshu_self/self_bench_result.json +404 -0
- package/docs/eval/bench_lingshu_self//347/201/265/346/236/242/350/207/252/345/272/223/347/253/257/345/210/260/347/253/257/346/243/200/347/264/242/345/256/236/346/265/213_v1.0.md +38 -0
- package/docs/eval//344/270/215/345/217/257/351/235/240/346/200/247/350/220/275/345/234/260_P0_v1.0.md +185 -0
- package/docs/eval//345/217/221/345/270/20306_/344/270/200/351/224/256/351/205/215/347/275/256/344/270/216DSH0172_v1.0.md +171 -0
- package/docs/eval//346/225/205/351/232/234/346/263/250/345/205/245/345/256/236/346/265/213_v1.0.md +422 -0
- package/docs/eval//347/253/257/345/210/260/347/253/257LoCoMoQA/345/220/214/345/217/243/345/276/204/345/257/271/347/205/247_v1.0.md +100 -0
- package/docs/eval//347/253/257/345/210/260/347/253/257/345/271/262/346/211/260/346/261/240/350/257/204/346/265/213_/347/241/256/345/256/232/346/200/247/350/243/201/345/206/263vsLLM_judge_v1.1.md +197 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v1.md +156 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v10.md +210 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v11.md +227 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v12.md +203 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v13.md +233 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v14.md +191 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v15.md +213 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v16.md +214 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v17.md +199 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v18.md +183 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v19.md +207 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v2.md +156 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v3.md +152 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v4.md +128 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v5.md +114 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v6.md +192 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v7.md +187 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v8.md +207 -0
- package/docs/eval//347/274/272/351/231/267/346/214/226/346/216/230_/350/207/252/344/270/273/350/277/255/344/273/243_v9.md +203 -0
- package/docs/hive//345/256/211/345/205/250/345/256/241/350/256/241/345/256/236/351/224/232_v0.1.md +202 -0
- package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +19 -3
- package/docs/images/lingshu-moonlight-covenant-poster-preview.jpg +0 -0
- package/docs/images/lingshu-moonlight-covenant-poster.png +0 -0
- package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +40 -40
- package/docs/theory//344/270/215/345/217/257/351/235/240/345/256/232/347/220/206/344/270/216/345/244/261/346/225/210/344/274/230/345/205/210/346/241/206/346/236/266_v0.3.md +365 -0
- package/docs//344/270/215/345/217/257/351/235/240/346/200/247/347/220/206/350/256/272_v0.1.md +343 -0
- package/dsh/cordis-patch-profile-web.example.yml +35 -0
- package/lib/bridge.d.ts +9 -0
- package/lib/bridge.js +35 -0
- package/lib/cli.d.ts +3 -0
- package/lib/cli.js +66 -0
- package/lib/index.js +7 -1
- package/lib/init.d.ts +88 -0
- package/lib/init.js +287 -0
- package/lib/lib/roleplay_web.js +530 -443
- package/lib/lib/token_store.d.ts +7 -1
- package/lib/lib/token_store.js +12 -3
- package/md_cg/audit.py +12 -1
- package/md_cg/backfill.py +16 -15
- package/md_cg/backfill_bucket_zh.py +35 -0
- package/md_cg/bench_e2e_judge.py +532 -0
- package/md_cg/bench_e2e_locomo_qa.py +368 -0
- package/md_cg/bench_e2e_qa.py +256 -0
- package/md_cg/branches.py +18 -2
- package/md_cg/ccgc.py +3 -2
- package/md_cg/chain.py +19 -4
- package/md_cg/consolidate.py +7 -6
- package/md_cg/crosscheck.py +4 -3
- package/md_cg/crypto.py +439 -437
- package/md_cg/datapath.py +395 -335
- package/md_cg/evidence.py +8 -3
- package/md_cg/export.py +3 -1
- package/md_cg/forgetting.py +2 -2
- package/md_cg/fsutil.py +48 -0
- package/md_cg/hotcache.py +255 -238
- package/md_cg/interop.py +161 -22
- package/md_cg/judgment_manifest.py +177 -0
- package/md_cg/links.py +655 -622
- package/md_cg/mcp_server.py +282 -31
- package/md_cg/mdcg.py +702 -126
- package/md_cg/mdcos.py +454 -68
- package/md_cg/mreview/govern.py +5 -4
- package/md_cg/postings.py +4 -2
- package/md_cg/readcache.py +76 -18
- package/md_cg/reconcile.py +228 -0
- package/md_cg/review_cli.py +215 -0
- package/md_cg/routing.py +28 -0
- package/md_cg/run_tests.py +211 -0
- package/md_cg/scrub.py +862 -852
- package/md_cg/security.py +385 -275
- package/md_cg/selfreport.py +3 -2
- package/md_cg/signer.py +565 -562
- package/md_cg/sources.py +3 -2
- package/md_cg/stg.py +6 -0
- package/md_cg/sustain.py +1168 -1138
- package/md_cg/test_access_hints.py +147 -0
- package/md_cg/test_branch_discard_tombstone.py +136 -0
- package/md_cg/test_branches.py +259 -249
- package/md_cg/test_chain_read_isolate.py +168 -0
- package/md_cg/test_datapath_device_name.py +203 -0
- package/md_cg/test_emit_negtail_cache.py +156 -0
- package/md_cg/test_en_pipeline.py +186 -166
- package/md_cg/test_govern_directread.py +421 -0
- package/md_cg/test_i32_hotcache_env_key.py +218 -0
- package/md_cg/test_identity_attribution.py +228 -147
- package/md_cg/test_index_crossprocess_reload.py +301 -0
- package/md_cg/test_index_durability.py +238 -224
- package/md_cg/test_interop.py +4 -2
- package/md_cg/test_interop_judgment.py +228 -0
- package/md_cg/test_issue39_utf8_stdio.py +273 -0
- package/md_cg/test_links_concurrent_write.py +188 -0
- package/md_cg/test_merge_upsert.py +168 -0
- package/md_cg/test_n123_derive_expiry_chain.py +205 -0
- package/md_cg/test_n130_verify_falsified_protect.py +185 -0
- package/md_cg/test_n131_merge_gate.py +205 -0
- package/md_cg/test_none_id_write_guard.py +165 -0
- package/md_cg/test_p1x_ref_root.py +160 -0
- package/md_cg/test_p27_docindex.py +774 -765
- package/md_cg/test_p2_mcp.py +3 -0
- package/md_cg/test_p32_backfill.py +304 -298
- package/md_cg/test_p39_verify_flow.py +90 -50
- package/md_cg/test_p47_session_view.py +60 -25
- package/md_cg/test_propose_tail_index.py +157 -0
- package/md_cg/test_read_scope_b27.py +277 -0
- package/md_cg/test_readcache_default_on.py +168 -0
- package/md_cg/test_readcache_precise_inval.py +270 -0
- package/md_cg/test_readcache_prodpath.py +55 -7
- package/md_cg/test_reconcile_v0.py +294 -0
- package/md_cg/test_retr_s1.py +6 -2
- package/md_cg/test_retr_s1b.py +67 -0
- package/md_cg/test_retr_s7.py +8 -0
- package/md_cg/test_retr_s9_entity_ctx.py +181 -175
- package/md_cg/test_retr_score_once.py +208 -0
- package/md_cg/test_review_cli_attribution.py +177 -0
- package/md_cg/test_review_cli_visibility.py +235 -0
- package/md_cg/test_review_onepass.py +170 -0
- package/md_cg/test_rrf_graph_seed_cache.py +154 -0
- package/md_cg/test_security_audit.py +155 -0
- package/md_cg/test_security_audit_b26.py +161 -0
- package/md_cg/test_security_audit_v21.py +250 -0
- package/md_cg/test_semantic_canonical.py +255 -241
- package/md_cg/test_session_isolation.py +168 -0
- package/md_cg/test_snapshot_autoclose.py +187 -0
- package/md_cg/test_tail_watermark_race.py +208 -0
- package/md_cg/test_tenant_env_override_warn.py +139 -0
- package/md_cg/test_tenant_registry_corrupt_warn.py +151 -0
- package/md_cg/test_v14_fixes.py +415 -397
- package/md_cg/test_verify_dirty_reconcile.py +157 -0
- package/md_cg/theory.py +276 -273
- package/md_cg/tokens.py +734 -677
- package/md_cg/units.py +3 -2
- package/md_cg/vision_evidence.py +4 -3
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +6195 -6195
- package/md_cg/whitebox_kb/wisdom/multilang_ir.py +128 -124
- package/md_cg/writepipe.py +554 -550
- package/package.json +11 -2
- package/src/bridge.ts +434 -401
- package/src/cli.ts +65 -0
- package/src/index.ts +526 -518
- package/src/init.ts +347 -0
- package/src/lib/roleplay_web.ts +1019 -932
- package/src/lib/token_store.ts +202 -192
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""md_cg · 常驻进程索引跨进程重载守卫(P1b-2 读面缓存代际,DSH 端在役复验)
|
|
3
|
+
|
|
4
|
+
缺陷(修复前):`MdCG.__init__` 仅启动时 `self.index = self._load_index()`
|
|
5
|
+
一次(mdcg.py),此后 read/get/search 全走内存态——其它进程(review_cli
|
|
6
|
+
autoflush=1)写盘对常驻进程不可见。复验证据链(dsh_restart_recheck_20260926,
|
|
7
|
+
时间戳锁死):22:13:16 常驻进程装载快照(14135 节点) → 22:14:37 review_cli
|
|
8
|
+
写入 → 22:14:43 磁盘 _index.json 已含探针 → 同常驻进程 read 仍 null、旁路
|
|
9
|
+
新进程可读。
|
|
10
|
+
|
|
11
|
+
修复:__init__ 记录 _index.json 签名 (st_mtime_ns, st_size);读路径入口
|
|
12
|
+
(get / search(基类与 MdCGOS)/ search_rrf(热缓存查询之前)/ list_goals)
|
|
13
|
+
单点调 _maybe_reload_index()——签名变化才重载(一次 stat 微秒级);自身
|
|
14
|
+
flush 只追加 _index_log 分片不改 _index.json(签名不变正合适)、compact/
|
|
15
|
+
rebuild 写快照后主动刷签名(自写不自载);maintain action=reload 显式探活。
|
|
16
|
+
|
|
17
|
+
守卫(六组,全部临时库 + 哑主密钥,绝不触真实 ~/.mdcg):
|
|
18
|
+
① 红转绿(同解释器双实例,复刻 DSH 形态):A 建库常驻 → B 独立实例
|
|
19
|
+
add+close(compact 落快照=review_cli 退出同款)→ 盘面快照已含探针 →
|
|
20
|
+
A 禁用 reload 时 read null(修前病灶形态)→ 恢复后 read/search 命中
|
|
21
|
+
(修复生效)→ C 实例 forget+close → A read 归 null(tombstone 重放)。
|
|
22
|
+
② 旁路新进程可读(subprocess 全新解释器 _load_index+read)——修前已绿,
|
|
23
|
+
钉住「盘面账本正确」,防两层兜底(批次39 指纹自愈)未来被移除时复发。
|
|
24
|
+
③ 自身写入零重载:A' 写入+flush+compact+rebuild 全程真实重载次数 0
|
|
25
|
+
(自写不自载循环防线);连续读 N 次=签名探测 N 次、重载 0 次。
|
|
26
|
+
④ maintain reload 动作在位:MAINTAIN_ACTIONS 登记 + {"reloaded": bool}
|
|
27
|
+
返回(他进程写入后 True 且读面可见;幂等二调 False)。
|
|
28
|
+
⑤ goal=list 链路跨进程可见(list_goals 接线腿,active_goals 同源)。
|
|
29
|
+
⑥ 性能粗测:无跨进程写入时接线态 vs 重载桩空转态逐读耗时对照(宽松
|
|
30
|
+
上界断言防「重载被改成无条件」的性能回归;绝对数字只打印不作硬门)。
|
|
31
|
+
|
|
32
|
+
如实边界:② 在修复前的 HEAD 上已绿(旁路进程本就走全新 _load_index);
|
|
33
|
+
修复前红的正是 ① 的常驻读面——本守卫以「禁用 reload → null」复现该形态,
|
|
34
|
+
并以 ③ 的计数桩钉住接线与自写不自载两道防线。
|
|
35
|
+
|
|
36
|
+
运行:python -m md_cg.test_index_crossprocess_reload
|
|
37
|
+
"""
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import os
|
|
42
|
+
import shutil
|
|
43
|
+
import subprocess
|
|
44
|
+
import sys
|
|
45
|
+
import tempfile
|
|
46
|
+
import time
|
|
47
|
+
|
|
48
|
+
from .mdcos import MdCGSecure
|
|
49
|
+
from .security import Principal
|
|
50
|
+
|
|
51
|
+
PASS = FAIL = 0
|
|
52
|
+
FAILS = []
|
|
53
|
+
|
|
54
|
+
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
55
|
+
|
|
56
|
+
DUMMY_KEY = "ab" * 32 # 哑主密钥:本进程与子进程绝不触真实 ~/.mdcg
|
|
57
|
+
|
|
58
|
+
READ_CHILD = r'''
|
|
59
|
+
import sys
|
|
60
|
+
sys.path.insert(0, sys.argv[2])
|
|
61
|
+
from md_cg.mdcos import MdCGSecure
|
|
62
|
+
from md_cg.security import Principal
|
|
63
|
+
p = Principal(actor="p1b2-reader", clearance="internal", can_write=True,
|
|
64
|
+
can_admin=True, role="designer", auth_mode="test")
|
|
65
|
+
cg = MdCGSecure(sys.argv[1], principal=p)
|
|
66
|
+
try:
|
|
67
|
+
print("READ " + " ".join("1" if cg.get(nid) is not None else "0"
|
|
68
|
+
for nid in sys.argv[3].split(",")))
|
|
69
|
+
finally:
|
|
70
|
+
cg.close()
|
|
71
|
+
'''
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def check(name, cond, detail=""):
|
|
75
|
+
global PASS, FAIL
|
|
76
|
+
if cond:
|
|
77
|
+
PASS += 1
|
|
78
|
+
print(f" [PASS] {name}" + (f" · {detail}" if detail else ""))
|
|
79
|
+
else:
|
|
80
|
+
FAIL += 1
|
|
81
|
+
FAILS.append(name)
|
|
82
|
+
print(f" [FAIL] {name} · {detail}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _principal(**kw):
|
|
86
|
+
d = dict(actor="p1b2", clearance="internal", can_write=True,
|
|
87
|
+
can_admin=True, role="designer", auth_mode="test")
|
|
88
|
+
d.update(kw)
|
|
89
|
+
return Principal(**d)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _spawn_read(root, ids):
|
|
93
|
+
"""独立子进程全新 _load_index + read(旁路读面)。"""
|
|
94
|
+
env = dict(os.environ)
|
|
95
|
+
env["MDCG_MASTER_KEY"] = DUMMY_KEY
|
|
96
|
+
env["PYTHONUTF8"] = "1"
|
|
97
|
+
env["PYTHONIOENCODING"] = "utf-8"
|
|
98
|
+
env["PYTHONPATH"] = REPO + os.pathsep + env.get("PYTHONPATH", "")
|
|
99
|
+
return subprocess.run(
|
|
100
|
+
[sys.executable, "-X", "utf8", "-c", READ_CHILD, root, REPO, ids],
|
|
101
|
+
capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
102
|
+
env=env, cwd=REPO)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _snapshot_ids(root):
|
|
106
|
+
"""盘面 _index.json 的节点 id 集(复验证据链 22:14:43 的观察点)。"""
|
|
107
|
+
p = os.path.join(root, "_index.json")
|
|
108
|
+
if not os.path.exists(p):
|
|
109
|
+
return None
|
|
110
|
+
with open(p, encoding="utf-8") as f:
|
|
111
|
+
return set(json.load(f).get("nodes") or {})
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _counting(cg):
|
|
115
|
+
"""给 _maybe_reload_index 挂计数桩,返回 (计数 dict, 解挂函数)。"""
|
|
116
|
+
calls = {"probe": 0, "reload": 0}
|
|
117
|
+
orig = cg._maybe_reload_index
|
|
118
|
+
|
|
119
|
+
def counted():
|
|
120
|
+
calls["probe"] += 1
|
|
121
|
+
r = orig()
|
|
122
|
+
if r:
|
|
123
|
+
calls["reload"] += 1
|
|
124
|
+
return r
|
|
125
|
+
|
|
126
|
+
cg._maybe_reload_index = counted
|
|
127
|
+
return calls, (lambda: setattr(cg, "_maybe_reload_index", orig))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main():
|
|
131
|
+
roots = []
|
|
132
|
+
# 纪律:哑主密钥先行——进程内任何 MdCGSecure 构造都不得触真实
|
|
133
|
+
# ~/.mdcg/master.key(_init_crypto 的 env 回落链,mdcos.py)。
|
|
134
|
+
os.environ["MDCG_MASTER_KEY"] = DUMMY_KEY
|
|
135
|
+
try:
|
|
136
|
+
# ---------- ① 红转绿:常驻 A + 独立写方 B(DSH 形态复刻) ----------
|
|
137
|
+
print("\n【①】常驻 A 建库 → 独立 B add+close(compact 落快照)→ 红转绿")
|
|
138
|
+
root1 = tempfile.mkdtemp(prefix="p1b2_1_")
|
|
139
|
+
roots.append(root1)
|
|
140
|
+
a = MdCGSecure(root1, principal=_principal())
|
|
141
|
+
a.add("seed_node_1", "# 功能名:种子节点\n# 正文:常驻 A 的初始记忆",
|
|
142
|
+
layer="knowledge")
|
|
143
|
+
a.flush() # 只追加 _index_log 分片,不动 _index.json
|
|
144
|
+
check("A 自身 flush 不改 _index.json 签名(分片追加不改快照)",
|
|
145
|
+
a._index_sig == a._index_signature())
|
|
146
|
+
b = MdCGSecure(root1, principal=_principal(actor="writer_b"))
|
|
147
|
+
b.add("reload_probe_1", "# 功能名:跨进程探针\n# 正文:B 写入的探针节点",
|
|
148
|
+
layer="knowledge")
|
|
149
|
+
b.close() # close → compact:写 _index.json(review_cli 退出同款)
|
|
150
|
+
ids = _snapshot_ids(root1)
|
|
151
|
+
check("盘面 _index.json 已含 B 的探针(复验证据链 22:14:43 形态)",
|
|
152
|
+
ids is not None and "reload_probe_1" in ids,
|
|
153
|
+
"snapshot_ids=%s" % (sorted(ids or [])[:8]))
|
|
154
|
+
# 红形态:修前无 _maybe_reload_index → 常驻 A 读不到
|
|
155
|
+
orig_reload = a._maybe_reload_index
|
|
156
|
+
a._maybe_reload_index = lambda: False
|
|
157
|
+
red = a.get("reload_probe_1")
|
|
158
|
+
a._maybe_reload_index = orig_reload
|
|
159
|
+
check("红(修前病灶形态):禁用 reload 时常驻 A read 探针 null",
|
|
160
|
+
red is None, repr(red)[:80])
|
|
161
|
+
fired = a._maybe_reload_index()
|
|
162
|
+
check("绿:_maybe_reload_index 签名变化触发重载", fired is True)
|
|
163
|
+
green = a.get("reload_probe_1")
|
|
164
|
+
check("绿:常驻 A read 探针命中", green is not None,
|
|
165
|
+
repr(green)[:80])
|
|
166
|
+
res, _meta = a.search("跨进程探针")
|
|
167
|
+
check("绿:常驻 A search(route/read 链路)命中探针",
|
|
168
|
+
any(n.get("id") == "reload_probe_1" for n, _s, _q in res),
|
|
169
|
+
str([n.get("id") for n, _s, _q in res])[:120])
|
|
170
|
+
# 删除可见性:C 实例 forget → tombstone 经重放对 A 生效
|
|
171
|
+
c = MdCGSecure(root1, principal=_principal(actor="writer_c"))
|
|
172
|
+
fr = c.forget("reload_probe_1", reason="P1b-2 tombstone 腿")
|
|
173
|
+
c.close()
|
|
174
|
+
check("C forget 成功(ok=True)", isinstance(fr, dict) and fr.get("ok"),
|
|
175
|
+
str(fr)[:80])
|
|
176
|
+
check("绿:常驻 A read 已删节点归 null(tombstone 重放)",
|
|
177
|
+
a.get("reload_probe_1") is None)
|
|
178
|
+
a.close()
|
|
179
|
+
|
|
180
|
+
# ---------- ② 旁路新进程可读(修前已绿,钉住盘面账本) ----------
|
|
181
|
+
print("\n【②】旁路全新子进程 read(_load_index 直读盘面)")
|
|
182
|
+
root2 = tempfile.mkdtemp(prefix="p1b2_2_")
|
|
183
|
+
roots.append(root2)
|
|
184
|
+
w = MdCGSecure(root2, principal=_principal(actor="writer_2"))
|
|
185
|
+
w.add("bypass_probe", "# 功能名:旁路探针\n# 正文:新进程应立即可见",
|
|
186
|
+
layer="knowledge")
|
|
187
|
+
w.close()
|
|
188
|
+
r2 = _spawn_read(root2, "bypass_probe,missing_node")
|
|
189
|
+
line = (r2.stdout or "").strip().splitlines()[-1] if (r2.stdout or "").strip() else ""
|
|
190
|
+
check("旁路新进程 read:探针 1 / 缺失 0(READ 1 0)",
|
|
191
|
+
line == "READ 1 0",
|
|
192
|
+
"stdout=%r stderr=%r" % ((r2.stdout or "")[:80], (r2.stderr or "")[:200]))
|
|
193
|
+
|
|
194
|
+
# ---------- ③ 自身写入零重载 + 自写不自载 ----------
|
|
195
|
+
print("\n【③】自身写入/flush/compact/rebuild 全程零真实重载")
|
|
196
|
+
root3 = tempfile.mkdtemp(prefix="p1b2_3_")
|
|
197
|
+
roots.append(root3)
|
|
198
|
+
a2 = MdCGSecure(root3, principal=_principal())
|
|
199
|
+
a2.add("own_seed", "# 功能名:自有种子\n# 正文:own seed", layer="knowledge")
|
|
200
|
+
a2.flush()
|
|
201
|
+
calls, uncount = _counting(a2)
|
|
202
|
+
for i in range(3):
|
|
203
|
+
a2.add("own_%d" % i, "# 功能名:自写%d\n# 正文:own write %d" % (i, i),
|
|
204
|
+
layer="knowledge")
|
|
205
|
+
a2.flush()
|
|
206
|
+
for i in range(3):
|
|
207
|
+
a2.get("own_%d" % i)
|
|
208
|
+
a2.search("自写")
|
|
209
|
+
a2.list_goals()
|
|
210
|
+
check("自身写入+flush+读全程:重载 0 次(写路径不接、flush 不改签名)",
|
|
211
|
+
calls["reload"] == 0, str(calls))
|
|
212
|
+
check("接线在位:读路径确经签名探测(3×get+1×search+1×list_goals=5 次)",
|
|
213
|
+
calls["probe"] >= 5, str(calls))
|
|
214
|
+
a2.compact_index()
|
|
215
|
+
check("compact 后签名已主动刷新(自写不自载前提)",
|
|
216
|
+
a2._index_sig == a2._index_signature())
|
|
217
|
+
a2.get("own_0")
|
|
218
|
+
a2.rebuild_index()
|
|
219
|
+
a2.get("own_0")
|
|
220
|
+
check("compact/rebuild 后紧接读:重载仍 0 次", calls["reload"] == 0,
|
|
221
|
+
str(calls))
|
|
222
|
+
uncount()
|
|
223
|
+
a2.close()
|
|
224
|
+
|
|
225
|
+
# ---------- ④ maintain reload 动作 ----------
|
|
226
|
+
print("\n【④】maintain action=reload 显式探活")
|
|
227
|
+
from .mdcos import MdCGOS
|
|
228
|
+
check('"reload" 已登记 MAINTAIN_ACTIONS', "reload" in MdCGOS.MAINTAIN_ACTIONS,
|
|
229
|
+
str(MdCGOS.MAINTAIN_ACTIONS))
|
|
230
|
+
a3 = MdCGSecure(root3, principal=_principal())
|
|
231
|
+
out0 = a3.maintain(action="reload")
|
|
232
|
+
check("幂等:签名未变时 reloaded=False",
|
|
233
|
+
isinstance(out0, dict) and out0.get("reloaded") is False,
|
|
234
|
+
str(out0)[:120])
|
|
235
|
+
bw = MdCGSecure(root3, principal=_principal(actor="writer_4"))
|
|
236
|
+
bw.add("maintain_probe", "# 功能名:维护探针\n# 正文:maintain reload 腿",
|
|
237
|
+
layer="knowledge")
|
|
238
|
+
bw.close()
|
|
239
|
+
out1 = a3.maintain(action="reload")
|
|
240
|
+
check("他进程写快照后 maintain reload 返回 reloaded=True",
|
|
241
|
+
isinstance(out1, dict) and out1.get("reloaded") is True,
|
|
242
|
+
str(out1)[:120])
|
|
243
|
+
check("探活后读面立即可见新节点", a3.get("maintain_probe") is not None)
|
|
244
|
+
a3.close()
|
|
245
|
+
|
|
246
|
+
# ---------- ⑤ goal=list 链路(list_goals 接线腿) ----------
|
|
247
|
+
print("\n【⑤】goal 跨进程可见(list_goals / active_goals)")
|
|
248
|
+
root4 = tempfile.mkdtemp(prefix="p1b2_4_")
|
|
249
|
+
roots.append(root4)
|
|
250
|
+
a4 = MdCGSecure(root4, principal=_principal())
|
|
251
|
+
check("常驻 A' 初始无目标", a4.list_goals() == [])
|
|
252
|
+
gw = MdCGSecure(root4, principal=_principal(actor="writer_5"))
|
|
253
|
+
gid = gw.add_goal("跨进程目标探针:P1b-2 list_goals 接线腿", priority=0.9)
|
|
254
|
+
gw.close()
|
|
255
|
+
gl = a4.list_goals()
|
|
256
|
+
check("goal=list 经 list_goals 重载后可见",
|
|
257
|
+
any(g.get("id") == gid for g in gl),
|
|
258
|
+
str([g.get("id") for g in gl])[:120])
|
|
259
|
+
ag = a4.active_goals()
|
|
260
|
+
check("active_goals 同源可见",
|
|
261
|
+
any(g.get("id") == gid for g in ag))
|
|
262
|
+
a4.close()
|
|
263
|
+
|
|
264
|
+
# ---------- ⑥ 性能粗测:无跨进程写入时逐读开销 ----------
|
|
265
|
+
print("\n【⑥】性能粗测(无跨进程写入,接线态 vs 重载桩空转态)")
|
|
266
|
+
N = 300
|
|
267
|
+
a2c = MdCGSecure(root3, principal=_principal())
|
|
268
|
+
for _ in range(5):
|
|
269
|
+
a2c.get("own_0")
|
|
270
|
+
t0 = time.perf_counter()
|
|
271
|
+
for _ in range(N):
|
|
272
|
+
a2c.get("own_0")
|
|
273
|
+
t_on = (time.perf_counter() - t0) / N * 1e6
|
|
274
|
+
orig = a2c._maybe_reload_index
|
|
275
|
+
a2c._maybe_reload_index = lambda: False
|
|
276
|
+
for _ in range(5):
|
|
277
|
+
a2c.get("own_0")
|
|
278
|
+
t0 = time.perf_counter()
|
|
279
|
+
for _ in range(N):
|
|
280
|
+
a2c.get("own_0")
|
|
281
|
+
t_off = (time.perf_counter() - t0) / N * 1e6
|
|
282
|
+
a2c._maybe_reload_index = orig
|
|
283
|
+
a2c.close()
|
|
284
|
+
print(" 接线态 %.1f µs/读 · 桩空转态 %.1f µs/读 · 增幅 %.1f%%"
|
|
285
|
+
% (t_on, t_off, (t_on - t_off) / max(t_off, 1e-9) * 100))
|
|
286
|
+
check("性能上界:接线态 ≤ 桩空转态×3 + 30µs(stat 微秒级;宽松防抖)",
|
|
287
|
+
t_on <= t_off * 3 + 30.0,
|
|
288
|
+
"on=%.1f off=%.1f" % (t_on, t_off))
|
|
289
|
+
finally:
|
|
290
|
+
for r_ in roots:
|
|
291
|
+
shutil.rmtree(r_, ignore_errors=True)
|
|
292
|
+
|
|
293
|
+
print("\n" + "=" * 64)
|
|
294
|
+
print(f"PASS={PASS} FAIL={FAIL}")
|
|
295
|
+
if FAILS:
|
|
296
|
+
print("失败项:" + ";".join(FAILS))
|
|
297
|
+
return 0 if FAIL == 0 else 1
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if __name__ == "__main__":
|
|
301
|
+
sys.exit(main())
|