Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize PG reservation query for PG >=9.5 #215

Merged

Conversation

stevenharman
Copy link
Contributor

@stevenharman stevenharman commented Aug 10, 2023

What does this optimization do?

It handles an issue where two DelayedJob servers (i.e., "workers") would try to pull the top delayed_jobs record at the same time. One would succeed and the second would block.

SKIP LOCKED, introduced in PostgreSQL 9.5, allows the second query to skip records that are already locked by another transaction. This results in significant improvement in performance when there are multiple worker servers all trying to get the next available delayed_jobs row at the same time.

What about older PostgreSQL versions?

ActiveRecord requires Postgres >= 9.3, so we cannot rely on the built-in version checks. But we can use the same mechanisms that ActiveRecord provides to check the current version and enable an optimization in that case. This means newer versions of ActiveRecord (at least 5.0+) will inspect the current Postgres version and make the optimization if they can. We're not going to worry about older ActiveRecord versions as everything that old is EOL'd anyhow. And if they're running a Rails that old, the Postgres is likely older too.

I understand there's been a hesitance to introduce Postgres version-specific functionality in the past, but this optimization drops our time to dequeue a job from several seconds to milliseconds. We've been running with this patch for years in production and I'm hoping this is a more favorable time to upstream it.

If so, I can adjust the code to be more intention revealing - extracting some methods so we need fewer comments, and to make it easier to pull this out when Postgres 9.5+ is required in some future version of this gem. We might also memoize the result of the check so we only do it once per connection object.

Thank you for your consideration.

What does this optimization do?

It handles an issue where two DJ servers "workers" would try to pull the
top `delayed_jobs` record at the same time. One would succeed and the
second would block.

"SKIP LOCKED", introduced in PostgreSQL 9.5, allows the second query to
skip records that are already locked by another transaction. This
results in significant improvement in performance when there are
multiple worker servers all trying to get the next available job row at
the same time.

What about older PostgreSQL versions?

ActiveRecord requires PG>=9.3, so we cannot rely on the built-in checks.
But we can use the same mechanisms that AR provides to check the current
version and enable an optimization in that case. Meaning newer versions
of AR (at least 5.0+) will inspect the current PG version and make the
optimization if they can. We're not going to worry about older ARs as
everything that old is EOL'd anyhow. And if they're running a Rails that
old, the PG is likely older too.
@mostlyobvious
Copy link

That should work for MySQL too: https://dev.mysql.com/blog-archive/mysql-8-0-1-using-skip-locked-and-nowait-to-handle-hot-rows/

@albus522 albus522 merged commit a50c45b into collectiveidea:master Aug 14, 2024
@stevenharman
Copy link
Contributor Author

Thanks for pulling this in, @albus522. Do you have a plan for an eventual release? Given the number of changes being pulled in, are you thinking a new major version, or just a minor (4.2.0) bump?

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.

5 participants