Skip to content
Browse files

Merge pull request #29 from scosman/master

Perf suggestion
  • Loading branch information...
2 parents 27dd035 + 5de3205 commit 666cbc16a8c73db78f80db3b806e4e088cf894bb @sferik sferik committed
Showing with 26 additions and 9 deletions.
  1. +26 −9 lib/delayed/backend/active_record.rb
View
35 lib/delayed/backend/active_record.rb
@@ -54,15 +54,32 @@ def self.clear_locks!(worker_name)
update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])
end
- # Find a few candidate jobs to run (in case some immediately get locked by others).
- def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
- scope = self.ready_to_run(worker_name, max_run_time)
- scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority
- scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority
- scope = scope.scoped(:conditions => ["queue IN (?)", Worker.queues]) if Worker.queues.any?
-
- ::ActiveRecord::Base.silence do
- scope.by_priority.all(:limit => limit)
+ 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)
+ 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')
+
+ 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
+ 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])
+ 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)
+ return nil if count == 0
+ return self.where(:locked_at => now, :locked_by => worker.name).first
end
end

0 comments on commit 666cbc1

Please sign in to comment.
Something went wrong with that request. Please try again.