Skip to content

Commit 08e06e6

Browse files
phernandezclaude
andcommitted
perf(search): cache metric instruments in hot vector-sync path
Codex review on PR #754 flagged that _log_vector_sync_complete runs per entity in large sync batches, and each call now goes through logfire.metric_histogram(...).record(...) — OTel's MeterProvider lookup is O(1) but still non-free under load. The original telemetry wrapper cached instruments in a process-wide dict; my strip lost that. Restores the cache as two small module-level helpers in search_repository_base.py (_metric_histogram / _metric_counter) so instruments are resolved once and reused. Callers switch to the helpers (11 sites). Tests clear _METRIC_INSTRUMENTS via monkeypatch so fake factories win on the first lookup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 7ce47d7 commit 08e06e6

2 files changed

Lines changed: 36 additions & 11 deletions

File tree

src/basic_memory/repository/search_repository_base.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,29 @@
4141
_SQLITE_MAX_PREPARE_WINDOW = 8
4242

4343

44+
# Cache instruments so the per-entity hot path in _log_vector_sync_complete
45+
# doesn't re-enter the OTel MeterProvider lookup on every sample.
46+
_METRIC_INSTRUMENTS: dict[tuple[str, str, str], Any] = {}
47+
48+
49+
def _metric_histogram(name: str, unit: str = "") -> Any:
50+
key = ("histogram", name, unit)
51+
instrument = _METRIC_INSTRUMENTS.get(key)
52+
if instrument is None:
53+
instrument = logfire.metric_histogram(name, unit=unit)
54+
_METRIC_INSTRUMENTS[key] = instrument
55+
return instrument
56+
57+
58+
def _metric_counter(name: str) -> Any:
59+
key = ("counter", name, "")
60+
instrument = _METRIC_INSTRUMENTS.get(key)
61+
if instrument is None:
62+
instrument = logfire.metric_counter(name)
63+
_METRIC_INSTRUMENTS[key] = instrument
64+
return instrument
65+
66+
4467
@dataclass
4568
class VectorSyncBatchResult:
4669
"""Aggregate result for batched semantic vector sync runs."""
@@ -1069,7 +1092,7 @@ def emit_progress(entity_id: int) -> None:
10691092
write_seconds_total=result.write_seconds_total,
10701093
)
10711094
batch_total_seconds = time.perf_counter() - batch_start
1072-
logfire.metric_histogram(
1095+
_metric_histogram(
10731096
"vector_sync_batch_total_seconds",
10741097
unit="s",
10751098
).record(
@@ -1079,42 +1102,42 @@ def emit_progress(entity_id: int) -> None:
10791102
"skip_only_batch": result.embedding_jobs_total == 0,
10801103
},
10811104
)
1082-
logfire.metric_counter("vector_sync_entities_total").add(
1105+
_metric_counter("vector_sync_entities_total").add(
10831106
result.entities_total,
10841107
attributes={
10851108
"backend": backend_name,
10861109
"skip_only_batch": result.embedding_jobs_total == 0,
10871110
},
10881111
)
1089-
logfire.metric_counter("vector_sync_entities_skipped").add(
1112+
_metric_counter("vector_sync_entities_skipped").add(
10901113
result.entities_skipped,
10911114
attributes={
10921115
"backend": backend_name,
10931116
"skip_only_batch": result.embedding_jobs_total == 0,
10941117
},
10951118
)
1096-
logfire.metric_counter("vector_sync_entities_deferred").add(
1119+
_metric_counter("vector_sync_entities_deferred").add(
10971120
result.entities_deferred,
10981121
attributes={
10991122
"backend": backend_name,
11001123
"skip_only_batch": result.embedding_jobs_total == 0,
11011124
},
11021125
)
1103-
logfire.metric_counter("vector_sync_embedding_jobs_total").add(
1126+
_metric_counter("vector_sync_embedding_jobs_total").add(
11041127
result.embedding_jobs_total,
11051128
attributes={
11061129
"backend": backend_name,
11071130
"skip_only_batch": result.embedding_jobs_total == 0,
11081131
},
11091132
)
1110-
logfire.metric_counter("vector_sync_chunks_total").add(
1133+
_metric_counter("vector_sync_chunks_total").add(
11111134
result.chunks_total,
11121135
attributes={
11131136
"backend": backend_name,
11141137
"skip_only_batch": result.embedding_jobs_total == 0,
11151138
},
11161139
)
1117-
logfire.metric_counter("vector_sync_chunks_skipped").add(
1140+
_metric_counter("vector_sync_chunks_skipped").add(
11181141
result.chunks_skipped,
11191142
attributes={
11201143
"backend": backend_name,
@@ -1694,7 +1717,7 @@ def _log_vector_sync_complete(
16941717
) -> None:
16951718
"""Log completion and slow-entity warnings with a consistent format."""
16961719
backend_name = type(self).__name__.removesuffix("SearchRepository").lower()
1697-
logfire.metric_histogram(
1720+
_metric_histogram(
16981721
"vector_sync_prepare_seconds",
16991722
unit="s",
17001723
).record(
@@ -1704,7 +1727,7 @@ def _log_vector_sync_complete(
17041727
"skip_only_entity": entity_skipped and embedding_jobs_count == 0,
17051728
},
17061729
)
1707-
logfire.metric_histogram(
1730+
_metric_histogram(
17081731
"vector_sync_queue_wait_seconds",
17091732
unit="s",
17101733
).record(
@@ -1714,7 +1737,7 @@ def _log_vector_sync_complete(
17141737
"skip_only_entity": entity_skipped and embedding_jobs_count == 0,
17151738
},
17161739
)
1717-
logfire.metric_histogram(
1740+
_metric_histogram(
17181741
"vector_sync_embed_seconds",
17191742
unit="s",
17201743
).record(
@@ -1724,7 +1747,7 @@ def _log_vector_sync_complete(
17241747
"skip_only_entity": entity_skipped and embedding_jobs_count == 0,
17251748
},
17261749
)
1727-
logfire.metric_histogram(
1750+
_metric_histogram(
17281751
"vector_sync_write_seconds",
17291752
unit="s",
17301753
).record(

tests/repository/test_semantic_search_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,8 @@ def add(self, amount, attributes=None) -> None:
712712

713713
monkeypatch.setattr(repo, "_prepare_entity_vector_jobs_window", _stub_prepare_window)
714714
monkeypatch.setattr(repo, "_flush_embedding_jobs", _stub_flush)
715+
# Reset the module-level metric cache so the fake factories below win.
716+
monkeypatch.setattr(search_repository_base_module, "_METRIC_INSTRUMENTS", {})
715717
monkeypatch.setattr(
716718
search_repository_base_module.logfire,
717719
"metric_histogram",

0 commit comments

Comments
 (0)