Skip to content

Fix race in conditional fillup - #31

Open
julik wants to merge 3 commits into
cheddar-me:mainfrom
julik:fix-race-fillup-3
Open

Fix race in conditional fillup#31
julik wants to merge 3 commits into
cheddar-me:mainfrom
julik:fix-race-fillup-3

Conversation

@julik

@julik julik commented Nov 30, 2025

Copy link
Copy Markdown
Contributor

Under heavy load Pecorino is susceptible to a race condition where two things can happen:

  • The CTE for the current bucket value selects a level of NULL, leading to a failure in the UPDATE
  • The UPDATE may indicate lower bucket level than it actually is.

In Postgres, a CTE used with an INSERT .. ON CONFLICT UPDATE is not guaranteed to be atomic with the UPDATE itself. This means, that if there is another fillup between the execution of the CTE (the WITH (SELECT level FROM ...) and the UPDATE - even if they are within the same statement - the fillup can get accepted. This makes Pecorino unsafe under high load when SQL-based rate limiting is used with PostgreSQL (the SQLite adapter will lock the entire database during update so it is much less of an issue there). Lesson from that is that a CTE used with an UPDATE is not atomic with that UPDATE, contrary to what I assumed when I designed it this way.

This also means that Pecorino needs proper row-level locking and a transaction around the fillups if the fillup may be rejected - as a formal way to block the concurrent fillups from taking place.

I have also updated the test config / a few other bits and bobs which were in the previous PRs.

* Conditional fillup could overflow the bucket under certain conditions and under elevated load.
* Hide the AR connection behind a shim, as otherwise there is too much management of it all. ActiveRecord connection management
  can be tricky, and we don't need it as such - we need to _integrate_ with it well.
  Also, all of our queries are .uncached. We do still want the queries to go through ActiveRecord adapters, because there is useful instrumentation in their `execute` which some folks are using
* Relocate sanitization into the shim as well, for future work
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant