优化加载后删除节点的逻辑;和数据库查询性能

This commit is contained in:
strepsiades
2026-04-02 19:31:04 +08:00
parent 74df53889c
commit 543c3ff906
17 changed files with 571 additions and 32 deletions
@@ -128,6 +128,16 @@ async def test_mock_based_full_event_and_state_coverage() -> None:
d = e01_bootstrap(runtime, node=node_e01_del, is_create_zombie=True)
record(node_e01_del, d)
node_e01_isolated = _new_node("e01_isolated", action=SyncAction.NONE, status=SyncStatus.PENDING)
d = e01_bootstrap(
runtime,
node=node_e01_isolated,
is_create_zombie=False,
bootstrap_source_present=False,
has_binding_record=True,
)
record(node_e01_isolated, d)
# E10 merged outcomes
for idx, (has_binding, local_res, peer_res) in enumerate(
[
@@ -507,7 +517,8 @@ async def test_sync_log_cleared_on_initial_load(tmp_path: Path) -> None:
await reloaded.load_from_persistence()
loaded_node = reloaded.get("log-node")
assert loaded_node is not None
assert loaded_node.sync_log is None
assert loaded_node.sync_log is not None
assert loaded_node.sync_log.count("加载来源: persistence 恢复节点") == 1
in_memory_after_reload_report = REPORT_DIR / "in_memory_sync_log_after_reload.txt"
in_memory_after_reload_report.write_text(
@@ -566,6 +577,8 @@ async def test_load_preserves_state_then_e01_normalizes_to_s00(tmp_path: Path) -
assert loaded_node.status == SyncStatus.FAILED
assert loaded_node.binding_status == BindingStatus.WARNING
assert loaded_node.error == "last run failed"
assert loaded_node.sync_log is not None
assert "加载来源: persistence 恢复节点" in loaded_node.sync_log
# E01 后归并到 S00,并清空历史 error
runtime = StateMachineRuntime(StateMachineConfig.load(CFG_PATH))
@@ -579,6 +592,114 @@ async def test_load_preserves_state_then_e01_normalizes_to_s00(tmp_path: Path) -
await persistence.close()
@pytest.mark.asyncio
async def test_persistence_only_unbound_node_is_cleaned_after_bootstrap_refresh_miss(tmp_path: Path) -> None:
db = tmp_path / "bootstrap_source_miss_unbound.db"
persistence = SQLitePersistenceBackend(str(db))
await persistence.initialize()
collection = DataCollection("project-bootstrap-unbound", persistence=persistence, auto_persist=False)
node = ProjectSyncNode(
node_id="stale-unbound-node",
data_id="proj-stale-unbound",
data={
"id": "proj-stale-unbound",
"type": "project",
"name": "Stale Unbound Project",
"code": "P-ST-UNBOUND",
"status": "active",
"progress_type": "C",
"project_type": "onshore_wind",
},
action=SyncAction.NONE,
status=SyncStatus.PENDING,
binding_status=BindingStatus.WARNING,
)
await collection.add(node)
await collection.persist()
reloaded = DataCollection("project-bootstrap-unbound", persistence=persistence, auto_persist=False)
await reloaded.load_from_persistence()
loaded_node = reloaded.get("stale-unbound-node")
assert loaded_node is not None
assert loaded_node.context.get("_loaded_from_persistence") is True
runtime = StateMachineRuntime(StateMachineConfig.load(CFG_PATH))
binding_manager = BindingManager(persistence, auto_persist=False)
remote_collection = DataCollection("empty-remote-unbound", persistence=persistence, auto_persist=False)
cleaned = await run_phase2_cleanup(
node_types=["project"],
local_collection=reloaded,
remote_collection=remote_collection,
binding_manager=binding_manager,
runtime=runtime,
)
assert cleaned == 1
assert reloaded.get("stale-unbound-node") is None
await persistence.close()
@pytest.mark.asyncio
async def test_bound_node_is_isolated_after_bootstrap_refresh_miss(tmp_path: Path) -> None:
db = tmp_path / "bootstrap_source_miss_bound.db"
persistence = SQLitePersistenceBackend(str(db))
await persistence.initialize()
collection = DataCollection("project-bootstrap-bound", persistence=persistence, auto_persist=False)
node = ProjectSyncNode(
node_id="stale-bound-local-node",
data_id="proj-stale-bound",
data={
"id": "proj-stale-bound",
"type": "project",
"name": "Stale Bound Project",
"code": "P-ST-BOUND",
"status": "active",
"progress_type": "C",
"project_type": "onshore_wind",
},
action=SyncAction.NONE,
status=SyncStatus.PENDING,
binding_status=BindingStatus.WARNING,
)
await collection.add(node)
await collection.persist()
reloaded = DataCollection("project-bootstrap-bound", persistence=persistence, auto_persist=False)
await reloaded.load_from_persistence()
loaded_node = reloaded.get("stale-bound-local-node")
assert loaded_node is not None
assert loaded_node.context.get("_loaded_from_persistence") is True
runtime = StateMachineRuntime(StateMachineConfig.load(CFG_PATH))
binding_manager = BindingManager(persistence, auto_persist=False)
await binding_manager.bind("project", "stale-bound-local-node", "remote-missing-node")
remote_collection = DataCollection("empty-remote-bound", persistence=persistence, auto_persist=False)
cleaned = await run_phase2_cleanup(
node_types=["project"],
local_collection=reloaded,
remote_collection=remote_collection,
binding_manager=binding_manager,
runtime=runtime,
)
isolated = reloaded.get("stale-bound-local-node")
assert cleaned == 0
assert isolated is not None
assert reloaded.is_bootstrap_blocked(isolated)
assert isolated.binding_status == BindingStatus.ABNORMAL
assert isolated.action == SyncAction.NONE
assert isolated.status == SyncStatus.FAILED
assert "BOOTSTRAP_SOURCE_MISSING" in (isolated.error or "")
assert isolated.context.get("bootstrap_block_reason") == "source_missing_after_persistence_refresh"
await persistence.close()
@pytest.mark.asyncio
async def test_invalid_persisted_enum_is_bootstrap_blocked_and_skipped_in_reset(tmp_path: Path) -> None:
REPORT_DIR.mkdir(parents=True, exist_ok=True)