delayed_job_unique_key_active_record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005 Tobias Luetke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
+ NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # DelayedJob ActiveRecord Backend
2
+
3
+ Please note! This is a pre-release version intended for use only with DelayedJob 3.0
4
+
5
+ ## Installation
6
+
7
+ Add the gems to your Gemfile:
8
+
9
+ gem 'delayed_job'
10
+ gem 'delayed_job_active_record'
11
+
12
+ Run `bundle install`
13
+
14
+ If you're using Rails, run the generator to create the migration for the delayed_job table: `rails g delayed_job:active_record`
15
+
16
+ That's it. Use [delayed_job as normal](http://github.com/collectiveidea/delayed_job).
@@ -0,0 +1,99 @@
1
+ require 'active_record'
2
+ require 'active_record/version'
3
+ module Delayed
4
+ module Backend
5
+ module ActiveRecord
6
+ # A job object that is persisted to the database.
7
+ # Contains the work object as a YAML field.
8
+ class Job < ::ActiveRecord::Base
9
+ include Delayed::Backend::Base
10
+ set_table_name :delayed_jobs
11
+
12
+ before_save :set_default_run_at
13
+ before_create :check_unique_key
14
+
15
+ def self.rails3?
16
+ ::ActiveRecord::VERSION::MAJOR == 3
17
+ end
18
+
19
+ if rails3?
20
+ scope :ready_to_run, lambda{|worker_name, max_run_time|
21
+ where('(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name)
22
+ }
23
+ scope :by_priority, order('priority ASC, run_at ASC')
24
+ else
25
+ named_scope :ready_to_run, lambda {|worker_name, max_run_time|
26
+ { :conditions => ['(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name] }
27
+ }
28
+ named_scope :by_priority, :order => 'priority ASC, run_at ASC'
29
+ end
30
+
31
+ def self.before_fork
32
+ ::ActiveRecord::Base.clear_all_connections!
33
+ end
34
+
35
+ def self.after_fork
36
+ ::ActiveRecord::Base.establish_connection
37
+ end
38
+
39
+ # When a worker is exiting, make sure we don't have any locked jobs.
40
+ def self.clear_locks!(worker_name)
41
+ update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])
42
+ end
43
+
44
+ # Find a few candidate jobs to run (in case some immediately get locked by others).
45
+ def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
46
+ scope = self.ready_to_run(worker_name, max_run_time)
47
+ scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority
48
+ scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority
49
+ scope = scope.scoped(:conditions => ["queue IN (?)", Worker.queues]) if Worker.queues.any?
50
+
51
+ ::ActiveRecord::Base.silence do
52
+ scope.by_priority.all(:limit => limit)
53
+ end
54
+ end
55
+
56
+ # Lock this job for this worker.
57
+ # Returns true if we have the lock, false otherwise.
58
+ def lock_exclusively!(max_run_time, worker)
59
+ now = self.class.db_time_now
60
+ affected_rows = if locked_by != worker
61
+ # We don't own this job so we will update the locked_by name and the locked_at
62
+ self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?) and (run_at <= ?)", id, (now - max_run_time.to_i), now])
63
+ else
64
+ # We already own this job, this may happen if the job queue crashes.
65
+ # Simply resume and update the locked_at
66
+ self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
67
+ end
68
+ if affected_rows == 1
69
+ self.locked_at = now
70
+ self.locked_by = worker
71
+ self.locked_at_will_change!
72
+ self.locked_by_will_change!
73
+ return true
74
+ else
75
+ return false
76
+ end
77
+ end
78
+
79
+ # Get the current time (GMT or local depending on DB)
80
+ # Note: This does not ping the DB to get the time, so all your clients
81
+ # must have syncronized clocks.
82
+ def self.db_time_now
83
+ if Time.zone
84
+ Time.zone.now
85
+ elsif ::ActiveRecord::Base.default_timezone == :utc
86
+ Time.now.utc
87
+ else
88
+ Time.now
89
+ end
90
+ end
91
+
92
+ def reload(*args)
93
+ reset
94
+ super
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require 'delayed_job'
3
+ require 'delayed/backend/active_record'
4
+
5
+ Delayed::Worker.backend = :active_record
@@ -0,0 +1,17 @@
1
+ require 'generators/delayed_job/delayed_job_generator'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record/migration'
4
+
5
+ # Extend the DelayedJobGenerator so that it creates an AR migration
6
+ module DelayedJob
7
+ class ActiveRecordGenerator < ::DelayedJobGenerator
8
+ include Rails::Generators::Migration
9
+ extend ActiveRecord::Generators::Migration
10
+
11
+ self.source_paths << File.join(File.dirname(__FILE__), 'templates')
12
+
13
+ def create_migration_file
14
+ migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ class CreateDelayedJobs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :delayed_jobs, :force => true do |table|
4
+ table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
5
+ table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
6
+ table.text :handler # YAML-encoded string of the object that will do work
7
+ table.text :last_error # reason for last failure (See Note below)
8
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
9
+ table.datetime :locked_at # Set when a client is working on this object
10
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
11
+ table.string :locked_by # Who is working on this object (if locked)
12
+ table.string :queue # The name of the queue this job is in
13
+ table.string :unique_key # The unique key prevents creating new jos if exists one with this key and was not running
14
+ table.timestamps
15
+ end
16
+
17
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
18
+ add_index :delayed_jobs, [:unique_key, :locked_at], :name => 'delayed_jobs_unique_key'
19
+ end
20
+
21
+ def self.down
22
+ drop_table :delayed_jobs
23
+ end
24
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,8 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+
5
+ mysql:
6
+ adapter: mysql
7
+ database: delayed_job
8
+ uesrname: root
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'delayed/backend/active_record'
3
+
4
+ describe Delayed::Backend::ActiveRecord::Job do
5
+ after do
6
+ Time.zone = nil
7
+ end
8
+
9
+ it_should_behave_like 'a delayed_job backend'
10
+
11
+ context "db_time_now" do
12
+ it "should return time in current time zone if set" do
13
+ Time.zone = 'Eastern Time (US & Canada)'
14
+ %w(EST EDT).should include(Delayed::Job.db_time_now.zone)
15
+ end
16
+
17
+ it "should return UTC time if that is the AR default" do
18
+ Time.zone = nil
19
+ ActiveRecord::Base.default_timezone = :utc
20
+ Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC'
21
+ end
22
+
23
+ it "should return local time if that is the AR default" do
24
+ Time.zone = 'Central Time (US & Canada)'
25
+ ActiveRecord::Base.default_timezone = :local
26
+ %w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)
27
+ end
28
+ end
29
+
30
+ describe "after_fork" do
31
+ it "should call reconnect on the connection" do
32
+ ActiveRecord::Base.should_receive(:establish_connection)
33
+ Delayed::Backend::ActiveRecord::Job.after_fork
34
+ end
35
+ end
36
+
37
+ describe "enqueue" do
38
+ it "should allow enqueue hook to modify job at DB level" do
39
+ later = described_class.db_time_now + 20.minutes
40
+ job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new
41
+ Delayed::Backend::ActiveRecord::Job.find(job.id).run_at.should be_within(1).of(later)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecord do
4
+ it 'should load classes with non-default primary key' do
5
+ lambda {
6
+ YAML.load(Story.create.to_yaml)
7
+ }.should_not raise_error
8
+ end
9
+
10
+ it 'should load classes even if not in default scope' do
11
+ lambda {
12
+ YAML.load(Story.create(:scoped => false).to_yaml)
13
+ }.should_not raise_error
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'rspec'
6
+ require 'logger'
7
+
8
+ require 'delayed_job_active_record'
9
+ require 'delayed/backend/shared_spec'
10
+
11
+ Delayed::Worker.logger = Logger.new('/tmp/dj.log')
12
+ ENV['RAILS_ENV'] = 'test'
13
+
14
+ config = YAML.load(File.read('spec/database.yml'))
15
+ # ActiveRecord::Base.configurations = {'test' => config['sqlite']}
16
+ ActiveRecord::Base.establish_connection config['sqlite']
17
+ ActiveRecord::Base.logger = Delayed::Worker.logger
18
+ ActiveRecord::Migration.verbose = false
19
+
20
+ ActiveRecord::Schema.define do
21
+ create_table :delayed_jobs, :force => true do |table|
22
+ table.integer :priority, :default => 0
23
+ table.integer :attempts, :default => 0
24
+ table.text :handler
25
+ table.text :last_error
26
+ table.datetime :run_at
27
+ table.datetime :locked_at
28
+ table.datetime :failed_at
29
+ table.string :locked_by
30
+ table.string :queue
31
+ table.string :unique_key
32
+ table.timestamps
33
+ end
34
+
35
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
36
+ add_index :delayed_jobs, [:unique_key, :locked_at], :name => 'delayed_jobs_unique_key'
37
+
38
+ create_table :stories, :primary_key => :story_id, :force => true do |table|
39
+ table.string :text
40
+ table.boolean :scoped, :default => true
41
+ end
42
+ end
43
+
44
+ # Purely useful for test cases...
45
+ class Story < ActiveRecord::Base
46
+ set_primary_key :story_id
47
+ def tell; text; end
48
+ def whatever(n, _); tell*n; end
49
+ default_scope where(:scoped => true)
50
+
51
+ handle_asynchronously :whatever
52
+ end
53
+
54
+ # Add this directory so the ActiveSupport autoloading works
55
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delayed_job_unique_key_active_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Griffin
9
+ - Oleg Muntyan
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-22 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ requirement: &86291980 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>'
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *86291980
26
+ - !ruby/object:Gem::Dependency
27
+ name: delayed_job_unique_key
28
+ requirement: &86291740 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - =
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *86291740
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ requirement: &86291510 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: '2.0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *86291510
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: &86291280 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '0.8'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *86291280
59
+ - !ruby/object:Gem::Dependency
60
+ name: sqlite3
61
+ requirement: &86291090 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *86291090
70
+ description: ActiveRecord backend for DelayedJobUniqueKey, originally authored by
71
+ Tobias Luetke
72
+ email:
73
+ - matt@griffinonline.org
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files:
77
+ - README.md
78
+ files:
79
+ - lib/delayed/backend/active_record.rb
80
+ - lib/generators/delayed_job/active_record_generator.rb
81
+ - lib/generators/delayed_job/templates/migration.rb
82
+ - lib/delayed_job_active_record.rb
83
+ - spec/delayed/serialization/active_record_spec.rb
84
+ - spec/delayed/backend/active_record_spec.rb
85
+ - spec/database.yml
86
+ - spec/spec_helper.rb
87
+ - LICENSE
88
+ - README.md
89
+ homepage: http://github.com/collectiveidea/delayed_job_active_record
90
+ licenses: []
91
+ post_install_message:
92
+ rdoc_options:
93
+ - --main
94
+ - README.md
95
+ - --inline-source
96
+ - --line-numbers
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 1.8.10
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: ActiveRecord backend for DelayedJobUniqueKey
117
+ test_files:
118
+ - spec/delayed/serialization/active_record_spec.rb
119
+ - spec/delayed/backend/active_record_spec.rb
120
+ - spec/database.yml
121
+ - spec/spec_helper.rb