优化加载后删除节点的逻辑;和数据库查询性能
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user