fleiss 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/fleiss.gemspec +1 -1
- data/lib/fleiss/backend/active_record/concern.rb +31 -15
- data/lib/fleiss/cli.rb +1 -12
- data/lib/fleiss/worker.rb +25 -31
- data/spec/fleiss/backend/active_record_spec.rb +12 -7
- data/spec/fleiss/worker_spec.rb +10 -10
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92e4e656c34af27e17ac918eaa0d21f5d01f31f3d5f04f4c08290ce2ebb49035
|
4
|
+
data.tar.gz: d80b83c7dcb419ff05bcee827b8e7ba314be13c7845d658a86e5142d13c34a84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bd0fc7a843d98020b0491fb5a1c457315f5589823b87d43ffebcce07933d548421ee9f3c705d9090d81ccc20319d363aba789559122a5fb54e65751a773c84a
|
7
|
+
data.tar.gz: 44fea18168d2e79bf9d0eec495a010f85edf313c543a1a271b5090132806c2c03a7cc653c77949a4ffe26cf46d88383192847d8d90565e37b3294a6e751494f1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fleiss (0.1.
|
4
|
+
fleiss (0.1.2)
|
5
5
|
activejob (>= 5.0)
|
6
6
|
activerecord (>= 5.0)
|
7
7
|
concurrent-ruby
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
pg (1.1.3)
|
41
41
|
powerpack (0.1.2)
|
42
42
|
rainbow (3.0.0)
|
43
|
-
rake (12.3.
|
43
|
+
rake (12.3.2)
|
44
44
|
rspec (3.8.0)
|
45
45
|
rspec-core (~> 3.8.0)
|
46
46
|
rspec-expectations (~> 3.8.0)
|
data/fleiss.gemspec
CHANGED
@@ -6,6 +6,7 @@ module Fleiss
|
|
6
6
|
|
7
7
|
included do
|
8
8
|
scope :in_queue, ->(qs) { where(queue_name: Array.wrap(qs)) }
|
9
|
+
scope :finished, -> { where.not(finished_at: nil) }
|
9
10
|
scope :not_finished, -> { where(finished_at: nil) }
|
10
11
|
scope :not_expired, ->(now=Time.zone.now) { where(arel_table[:expires_at].eq(nil).or(arel_table[:expires_at].gt(now))) }
|
11
12
|
scope :started, -> { where(arel_table[:started_at].not_eq(nil)) }
|
@@ -29,15 +30,6 @@ module Fleiss
|
|
29
30
|
started.not_finished.where(owner: owner)
|
30
31
|
end
|
31
32
|
|
32
|
-
# Reschedules jobs to run again.
|
33
|
-
def reschedule_all(at=Time.zone.now)
|
34
|
-
update_all(
|
35
|
-
started_at: nil,
|
36
|
-
owner: nil,
|
37
|
-
scheduled_at: at,
|
38
|
-
)
|
39
|
-
end
|
40
|
-
|
41
33
|
# @param [ActiveJob::Base] job the job instance
|
42
34
|
# @option [Time] :scheduled_at schedule job at
|
43
35
|
def enqueue(job, scheduled_at: nil)
|
@@ -68,23 +60,47 @@ module Fleiss
|
|
68
60
|
# @param [String] owner
|
69
61
|
# @return [Boolean] true if job was started.
|
70
62
|
def start(owner, now: Time.zone.now)
|
71
|
-
|
63
|
+
with_isolation do
|
72
64
|
self.class.pending(now)
|
73
65
|
.where(id: id)
|
74
|
-
.update_all(started_at: now, owner: owner)
|
75
|
-
end
|
66
|
+
.update_all(started_at: now, owner: owner)
|
67
|
+
end == 1
|
68
|
+
rescue ::ActiveRecord::SerializationFailure
|
69
|
+
false
|
76
70
|
end
|
77
71
|
|
78
72
|
# Marks a job as finished.
|
79
73
|
# @param [String] owner
|
80
74
|
# @return [Boolean] true if successful.
|
81
75
|
def finish(owner, now: Time.zone.now)
|
82
|
-
|
76
|
+
with_isolation do
|
83
77
|
self.class
|
84
78
|
.in_progress(owner)
|
85
79
|
.where(id: id)
|
86
|
-
.update_all(finished_at: now)
|
87
|
-
end
|
80
|
+
.update_all(finished_at: now)
|
81
|
+
end == 1
|
82
|
+
rescue ::ActiveRecord::SerializationFailure
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
# Reschedules the job to run again.
|
87
|
+
def reschedule(owner, now: Time.zone.now)
|
88
|
+
with_isolation do
|
89
|
+
self.class
|
90
|
+
.in_progress(owner)
|
91
|
+
.where(id: id)
|
92
|
+
.update_all(started_at: nil, owner: nil, scheduled_at: now)
|
93
|
+
end == 1
|
94
|
+
rescue ::ActiveRecord::SerializationFailure
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def with_isolation(&block)
|
101
|
+
return yield unless self.class.connection.supports_transaction_isolation?
|
102
|
+
|
103
|
+
self.class.transaction(isolation: :repeatable_read, &block)
|
88
104
|
end
|
89
105
|
end
|
90
106
|
end
|
data/lib/fleiss/cli.rb
CHANGED
@@ -42,26 +42,15 @@ module Fleiss
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def run!
|
45
|
-
return if @worker
|
46
|
-
|
47
45
|
$LOAD_PATH.concat opts[:include]
|
48
46
|
opts[:require].each {|n| require n }
|
49
47
|
require 'fleiss/worker'
|
50
48
|
|
51
|
-
|
52
|
-
Signal.trap('INT') { shutdown }
|
53
|
-
|
54
|
-
@worker = Fleiss::Worker.new \
|
49
|
+
Fleiss::Worker.run \
|
55
50
|
queues: opts[:queues],
|
56
51
|
concurrency: opts[:concurrency],
|
57
52
|
wait_time: opts[:wait_time],
|
58
53
|
logger: Logger.new(opts[:logfile])
|
59
|
-
@worker.run
|
60
|
-
@worker.wait
|
61
|
-
end
|
62
|
-
|
63
|
-
def shutdown
|
64
|
-
@worker.shutdown if @worker
|
65
54
|
end
|
66
55
|
|
67
56
|
def parser # rubocop:disable Metrics/MethodLength
|
data/lib/fleiss/worker.rb
CHANGED
@@ -7,6 +7,11 @@ require 'securerandom'
|
|
7
7
|
class Fleiss::Worker
|
8
8
|
attr_reader :queues, :uuid, :wait_time, :logger
|
9
9
|
|
10
|
+
# Shortcut for new(*args).run
|
11
|
+
def self.run(*args)
|
12
|
+
new(*args).run
|
13
|
+
end
|
14
|
+
|
10
15
|
# Init a new worker instance
|
11
16
|
# @param [ConnectionPool] disque client connection pool
|
12
17
|
# @param [Hash] options
|
@@ -27,36 +32,20 @@ class Fleiss::Worker
|
|
27
32
|
logger.info "Worker #{uuid} starting - queues: #{queues.inspect}, concurrency: #{@pool.max_length}"
|
28
33
|
loop do
|
29
34
|
run_cycle
|
30
|
-
break if @stopped
|
31
|
-
|
32
35
|
sleep @wait_time
|
33
36
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# Blocks until worker until it's stopped.
|
38
|
-
def wait
|
37
|
+
rescue SignalException => e
|
38
|
+
logger.info "Worker #{uuid} received #{e.message}. Shutting down..."
|
39
|
+
ensure
|
39
40
|
@pool.shutdown
|
40
|
-
@pool.wait_for_termination(1)
|
41
|
-
|
42
|
-
Fleiss.backend
|
43
|
-
.in_queue(queues)
|
44
|
-
.in_progress(uuid)
|
45
|
-
.reschedule_all(10.seconds.from_now)
|
46
41
|
@pool.wait_for_termination
|
47
|
-
logger.info "Worker #{uuid} shutdown complete"
|
48
|
-
rescue StandardError => e
|
49
|
-
handle_exception e, 'shutdown'
|
50
|
-
end
|
51
|
-
|
52
|
-
# Initiates the shutdown process
|
53
|
-
def shutdown
|
54
|
-
@stopped = true
|
55
42
|
end
|
56
43
|
|
57
44
|
private
|
58
45
|
|
59
46
|
def run_cycle
|
47
|
+
return if @pool.shuttingdown?
|
48
|
+
|
60
49
|
capacity = @pool.max_length - @pool.scheduled_task_count + @pool.completed_task_count
|
61
50
|
return unless capacity.positive?
|
62
51
|
|
@@ -67,8 +56,6 @@ class Fleiss::Worker
|
|
67
56
|
.to_a
|
68
57
|
|
69
58
|
batch.each do |job|
|
70
|
-
break if @stopped
|
71
|
-
|
72
59
|
@pool.post { perform(job) }
|
73
60
|
end
|
74
61
|
rescue StandardError => e
|
@@ -76,16 +63,23 @@ class Fleiss::Worker
|
|
76
63
|
end
|
77
64
|
|
78
65
|
def perform(job)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
66
|
+
thread_id = Thread.current.object_id.to_s(16).reverse
|
67
|
+
owner = "#{uuid}/#{thread_id}"
|
68
|
+
return unless job.start(owner)
|
69
|
+
|
70
|
+
logger.info { "Worker #{uuid} execute job ##{job.id} (by thread #{thread_id})" }
|
71
|
+
finished = false
|
72
|
+
begin
|
73
|
+
ActiveJob::Base.execute job.job_data
|
74
|
+
finished = true
|
75
|
+
rescue StandardError
|
76
|
+
finished = true
|
77
|
+
raise
|
78
|
+
ensure
|
79
|
+
finished ? job.finish(owner) : job.reschedule(owner)
|
80
|
+
end
|
85
81
|
rescue StandardError => e
|
86
82
|
handle_exception e, "processing job ##{job.id} (by thread #{thread_id})"
|
87
|
-
ensure
|
88
|
-
job.finish(uuid)
|
89
83
|
end
|
90
84
|
|
91
85
|
def handle_exception(err, intro)
|
@@ -46,13 +46,6 @@ RSpec.describe Fleiss::Backend::ActiveRecord do
|
|
46
46
|
expect(rec.job_id.size).to eq(36)
|
47
47
|
end
|
48
48
|
|
49
|
-
it 'should reschedule' do
|
50
|
-
job = TestJob.perform_later
|
51
|
-
described_class.reschedule_all(1.hour.from_now)
|
52
|
-
rec = retrieve(job)
|
53
|
-
expect(rec.scheduled_at).to be_within(2.seconds).of(1.hour.from_now)
|
54
|
-
end
|
55
|
-
|
56
49
|
it 'should scope pending' do
|
57
50
|
j1 = TestJob.perform_later
|
58
51
|
expect(retrieve(j1).start('owner')).to be_truthy
|
@@ -118,4 +111,16 @@ RSpec.describe Fleiss::Backend::ActiveRecord do
|
|
118
111
|
expect(rec.started_at).to be_within(2.seconds).of(Time.zone.now)
|
119
112
|
expect(rec.finished_at).to be_within(2.seconds).of(Time.zone.now)
|
120
113
|
end
|
114
|
+
|
115
|
+
it 'should reschedule' do
|
116
|
+
job = TestJob.perform_later
|
117
|
+
rec = retrieve(job)
|
118
|
+
expect(rec.reschedule('owner')).to be_falsey
|
119
|
+
expect(rec.start('owner')).to be_truthy
|
120
|
+
expect(rec.reschedule('other')).to be_falsey
|
121
|
+
expect(rec.reschedule('owner')).to be_truthy
|
122
|
+
expect(rec.reload.owner).to be_nil
|
123
|
+
expect(rec.started_at).to be_nil
|
124
|
+
expect(rec.scheduled_at).to be_within(2.seconds).of(Time.zone.now)
|
125
|
+
end
|
121
126
|
end
|
data/spec/fleiss/worker_spec.rb
CHANGED
@@ -13,12 +13,11 @@ RSpec.describe Fleiss::Worker do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
after do
|
16
|
-
|
17
|
-
subject.wait
|
16
|
+
runner.kill
|
18
17
|
end
|
19
18
|
|
20
19
|
def wait_for
|
21
|
-
|
20
|
+
100.times do
|
22
21
|
break if yield
|
23
22
|
|
24
23
|
sleep(0.1)
|
@@ -26,22 +25,23 @@ RSpec.describe Fleiss::Worker do
|
|
26
25
|
expect(yield).to be_truthy
|
27
26
|
end
|
28
27
|
|
29
|
-
it 'should run
|
30
|
-
# seed
|
31
|
-
|
32
|
-
wait_for { Fleiss.backend.not_finished.count
|
28
|
+
it 'should run' do
|
29
|
+
# seed 24 jobs
|
30
|
+
24.times {|n| TestJob.perform_later(n) }
|
31
|
+
wait_for { Fleiss.backend.not_finished.count.positive? }
|
33
32
|
|
34
33
|
# ensure runner processes them all
|
35
34
|
wait_for { Fleiss.backend.not_finished.count.zero? }
|
36
35
|
|
37
36
|
# check what's been performed
|
38
|
-
expect(TestJob.performed.size).to eq(
|
39
|
-
expect(
|
37
|
+
expect(TestJob.performed.size).to eq(24)
|
38
|
+
expect(Fleiss.backend.finished.count).to eq(24)
|
39
|
+
expect(TestJob.performed).to match_array(0..23)
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'should handle failing jobs' do
|
43
43
|
TestJob.perform_later('raise')
|
44
44
|
wait_for { Fleiss.backend.not_finished.count.zero? }
|
45
|
-
expect(Fleiss.backend.count).to eq(1)
|
45
|
+
expect(Fleiss.backend.finished.count).to eq(1)
|
46
46
|
end
|
47
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fleiss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Black Square Media Ltd
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -201,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
201
|
version: '0'
|
202
202
|
requirements: []
|
203
203
|
rubyforge_project:
|
204
|
-
rubygems_version: 2.7.
|
204
|
+
rubygems_version: 2.7.6
|
205
205
|
signing_key:
|
206
206
|
specification_version: 4
|
207
207
|
summary: Minimialist background jobs backed by ActiveJob and ActiveRecord.
|