优化性能

This commit is contained in:
strepsiades
2026-04-01 18:13:18 +08:00
parent 9a3a34c58b
commit f93a6c5c68
8 changed files with 198 additions and 107 deletions
@@ -263,7 +263,7 @@ async def test_create_all_marks_duplicate_project_key_in_same_batch_as_error() -
@pytest.mark.asyncio
async def test_create_node_raises_on_existing_project_key_with_different_data_id() -> None:
async def test_create_node_allows_existing_project_key_with_different_data_id() -> None:
collection = DataCollection("remote")
handler = ProjectDetailApiHandler()
@@ -278,15 +278,18 @@ async def test_create_node_raises_on_existing_project_key_with_different_data_id
)
await collection.add(existing)
with pytest.raises(RuntimeError, match="project_detail_data load conflict"):
handler._create_node(
{
"id": "remote-detail-99",
"project_id": "project-1",
"key": "plan_construction",
"value": [{"date": "2026-01-01", "capacity": 21.0}],
}
)
node = handler._create_node(
{
"id": "remote-detail-99",
"project_id": "project-1",
"key": "plan_construction",
"value": [{"date": "2026-01-01", "capacity": 21.0}],
}
)
assert node.node_id != existing.node_id
assert node.data_id == "remote-detail-99"
assert node.context["project_id"] == "project-1"
@pytest.mark.asyncio
@@ -9,7 +9,7 @@ from sync_state_machine.common.binding import BindingManager
from sync_state_machine.common.collection import DataCollection
from sync_state_machine.datasource.jsonl import JsonlDataSource
import sync_state_machine.pipeline.full_sync_pipeline as full_sync_pipeline_module
from sync_state_machine.pipeline.full_sync_pipeline import FullSyncPipeline
from sync_state_machine.pipeline.full_sync_pipeline import FullSyncPipeline, WriteOutcome
class _FakeDataSource:
@@ -81,22 +81,33 @@ async def test_phase_sync_loads_each_node_type_before_strategy(monkeypatch: pyte
reload_calls: list[tuple[str, str]] = []
async def fake_commit_creates(node_type: str) -> bool:
return node_type == "project"
async def fake_commit_creates(node_type: str) -> WriteOutcome:
return WriteOutcome(local_written=(node_type == "project"), remote_written=False)
async def fake_commit_updates(node_type: str) -> bool:
async def fake_commit_updates(node_type: str) -> WriteOutcome:
del node_type
return False
return WriteOutcome()
async def fake_load_node_type(node_type: str, *, reason: str) -> None:
reload_calls.append((node_type, reason))
async def fake_reload_after_write(
node_type: str,
*,
reason: str,
reload_local: bool,
reload_remote: bool,
) -> None:
del reload_local, reload_remote
reload_calls.append((node_type, reason))
async def fake_normalize(node_type: str) -> None:
del node_type
monkeypatch.setattr(pipeline, "commit_creates", fake_commit_creates)
monkeypatch.setattr(pipeline, "commit_updates", fake_commit_updates)
monkeypatch.setattr(pipeline, "_load_node_type", fake_load_node_type)
monkeypatch.setattr(pipeline, "_reload_node_type_after_write", fake_reload_after_write)
monkeypatch.setattr(pipeline, "normalize_create_success_to_update_entry", fake_normalize)
await pipeline.phase_sync_by_node_type()
@@ -132,12 +143,12 @@ async def test_phase_sync_skips_reload_for_jsonl_datasources(
reload_calls: list[tuple[str, str]] = []
async def fake_commit_creates(node_type: str) -> bool:
return node_type == "project"
async def fake_commit_creates(node_type: str) -> WriteOutcome:
return WriteOutcome(local_written=(node_type == "project"), remote_written=False)
async def fake_commit_updates(node_type: str) -> bool:
async def fake_commit_updates(node_type: str) -> WriteOutcome:
del node_type
return False
return WriteOutcome()
async def fake_load_node_type(node_type: str, *, reason: str) -> None:
reload_calls.append((node_type, reason))
@@ -157,6 +168,46 @@ async def test_phase_sync_skips_reload_for_jsonl_datasources(
]
@pytest.mark.asyncio
async def test_reload_after_local_only_create_skips_remote_datasource() -> None:
class _RecordingDataSource(_FakeDataSource):
def __init__(self, label: str):
self.label = label
self.load_calls: list[tuple[str | None, tuple[str, ...] | None]] = []
async def load_all(self, order=None, data_type=None) -> None:
order_tuple = tuple(order) if order is not None else None
self.load_calls.append((data_type, order_tuple))
local_ds = _RecordingDataSource("local")
remote_ds = _RecordingDataSource("remote")
project_strategy = _StubStrategy("project")
pipeline = FullSyncPipeline(
local_datasource=local_ds,
remote_datasource=remote_ds,
strategies=[project_strategy],
persistence=_FakePersistence(),
local_collection=DataCollection("local"),
remote_collection=DataCollection("remote"),
binding_manager=BindingManager(_FakePersistence(), auto_persist=False),
node_types=["project"],
load_order=["project"],
sync_order=["project"],
enable_persistence=False,
)
await pipeline._reload_node_type_after_write(
"project",
reason="create wrote data",
reload_local=True,
reload_remote=False,
)
assert local_ds.load_calls == [(None, ("project",))]
assert remote_ds.load_calls == []
@pytest.mark.asyncio
async def test_evaluate_consistency_merges_compare_and_post_check_ignore_fields() -> None:
project_strategy = _StubStrategy("project")
+14 -10
View File
@@ -96,7 +96,7 @@ async def test_project_detail_create_failure_preserves_push_id_and_biz_id() -> N
assert result.metadata["key"] == "production"
def test_project_detail_load_conflict_raises_on_same_project_key_different_data_id() -> None:
def test_project_detail_load_allows_same_project_key_with_different_data_id() -> None:
collection = DataCollection("remote")
project = _ProjectNode(
@@ -119,16 +119,20 @@ def test_project_detail_load_conflict_raises_on_same_project_key_different_data_
)
collection._nodes[existing_detail.node_id] = existing_detail
collection._data_id_to_node_id[existing_detail.data_id] = existing_detail.node_id
collection._node_id_to_data_id[existing_detail.node_id] = existing_detail.data_id
handler = ProjectDetailApiHandler()
handler.set_collection(collection)
with pytest.raises(RuntimeError, match="project_detail_data load conflict"):
handler._create_node(
{
"id": "detail-new",
"project_id": "project-1",
"key": "actual_construction",
"value": [{"date": "2026-01-02", "capacity": 2.0}],
}
)
node = handler._create_node(
{
"id": "detail-new",
"project_id": "project-1",
"key": "actual_construction",
"value": [{"date": "2026-01-02", "capacity": 2.0}],
}
)
assert node.node_id != existing_detail.node_id
assert node.data_id == "detail-new"
assert node.context["project_id"] == "project-1"