postjob 0.1.8 → 0.1.9
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/README.md +25 -8
- data/lib/postjob/cli/db.rb +2 -2
- data/lib/postjob/cli/job.rb +21 -0
- data/lib/postjob/cli/ps.rb +4 -4
- data/lib/postjob/job.rb +3 -0
- data/lib/postjob/queue/encoder.rb +0 -1
- data/lib/postjob/queue/notifications.rb +10 -8
- data/lib/postjob/queue/search.rb +0 -1
- data/lib/postjob/queue.rb +3 -2
- data/lib/postjob/runner.rb +8 -5
- data/lib/postjob.rb +2 -1
- data/spec/postjob/full_workflow_spec.rb +1 -1
- data/spec/postjob/job_control/error_status_spec.rb +1 -3
- data/spec/postjob/job_control/max_attempts_spec.rb +2 -6
- data/spec/spec_helper.rb +1 -1
- data/spec/support/connect_active_record.rb +0 -1
- metadata +4 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a648c2a0a8e5dd0ab60285c9cc59ceb7a68ed863
|
|
4
|
+
data.tar.gz: 825bf32c99d020cd9b4c45910da67a1c5923394a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b906419d181d39e540654f351b13c7c0a7ce90e3e0aef10cdcd53df47c87dfcccb0b09641303e55032191784990845f41009ae280311d1b65196d7bf972da97
|
|
7
|
+
data.tar.gz: 7bcbb6b009ed360328b5b0b6e032d836f7d84e25f2bfaf074dfcb9215262a4ed9a47504aab24bb6f5c80e547efd63d0f3848bafc1ed527a7bdc9f1acef7ab966
|
data/README.md
CHANGED
|
@@ -1,23 +1,40 @@
|
|
|
1
|
+
[](https://travis-ci.org/mediapeers/postjob)
|
|
2
|
+
|
|
1
3
|
# Postjob
|
|
2
4
|
|
|
3
5
|
The `postjob` gem implements a simple way to have restartable, asynchronous, and distributed processes.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
To install gem run `gem install postjob` or add `gem postjob` to your Gemfile and bundle.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Development setup
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
1. Make sure you have PostgreSQL >= 9.5 installed
|
|
14
|
+
2. Clone repo `git clone git@github.com:mediapeers/postjob.git && cd postjob`
|
|
15
|
+
3. Run `./scripts/setup` to bundle and setup DB (requires passwordless postgres user).
|
|
12
16
|
|
|
13
|
-
To
|
|
17
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
14
18
|
|
|
15
|
-
##
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Tests are set up to run twice, once against an ActiveRecord adapter, and once against a Simple::SQL provided adapter. To run the full test suite run
|
|
22
|
+
|
|
23
|
+
make ci_tests
|
|
24
|
+
|
|
25
|
+
To run a single set of tests - this is usually fine during development - run
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
bundle exec rspec
|
|
18
28
|
|
|
29
|
+
## Release gem
|
|
30
|
+
|
|
31
|
+
To release a new version of this gem , run `./scripts/release`, which will bump the version number, create a git tag for the version,
|
|
32
|
+
push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
33
|
+
|
|
34
|
+
## Contributing
|
|
35
|
+
|
|
36
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mediapeers/postqueue.
|
|
19
37
|
|
|
20
38
|
## License
|
|
21
39
|
|
|
22
40
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
23
|
-
|
data/lib/postjob/cli/db.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Postjob::CLI
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def db_unmigrate
|
|
11
|
-
|
|
11
|
+
unless force
|
|
12
12
|
confirm! <<~TXT
|
|
13
13
|
Really unmigrating database? This will destroy all data about postjobs!
|
|
14
14
|
(To prevent the need to confirm run with '--force'.)
|
|
@@ -20,7 +20,7 @@ module Postjob::CLI
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def db_remigrate(force: false)
|
|
23
|
-
|
|
23
|
+
unless force
|
|
24
24
|
confirm! <<~TXT
|
|
25
25
|
Really remigrating database? This will destroy all data about postjobs!
|
|
26
26
|
(To prevent the need to confirm run with '--force'.)
|
data/lib/postjob/cli/job.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# rubocop:disable Metrics/MethodLength
|
|
2
|
+
|
|
1
3
|
module Postjob::CLI
|
|
2
4
|
# Enqueues a workflow
|
|
3
5
|
#
|
|
@@ -17,6 +19,8 @@ module Postjob::CLI
|
|
|
17
19
|
# This resets all failed jobs within the job tree, below the passed in
|
|
18
20
|
# job id.
|
|
19
21
|
def job_reset(job_id)
|
|
22
|
+
connect_to_database!
|
|
23
|
+
|
|
20
24
|
job_id = Integer(job_id)
|
|
21
25
|
full_job_id = Simple::SQL.ask "SELECT full_id FROM postjob.postjobs WHERE id=$1", job_id
|
|
22
26
|
full_job_id || logger.error("No such job: #{job_id}")
|
|
@@ -45,6 +49,23 @@ module Postjob::CLI
|
|
|
45
49
|
logger.warn "The following jobs have been reset: #{job_ids.join(', ')}"
|
|
46
50
|
end
|
|
47
51
|
|
|
52
|
+
# Kills a specific job
|
|
53
|
+
def job_kill(job_id)
|
|
54
|
+
connect_to_database!
|
|
55
|
+
|
|
56
|
+
job_id = Integer(job_id)
|
|
57
|
+
|
|
58
|
+
Simple::SQL.ask <<~SQL, job_id
|
|
59
|
+
UPDATE postjob.postjobs
|
|
60
|
+
SET
|
|
61
|
+
status='failed',
|
|
62
|
+
next_run_at=null,
|
|
63
|
+
error='Manually terminated',
|
|
64
|
+
error_message='Manually terminated'
|
|
65
|
+
WHERE id = $1 AND status IN ('ready', 'err', 'sleep')
|
|
66
|
+
SQL
|
|
67
|
+
end
|
|
68
|
+
|
|
48
69
|
private
|
|
49
70
|
|
|
50
71
|
# parses "foo:bar,baz:quibble" into { "foo" => "bar", "baz" => "quibble"}
|
data/lib/postjob/cli/ps.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Postjob::CLI
|
|
2
2
|
private
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
def ps_query(conditions = [])
|
|
5
5
|
conditions.compact!
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ module Postjob::CLI
|
|
|
31
31
|
ORDER BY root_id DESC, id ASC
|
|
32
32
|
SQL
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def tags_condition(tags)
|
|
36
36
|
return nil unless tags
|
|
37
37
|
|
|
@@ -46,8 +46,8 @@ module Postjob::CLI
|
|
|
46
46
|
# This command lists the statuses of all jobs that are either root jobs,
|
|
47
47
|
# i.e. enqueued workflows, or that have failed.
|
|
48
48
|
#
|
|
49
|
-
# Example:
|
|
50
|
-
#
|
|
49
|
+
# Example:
|
|
50
|
+
#
|
|
51
51
|
# postjob ps --tags=foo:bar,bar:baz --limit=100
|
|
52
52
|
#
|
|
53
53
|
# For a listing of all jobs in the system use ps:full, see 'postjob help ps:full'
|
data/lib/postjob/job.rb
CHANGED
|
@@ -11,19 +11,21 @@ module Postjob::Queue::Notifications
|
|
|
11
11
|
SQL.ask "NOTIFY #{CHANNEL}"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
MAX_WAIT_TIME = 120
|
|
15
|
+
|
|
14
16
|
def wait_for_new_job
|
|
15
17
|
started_at = Time.now
|
|
16
18
|
|
|
17
19
|
start_listening
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
# Determine when the next job is up. If we don't have a next job within MAX_WAIT_TIME
|
|
22
|
+
# we wake up regardless.
|
|
23
|
+
wait_time = time_to_next_job
|
|
24
|
+
return if wait_time && wait_time <= 0
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
end
|
|
26
|
+
wait_time ||= MAX_WAIT_TIME
|
|
27
|
+
Postjob.logger.debug "postjob: waiting for notification for up to #{wait_time.inspect} seconds"
|
|
28
|
+
Simple::SQL.wait_for_notify(wait_time)
|
|
27
29
|
|
|
28
30
|
# flush notifications. It is possible that a huge number of notifications
|
|
29
31
|
# piled up while we have been waiting. The following line takes care of
|
|
@@ -51,7 +53,7 @@ module Postjob::Queue::Notifications
|
|
|
51
53
|
SELECT EXTRACT(EPOCH FROM (MIN(next_event_at) - (now() at time zone 'utc'))) FROM (
|
|
52
54
|
SELECT MIN(timing_out_at) AS next_event_at
|
|
53
55
|
FROM #{TABLE_NAME}
|
|
54
|
-
WHERE status IN ('ready', 'sleep')
|
|
56
|
+
WHERE status IN ('ready', 'sleep', 'err')
|
|
55
57
|
UNION
|
|
56
58
|
SELECT MIN(next_run_at) AS next_event_at
|
|
57
59
|
FROM #{TABLE_NAME}
|
data/lib/postjob/queue/search.rb
CHANGED
data/lib/postjob/queue.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# rubocop:disable Metrics/ModuleLength
|
|
4
4
|
# rubocop:disable Metrics/LineLength
|
|
5
5
|
# rubocop:disable Lint/EndAlignment
|
|
6
|
-
# rubocop:disable
|
|
6
|
+
# rubocop:disable Metrics/MethodLength
|
|
7
|
+
# rubocop:disable Metrics/ParameterLists
|
|
7
8
|
|
|
8
9
|
require "securerandom"
|
|
9
10
|
|
|
@@ -28,7 +29,7 @@ module Postjob::Queue
|
|
|
28
29
|
version: "",
|
|
29
30
|
queue: "q",
|
|
30
31
|
max_attempts: 5
|
|
31
|
-
}
|
|
32
|
+
}
|
|
32
33
|
|
|
33
34
|
# enqueues a new job with the given arguments
|
|
34
35
|
#
|
data/lib/postjob/runner.rb
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
# rubocop:disable
|
|
1
|
+
# rubocop:disable Metrics/ModuleLength
|
|
2
|
+
# rubocop:disable Style/BlockDelimiters
|
|
3
|
+
# rubocop:disable Lint/RescueException
|
|
4
|
+
|
|
2
5
|
module Postjob::Runner
|
|
3
6
|
extend self
|
|
4
7
|
|
|
@@ -114,11 +117,11 @@ module Postjob::Runner
|
|
|
114
117
|
when :pending then [ :pending, nil ]
|
|
115
118
|
else [ :ok, value ]
|
|
116
119
|
end
|
|
117
|
-
rescue
|
|
118
|
-
return_exception :err, $!
|
|
119
|
-
rescue StandardError
|
|
120
|
+
rescue ArgumentError, LocalJumpError, NameError, RegexpError, ScriptError, TypeError
|
|
120
121
|
Postjob.logger.error "#{$!}, from\n\t#{$!.backtrace[0, 10].join("\n\t")}"
|
|
121
122
|
return_exception :failed, $!
|
|
123
|
+
rescue Exception
|
|
124
|
+
return_exception :err, $!
|
|
122
125
|
end
|
|
123
126
|
|
|
124
127
|
def return_exception(state, exception)
|
|
@@ -145,7 +148,7 @@ module Postjob::Runner
|
|
|
145
148
|
def error_message(job, status, value)
|
|
146
149
|
runtime = Time.now.utc - job.created_at
|
|
147
150
|
runtime = "%.03f secs" % runtime
|
|
148
|
-
error_class, err_message,
|
|
151
|
+
error_class, err_message, _error_backtrace = value
|
|
149
152
|
|
|
150
153
|
"#{job} #{status} #{error_class} #{err_message.inspect}: #{runtime}"
|
|
151
154
|
# + "\n backtrace information:\n #{error_backtrace.join("\n ")}"
|
data/lib/postjob.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# rubocop:disable Metrics/ParameterLists
|
|
2
|
+
|
|
1
3
|
require "expectation"
|
|
2
4
|
require "simple/sql"
|
|
3
5
|
require "timeout"
|
|
@@ -127,7 +129,6 @@ module Postjob
|
|
|
127
129
|
private
|
|
128
130
|
|
|
129
131
|
# This method is called from tests. Otherwise it is supposed to be private.
|
|
130
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
131
132
|
def process_job(job) # :nodoc:
|
|
132
133
|
expect! job => Job
|
|
133
134
|
|
|
@@ -74,7 +74,7 @@ describe "Sub Workflows" do
|
|
|
74
74
|
let!(:user_ids) { 100.upto(100 + users_count - 1).to_a }
|
|
75
75
|
let!(:id) { Postjob.enqueue! "RecommendGroupWorkflow", 1, user_ids }
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
xit "runs the job returning the result" do
|
|
78
78
|
expect(MPX::Impl).to receive(:clone_group).exactly(1).times.and_call_original
|
|
79
79
|
expect(MPX::Impl).to receive(:load_users).exactly(1).times.and_call_original
|
|
80
80
|
expect(MPX::Impl).to receive(:send_email).exactly(users_count).times.and_call_original
|
|
@@ -39,9 +39,7 @@ describe "max_attempts" do
|
|
|
39
39
|
let!(:id) { Postjob.enqueue!("MaxAttemptWorkflow", 1, max_attempts: 1) }
|
|
40
40
|
|
|
41
41
|
it "fails the childjob with a timeout" do
|
|
42
|
-
while Postjob.process_all > 0
|
|
43
|
-
sleep 0.03
|
|
44
|
-
end
|
|
42
|
+
sleep 0.03 while Postjob.process_all > 0
|
|
45
43
|
|
|
46
44
|
expect(load_child_job.failed_attempts).to eq(1)
|
|
47
45
|
expect(load_child_job.status).to eq("failed")
|
|
@@ -54,9 +52,7 @@ describe "max_attempts" do
|
|
|
54
52
|
let!(:id) { Postjob.enqueue!("MaxAttemptWorkflow", 5, max_attempts: 1) }
|
|
55
53
|
|
|
56
54
|
it "fails the childjob with a timeout" do
|
|
57
|
-
while Postjob.process_all > 0
|
|
58
|
-
sleep 0.03
|
|
59
|
-
end
|
|
55
|
+
sleep 0.03 while Postjob.process_all > 0
|
|
60
56
|
|
|
61
57
|
expect(load_child_job.failed_attempts).to eq(5)
|
|
62
58
|
expect(load_child_job.status).to eq("failed")
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: postjob
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- radiospiel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-03-
|
|
11
|
+
date: 2018-03-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -86,34 +86,20 @@ dependencies:
|
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '4.2'
|
|
89
|
-
- !ruby/object:Gem::Dependency
|
|
90
|
-
name: timecop
|
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '0'
|
|
96
|
-
type: :development
|
|
97
|
-
prerelease: false
|
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0'
|
|
103
89
|
- !ruby/object:Gem::Dependency
|
|
104
90
|
name: rubocop
|
|
105
91
|
requirement: !ruby/object:Gem::Requirement
|
|
106
92
|
requirements:
|
|
107
93
|
- - "~>"
|
|
108
94
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
95
|
+
version: 0.52.1
|
|
110
96
|
type: :development
|
|
111
97
|
prerelease: false
|
|
112
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
113
99
|
requirements:
|
|
114
100
|
- - "~>"
|
|
115
101
|
- !ruby/object:Gem::Version
|
|
116
|
-
version:
|
|
102
|
+
version: 0.52.1
|
|
117
103
|
- !ruby/object:Gem::Dependency
|
|
118
104
|
name: awesome_print
|
|
119
105
|
requirement: !ruby/object:Gem::Requirement
|