@furongjun1999/dsh-memory 0.6.0 → 0.7.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 +618 -572
- package/codebuddy/CODEBUDDY.md +10 -10
- package/data/policy.json +27 -0
- package/docs/README.md +1 -0
- package/docs/discipline/harnesses.yaml +31 -0
- package/docs/discipline/templates/full.md.tmpl +1 -1
- package/docs/discipline/templates/rules.mdc.tmpl +1 -1
- package/docs/eval/N225_/347/264/242/345/274/225/346/227/245/345/277/227/351/235/236/345/257/271/350/261/241/350/243/205/350/275/275/351/235/242/347/261/273/345/236/213/351/227/270_v1.0.md +418 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/344/270/216/351/224/256/347/261/273/345/236/213/351/227/270_v1.1.md +441 -0
- package/docs/eval/issue43_/351/273/230/350/256/244/347/255/226/347/225/245/345/212/240/350/275/275/344/270/216/345/207/255/346/215/256/346/230/216/346/226/207/351/230/237/345/210/227_v1.0.md +398 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/200/346/211/271_/346/216/245/347/272/277/344/270/216/347/255/211/344/273/267/345/217/230/346/215/242_v1.0.md +498 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/270/211/346/211/271_/351/227/250/347/246/201/350/275/254/346/255/243/344/270/216/350/260/203/345/272/246/346/255/242/350/241/200/344/270/216/351/227/250/346/216/247/346/224/266/345/217/243_v1.0.md +669 -0
- package/docs/eval//344/274/230/345/214/226/347/254/254/344/272/214/346/211/271_/344/276/235/350/265/226/351/200/217/344/274/240/344/270/216/345/257/271/346/213/215/345/217/243/345/276/204/344/270/216/350/264/237/347/274/223/345/255/230_v1.0.md +683 -0
- package/docs/eval//345/207/272/350/264/247/351/235/242/345/206/222/347/203/237_/350/277/233/350/264/247/351/227/250/347/246/201_v1.0.md +460 -0
- package/docs/eval//345/217/221/345/270/20307_/345/244/226/351/203/250/346/212/245/345/221/212/345/233/233/346/211/271/344/277/256/345/244/215/344/270/216/346/217/222/344/273/266/351/235/242/345/212/240/345/233/272_v1.0.md +205 -0
- package/docs/eval//345/217/221/345/270/20308_/350/207/252/350/277/255/344/273/243/344/270/216/347/235/241/347/234/240_/345/233/276/346/243/200/347/264/242/350/267/257/344/270/216/346/235/203/351/207/215_v1.0.md +97 -0
- package/docs/eval//345/275/222/344/270/200/345/261/202/347/274/272/347/234/201/347/277/273/345/205/263_/344/277/256/345/244/215/350/256/260/345/275/225_v1.0.md +458 -0
- package/docs/eval//346/200/247/350/203/275/344/270/223/351/241/271_/345/206/267/346/237/245/350/257/242/344/270/216/345/206/205/345/255/230_v1.0.md +218 -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 +10 -0
- package/docs/eval//347/274/226/347/240/201/351/235/242/345/211/215/347/275/256_/345/205/245/345/217/243/350/207/252/344/277/235/350/257/201UTF8/344/270/216/346/226/207/346/234/254open/345/256/210/345/215/253_v1.0.md +646 -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_v20.md +283 -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_v21.md +224 -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_v22.md +223 -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_v23.md +293 -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_v24.md +303 -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_v25.md +230 -0
- package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +136 -11
- package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +52 -2
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +1 -1
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +100 -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/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +57 -9
- package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +7 -2
- package/docs/mdcg//347/235/241/347/234/240/345/221/250/346/234/237_/350/277/220/347/273/264/345/211/215/346/217/220/344/270/216/347/273/264/346/212/244/346/214/207/345/215/227_v1.0.md +183 -0
- package/docs/mdcg//350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +39 -3
- package/docs/plans//345/205/250/344/270/255/346/226/207/347/274/226/347/240/201/344/270/216/350/234/202/345/267/242/344/273/273/345/212/241/346/240/207/350/257/206/345/245/221/347/272/246_v2.0.md +220 -0
- package/docs/plans//347/234/237/346/272/220/347/264/242/345/274/225_/351/200/232/347/224/250/346/234/272/345/210/266_v0.3.md +227 -0
- package/docs/plans//347/235/241/347/234/240/344/270/216/350/207/252/350/277/255/344/273/243_/345/212/237/350/203/275/344/274/230/345/214/226/350/256/276/350/256/241_v0.4.md +547 -0
- package/docs/plans//350/234/202/345/267/242/346/250/241/345/236/213/345/257/206/351/222/245/351/205/215/347/275/256/351/235/242_v1.0.md +385 -0
- package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +460 -460
- package/lib/bridge.js +24 -2
- package/lib/hooks.d.ts +31 -2
- package/lib/hooks.js +219 -16
- package/lib/init.js +16 -1
- package/lib/lib/prompt_safety.d.ts +52 -1
- package/lib/lib/prompt_safety.js +76 -1
- package/md_cg/audit.py +652 -379
- package/md_cg/bench6_arms.py +1 -1
- package/md_cg/bench_e2e_locomo_qa.py +11 -4
- package/md_cg/bench_p0.py +1 -1
- package/md_cg/branches.py +2 -2
- package/md_cg/ccgc.py +1071 -1006
- package/md_cg/chain.py +48 -1
- package/md_cg/consistency.py +97 -9
- package/md_cg/consolidate.py +34 -9
- package/md_cg/crypto.py +59 -26
- package/md_cg/docindex.py +16 -1
- package/md_cg/evolution.py +17 -1
- package/md_cg/export.py +1 -1
- package/md_cg/forgetting.py +309 -13
- package/md_cg/freshness.py +511 -0
- package/md_cg/fsutil.py +454 -8
- package/md_cg/generation.py +409 -0
- package/md_cg/hotcache.py +4 -1
- package/md_cg/identity.py +3 -3
- package/md_cg/insight.py +24 -3
- package/md_cg/linkref.py +1 -1
- package/md_cg/logref.py +327 -0
- package/md_cg/mcp_server.py +4261 -3818
- package/md_cg/mdcg.py +4695 -3547
- package/md_cg/mdcos.py +4672 -4159
- package/md_cg/mreview/pipeline.py +15 -1
- package/md_cg/nodefile.py +713 -576
- package/md_cg/protect.py +158 -11
- package/md_cg/protocol.py +41 -2
- package/md_cg/provenance.py +22 -4
- package/md_cg/reach.py +4 -4
- package/md_cg/readcache.py +76 -15
- package/md_cg/reconcile.py +1 -1
- package/md_cg/refindex.py +246 -37
- package/md_cg/refine.py +1 -1
- package/md_cg/routing.py +32 -5
- package/md_cg/run_tests.py +17 -0
- package/md_cg/scrub.py +30 -3
- package/md_cg/security.py +47 -1
- package/md_cg/self_state.py +5 -5
- package/md_cg/semantic/canonical.py +22 -0
- package/md_cg/semantic/unify.py +73 -22
- package/md_cg/semantic/unify_fixture.json +25 -0
- package/md_cg/sleep.py +1297 -0
- package/md_cg/sources.py +23 -6
- package/md_cg/srcindex.py +352 -0
- package/md_cg/stg.py +47 -3
- package/md_cg/subgraph.py +2 -2
- package/md_cg/sustain.py +1436 -1168
- package/md_cg/tasks.py +469 -470
- package/md_cg/test_auto_defaults.py +424 -0
- package/md_cg/test_b1_auto_id_multiproc.py +277 -0
- package/md_cg/test_b1b2_write_face.py +417 -0
- package/md_cg/test_b2_sensitivity_landing.py +223 -0
- package/md_cg/test_b3_merge_keeps_content.py +576 -0
- package/md_cg/test_b4_shard_dir_selfheal.py +843 -0
- package/md_cg/test_b4_shard_dir_selfheal_guard.py +238 -0
- package/md_cg/test_boundary_hit.py +410 -0
- package/md_cg/test_c3_transient_read_negative.py +793 -0
- package/md_cg/test_c8_search_rrf_gates.py +502 -0
- package/md_cg/test_ccg_form_parity.py +188 -0
- package/md_cg/test_ccgc.py +28 -3
- package/md_cg/test_en_pipeline.py +22 -13
- package/md_cg/test_generation_guard.py +352 -0
- package/md_cg/test_govern_directread.py +17 -4
- package/md_cg/test_h2_session_view_norm.py +233 -0
- package/md_cg/test_h4_sustain_snapshot.py +1387 -0
- package/md_cg/test_hive_ingest.py +285 -285
- package/md_cg/test_issue39_utf8_stdio.py +307 -16
- package/md_cg/test_issue43_default_policy.py +865 -0
- package/md_cg/test_legacy_p3_node_id_type.py +375 -0
- package/md_cg/test_linkref.py +10 -3
- package/md_cg/test_lock.py +2 -2
- package/md_cg/test_logref.py +1109 -0
- package/md_cg/test_m3_h9_bucket_health_protect_mark.py +301 -0
- package/md_cg/test_m3_h9_semantic_guard.py +525 -0
- package/md_cg/test_mr_m2.py +15 -3
- package/md_cg/test_n130_verify_falsified_protect.py +1 -1
- package/md_cg/test_n139_dek_provision_failclosed.py +185 -0
- package/md_cg/test_n176_link_trust.py +221 -0
- package/md_cg/test_n178_units_jobid_gate.py +212 -0
- package/md_cg/test_n184_keys_concurrent_provision.py +307 -0
- package/md_cg/test_n195_writepath_reload.py +283 -0
- package/md_cg/test_n196_stale_gate_skip.py +169 -0
- package/md_cg/test_n197_n208_write_face_gates.py +471 -0
- package/md_cg/test_n198_tokens_corrupt_failclosed.py +225 -0
- package/md_cg/test_n199_tokens_concurrent_write.py +378 -0
- package/md_cg/test_n201_proposal_visibility.py +382 -0
- package/md_cg/test_n202_session_notes_visibility.py +461 -0
- package/md_cg/test_n204_n205_n226_n227_n228_n229_exit_gates.py +542 -0
- package/md_cg/test_n206_stdio_jsonrpc_type.py +403 -0
- package/md_cg/test_n209_verify_write_face_gates.py +461 -0
- package/md_cg/test_n212_n213_n224_generation_gates.py +519 -0
- package/md_cg/test_n214_n215_n221_n222_write_face_gates.py +563 -0
- package/md_cg/test_n225_nonobject_load.py +1110 -0
- package/md_cg/test_n230_dirty_replay.py +375 -0
- package/md_cg/test_n62_tenant_bind_failclosed.py +200 -0
- package/md_cg/test_neg_condition_hits.py +333 -0
- package/md_cg/test_neg_tail_honesty.py +663 -0
- package/md_cg/test_none_id_write_guard.py +4 -3
- package/md_cg/test_opt_batch1_md_cg.py +451 -0
- package/md_cg/test_p1.py +5 -5
- package/md_cg/test_p11_consistency.py +161 -12
- package/md_cg/test_p26_refindex.py +2 -2
- package/md_cg/test_p27_docindex.py +777 -774
- package/md_cg/test_p28_refcheck.py +740 -13
- package/md_cg/test_p29_session_ingest_export.py +2 -2
- package/md_cg/test_p2_mcp.py +12 -1
- package/md_cg/test_p2_six_elements.py +463 -0
- package/md_cg/test_p30_maintain.py +33 -2
- package/md_cg/test_p31_insight.py +1 -0
- package/md_cg/test_p3_legacy_closure.py +433 -0
- package/md_cg/test_p4_freshness.py +680 -0
- package/md_cg/test_p8_subgraph_chain.py +14 -1
- package/md_cg/test_p9c_dedup_hints.py +276 -0
- package/md_cg/test_policy_required_ccg.py +426 -0
- package/md_cg/test_protocol.py +22 -0
- package/md_cg/test_rank_parity_score_mode.py +522 -0
- package/md_cg/test_read_face_input_gates.py +363 -0
- package/md_cg/test_read_face_semantics.py +489 -0
- package/md_cg/test_recall_face_guards.py +812 -0
- package/md_cg/test_rejected_credential_forms.py +188 -0
- package/md_cg/test_rejected_redact.py +146 -0
- package/md_cg/test_retr_s1b.py +2 -2
- package/md_cg/test_retr_s5.py +20 -7
- package/md_cg/test_review_conformance.py +21 -3
- package/md_cg/test_security_audit_v21.py +2 -2
- package/md_cg/test_semantic_canonical.py +5 -3
- package/md_cg/test_server_version.py +67 -0
- package/md_cg/test_sleep.py +611 -0
- package/md_cg/test_sleep_p1.py +784 -0
- package/md_cg/test_srcindex.py +171 -0
- package/md_cg/test_tenant_env_override_warn.py +63 -50
- package/md_cg/test_time_core_lint.py +968 -0
- package/md_cg/test_token_lowercase_form.py +315 -0
- package/md_cg/test_unify_default_off.py +701 -0
- package/md_cg/test_unify_scope.py +182 -0
- package/md_cg/test_v21r1_package_version.py +310 -0
- package/md_cg/test_writepipe.py +10 -4
- package/md_cg/tokens.py +225 -95
- package/md_cg/tool_face.py +4 -4
- package/md_cg/trust.py +49 -5
- package/md_cg/units.py +204 -6
- package/md_cg/weights.py +4 -4
- package/md_cg/whitebox_kb/aeis_core/time_core.py +8 -0
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +1 -1
- package/md_cg/writelimit.py +42 -8
- package/md_cg/writepipe.py +651 -554
- package/package.json +4 -2
- package/skills/plugin.json +1 -1
- package/src/bridge.ts +25 -2
- package/src/hooks.ts +229 -16
- package/src/init.ts +15 -1
- package/src/lib/prompt_safety.ts +88 -1
- package/utf8_boot.py +237 -0
- package/zcode/AGENTS.md +10 -10
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# 功能名:B1 自动节点 id 唯一性的「多进程写面」守卫(N 进程 × M 条自动写入不传 node_id)
|
|
3
|
+
# 生效条件:md_cg/writepipe.WritePipeline.execute 与 md_cg.mcp_server._dispatch 的
|
|
4
|
+
# mdcg_remember 分支均**委托** md_cg.mdcg.mint_auto_id 铸造自动 id 时
|
|
5
|
+
# (判据面:两处都是 `a.get("node_id") or mint_auto_id(cg)`,无一处在
|
|
6
|
+
# 本文件之外自铸 `mem_<毫秒>`);
|
|
7
|
+
# 沙箱条件:MDCG_ROOT / MDCG_AUX_ROOT / MDCG_MASTER_KEY / MDCG_POLICY_FILE
|
|
8
|
+
# 全部指向 tempfile.mkdtemp 建的独立目录,绝不触在役库。
|
|
9
|
+
# 子功能:① 真实时钟 N 进程并发写(pipe 面与 mdcg_remember 面混用同一库根)——
|
|
10
|
+
# 回执 id 唯一数 == 总笔数,且逐笔正文按 id 读回命中;
|
|
11
|
+
# ② 同一毫秒(固定毫秒替身)N 进程并发写——缺陷触发条件;
|
|
12
|
+
# ③ 显式 node_id 的「同 id 即改写」覆写语义原样保留(不得被自动 id 闸误伤)。
|
|
13
|
+
# 执行:python -X utf8 -m md_cg.test_b1_auto_id_multiproc
|
|
14
|
+
# (cwd = 仓库根;不带参数、无网络、无外部依赖,全程沙箱)
|
|
15
|
+
# 验证方式:本文件自跑(自带断言计数与退出码 rc=0/1);定点变异自证见文件尾
|
|
16
|
+
# `--drop-fix`(抽掉 mint_auto_id 的随机段 → 本守卫必红,恢复即绿)。
|
|
17
|
+
# 不适用条件:情感交互|闲聊|纯查询无改动;
|
|
18
|
+
# 跨进程「存在性闸」的原子性不在本守卫面内(各进程持各自索引快照,
|
|
19
|
+
# 跨进程唯一性主要靠随机段熵——见 mint_auto_id 注释的边界声明)。
|
|
20
|
+
"""B1 守卫:自动 id 在多进程写面下的唯一性与可检索性。
|
|
21
|
+
|
|
22
|
+
缺陷(修复前,本守卫的定点变异--drop-fix 现场实抓):
|
|
23
|
+
自动 id 曾在两处**各写一份** `"mem_" + 毫秒时间戳`(writepipe.execute 与
|
|
24
|
+
mcp_server.mdcg_remember)。同一毫秒内自动写入铸出同一 id → `add` 的 upsert
|
|
25
|
+
语义把后一笔**静默顶替**前一笔:回执 committed=true,而正文全库 0 命中。
|
|
26
|
+
|
|
27
|
+
本守卫的判别力锚点(每条断言都可定点变异):
|
|
28
|
+
A 组 真实时钟并发:唯一 id 数 == 总笔数(N*M);逐笔正文按 id 读回命中。
|
|
29
|
+
B 组 同毫秒并发:把子进程的时钟钉在**同一毫秒**(缺陷现场条件),上述两条仍须成立。
|
|
30
|
+
C 组 显式 node_id:同 id 二次写入 = 改写(索引单条目、正文为第二次、id 未被换)。
|
|
31
|
+
|
|
32
|
+
沙箱(硬约束):一切读写都在 tempfile.mkdtemp 内——aux(密钥/身份)、各段库根、
|
|
33
|
+
策略文件、子进程 env 全部指到沙箱;跑完 rmtree。**绝不碰在役库与在役 serve。**
|
|
34
|
+
"""
|
|
35
|
+
import json
|
|
36
|
+
import os
|
|
37
|
+
import shutil
|
|
38
|
+
import subprocess
|
|
39
|
+
import sys
|
|
40
|
+
import tempfile
|
|
41
|
+
|
|
42
|
+
# 沙箱必须在**任何** md_cg 子模块 import 之前设好:`crypto.MASTER_FILE` 在模块
|
|
43
|
+
# 导入时求值(`aux_root()`),晚设会让密钥面指回 ~/.mcg。
|
|
44
|
+
_SANDBOX = tempfile.mkdtemp(prefix="b1mp_sandbox_")
|
|
45
|
+
os.environ["MDCG_AUX_ROOT"] = _SANDBOX
|
|
46
|
+
os.environ["MDCG_ROOT"] = os.path.join(_SANDBOX, "root")
|
|
47
|
+
os.environ["MDCG_MASTER_KEY"] = os.urandom(32).hex()
|
|
48
|
+
_POLICY = os.path.join(_SANDBOX, "policy.json")
|
|
49
|
+
with open(_POLICY, "w", encoding="utf-8") as _f: # audit 闸 ACCEPT 面:
|
|
50
|
+
json.dump({"forbidden": [], "required": ["B1PROBE"]}, _f) # required 命中
|
|
51
|
+
os.environ["MDCG_POLICY_FILE"] = _POLICY
|
|
52
|
+
os.environ.pop("MDCG_TEST_LIVE_ROOT", None)
|
|
53
|
+
|
|
54
|
+
from . import writepipe # noqa: E402
|
|
55
|
+
from .mdcos import MdCGSecure # noqa: E402
|
|
56
|
+
from .security import Principal # noqa: E402
|
|
57
|
+
|
|
58
|
+
_REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
59
|
+
_KEK = os.urandom(32)
|
|
60
|
+
_KEK_FILE = os.path.join(_SANDBOX, "kek.hex")
|
|
61
|
+
with open(_KEK_FILE, "w", encoding="utf-8") as _f:
|
|
62
|
+
_f.write(_KEK.hex())
|
|
63
|
+
|
|
64
|
+
MIXED_MS = 1790743939104 # 固定毫秒(同毫秒 = 缺陷触发条件)
|
|
65
|
+
N_PROCS = 4
|
|
66
|
+
M_EACH = 6
|
|
67
|
+
|
|
68
|
+
_OK = 0
|
|
69
|
+
_BAD = []
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _check(name, cond, detail=""):
|
|
73
|
+
global _OK
|
|
74
|
+
if cond:
|
|
75
|
+
_OK += 1
|
|
76
|
+
print(" ok %s" % name)
|
|
77
|
+
else:
|
|
78
|
+
_BAD.append(name)
|
|
79
|
+
print(" FAIL %s %s" % (name, detail))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# 子进程 worker:独立 python 进程,用与主进程同一沙箱 env + 显式 argv。
|
|
83
|
+
_WORKER = os.path.join(_SANDBOX, "_b1mp_worker.py")
|
|
84
|
+
_WORKER_SRC = '''# -*- coding: utf-8 -*-
|
|
85
|
+
import json, os, sys
|
|
86
|
+
repo, root, kek_hex, idx, count, face, ms_arg = sys.argv[1:8]
|
|
87
|
+
idx, count = int(idx), int(count)
|
|
88
|
+
sys.path.insert(0, repo)
|
|
89
|
+
import time as _time
|
|
90
|
+
import md_cg.mdcg as mdcg
|
|
91
|
+
if ms_arg != "-":
|
|
92
|
+
class _FakeTime(object):
|
|
93
|
+
def __init__(self, ts): self._ts = ts
|
|
94
|
+
def time(self): return self._ts
|
|
95
|
+
def __getattr__(self, k): return getattr(_time, k)
|
|
96
|
+
mdcg.time = _FakeTime(float(ms_arg) / 1000.0)
|
|
97
|
+
from md_cg import mcp_server, writepipe
|
|
98
|
+
from md_cg.mdcos import MdCGSecure
|
|
99
|
+
from md_cg.security import Principal
|
|
100
|
+
p = Principal(tenant="t1", actor="alice", role="designer", clearance="secret",
|
|
101
|
+
can_write=True, can_admin=True)
|
|
102
|
+
cg = MdCGSecure(root, principal=p, master_key=bytes.fromhex(kek_hex))
|
|
103
|
+
recs = []
|
|
104
|
+
for i in range(count):
|
|
105
|
+
mark = "B1PROBE pid=%d idx=%d no=%d" % (os.getpid(), idx, i)
|
|
106
|
+
out, err = None, None
|
|
107
|
+
try:
|
|
108
|
+
if face == "pipe":
|
|
109
|
+
out = writepipe.default_pipeline().execute(
|
|
110
|
+
cg, {"content_kind": "text", "content": mark})
|
|
111
|
+
else:
|
|
112
|
+
out = mcp_server._dispatch(cg, "mdcg_remember",
|
|
113
|
+
{"content": mark, "content_kind": "text"})
|
|
114
|
+
except Exception as exc:
|
|
115
|
+
err = "%s: %s" % (type(exc).__name__, str(exc)[:160])
|
|
116
|
+
recs.append({"mark": mark, "id": (out or {}).get("id"),
|
|
117
|
+
"ok": (out or {}).get("ok"),
|
|
118
|
+
"committed": (out or {}).get("committed"), "err": err})
|
|
119
|
+
cg.close()
|
|
120
|
+
sys.stdout.write(json.dumps({"idx": idx, "recs": recs}, ensure_ascii=False) + "\\n")
|
|
121
|
+
'''
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _write_worker():
|
|
125
|
+
with open(_WORKER, "w", encoding="utf-8", newline="\n") as f:
|
|
126
|
+
f.write(_WORKER_SRC)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _mk_secure(tag):
|
|
130
|
+
root = os.path.join(_SANDBOX, "lib_" + tag)
|
|
131
|
+
p = Principal(tenant="t1", actor="alice", role="designer",
|
|
132
|
+
clearance="secret", can_write=True, can_admin=True)
|
|
133
|
+
return MdCGSecure(root, principal=p, master_key=_KEK), root
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _spawn(root, n, m, face, ms):
|
|
137
|
+
"""起 n 个独立进程各写 m 条,返回全部回执记录。"""
|
|
138
|
+
procs = []
|
|
139
|
+
for i in range(n):
|
|
140
|
+
f = ("pipe" if i % 2 == 0 else "mcp") if face == "mix" else face
|
|
141
|
+
procs.append(subprocess.Popen(
|
|
142
|
+
[sys.executable, "-X", "utf8", _WORKER, _REPO, root, _KEK.hex(),
|
|
143
|
+
str(i), str(m), f, ms],
|
|
144
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
|
145
|
+
encoding="utf-8", errors="replace", env=dict(os.environ),
|
|
146
|
+
shell=False))
|
|
147
|
+
recs, rcs = [], []
|
|
148
|
+
for pr in procs:
|
|
149
|
+
out, err = pr.communicate()
|
|
150
|
+
rcs.append(pr.returncode)
|
|
151
|
+
for line in out.splitlines():
|
|
152
|
+
if line.strip().startswith("{"):
|
|
153
|
+
recs.extend(json.loads(line)["recs"])
|
|
154
|
+
if pr.returncode != 0:
|
|
155
|
+
print(" [worker rc=%d] %s" % (pr.returncode, err.strip()[-200:]))
|
|
156
|
+
return recs, rcs
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _readback(root, recs):
|
|
160
|
+
"""按回执 id 逐笔读回(新实例 = 从盘上重载索引),返回 (miss, mism, n_index)。"""
|
|
161
|
+
cg = MdCGSecure(root, principal=Principal(
|
|
162
|
+
tenant="t1", actor="alice", role="designer", clearance="secret",
|
|
163
|
+
can_write=True, can_admin=True), master_key=_KEK)
|
|
164
|
+
miss, mism = [], []
|
|
165
|
+
for r in recs:
|
|
166
|
+
nid = r.get("id")
|
|
167
|
+
node = cg.get(nid) if nid else None
|
|
168
|
+
body = (node or {}).get("content") or ""
|
|
169
|
+
if not node:
|
|
170
|
+
miss.append((r["mark"], nid))
|
|
171
|
+
elif r["mark"] not in body:
|
|
172
|
+
mism.append((r["mark"], nid, body[:50]))
|
|
173
|
+
n_index = len(cg.index["nodes"])
|
|
174
|
+
cg.close()
|
|
175
|
+
return miss, mism, n_index
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _one_segment(tag, n, m, face, ms):
|
|
179
|
+
"""一段并发写 + 三条读数。返回 (recs, miss, mism, n_index, rcs)。"""
|
|
180
|
+
cg_pre, root = _mk_secure(tag) # 预热:先把 DEK/身份落定,避免 N 进程竞态
|
|
181
|
+
cg_pre.close()
|
|
182
|
+
recs, rcs = _spawn(root, n, m, face, ms)
|
|
183
|
+
miss, mism, n_index = _readback(root, recs)
|
|
184
|
+
return recs, miss, mism, n_index, rcs
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def main():
|
|
188
|
+
_write_worker()
|
|
189
|
+
print("\n[B1-A] 真实时钟 · %d 进程 × %d 条 · 混合写面(pipe / mdcg_remember)"
|
|
190
|
+
% (N_PROCS, M_EACH))
|
|
191
|
+
recs, miss, mism, n_index, rcs = _one_segment(
|
|
192
|
+
"A", N_PROCS, M_EACH, "mix", "-")
|
|
193
|
+
total = len(recs)
|
|
194
|
+
ids = [r["id"] for r in recs if r.get("id")]
|
|
195
|
+
uniq = len(set(ids))
|
|
196
|
+
errs = [r for r in recs if r.get("err")]
|
|
197
|
+
noid = [r for r in recs if not r.get("err") and not r.get("id")]
|
|
198
|
+
notok = [r for r in recs if not r.get("err") and r.get("ok") is not True]
|
|
199
|
+
print(" total=%d with_id=%d uniq=%d errs=%d no_id=%d not_ok=%d "
|
|
200
|
+
"miss=%d mism=%d index=%d rc=%s"
|
|
201
|
+
% (total, len(ids), uniq, len(errs), len(noid), len(notok),
|
|
202
|
+
len(miss), len(mism), n_index, rcs))
|
|
203
|
+
if errs:
|
|
204
|
+
print(" first err:", errs[0]["err"])
|
|
205
|
+
|
|
206
|
+
_check("B1-A1 每笔都拿到 ok 回执与 id(零异常/零未回执)",
|
|
207
|
+
total == N_PROCS * M_EACH and not errs and not noid and not notok,
|
|
208
|
+
"total=%d errs=%d no_id=%d not_ok=%d %s"
|
|
209
|
+
% (total, len(errs), len(noid), len(notok),
|
|
210
|
+
(errs or noid or notok)[:1]))
|
|
211
|
+
_check("B1-A2 唯一 id 数 == 总笔数", uniq == total and total > 0,
|
|
212
|
+
"uniq=%d total=%d" % (uniq, total))
|
|
213
|
+
_check("B1-A3 每笔正文都能按 id 读回(无「已回执却 0 命中」)",
|
|
214
|
+
total > 0 and not miss and not mism, "miss=%d mism=%d %s"
|
|
215
|
+
% (len(miss), len(mism), (miss + mism)[:2]))
|
|
216
|
+
_check("B1-A4 库内节点数 == 总笔数(回执与盘面对账)",
|
|
217
|
+
total > 0 and n_index == total, "index=%d total=%d" % (n_index, total))
|
|
218
|
+
|
|
219
|
+
print("\n[B1-B] 同毫秒(固定毫秒 %d)· %d 进程 × %d 条 · pipe 面"
|
|
220
|
+
% (MIXED_MS, N_PROCS, M_EACH))
|
|
221
|
+
recs_b, miss_b, mism_b, n_index_b, rcs_b = _one_segment(
|
|
222
|
+
"B", N_PROCS, M_EACH, "pipe", str(MIXED_MS))
|
|
223
|
+
total_b = len(recs_b)
|
|
224
|
+
ids_b = [r["id"] for r in recs_b if r.get("id")]
|
|
225
|
+
uniq_b = len(set(ids_b))
|
|
226
|
+
errs_b = [r for r in recs_b if r.get("err")]
|
|
227
|
+
print(" total=%d with_id=%d uniq=%d errs=%d miss=%d mism=%d index=%d rc=%s"
|
|
228
|
+
% (total_b, len(ids_b), uniq_b, len(errs_b), len(miss_b),
|
|
229
|
+
len(mism_b), n_index_b, rcs_b))
|
|
230
|
+
if errs_b:
|
|
231
|
+
print(" first err:", errs_b[0]["err"])
|
|
232
|
+
_check("B1-B1 同毫秒下每笔都拿到 ok 回执与 id",
|
|
233
|
+
total_b == N_PROCS * M_EACH and not errs_b
|
|
234
|
+
and all(r.get("ok") is True and r.get("id") for r in recs_b),
|
|
235
|
+
"total=%d errs=%d with_id=%d" % (total_b, len(errs_b), len(ids_b)))
|
|
236
|
+
_check("B1-B2 同毫秒下唯一 id 数 == 总笔数",
|
|
237
|
+
uniq_b == total_b and total_b > 0,
|
|
238
|
+
"uniq=%d total=%d ids=%s" % (uniq_b, total_b, ids_b[:3]))
|
|
239
|
+
_check("B1-B3 同毫秒下每笔正文都能按 id 读回",
|
|
240
|
+
total_b > 0 and not miss_b and not mism_b, "miss=%d mism=%d %s"
|
|
241
|
+
% (len(miss_b), len(mism_b), (miss_b + mism_b)[:2]))
|
|
242
|
+
|
|
243
|
+
print("\n[B1-C] 显式 node_id 的覆写语义(同 id 二次写入是改写)")
|
|
244
|
+
cg_c, _root_c = _mk_secure("C")
|
|
245
|
+
pipe = writepipe.default_pipeline()
|
|
246
|
+
eid = "b1mp_explicit_upsert"
|
|
247
|
+
o1 = pipe.execute(cg_c, {"node_id": eid, "content_kind": "text",
|
|
248
|
+
"content": "B1PROBE 显式 id 第一次"})
|
|
249
|
+
o2 = pipe.execute(cg_c, {"node_id": eid, "content_kind": "text",
|
|
250
|
+
"content": "B1PROBE 显式 id 第二次改写"})
|
|
251
|
+
node_c = cg_c.get(eid) or {}
|
|
252
|
+
body_c = node_c.get("content") or ""
|
|
253
|
+
same_key = [k for k in cg_c.index["nodes"] if k == eid]
|
|
254
|
+
auto_keys = [k for k in cg_c.index["nodes"] if k.startswith("mem_")]
|
|
255
|
+
_check("B1-C1 显式 id 两次返回同一 id(未被换成自动 id)",
|
|
256
|
+
o1.get("id") == o2.get("id") == eid,
|
|
257
|
+
"%r %r" % (o1.get("id"), o2.get("id")))
|
|
258
|
+
_check("B1-C2 索引里该 id 唯一(是改写不是新建)",
|
|
259
|
+
len(same_key) == 1, "命中 %d 个" % len(same_key))
|
|
260
|
+
_check("B1-C3 正文为第二次(覆写生效)",
|
|
261
|
+
"第二次改写" in body_c and "第一次" not in body_c, body_c[:50])
|
|
262
|
+
_check("B1-C4 显式 id 未产生自动 id 旁路节点",
|
|
263
|
+
not auto_keys and cg_c.index["nodes"].get(eid) is not None,
|
|
264
|
+
"auto_keys=%s" % auto_keys)
|
|
265
|
+
cg_c.close()
|
|
266
|
+
|
|
267
|
+
print("\n==== B1 多进程写面守卫:%d 通过%s ====" % (
|
|
268
|
+
_OK, ",%d 失败:%s" % (len(_BAD), "; ".join(_BAD)) if _BAD else ""))
|
|
269
|
+
return 1 if _BAD else 0
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
try:
|
|
274
|
+
_rc = main()
|
|
275
|
+
finally:
|
|
276
|
+
shutil.rmtree(_SANDBOX, ignore_errors=True)
|
|
277
|
+
sys.exit(_rc)
|