Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Possible fix for MySQL support

  • Loading branch information...
commit c9703faad12a3e3051c3d802176f4752a218bcde 1 parent 5de3205
@scosman authored
Showing with 10 additions and 9 deletions.
  1. +10 −9 lib/delayed/backend/active_record.rb
View
19 lib/delayed/backend/active_record.rb
@@ -58,26 +58,27 @@ def self.reserve(worker, max_run_time = Worker.max_run_time)
# scope to filter to records that are "ready to run"
readyScope = self.ready_to_run(worker.name, max_run_time)
- # scope to filter to the single next eligible job (locking it for update http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-FOR-UPDATE-SHARE)
+ # scope to filter to the single next eligible job
nextScope = readyScope.scoped
nextScope = nextScope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority
nextScope = nextScope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority
nextScope = nextScope.scoped(:conditions => ["queue IN (?)", Worker.queues]) if Worker.queues.any?
- nextScope = nextScope.scoped.by_priority.limit(1).lock(true)
- nextScope = nextScope.scoped.select('id')
+ nextScope = nextScope.scoped.by_priority.limit(1)
now = self.db_time_now
if rails3? && (::ActiveRecord::Base.connection.adapter_name == "PostgreSQL")
- # This works on PostgreSQL and uses 1 less query, but uses SQL not supported nativly through ActiveRecord
+ # 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
quotedTableName = ::ActiveRecord::Base.connection.quote_column_name(self.table_name)
- reserved = self.find_by_sql(["UPDATE #{quotedTableName} SET locked_at = ?, locked_by = ? WHERE id IN (#{nextScope.to_sql}) RETURNING *",now,worker.name])
+ subquerySql = nextScope.lock(true).select('id').to_sql
+ reserved = self.find_by_sql(["UPDATE #{quotedTableName} SET locked_at = ?, locked_by = ? WHERE id IN (#{subquerySql}) RETURNING *",now,worker.name])
return reserved[0]
else
- # This works on any database and uses seperate queries to lock and return the job
- # Databases like PostgreSQL and MySQL that support "SELECT .. FOR UPDATE" (ActiveRecord Pessimistic locking) don't need the second application
- # of 'readyScope' but it doesn't hurt and it ensures that the job being locked still meets ready_to_run criteria.
- count = readyScope.where(:id => nextScope).update_all(:locked_at => now, :locked_by => worker.name)
+ # This works on MySQL and other DBs that support UPDATE...LIMIT. It uses separate queries to lock and return the job
+ count = nextScope.update_all(:locked_at => now, :locked_by => worker.name)
return nil if count == 0
return self.where(:locked_at => now, :locked_by => worker.name).first
end
Please sign in to comment.
Something went wrong with that request. Please try again.