talentbox-delayed_job_sequel 0.1.0

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) 2012 TalentBox SA
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,33 @@
1
+ # DelayedJob Sequel Backend
2
+
3
+ ## Installation
4
+
5
+ Add the gem to your Gemfile:
6
+
7
+ gem 'delayed_job_sequel'
8
+
9
+ Run `bundle install`.
10
+
11
+ If you're using Rails, run the generator to create the migration for the delayed_job table.
12
+
13
+ rails g delayed_job:sequel
14
+ rake db:migrate
15
+
16
+ ## Build Status
17
+
18
+ [![Build Status](http://travis-ci.org/TalentBox/delayed_job_sequel.png)](https://travis-ci.org/TalentBox/delayed_job_sequel)
19
+
20
+ ## How to contribute
21
+
22
+ If you find what looks like a bug:
23
+
24
+ * Search the [mailing list](http://groups.google.com/group/delayed_job) to see if anyone else had the same issue.
25
+ * Check the [GitHub issue tracker](http://github.com/TalentBox/delayed_job_sequel/issues/) to see if anyone else has reported issue.
26
+ * If you don't see anything, create an issue with information on how to reproduce it.
27
+
28
+ If you want to contribute an enhancement or a fix:
29
+
30
+ * Fork the project on github.
31
+ * Make your changes with tests.
32
+ * Commit the changes without making changes to the Rakefile or any other files that aren't related to your enhancement or fix
33
+ * Send a pull request.
@@ -0,0 +1,131 @@
1
+ require 'sequel'
2
+ module Delayed
3
+ module Backend
4
+ module Sequel
5
+ # A job object that is persisted to the database.
6
+ # Contains the work object as a YAML field.
7
+ class Job < ::Sequel::Model(:delayed_jobs)
8
+ include Delayed::Backend::Base
9
+ plugin :timestamps
10
+
11
+ def before_save
12
+ super
13
+ set_default_run_at
14
+ end
15
+
16
+ def_dataset_method :ready_to_run do |worker_name, max_run_time|
17
+ db_time_now = model.db_time_now
18
+ lock_upper_bound = db_time_now - max_run_time
19
+ filter do
20
+ ( (run_at <= db_time_now) &
21
+ ({:locked_at => nil} | (locked_at < lock_upper_bound)) |
22
+ {:locked_by => worker_name}
23
+ ) & {:failed_at => nil}
24
+ end
25
+ end
26
+
27
+ def_dataset_method :by_priority do
28
+ order(:priority.asc, :run_at.asc)
29
+ end
30
+
31
+ def self.before_fork
32
+ ::Sequel::Model.db.disconnect
33
+ end
34
+
35
+ # When a worker is exiting, make sure we don't have any locked jobs.
36
+ def self.clear_locks!(worker_name)
37
+ filter(:locked_by => worker_name).update(:locked_by => nil, :locked_at => nil)
38
+ end
39
+
40
+ # Find a few candidate jobs to run (in case some immediately get locked by others).
41
+ def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
42
+ set = ready_to_run(worker_name, max_run_time)
43
+ set = set.filter("priority >= ?", Worker.min_priority) if Worker.min_priority
44
+ set = set.filter("priority <= ?", Worker.max_priority) if Worker.max_priority
45
+ set = set.filter(:queue => Worker.queues) if Worker.queues.any?
46
+
47
+ silence_log do
48
+ set.by_priority.limit(limit).all
49
+ end
50
+ end
51
+
52
+ # Lock this job for this worker.
53
+ # Returns true if we have the lock, false otherwise.
54
+ def lock_exclusively!(max_run_time, worker)
55
+ now = self.class.db_time_now
56
+ affected_rows = if locked_by != worker
57
+ # We don't own this job so we will update the locked_by name and the locked_at
58
+ lock_upper_bound = now - max_run_time.to_i
59
+ closure_pk = pk
60
+ self.class.filter{
61
+ {:id => closure_pk} &
62
+ ({:locked_at => nil} | (locked_at < lock_upper_bound)) &
63
+ (run_at <= now)
64
+ }.update(:locked_at => now, :locked_by => worker)
65
+ else
66
+ # We already own this job, this may happen if the job queue crashes.
67
+ # Simply resume and update the locked_at
68
+ self.class.filter(:id => id, :locked_by => worker).update(:locked_at => now)
69
+ end
70
+ if affected_rows == 1
71
+ self.locked_at = now
72
+ self.locked_by = worker
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 ::Sequel.database_timezone == :utc
86
+ Time.now.utc
87
+ else
88
+ Time.now
89
+ end
90
+ end
91
+
92
+ def self.delete_all
93
+ delete
94
+ end
95
+
96
+ def reload(*args)
97
+ reset
98
+ super
99
+ end
100
+
101
+ def save!
102
+ save :raise_on_failure => true
103
+ end
104
+
105
+ def update_attributes(attrs)
106
+ update attrs
107
+ end
108
+
109
+ def self.create!(attrs)
110
+ new(attrs).save :raise_on_failure => true
111
+ end
112
+
113
+ def self.silence_log(&block)
114
+ if db.respond_to?(:logger) && db.logger.respond_to?(:silence)
115
+ db.logger.silence &block
116
+ else
117
+ yield
118
+ end
119
+ end
120
+
121
+ # The default behaviour for sequel on #==/#eql? is to check if all
122
+ # values are matching.
123
+ # This differs from ActiveRecord which checks class and id only.
124
+ # To pass the specs we're reverting to what AR does here.
125
+ def eql?(obj)
126
+ (obj.class == model) && (obj.pk == pk)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,50 @@
1
+ require "sequel/model"
2
+
3
+ module Delayed
4
+ module Serialization
5
+ module Sequel
6
+ def self.configure(klass)
7
+ klass.class_eval do
8
+ def self.inherited(child)
9
+ super
10
+ if YAML.parser.class.name =~ /syck/i
11
+ child.yaml_as "tag:ruby.yaml.org,2002:#{child.name}"
12
+ else
13
+ child.yaml_as ['!ruby/Sequel', child.name].join(':')
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ if YAML.parser.class.name =~ /syck/i
20
+ module ClassMethods
21
+ def yaml_new(klass, tag, val)
22
+ klass[val['values'][klass.primary_key]] ||
23
+ raise(Delayed::DeserializationError, "Sequel Record not found, class: #{klass} , primary key: #{val['values'][klass.primary_key]}")
24
+ end
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ if YAML.parser.class.name =~ /syck/i
30
+ def to_yaml_properties
31
+ ['@values']
32
+ end
33
+ else
34
+ def encode_with(coder)
35
+ coder["values"] = @values
36
+ coder.tag = ['!ruby/Sequel', self.class.name].join(':')
37
+ end
38
+
39
+ def init_with(coder)
40
+ @values = coder["values"]
41
+ reload
42
+ rescue Sequel::Error
43
+ raise Delayed::DeserializationError, "Sequel Record not found, class: #{self.class.name} , primary key: #{pk}"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ Sequel::Model.plugin Delayed::Serialization::Sequel
@@ -0,0 +1,5 @@
1
+ require 'sequel'
2
+ require 'delayed_job'
3
+ require 'delayed/backend/sequel'
4
+
5
+ Delayed::Worker.backend = :sequel
@@ -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,22 @@
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.timestamps
14
+ end
15
+
16
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
17
+ end
18
+
19
+ def self.down
20
+ drop_table :delayed_jobs
21
+ end
22
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,4 @@
1
+ sqlite:
2
+ adapter: sqlite
3
+ database: ":memory:"
4
+
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+ require "delayed/backend/sequel"
3
+
4
+ describe Delayed::Backend::Sequel::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 Sequel.database_timezone default" do
18
+ Time.zone = nil
19
+ Sequel.database_timezone = :utc
20
+ Delayed::Backend::Sequel::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
+ Sequel.database_timezone = :local
26
+ %w(CST CDT).should include(Delayed::Backend::Sequel::Job.db_time_now.zone)
27
+ end
28
+ end
29
+
30
+ describe "before_fork" do
31
+ it "should call disconnect on the connection" do
32
+ Sequel::Model.db.should_receive(:disconnect)
33
+ Delayed::Backend::Sequel::Job.before_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::Sequel::Job.enqueue :payload_object => EnqueueJobMod.new
41
+ Delayed::Backend::Sequel::Job[job.id].run_at.should be_within(1).of(later)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sequel::Model 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
+ end
@@ -0,0 +1,58 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
2
+
3
+ require "rubygems"
4
+ require "bundler/setup"
5
+ require "rspec"
6
+ require "logger"
7
+ require "sqlite3"
8
+ require "sequel"
9
+
10
+ config = YAML.load(File.read("spec/database.yml"))
11
+ DB = Sequel.connect config["sqlite"]
12
+
13
+ Sequel.extension :migration
14
+ Class.new(Sequel::Migration) do
15
+ def up
16
+ create_table :delayed_jobs do
17
+ primary_key :id
18
+ Integer :priority, :default => 0
19
+ Integer :attempts, :default => 0
20
+ String :handler, :text => true
21
+ String :last_error, :text => true
22
+ column :run_at, :datetime#"timestamp(0)"
23
+ column :locked_at, :datetime#"timestamp(0)"
24
+ column :failed_at, :datetime#"timestamp(0)"
25
+ String :locked_by
26
+ String :queue
27
+ Time :created_at
28
+ Time :updated_at
29
+ index [:priority, :run_at]
30
+ end
31
+
32
+ create_table :stories do
33
+ primary_key :story_id
34
+ String :text
35
+ TrueClass :scoped, :default => true
36
+ end
37
+ end
38
+ end.apply(DB, :up)
39
+
40
+ require "delayed_job_sequel"
41
+ require "delayed/backend/shared_spec"
42
+
43
+ Delayed::Worker.logger = Logger.new("/tmp/dj.log")
44
+ ENV["RAILS_ENV"] = "test"
45
+ DB.logger = Delayed::Worker.logger
46
+
47
+ # Purely useful for test cases...
48
+ class Story < Sequel::Model
49
+ def tell; text; end
50
+ def whatever(n, _); tell*n; end
51
+ def update_attributes(*args)
52
+ update *args
53
+ end
54
+ handle_asynchronously :whatever
55
+ end
56
+
57
+ # Add this directory so the ActiveSupport autoloading works
58
+ # ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: talentbox-delayed_job_sequel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonathan Tron
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sequel
16
+ requirement: &2161185360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.28'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2161185360
25
+ - !ruby/object:Gem::Dependency
26
+ name: delayed_job
27
+ requirement: &2160614300 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2160614300
36
+ - !ruby/object:Gem::Dependency
37
+ name: tzinfo
38
+ requirement: &2160611720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2160611720
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &2160609540 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2160609540
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: &2161543600 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '0.8'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2161543600
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: &2161541640 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2161541640
80
+ description: Sequel backend for DelayedJob, originally authored by Tobias Luetke
81
+ email:
82
+ - jonathan@tron.name
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files:
86
+ - README.md
87
+ files:
88
+ - lib/delayed/backend/sequel.rb
89
+ - lib/delayed/serialization/sequel.rb
90
+ - lib/delayed_job_sequel.rb
91
+ - lib/generators/delayed_job/sequel_generator.rb
92
+ - lib/generators/delayed_job/templates/migration.rb
93
+ - spec/database.yml
94
+ - spec/delayed/backend/sequel_spec.rb
95
+ - spec/delayed/serialization/sequel_spec.rb
96
+ - spec/spec_helper.rb
97
+ - LICENSE
98
+ - README.md
99
+ homepage: http://github.com/TalentBox/delayed_job_sequel
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options:
103
+ - --main
104
+ - README.md
105
+ - --inline-source
106
+ - --line-numbers
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: -179484122693818536
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ segments:
125
+ - 0
126
+ hash: -179484122693818536
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 1.8.10
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Sequel backend for DelayedJob
133
+ test_files:
134
+ - spec/database.yml
135
+ - spec/delayed/backend/sequel_spec.rb
136
+ - spec/delayed/serialization/sequel_spec.rb
137
+ - spec/spec_helper.rb