feat: enable sharding across DBs - #104
Conversation
|
| Filename | Overview |
|---|---|
| app/services/call_imports/bulk_ops.py | Heavily modified to route row operations through shard DB sessions. Deferred-commit ordering (catalog then shards) is architecturally sound, but partial shard commit failure leaves import in FAILED + total_rows>0 state that blocks re-materialization. |
| app/db_sharding/row_ops.py | New file implementing shard-aware row placement, locate, and bulk insert. Deferred-commit pattern addresses the previous shard-before-catalog ordering issue but introduces a new failure mode: partial shard commit after catalog commit leaves the import unrecoverable. |
| app/db_sharding/scatter_gather.py | Large new scatter-gather module; full in-memory load before pagination noted in previous review as P2. Core logic looks correct for the sharded routing. |
| app/db_sharding/rebalance.py | New rebalance tooling with correct ordering: copy to target then update catalog registry then commit catalog then delete from source. Addresses the previously flagged catalog-after-delete issue. Redis lock guards in-flight workers. |
| app/db_sharding/eval_rows.py | New scatter-gather helpers for evaluation rows. evaluation_row_session context manager cleanly wraps locate+cleanup. Bounded fetch for row_index sort only; non-row_index sort keys still load all rows per shard (noted in previous review). |
| app/workers/tasks/process_call_import_row.py | Shard-aware: locate_call_import_row now wrapped in try/except LookupError (addressing previous P1), catalog vs shard session split handled throughout. Rollup ordering (shard first then catalog) is correct. |
| app/workers/tasks/evaluate_call_import_row.py | Both first and second phase locate calls now wrapped in LookupError guards (addressing previous P1s). llm_credential_id threading added to scoring inputs. Session split for row_db vs catalog_db handled correctly. |
| app/workers/tasks/evaluate_call_import_row_audio.py | Switched from SessionLocal to evaluation_row_session context manager; LookupError is now caught at the outer level and returns the skip sentinel, addressing the previous P1. |
| app/services/telephony/recording_download.py | HTTP 429 on public-URL path now raises ExotelTransientError instead of ExotelInvalidContentError (fixing permanent failure). HTTP 400 on credentialed path now raises ExotelTransientError instead of penalizing credential (fixing throttle storms). |
| app/migrations/054_call_import_sharding.py | Adds workspace_id and sharding tables. workspace_id is now correctly threaded through bulk_insert_evaluation_rows (addressing previous P1 on NOT NULL constraint violation). |
Comments Outside Diff (1)
-
app/services/call_imports/bulk_ops.py, line 340-345 (link)Partial shard commit after catalog commit leaves import unrecoverable
After
db.commit()succeeds (catalog now hastotal_rows = Nand slice registry),commit_pending_shard_sessionsiterates sessions and commits each one. If session k fails, an exception is raised and theexceptblock callsdb.rollback()(no-op — catalog already committed) thenrollback_pending_shard_sessions(pending_shard_sessions). Sessions 1..k-1 are already durably committed on their shards; session k and beyond are rolled back. The catalog is then updated tostatus = FAILEDwithtotal_rows = N.Recovery is blocked on both guards: the
status not in {PROCESSING, PENDING}check returns early, and thetotal_rows > 0check also short-circuits. The stranded rows on shards 1..k-1 prevent simple re-insertion (unique constraint on(call_import_id, row_index)), so the import cannot be recovered without direct DB surgery (resettotal_rows = 0on catalog, drop partial shard rows, reset status, then re-upload). The same failure mode exists inmaterialize_and_enqueue_evaluationfor eval rows.
Reviews (12): Last reviewed commit: "fix: fixing integration tests" | Re-trigger Greptile
Add bounded sharded pagination for evaluation row listing, reduce UI polling load on large runs, lighten eval materialization queries, stop premature eval-row failures during transient import retries, and retry Exotel HTTP 400s without penalizing the shared telephony credential.
Address Greptile P1 items: transient public 429 retries, LookupError guards on eval workers, catalog-first materialization, rebalance registry-before- delete ordering, and broken sharding docs route.
Summary
Why
Large call imports and evaluations (70k+ rows) were slow or unstable due to full-shard scans, aggressive UI polling, eval rows failing while imports retried, and Exotel 400 responses blocking all workers sharing a credential.
Test plan
pytest tests/test_db_sharding/test_eval_rows_pagination.py tests/test_workers/test_eval_dispatch_import.py tests/test_services/test_telephony/test_recording_download.py tests/test_workers/test_process_call_import_row.pyRelease Label
majorminorfix- backward-compatible bug fix / performance improvementsChecklist