From a69e22ed6abcf7a7ae060e5474dba1d9aede66b2 Mon Sep 17 00:00:00 2001 From: Alban Peignier Date: Mon, 26 Sep 2022 15:55:38 +0200 Subject: [PATCH] CHOUETTE-2358 Change query syntax to avoid multiple rows in the Delayed Job reserve subquery Approach described into https://github.com/collectiveidea/delayed_job_active_record/pull/169 --- app/lib/delayed/uniq_reservation.rb | 31 +++++++++++++++++++++++++++++ config/initializers/delayed_job.rb | 1 + 2 files changed, 32 insertions(+) create mode 100644 app/lib/delayed/uniq_reservation.rb diff --git a/app/lib/delayed/uniq_reservation.rb b/app/lib/delayed/uniq_reservation.rb new file mode 100644 index 0000000000..e37426b02a --- /dev/null +++ b/app/lib/delayed/uniq_reservation.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Delayed + # Used to customize Delayed::Backend::ActiveRecord::Job + # + # Fixes PostgreSQL SQL to ensure reservation uniqueness + # See https://github.com/collectiveidea/delayed_job_active_record/pull/169 + module UniqReservation + def self.prepended(base) + class << base + prepend ClassMethods + end + end + + module ClassMethods # rubocop:disable Style/Documentation + def reserve_with_scope_using_optimized_postgres(ready_scope, worker, now) + # Custom SQL required for PostgreSQL because postgres does not support UPDATE...LIMIT + # This locks the single record 'FOR UPDATE' in the subquery + # http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-FOR-UPDATE-SHARE + # Note: active_record would attempt to generate UPDATE...LIMIT like + # SQL for Postgres if we use a .limit() filter, but it would not + # use 'FOR UPDATE' and we would have many locking conflicts + quoted_name = connection.quote_table_name(table_name) + subquery = ready_scope.limit(1).lock(true).select('id').to_sql + sql = "UPDATE #{quoted_name} SET locked_at = ?, locked_by = ? WHERE id = (#{subquery}) RETURNING *" + reserved = find_by_sql([sql, now, worker.name]) + reserved[0] + end + end + end +end diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb index fce8c062e8..41b0ed2947 100644 --- a/config/initializers/delayed_job.rb +++ b/config/initializers/delayed_job.rb @@ -5,6 +5,7 @@ # Add organisation management for Job class Delayed::Job # rubocop:disable Style/ClassAndModuleChildren(RuboCop) prepend Delayed::WithOrganisation + prepend Delayed::UniqReservation end # Add :dead_worker hook