faktory_worker_ruby 0.7.0 → 1.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.
- checksums.yaml +5 -5
- data/.travis.yml +11 -0
- data/Changes.md +27 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +34 -8
- data/README.md +10 -5
- data/faktory_worker_ruby.gemspec +4 -3
- data/lib/active_job/queue_adapters/faktory_adapter.rb +61 -0
- data/lib/faktory.rb +3 -1
- data/lib/faktory/batch.rb +178 -0
- data/lib/faktory/cli.rb +9 -0
- data/lib/faktory/client.rb +158 -35
- data/lib/faktory/connection.rb +1 -1
- data/lib/faktory/io.rb +51 -0
- data/lib/faktory/job.rb +41 -29
- data/lib/faktory/launcher.rb +20 -5
- data/lib/faktory/logging.rb +2 -2
- data/lib/faktory/manager.rb +3 -0
- data/lib/faktory/middleware/batch.rb +38 -0
- data/lib/faktory/middleware/i18n.rb +2 -4
- data/lib/faktory/mutate.rb +85 -0
- data/lib/faktory/processor.rb +6 -4
- data/lib/faktory/rails.rb +31 -0
- data/lib/faktory/testing.rb +3 -7
- data/lib/faktory/tracking.rb +41 -0
- data/lib/faktory/version.rb +1 -1
- metadata +34 -14
data/lib/faktory/launcher.rb
CHANGED
@@ -11,7 +11,7 @@ module Faktory
|
|
11
11
|
def initialize(options)
|
12
12
|
merged_options = Faktory.options.merge(options)
|
13
13
|
@manager = Faktory::Manager.new(merged_options)
|
14
|
-
@
|
14
|
+
@current_state = nil
|
15
15
|
@options = merged_options
|
16
16
|
end
|
17
17
|
|
@@ -22,7 +22,7 @@ module Faktory
|
|
22
22
|
|
23
23
|
# Stops this instance from processing any more jobs,
|
24
24
|
def quiet
|
25
|
-
@
|
25
|
+
@current_state = 'quiet'
|
26
26
|
@manager.quiet
|
27
27
|
end
|
28
28
|
|
@@ -32,13 +32,17 @@ module Faktory
|
|
32
32
|
def stop
|
33
33
|
deadline = Time.now + @options[:timeout]
|
34
34
|
|
35
|
-
@
|
35
|
+
@current_state = 'terminate'
|
36
36
|
@manager.quiet
|
37
37
|
@manager.stop(deadline)
|
38
38
|
end
|
39
39
|
|
40
40
|
def stopping?
|
41
|
-
@
|
41
|
+
@current_state == 'terminate'
|
42
|
+
end
|
43
|
+
|
44
|
+
def quiet?
|
45
|
+
@current_state == 'quiet'
|
42
46
|
end
|
43
47
|
|
44
48
|
PROCTITLES = []
|
@@ -50,12 +54,23 @@ module Faktory
|
|
50
54
|
PROCTITLES << proc { title }
|
51
55
|
PROCTITLES << proc { "[#{Processor.busy_count} of #{@options[:concurrency]} busy]" }
|
52
56
|
PROCTITLES << proc { "stopping" if stopping? }
|
57
|
+
PROCTITLES << proc { "quiet" if quiet? }
|
53
58
|
|
54
59
|
loop do
|
55
60
|
$0 = PROCTITLES.map {|p| p.call }.join(" ")
|
56
61
|
|
57
62
|
begin
|
58
|
-
Faktory.server {|c| c.beat }
|
63
|
+
result = Faktory.server {|c| c.beat(@current_state) }
|
64
|
+
case result
|
65
|
+
when "OK"
|
66
|
+
# all good
|
67
|
+
when "terminate"
|
68
|
+
::Process.kill('TERM', $$)
|
69
|
+
when "quiet"
|
70
|
+
::Process.kill('TSTP', $$)
|
71
|
+
else
|
72
|
+
Faktory.logger.warn "Got unexpected BEAT: #{result}"
|
73
|
+
end
|
59
74
|
rescue => ex
|
60
75
|
# best effort, try again in a few secs
|
61
76
|
end
|
data/lib/faktory/logging.rb
CHANGED
@@ -29,8 +29,8 @@ module Faktory
|
|
29
29
|
def self.job_hash_context(job_hash)
|
30
30
|
# If we're using a wrapper class, like ActiveJob, use the "wrapped"
|
31
31
|
# attribute to expose the underlying thing.
|
32
|
-
klass = job_hash
|
33
|
-
"#{klass} JID-#{job_hash['jid'
|
32
|
+
klass = job_hash.dig('custom', 'wrapped') || job_hash["jobtype"]
|
33
|
+
"#{klass} JID-#{job_hash['jid']}"
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.with_job_hash_context(job_hash, &block)
|
data/lib/faktory/manager.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Simple middleware to save the current batch and restore it when the job executes.
|
4
|
+
#
|
5
|
+
module Faktory::Middleware::Batch
|
6
|
+
class Client
|
7
|
+
def call(payload, pool)
|
8
|
+
b = Thread.current["faktory_batch"]
|
9
|
+
if b
|
10
|
+
payload["custom"] ||= {}
|
11
|
+
payload["custom"]["bid"] = b.bid
|
12
|
+
end
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Worker
|
18
|
+
def call(jobinst, payload)
|
19
|
+
jobinst.bid = payload.dig("custom", "bid")
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Faktory.configure_client do |config|
|
26
|
+
config.client_middleware do |chain|
|
27
|
+
chain.add Faktory::Middleware::Batch::Client
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Faktory.configure_worker do |config|
|
32
|
+
config.client_middleware do |chain|
|
33
|
+
chain.add Faktory::Middleware::Batch::Client
|
34
|
+
end
|
35
|
+
config.worker_middleware do |chain|
|
36
|
+
chain.add Faktory::Middleware::Batch::Worker
|
37
|
+
end
|
38
|
+
end
|
@@ -19,10 +19,8 @@ module Faktory::Middleware::I18n
|
|
19
19
|
# Pull the msg locale out and set the current thread to use it.
|
20
20
|
class Worker
|
21
21
|
def call(jobinst, payload)
|
22
|
-
|
23
|
-
yield
|
24
|
-
ensure
|
25
|
-
I18n.locale = I18n.default_locale
|
22
|
+
locale = payload.dig("custom", "locale") || I18n.default_locale
|
23
|
+
I18n.with_locale(locale) { yield }
|
26
24
|
end
|
27
25
|
end
|
28
26
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'faktory/client'
|
2
|
+
|
3
|
+
# require 'faktory/mutate'
|
4
|
+
# cl = Faktory::Client.new
|
5
|
+
# cl.discard(Faktory::RETRIES) do |filter|
|
6
|
+
# filter.with_type("QuickBooksSyncJob")
|
7
|
+
# filter.matching("*uid:12345*"))
|
8
|
+
# end
|
9
|
+
module Faktory
|
10
|
+
|
11
|
+
# Valid targets
|
12
|
+
RETRIES = "retries"
|
13
|
+
SCHEDULED = "scheduled"
|
14
|
+
DEAD = "dead"
|
15
|
+
|
16
|
+
module Mutator
|
17
|
+
class Filter
|
18
|
+
attr_accessor :hash
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@hash = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# This must be the exact type of the job, no pattern matching
|
25
|
+
def with_type(jobtype)
|
26
|
+
@hash[:jobtype] = jobtype
|
27
|
+
end
|
28
|
+
|
29
|
+
# This is a regexp that will be passed as is to Redis's SCAN.
|
30
|
+
# Notably you should surround it with * to ensure it matches
|
31
|
+
# substrings within the job payload.
|
32
|
+
# See https://redis.io/commands/scan for details.
|
33
|
+
def matching(regexp)
|
34
|
+
@hash[:regexp] = regexp
|
35
|
+
end
|
36
|
+
|
37
|
+
# One or more JIDs to target:
|
38
|
+
# filter.jids << 'abcdefgh1234'
|
39
|
+
# filter.jids = ['abcdefgh1234', '1234567890']
|
40
|
+
def jids
|
41
|
+
@hash[:jids] ||= []
|
42
|
+
end
|
43
|
+
def jids=(ary)
|
44
|
+
@hash[:jids] = Array(ary)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def discard(target, &block)
|
49
|
+
filter = Filter.new
|
50
|
+
block.call(filter) if block
|
51
|
+
mutate('discard', target, filter)
|
52
|
+
end
|
53
|
+
|
54
|
+
def kill(target, &block)
|
55
|
+
filter = Filter.new
|
56
|
+
block.call(filter) if block
|
57
|
+
mutate('kill', target, filter)
|
58
|
+
end
|
59
|
+
|
60
|
+
def requeue(target, &block)
|
61
|
+
filter = Filter.new
|
62
|
+
block.call(filter) if block
|
63
|
+
mutate('requeue', target, filter)
|
64
|
+
end
|
65
|
+
|
66
|
+
def clear(target)
|
67
|
+
mutate('discard', target, nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def mutate(cmd, target, filter)
|
73
|
+
payload = {:cmd => cmd,:target => target}
|
74
|
+
payload[:filter] = filter.hash if filter && !filter.hash.empty?
|
75
|
+
|
76
|
+
transaction do
|
77
|
+
command("MUTATE", JSON.dump(payload))
|
78
|
+
ok
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Faktory::Client.send(:include, Faktory::Mutator)
|
data/lib/faktory/processor.rb
CHANGED
@@ -88,6 +88,7 @@ module Faktory
|
|
88
88
|
@@busy_count = @@busy_count + 1
|
89
89
|
end
|
90
90
|
begin
|
91
|
+
@job = work.job
|
91
92
|
process(work)
|
92
93
|
ensure
|
93
94
|
@@busy_lock.synchronize do
|
@@ -148,10 +149,11 @@ module Faktory
|
|
148
149
|
end
|
149
150
|
end
|
150
151
|
work.acknowledge
|
151
|
-
rescue Faktory::Shutdown
|
152
|
-
# Had to force kill this job because it didn't finish
|
153
|
-
#
|
154
|
-
#
|
152
|
+
rescue Faktory::Shutdown => shut
|
153
|
+
# Had to force kill this job because it didn't finish within
|
154
|
+
# the timeout. Fail it so we can release any locks server-side
|
155
|
+
# and immediately restart it.
|
156
|
+
work.fail(shut)
|
155
157
|
rescue Exception => ex
|
156
158
|
handle_exception(ex, { :context => "Job raised exception", :job => work.job })
|
157
159
|
work.fail(ex)
|
data/lib/faktory/rails.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Faktory
|
3
3
|
class Rails < ::Rails::Engine
|
4
|
+
# This hook happens after `Rails::Application` is inherited within
|
5
|
+
# config/application.rb and before config is touched, usually within the
|
6
|
+
# class block. Definitely before config/environments/*.rb and
|
7
|
+
# config/initializers/*.rb.
|
8
|
+
config.before_configuration do
|
9
|
+
if defined?(::ActiveJob)
|
10
|
+
require 'active_job/queue_adapters/faktory_adapter'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
4
14
|
config.after_initialize do
|
5
15
|
# This hook happens after all initializers are run, just before returning
|
6
16
|
# from config/environment.rb back to faktory/cli.rb.
|
@@ -10,8 +20,22 @@ module Faktory
|
|
10
20
|
# None of this matters on the client-side, only within the Faktory executor itself.
|
11
21
|
#
|
12
22
|
Faktory.configure_worker do |_|
|
23
|
+
if ::Rails::VERSION::MAJOR < 5
|
24
|
+
raise "Your current version of Rails, #{::Rails::VERSION::STRING}, is not supported"
|
25
|
+
end
|
26
|
+
|
13
27
|
Faktory.options[:reloader] = Faktory::Rails::Reloader.new
|
14
28
|
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
# https://github.com/rails/rails/pull/41248
|
32
|
+
if defined?(::Mail::SMTP)
|
33
|
+
::Mail::SMTP::DEFAULTS[:read_timeout] ||= 5
|
34
|
+
::Mail::SMTP::DEFAULTS[:open_timeout] ||= 5
|
35
|
+
end
|
36
|
+
rescue => ex
|
37
|
+
# ignore
|
38
|
+
end
|
15
39
|
end
|
16
40
|
|
17
41
|
class Reloader
|
@@ -30,4 +54,11 @@ module Faktory
|
|
30
54
|
end
|
31
55
|
end
|
32
56
|
end if defined?(::Rails)
|
57
|
+
|
58
|
+
if defined?(::Rails) && ::Rails::VERSION::MAJOR < 5
|
59
|
+
$stderr.puts("**************************************************")
|
60
|
+
$stderr.puts("🚫 ERROR: Faktory Worker does not support Rails versions under 5.x - please ensure your workers are updated")
|
61
|
+
$stderr.puts("**************************************************")
|
62
|
+
$stderr.puts("")
|
63
|
+
end
|
33
64
|
end
|
data/lib/faktory/testing.rb
CHANGED
@@ -31,7 +31,7 @@ module Faktory
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.inline!(&block)
|
34
|
-
# Only allow
|
34
|
+
# Only allow inline testing inside of a block
|
35
35
|
# https://github.com/mperham/sidekiq/issues/3495
|
36
36
|
unless block_given?
|
37
37
|
raise 'Must provide a block to Faktory::Testing.inline!'
|
@@ -40,10 +40,6 @@ module Faktory
|
|
40
40
|
__set_test_mode(:inline, &block)
|
41
41
|
end
|
42
42
|
|
43
|
-
def self.blockless_inline_is_a_bad_idea_but_I_wanna_do_it_anyway!
|
44
|
-
__set_test_mode(:inline)
|
45
|
-
end
|
46
|
-
|
47
43
|
def self.enabled?
|
48
44
|
self.__test_mode != :disable
|
49
45
|
end
|
@@ -98,9 +94,9 @@ module Faktory
|
|
98
94
|
end
|
99
95
|
end
|
100
96
|
|
101
|
-
def open
|
97
|
+
def open(*args)
|
102
98
|
unless Faktory::Testing.enabled?
|
103
|
-
real_open
|
99
|
+
real_open(*args)
|
104
100
|
end
|
105
101
|
end
|
106
102
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Faktory
|
2
|
+
module Trackable
|
3
|
+
|
4
|
+
##
|
5
|
+
# Tracking allows a long-running Faktory job to report its progress:
|
6
|
+
#
|
7
|
+
# def perform(...)
|
8
|
+
# track_progress(10, "Calculating values")
|
9
|
+
# # do some work
|
10
|
+
#
|
11
|
+
# track_progress(20, "Sending emails")
|
12
|
+
# # do some more work
|
13
|
+
#
|
14
|
+
# track_progress(20, "Sending emails", reserve_until: 10.minutes.from_now)
|
15
|
+
# # do some more work
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Note:
|
19
|
+
# 1. jobs should be small and fine-grained (and so fast) if possible.
|
20
|
+
# 2. tracking is useful for long-running jobs, tracking a fast job will only add overhead
|
21
|
+
# 3. tracking only works with a single job, use Batches to monitor a group of jobs
|
22
|
+
# 4. reserve_until allows a job to dynamically extend its reservation so it is not garbage collected by Faktory while running
|
23
|
+
# 5. you can only reserve up to 24 hours.
|
24
|
+
#
|
25
|
+
def track_progress(percent, desc=nil, reserve_until:nil)
|
26
|
+
hash = { 'jid' => jid, 'percent' => percent.to_i, 'desc' => desc }
|
27
|
+
hash["reserve_until"] = convert(reserve_until) if reserve_until
|
28
|
+
Faktory.server {|c| c.set_track(hash) }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def convert(ts)
|
34
|
+
raise ArgumentError, "Timestamp in the past: #{ts}" if Time.now > ts
|
35
|
+
raise ArgumentError, "Timestamp too far in the future: #{ts}" if (Time.now + 86400) < ts
|
36
|
+
|
37
|
+
tsf = ts.to_f
|
38
|
+
Time.at(tsf).utc.iso8601
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/faktory/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faktory_worker_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '2.2'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 2.2.
|
22
|
+
version: 2.2.2
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,21 @@ dependencies:
|
|
29
29
|
version: '2.2'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 2.2.
|
32
|
+
version: 2.2.2
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activejob
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 5.2.0
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 5.2.0
|
33
47
|
- !ruby/object:Gem::Dependency
|
34
48
|
name: minitest
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,16 +76,16 @@ dependencies:
|
|
62
76
|
name: rake
|
63
77
|
requirement: !ruby/object:Gem::Requirement
|
64
78
|
requirements:
|
65
|
-
- - "
|
79
|
+
- - ">="
|
66
80
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
81
|
+
version: '0'
|
68
82
|
type: :development
|
69
83
|
prerelease: false
|
70
84
|
version_requirements: !ruby/object:Gem::Requirement
|
71
85
|
requirements:
|
72
|
-
- - "
|
86
|
+
- - ">="
|
73
87
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
88
|
+
version: '0'
|
75
89
|
description: Ruby worker for Faktory.
|
76
90
|
email:
|
77
91
|
- mike@contribsys.com
|
@@ -81,6 +95,7 @@ extensions: []
|
|
81
95
|
extra_rdoc_files: []
|
82
96
|
files:
|
83
97
|
- ".gitignore"
|
98
|
+
- ".travis.yml"
|
84
99
|
- Changes.md
|
85
100
|
- Gemfile
|
86
101
|
- Gemfile.lock
|
@@ -89,22 +104,28 @@ files:
|
|
89
104
|
- Rakefile
|
90
105
|
- bin/faktory-worker
|
91
106
|
- faktory_worker_ruby.gemspec
|
107
|
+
- lib/active_job/queue_adapters/faktory_adapter.rb
|
92
108
|
- lib/faktory.rb
|
109
|
+
- lib/faktory/batch.rb
|
93
110
|
- lib/faktory/cli.rb
|
94
111
|
- lib/faktory/client.rb
|
95
112
|
- lib/faktory/connection.rb
|
96
113
|
- lib/faktory/exception_handler.rb
|
97
114
|
- lib/faktory/fetch.rb
|
115
|
+
- lib/faktory/io.rb
|
98
116
|
- lib/faktory/job.rb
|
99
117
|
- lib/faktory/job_logger.rb
|
100
118
|
- lib/faktory/launcher.rb
|
101
119
|
- lib/faktory/logging.rb
|
102
120
|
- lib/faktory/manager.rb
|
121
|
+
- lib/faktory/middleware/batch.rb
|
103
122
|
- lib/faktory/middleware/chain.rb
|
104
123
|
- lib/faktory/middleware/i18n.rb
|
124
|
+
- lib/faktory/mutate.rb
|
105
125
|
- lib/faktory/processor.rb
|
106
126
|
- lib/faktory/rails.rb
|
107
127
|
- lib/faktory/testing.rb
|
128
|
+
- lib/faktory/tracking.rb
|
108
129
|
- lib/faktory/util.rb
|
109
130
|
- lib/faktory/version.rb
|
110
131
|
- lib/faktory_worker_ruby.rb
|
@@ -112,7 +133,7 @@ homepage: https://github.com/contribsys/faktory_worker_ruby
|
|
112
133
|
licenses:
|
113
134
|
- LGPL-3.0
|
114
135
|
metadata: {}
|
115
|
-
post_install_message:
|
136
|
+
post_install_message:
|
116
137
|
rdoc_options: []
|
117
138
|
require_paths:
|
118
139
|
- lib
|
@@ -120,16 +141,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
141
|
requirements:
|
121
142
|
- - ">="
|
122
143
|
- !ruby/object:Gem::Version
|
123
|
-
version: 2.
|
144
|
+
version: 2.5.0
|
124
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
146
|
requirements:
|
126
147
|
- - ">="
|
127
148
|
- !ruby/object:Gem::Version
|
128
149
|
version: '0'
|
129
150
|
requirements: []
|
130
|
-
|
131
|
-
|
132
|
-
signing_key:
|
151
|
+
rubygems_version: 3.2.6
|
152
|
+
signing_key:
|
133
153
|
specification_version: 4
|
134
154
|
summary: Ruby worker for Faktory
|
135
155
|
test_files: []
|