honcho/docs/v2
Vineeth Voruganti 396976db34
Connection Exponential Backoff (#758)
* feat(db): add connection retry, adaptive deriver polling, and pool metrics

Add resilience and visibility for DB connection handling under transaction-
pooler (Supavisor) saturation, where client-connection limits get exhausted
across many tenants.

- get_db/tracked_db now force an eager pool checkout with bounded exponential
  backoff (tenacity), retrying SQLAlchemy TimeoutError + OperationalError so
  transient pooler rejections degrade gracefully instead of 500ing. Toggle via
  DB_CONNECTION_RETRY_ENABLED (+ delay/backoff knobs); ~10s default budget.
- Deriver polling backs off when idle or erroring (base -> max, x2 each cycle)
  and snaps back to base on claimed work, cutting steady-state query load.
  Toggle via DERIVER_POLLING_BACKOFF_ENABLED (+ max/multiplier).
- Add scrape-time db_pool_connections Prometheus gauge (checked_out/checked_in/
  size/overflow, labeled api|deriver), registered in both the API lifespan and
  the deriver metrics server.
- Make SqlalchemyIntegration explicit in both Sentry inits; wrap connection
  acquisition in a db.pool.acquire span and capture live pool stats on
  retry-exhaustion.

* feat(db): add acquisition counter and in-flight query gauge

Build on the pool-connection metrics with two signals that turn detection
into diagnosis under transaction-pooler saturation:

- db_connection_acquisitions{outcome=ok|retried|exhausted}: counts how often
  connection checkout retries through pooler rejection — the alertable early
  warning before requests start failing.
- db_queries_in_flight: statements actually executing on the wire (via
  SQLAlchemy cursor-execute events, drift-proof across query errors). Pairs
  with checked_out: the gap reveals connections held but parked (the "idle in
  transaction during an external call" antipattern). Labeled namespace +
  instance_type only; gated on METRICS.ENABLED for zero overhead when off.

Add DB-free unit tests for retry outcomes, polling backoff, and in-flight
gauge drift handling.

* fix: address CodeRabbit review on PR #758

- db: roll back the session on a retryable checkout failure before
  retrying — a failed autobegin can leave it pending-rollback, making the
  next db.connection() raise instead of re-checking-out cleanly. Cheap
  Python-side cleanup when no connection was bound.
- metrics: guard DBPoolCollector.collect() so a pool-read/import hiccup
  can't raise and abort the whole /metrics scrape (Prometheus drops ALL
  metrics if any collector raises) — log and fall back to empty.

* fix(db): lazy retrying session + review fixes for connection backoff

Address Codex/CodeRabbit review on PR #758.

- Replace eager checkout with HonchoAsyncSession: a lazy AsyncSession that
  checks out its connection (with retry) on the first DB-touching call, not at
  construction. Request handlers doing non-DB work (embedding/file/LLM) before
  their first query no longer pin a connection across it, while the API path
  still gets checkout retry. Only the checkout is retried — the statement runs
  once via super(), so writes are never duplicated. Tracing's set_config moves
  into the same lazy acquire hook.
- Roll the session back on a retryable checkout failure before retrying, so a
  failed autobegin can't leave it pending-rollback.
- Lower default POOL_TIMEOUT to 5s and validate it stays under the retry budget
  for pooled (non-null) POOL_CLASS; update config.toml.example and v2/v3 docs.
- Clamp pool overflow gauge to >= 0 (was negative before the pool fills).
- Remove double-sleep in the deriver idle poll (true backoff cap, not 2x);
  make in-flight instrumentation registration idempotent.
- Tests: HonchoAsyncSession lazy/idempotent acquire, statement-runs-once,
  tracing, commit/rollback flag reset, get_db no-acquire-at-entry, polling-loop
  single-sleep, and the POOL_TIMEOUT/retry-budget validator.

* fix(db): cover all DB-touching session methods; clear flag on close/reset

Address Codex follow-up review on PR #758 (polish, no behavior-critical bug).

- HonchoAsyncSession: wrap get/get_one/stream/stream_scalars/delete in addition
  to execute/scalar/scalars/flush/merge/refresh/commit, so the "lazy checkout
  with retry on first DB use" guarantee has no holes. connection() stays
  unwrapped (acquire_connection_with_retry calls it — wrapping would recurse).
- Reset the acquired flag on close()/reset() too, so a session reused after
  close/reset re-acquires (and re-wraps retry) on its next DB use.
- Fix stale comments: connection retry now applies lazily to the request path
  via HonchoAsyncSession (config.py), and the FakeSession helper note.
- Tests: close/reset flag reset, and get/delete route through acquisition.
2026-06-01 12:57:07 -04:00
..
api-reference Resolve SDK Inconsistencies and Add Observation Creation Endpoints (#288) 2025-12-04 15:24:45 -05:00
contributing Connection Exponential Backoff (#758) 2026-06-01 12:57:07 -04:00
documentation Kass/readme refresh (#681) 2026-05-14 13:15:37 -04:00
guides Adding granola integration (#417) 2026-03-18 13:08:20 -04:00
integrations Kass/readme refresh (#681) 2026-05-14 13:15:37 -04:00
migrations Mem0 -> Honcho migration (#281) 2025-12-05 11:53:54 -05:00
README.md Add Pre-commit Hooks (#165) 2025-07-22 15:17:53 -04:00
openapi.json fix message id range validation error (#300) 2025-12-16 11:13:01 -05:00

README.md

This subdirectory contains the peer-paradigm documentation for Honcho (Honcho v2.0.0 onwards).