delayed_job_unique_key_active_record 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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