fleet-memory/hindsight-api-slim/hindsight_api/engine
Chris Bartholomew 3fc87e767c
fix(retain): run _ann_seeds temp table inside a transaction (#954)
compute_semantic_links_ann created a TEMP TABLE outside any transaction,
then ran a TRUNCATE / COPY / SELECT / DROP sequence as separate statements
on the same asyncpg connection. This is fine against a direct Postgres
connection but fails intermittently when the caller is routed through
PgBouncer in transaction pool mode:

  CREATE TEMP TABLE IF NOT EXISTS _ann_seeds (...)   -- backend A
  TRUNCATE _ann_seeds                                 -- backend B -> FAILS

Temp tables are session-scoped to the backend that created them. In
PgBouncer transaction mode the backend is only pinned to the client for
the duration of an actual transaction, so between standalone statements
the pooler can (and under concurrency, will) rebind the client to a
different backend. When that happens the _ann_seeds table disappears
and the follow-up statement fails with:

  relation "_ann_seeds" does not exist

Symptom: ~3% of sync retain calls (2 of 61) failed the Hindsight Cloud
smoke test on a recent hindsight-dev deploy. Async retains are masked
by the 3-attempt retry loop so they usually eventually succeed.

Fix: wrap the CREATE TEMP TABLE -> COPY -> SELECT sequence in a single
`async with conn.transaction():` block, and use ON COMMIT DROP so the
temp table is transaction-scoped and auto-cleaned at commit. Also
switch `SET hnsw.ef_search = 60` to `SET LOCAL` so the tuning is
transaction-scoped and no longer leaks onto the pooled backend for
subsequent recall queries. Drop the now-unnecessary manual TRUNCATE,
explicit DROP TABLE, and RESET hnsw.ef_search.

The function docstring still correctly describes this as running on a
separate connection outside the surrounding write transaction — this
change only adds an inner transaction around the ANN work itself to
keep the temp table visible to PgBouncer.

Tests:
- Add TestComputeSemanticLinksAnnPgBouncerSafety with 5 regression
  tests using a mocked connection. These are structural asserts — they
  check that the function enters conn.transaction(), uses ON COMMIT DROP,
  uses SET LOCAL, and does not reintroduce manual TRUNCATE / DROP /
  RESET calls. They would have caught the original bug if they had
  existed, and will catch any future reversion.
2026-04-10 18:36:03 +02:00
..
consolidation fix(consolidation): improve observation quality with structured processing rules (#814) 2026-04-01 12:44:52 +02:00
directives feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
parsers feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
providers feat: add built-in llama.cpp LLM provider for local inference (#933) 2026-04-08 15:22:10 +02:00
reflect feat(reflect): make source facts in search_observations configurable (#688) 2026-03-25 17:54:49 +01:00
retain fix(retain): run _ann_seeds temp table inside a transaction (#954) 2026-04-10 18:36:03 +02:00
search feat: include occurred_end and mentioned_at in think-prompt fact serialization (#929) 2026-04-08 10:33:14 +02:00
storage feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
__init__.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
audit.py feat: add audit log for feature usage tracking (#717) 2026-03-27 09:52:03 +01:00
cross_encoder.py feat: add OpenRouter support for LLM, embeddings, and reranking (#930) 2026-04-08 11:24:21 +02:00
db_budget.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
db_utils.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
embeddings.py feat: add OpenRouter support for LLM, embeddings, and reranking (#930) 2026-04-08 11:24:21 +02:00
entity_resolver.py perf: 3-phase retain pipeline — fix deadlocks, cap temporal links, query-time entity expansion (#722) 2026-04-01 12:52:49 +02:00
interface.py fix(api): clear memories endpoint no longer deletes the bank profile (#837) 2026-04-01 18:34:39 +02:00
jina_mlx_reranker.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
llm_interface.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
llm_wrapper.py feat: add built-in llama.cpp LLM provider for local inference (#933) 2026-04-08 15:22:10 +02:00
memory_engine.py feat: add HINDSIGHT_API_DEFAULT_BANK_TEMPLATE env var (#966) 2026-04-10 16:41:28 +02:00
operation_metadata.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00
query_analyzer.py fix(query_analyzer): handle dateparser internal crashes gracefully (#893) 2026-04-07 09:26:27 +02:00
response_models.py fix(retain): preserve normalized experience fact types (#848) 2026-04-02 12:20:37 +02:00
task_backend.py fix(ci): resolve all CI failures (#847) 2026-04-02 17:17:42 +02:00
utils.py feat: introduce hindsight-api-slim and hindsight-all-slim packages (#560) 2026-03-13 13:50:03 +01:00