Skip to content

Replace auction_orders table with index on order_uids column#4568

Merged
MartinquaXD merged 6 commits into
mainfrom
remove-auction-orders-table
Jul 10, 2026
Merged

Replace auction_orders table with index on order_uids column#4568
MartinquaXD merged 6 commits into
mainfrom
remove-auction-orders-table

Conversation

@MartinquaXD

@MartinquaXD MartinquaXD commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Description

When I investigated some of the fallout of the recent DNS issues I noticed that the post-processing that happens in the autopilot run loop (storing important bookkeeping information in the DB before telling the winner to /settle) can be very slow. The biggest offender was the save_auction call that first writes into competition_auctions and then into the auction_orders table. This currently routinely takes 250-500ms according to our metrics.
Screenshot 2026-06-27 at 16 37 48

However, it seems like the write into the auction_orders table is completely redundant as it duplicates the information we already have in competition_auctions::order_uids. The new redundant table was introduced in #4233 to quickly find all auctions that contained a given order uid. However, this can also be achieved using a GIN index on the existing order_uids table.

partially addresses #3057

Changes

Create GIN index on competition_auctions::order_uids and update the code to use that instead of the auction_orders table. To not have anything break during the release the auction_orders table will be dropped in the release after that to make sure no pods are using the old table during the rollover.

How to test

I cloned the prod DB and created the new GIN index. While it took ~6.5h and 12% CPU usage the resulting index was significantly smaller than the deprecated table + index (20GB vs 470GB) and the new queries were actually faster on the new index.

migration (6.5h @ 12% CPU)
Screenshot 2026-07-08 at 08 37 12

size comparison (19GB vs 492GB)
Screenshot 2026-07-08 at 08 08 25

query performance (77ms vs 147ms)
Screenshot 2026-07-08 at 08 05 44

and as a last sanity check I also compared the data of the 2 tables and since the competition_auctions table was created before the auction_orders table it even contains 6 times as much data.
Screenshot 2026-07-08 at 09 10 35

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Reminder: Please update the DB Readme and comment whether migrations are reversible (include rollback scripts if applicable).

  • If creating new tables, update the tables list.
  • When adding a new index, consider using CREATE INDEX CONCURRENTLY for tables involved in the critical execution path.
  • For breaking changes, remember that during rollout k8s starts the new autopilot, runs the Flyway migration, and only then shuts down the old pod. That overlap means the previous version can still be processing requests on the migrated schema, so make it compatible first and ship the breaking DB change in the following release.

Caused by:

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions Bot added the stale label Jul 5, 2026
@MartinquaXD MartinquaXD marked this pull request as ready for review July 8, 2026 09:12
@MartinquaXD MartinquaXD requested a review from a team as a code owner July 8, 2026 09:12
@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MartinquaXD's task in 2m 6s —— View job


PR Review: Replace auction_orders table with GIN index

  • Read changed files & gather context
  • Analyze migrations, DB queries, and code changes
  • Post inline comments for any issues
  • Post review summary

Nice change — the GIN index is a clean replacement and the code cleanup is straightforward. The new @> query is functionally equivalent to the old junction-table lookup and the removal of save_auction_orders from the hot path is a good win. Two blocking issues to address, posted inline:

  1. Migration version collision (blocker)main now has V111__drop_pool_indexer_uniswap_v3.sql, so this PR's V111 is a duplicate and Flyway will refuse to start. Renumber to V112/V113. details
  2. Rollout safety of DROP TABLE — the old autopilot still writes to auction_orders during the rolling-update overlap, so the drop should ship in a following release (per the CI reminder). details

No other issues — the README, LARGE_TABLES, and debug-skill updates all look correct and consistent.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the redundant auction_orders table and replaces it with a GIN index on the order_uids column of the competition_auctions table, updating the database queries, schema, and documentation accordingly. Feedback highlights a critical deployment issue: creating the GIN index on production takes approximately 6.5 hours, which will exceed the 5-minute deployment limit and cause the automated Flyway migration to fail. To prevent this, the index must be created manually on the production database prior to deployment.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +2 to +3
CREATE INDEX CONCURRENTLY IF NOT EXISTS competition_auctions_order_uids_gin
ON competition_auctions USING GIN (order_uids);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

According to the database/README.md guidelines, migrations that require a long-running process must be executed manually because the weekly release process imposes a 5-minute deployment limit (and a 600-second pod deadline). Since creating this GIN index on the production database takes approximately 6.5 hours (as noted in the PR description), running this as an automated Flyway migration during deployment will cause the release to time out and fail.

To prevent deployment failure, this index must be created manually on the production database prior to deploying this release. Once created manually, the IF NOT EXISTS clause will allow the Flyway migration to complete instantly during deployment.

Comment thread database/sql/V112__gin_index_orders_in_auction.sql
Comment thread database/sql/V113__drop_auction_orders.sql Outdated

@jmg-duarte jmg-duarte left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹

@jmg-duarte jmg-duarte removed the stale label Jul 8, 2026
@MartinquaXD

Copy link
Copy Markdown
Contributor Author

Since opening the PR I already went ahead and created the new index on all tables - mainnet is still working on it but should be ready in the morning.
I also wanted to reproduce the results on the actual prod DB just as a last sanity check. That's when I ran into a wall for a while.
When originally testing the PoC the query I tested fetches all the auctions which reliably picked the new index for milliseconds results.
However, when I tested in prod I always tested with LIMIT 1 which ALWAYS triggered a squential scan which I aborted after 10s of seconds runtime.
Fortunately I was able to figure out what was happening. The issue lies in the "shape" of the auction and the specific order I picked. Auctions currently have ~3K orders on base and the vast majority of orders shows up in thousands of auctions because they are long standing limit orders. The order I picked was one that was evicted from the auction - either because it was settled or it expired. In other words I picked a super rare order.
Postgres keeps stats on common values and a rough estimate of unique values. Given that the vast majority of orders shows up in the vast majority of auctions it's a reasonable decision to do a sequential scan for searching 1 instance of a random order as that still beats the low constant overhead of using the GIN index.

So while it's not needed for this PR I want to spare others going through the same investigation as I did.
So if you really have to find a limited number of items, either return all as a stream and only take 1 item from the stream (i.e. solution in rust) or you do the unbounded query in a materialized CTE and only select 1 item from it (solution in SQL).

WITH matches AS MATERIALIZED (
  SELECT * FROM competition_auctions
  WHERE order_uids @> ARRAY['\x13f4...f5fb'::bytea]
)
SELECT * FROM matches LIMIT 1;

@MartinquaXD MartinquaXD force-pushed the remove-auction-orders-table branch from e95456d to 661f19d Compare July 10, 2026 06:21
@MartinquaXD MartinquaXD enabled auto-merge July 10, 2026 06:22
@MartinquaXD MartinquaXD added this pull request to the merge queue Jul 10, 2026
Merged via the queue into main with commit 69ae1f4 Jul 10, 2026
22 checks passed
@MartinquaXD MartinquaXD deleted the remove-auction-orders-table branch July 10, 2026 06:58
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants