fix(backfill): address Gemini review on /backfill-age/indexes (#206 follow-up) - #207
Conversation
Two correctness improvements from the PR #206 review: 1. Presence probe now checks pg_index.indisvalid instead of the pg_indexes view. A CREATE INDEX CONCURRENTLY that fails partway leaves an INVALID index in the catalog — pg_indexes lists it but the planner can't use it. Counting it as already_present would silently skip the rebuild and leave the latency bug unfixed. Filtering on indisvalid means an invalid index reads as absent, so the route re-attempts it (and the rebuild's clear "already exists" error alerts the operator to DROP the invalid one). 2. Connection-leak hardening: init conn=None and wrap the whole connect + autocommit + execute sequence in a single try/finally. Previously if connect() succeeded but `conn.autocommit = True` raised, the finally (in a separate nested try) was never entered and the connection leaked. New tests: probe queries pg_index/indisvalid (not the view); connection is closed even when setting autocommit raises. indisvalid probe SQL verified valid against a real apache/age PG16 graph. 9/9 index-route tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses two correctness issues identified in the post-merge review of the backfill index functionality. It improves the reliability of index detection by filtering for valid indexes in the PostgreSQL catalog and ensures robust connection lifecycle management to prevent potential leaks during error states. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request improves the robustness of database index backfilling in backfill_routes.py by checking pg_index.indisvalid to filter out invalid indexes and restructuring connection management to prevent connection leaks if errors occur during initialization. Corresponding unit tests were added to verify these behaviors. The reviewer suggested wrapping the autocommit configuration and initial setup queries inside the inner try block to ensure that psycopg2.OperationalError exceptions (such as when the database is in recovery mode) are properly caught and recorded.
| try: | ||
| conn = psycopg2.connect(dsn, connect_timeout=5) | ||
| except psycopg2.OperationalError as e: | ||
| _record_db_error(e) | ||
| raise | ||
| # CONCURRENTLY forbids an open transaction; autocommit each DDL. | ||
| conn.autocommit = True | ||
| with conn.cursor() as cur: | ||
| cur.execute("LOAD 'age'") | ||
| cur.execute('SET search_path = ag_catalog, "$user", public') |
There was a problem hiding this comment.
The inner try/except psycopg2.OperationalError block currently only wraps psycopg2.connect. However, if the database is in recovery mode (e.g., post-OOM restart), the connection might succeed but subsequent setup queries (LOAD 'age' or SET search_path) will raise a psycopg2.OperationalError with a message like the database system is in recovery mode.
To ensure these recovery-mode operational errors are captured and recorded via _record_db_error(e) (preserving the observability pattern established in #97 and #108), the setup queries and autocommit configuration should also be wrapped inside the inner try block.
| try: | |
| conn = psycopg2.connect(dsn, connect_timeout=5) | |
| except psycopg2.OperationalError as e: | |
| _record_db_error(e) | |
| raise | |
| # CONCURRENTLY forbids an open transaction; autocommit each DDL. | |
| conn.autocommit = True | |
| with conn.cursor() as cur: | |
| cur.execute("LOAD 'age'") | |
| cur.execute('SET search_path = ag_catalog, "$user", public') | |
| try: | |
| conn = psycopg2.connect(dsn, connect_timeout=5) | |
| # CONCURRENTLY forbids an open transaction; autocommit each DDL. | |
| conn.autocommit = True | |
| with conn.cursor() as cur: | |
| cur.execute("LOAD 'age'") | |
| cur.execute('SET search_path = ag_catalog, "$user", public') | |
| except psycopg2.OperationalError as e: | |
| _record_db_error(e) | |
| raise |
Follow-up to #206 (squash-merged as c8df975) — addresses the two correctness points from Gemini's review that landed after the merge.
1. Presence probe checks
pg_index.indisvalid(waspg_indexesview)A
CREATE INDEX CONCURRENTLYthat fails partway leaves an INVALID index in the catalog. Thepg_indexesview lists it, but the planner can't use it. The original_existing_age_indexeswould see that name, report italready_present, and skip the rebuild — silently leaving the latency bug unfixed. Now we joinpg_class/pg_namespace/pg_indexand filteri.indisvalid, so an invalid index reads as absent → the route re-attempts it, and the rebuild's clear "already exists" error surfaces in the response to alert the operator toDROPthe invalid one.Verified the new probe SQL is valid against a real
apache/age:release_PG16_1.6.0graph (returns only the valid index).2. Connection-leak hardening
If
psycopg2.connect()succeeded butconn.autocommit = Trueraised, the original code leaked the connection — thefinallylived in a separate nestedtrythat was never entered. Nowconn = Noneup front and the whole connect + autocommit + execute sequence is wrapped in a singletry/finallywith aconn is not Noneguard.Tests
Two new tests on top of the existing 7 (9/9 green against merged
main):test_presence_probe_filters_invalid_indexes— asserts the probe queriespg_index/indisvalid, not the view.test_connection_closed_when_autocommit_raises— asserts the connection is closed even when setting autocommit raises.Branched fresh off current
main(post-#206-squash) and cherry-picked only the review-fix commit, so this is a clean +74/−14 diff with no phantom re-introduction of the merged #206 change.🤖 Generated with Claude Code