perf(db): functional trip-search index + company.Status + filtered outbox index#18
Conversation
…tbox index
Tighten indexing for the hottest queries (DB review from PROJECT_STATUS):
- trip search: the query compares lower(origin)/lower(destination) and only wants
Scheduled trips, so the old plain (Origin,Destination,DepartureUtc) index could
never serve it. Replace it with a functional, partial index
(lower("Origin"), lower("Destination"), "DepartureUtc") WHERE "Status"='Scheduled'
(raw SQL — EF's fluent API can't express lower()+partial).
- company.Status: indexed so the "active companies" subquery in trip search and the
admin list-by-status filter don't table-scan.
- outbox: replace the (ProcessedAtUtc,OccurredAtUtc) composite with a partial index
on OccurredAtUtc WHERE "ProcessedAtUtc" IS NULL — matches the publisher's exact
poll predicate and stays small as processed rows accumulate.
Migration AddSearchIndexes. 50 unit + 18 integration green (applies cleanly to a
fresh Postgres via Testcontainers).
|
Warning Review limit reached
More reviews will be available in 46 minutes and 55 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds a comprehensive system status document describing authentication, feature completeness (~35–40%), implemented and missing capabilities by actor, database schema coverage, and DevOps status, alongside database index optimizations for Company, OutboxMessage, and Trip entities to improve search and outbox processing performance. ChangesSystem Status and Database Index Optimization
🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/TransportPlatform.Infrastructure/Persistence/Migrations/20260602210228_AddSearchIndexes.cs (1)
35-38: Consider table-locking impact of a plainCREATE INDEXon a populatedtriptable.A non-concurrent
CREATE INDEXholds a lock that blocks writes for the duration of the build. On an empty/fresh DB (as tested via Testcontainers) this is a non-issue, but against a production table with existing rows it can cause write downtime. If this migration will run against populated data, evaluate building withCREATE INDEX CONCURRENTLY— note that requires running outside a transaction, so it would need a separate migration with transaction suppression rather than the default EF migration transaction.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/TransportPlatform.Infrastructure/Persistence/Migrations/20260602210228_AddSearchIndexes.cs` around lines 35 - 38, The plain CREATE INDEX in AddSearchIndexes (migrationBuilder.Sql(... "CREATE INDEX \"IX_trip_search\" ON trip ...")) will take a write lock on populated trip rows; change to build the index concurrently by issuing CREATE INDEX CONCURRENTLY "IX_trip_search" ... instead, and ensure this statement runs outside EF's transaction by moving it into a separate migration or by marking the migration as non-transactional (so the SQL runs without the default transaction). Update the migration that contains migrationBuilder.Sql(...) to use the CONCURRENTLY form and ensure the migration class is configured to run without a transaction (so the concurrent index creation is allowed).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@PROJECT_STATUS.md`:
- Line 3: The snapshot metadata line "Snapshot date: 2026-06-02 · Branch
scanned: `main` (after PR `#17`)" is incorrect for this PR and the documented
index structures referenced later will be outdated; update that snapshot
metadata to "after PR `#18`" and edit the index descriptions that currently
describe the old schema (the index blocks around the current index description
entries) to reflect the index changes introduced by this PR, or alternatively
replace the index descriptions with a short note that index changes are pending
and will be finalized when PR `#18` merges; ensure the snapshot header string and
the index description sections are consistent.
- Around line 175-178: The trip table's documentation still lists the old index
on (Origin,Destination,DepartureUtc); update it to describe the new functional
partial index that supports case-insensitive search and only indexes scheduled
trips by replacing that index description with: a functional, partial index on
(lower("Origin"), lower("Destination"), "DepartureUtc") WHERE "Status" =
'Scheduled' (reference: trip table, index on Origin/Destination/DepartureUtc,
and the lower(...) functional expressions and WHERE "Status" = 'Scheduled').
- Around line 168-169: Add documentation for the new index IX_company_Status
under the company schema in PROJECT_STATUS.md: state that IX_company_Status is
an index on company.Status (varchar(20)) and briefly describe its purpose—to
speed filtering for "active companies" subqueries and admin list-by-status
operations (avoiding table scans); place the note adjacent to the company schema
line that lists `Status` so readers can see the index and its intent together.
- Around line 200-201: Update the outbox_message index docs to describe the new
filtered index: replace the old composite index note `(ProcessedAtUtc,
OccurredAtUtc)` with a description that there is now a filtered index on
OccurredAtUtc WHERE "ProcessedAtUtc" IS NULL (to match the publisher poll
predicate and remain small as processed rows accumulate), and mention which
columns are covered (`OccurredAtUtc`) and the filter on `ProcessedAtUtc`; keep
the rest of the schema line intact.
---
Nitpick comments:
In
`@src/TransportPlatform.Infrastructure/Persistence/Migrations/20260602210228_AddSearchIndexes.cs`:
- Around line 35-38: The plain CREATE INDEX in AddSearchIndexes
(migrationBuilder.Sql(... "CREATE INDEX \"IX_trip_search\" ON trip ...")) will
take a write lock on populated trip rows; change to build the index concurrently
by issuing CREATE INDEX CONCURRENTLY "IX_trip_search" ... instead, and ensure
this statement runs outside EF's transaction by moving it into a separate
migration or by marking the migration as non-transactional (so the SQL runs
without the default transaction). Update the migration that contains
migrationBuilder.Sql(...) to use the CONCURRENTLY form and ensure the migration
class is configured to run without a transaction (so the concurrent index
creation is allowed).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a528b7a7-4343-4096-9568-461aebf4b2a4
⛔ Files ignored due to path filters (3)
src/TransportPlatform.Infrastructure/Persistence/Migrations/20260602210228_AddSearchIndexes.Designer.csis excluded by!**/Migrations/*.Designer.cssrc/TransportPlatform.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.csis excluded by!**/Migrations/*ModelSnapshot.csweb/customer/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (5)
PROJECT_STATUS.mdsrc/TransportPlatform.Infrastructure/Persistence/Configurations/CompanyConfiguration.cssrc/TransportPlatform.Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cssrc/TransportPlatform.Infrastructure/Persistence/Configurations/TripConfiguration.cssrc/TransportPlatform.Infrastructure/Persistence/Migrations/20260602210228_AddSearchIndexes.cs
PROJECT_STATUS.md was swept into this PR; sync its DB-design section with the AddSearchIndexes migration (functional partial trip-search index, company.Status index, filtered outbox index) per review.
What
First step of the full-system build-out: tighten DB indexing for the hottest queries (from the
PROJECT_STATUS.mdDB review).Changes (migration
AddSearchIndexes)lower(origin)/lower(destination)and only wantsScheduledtrips, so the old plain(Origin,Destination,DepartureUtc)index could never be used. Replaced with a functional, partial index:(lower("Origin"), lower("Destination"), "DepartureUtc") WHERE "Status" = 'Scheduled'(raw SQL — EF's fluent API can't expresslower()+ partial together).(ProcessedAtUtc, OccurredAtUtc)composite with a partial index onOccurredAtUtc WHERE "ProcessedAtUtc" IS NULL, matching the publisher's exact poll predicate and staying small as processed rows accumulate.Verification
dotnet test→ 50 unit + 18 integration green; the migration applies cleanly to a fresh Postgres (Testcontainers), which exercises the raw functional-index SQL.Part of the phased roadmap (indexes → Google OAuth → staff → payments → notifications/real-time → reports/demand → observability).
Summary by CodeRabbit
Documentation
Chores