talentbox-delayed_job_sequel 0.1.0

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) 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