good_job 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -2
- data/README.md +2 -9
- data/lib/good_job/lockable.rb +47 -39
- data/lib/good_job/scheduler.rb +10 -10
- data/lib/good_job/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a3b5327b3b95b67901f3be13858f0bbf9cf1d0818793ed653f084a77b8e6ff9
|
4
|
+
data.tar.gz: 056fd886d26d7ee60fd63d7f45714c7b843f7f4e6661b58a00dc4c6ec17d9b9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12221043ad332a684e062aae176d529bbd39da7998e9ae6d355bdb1f68f2f7ace21c4f4196ebe0306841a761161e564125f35c7522f30f3b2f718830bd15c788
|
7
|
+
data.tar.gz: 7d389ac1aafb614b813fdb1eb75ce7dceba40238111966d8031578ac88553d989cee08dcef03d72fd8e53ce3215692779e2e03c538ea90cb3a477361ef2442d8
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [0.
|
3
|
+
## [0.4.0](https://github.com/bensheldon/good_job/tree/0.4.0) (2020-03-30)
|
4
4
|
|
5
|
-
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.3.0...0.4.0)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Improve ActiveRecord usage for advisory locking [\#24](https://github.com/bensheldon/good_job/pull/24) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
- Remove support for Rails 5.1 [\#23](https://github.com/bensheldon/good_job/pull/23) ([bensheldon](https://github.com/bensheldon))
|
11
|
+
|
12
|
+
## [v0.3.0](https://github.com/bensheldon/good_job/tree/v0.3.0) (2020-03-22)
|
13
|
+
|
14
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.2.2...v0.3.0)
|
6
15
|
|
7
16
|
**Merged pull requests:**
|
8
17
|
|
9
18
|
- Update development Ruby to 2.6.5 [\#22](https://github.com/bensheldon/good_job/pull/22) ([bensheldon](https://github.com/bensheldon))
|
10
19
|
- Simplify the internal API, removing JobWrapper and InlineScheduler [\#21](https://github.com/bensheldon/good_job/pull/21) ([bensheldon](https://github.com/bensheldon))
|
11
20
|
- Generate a new future for every executed job [\#20](https://github.com/bensheldon/good_job/pull/20) ([bensheldon](https://github.com/bensheldon))
|
21
|
+
|
22
|
+
## [v0.2.2](https://github.com/bensheldon/good_job/tree/v0.2.2) (2020-03-08)
|
23
|
+
|
24
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v0.2.1...v0.2.2)
|
25
|
+
|
26
|
+
**Merged pull requests:**
|
27
|
+
|
12
28
|
- Configuration for maximum number of job execution threads [\#18](https://github.com/bensheldon/good_job/pull/18) ([bensheldon](https://github.com/bensheldon))
|
13
29
|
- Gracefully shutdown Scheduler when executable receives TERM or INT [\#17](https://github.com/bensheldon/good_job/pull/17) ([bensheldon](https://github.com/bensheldon))
|
14
30
|
- Update Appraisals [\#16](https://github.com/bensheldon/good_job/pull/16) ([bensheldon](https://github.com/bensheldon))
|
data/README.md
CHANGED
@@ -114,17 +114,10 @@ Package maintainers can release this gem with the following [gem-release](https:
|
|
114
114
|
# Sign into rubygems
|
115
115
|
$ gem signin
|
116
116
|
|
117
|
-
#
|
118
|
-
$ gem bump -v minor --no-commit
|
119
|
-
|
120
|
-
# Update the changelog
|
121
|
-
$ bundle exec rake changelog
|
122
|
-
|
123
|
-
# Commit the version and changelog to git
|
117
|
+
# Update version number, changelog, and create git commit:
|
124
118
|
$ bundle exec rake commit_version
|
125
119
|
|
126
|
-
#
|
127
|
-
$ gem release
|
120
|
+
# ..and follow subsequent directions.
|
128
121
|
```
|
129
122
|
|
130
123
|
## Contributing
|
data/lib/good_job/lockable.rb
CHANGED
@@ -5,36 +5,66 @@ module GoodJob
|
|
5
5
|
RecordAlreadyAdvisoryLockedError = Class.new(StandardError)
|
6
6
|
|
7
7
|
included do
|
8
|
+
scope :advisory_lock, (lambda do
|
9
|
+
original_query = self
|
10
|
+
|
11
|
+
cte_table = Arel::Table.new(:rows)
|
12
|
+
composed_cte = Arel::Nodes::As.new(cte_table, original_query.select(primary_key).except(:limit).arel)
|
13
|
+
|
14
|
+
query = cte_table.project(cte_table[:id])
|
15
|
+
.with(composed_cte)
|
16
|
+
.where(Arel.sql(sanitize_sql_for_conditions(["pg_try_advisory_lock(('x'||substr(md5(:table_name || \"#{cte_table.name}\".\"#{primary_key}\"::text), 1, 16))::bit(64)::bigint)", { table_name: table_name }])))
|
17
|
+
|
18
|
+
limit = original_query.arel.ast.limit
|
19
|
+
query.limit = limit.value if limit.present?
|
20
|
+
|
21
|
+
unscoped.where(arel_table[:id].in(query)).merge(original_query.only(:order))
|
22
|
+
end)
|
23
|
+
|
8
24
|
scope :joins_advisory_locks, (lambda do
|
9
|
-
|
25
|
+
join_sql = <<~SQL
|
10
26
|
LEFT JOIN pg_locks ON pg_locks.locktype = 'advisory'
|
11
27
|
AND pg_locks.objsubid = 1
|
12
|
-
AND pg_locks.classid = ('x'||substr(md5(
|
13
|
-
AND pg_locks.objid = (('x'||substr(md5(
|
28
|
+
AND pg_locks.classid = ('x'||substr(md5(:table_name || "#{table_name}"."#{primary_key}"::text), 1, 16))::bit(32)::int
|
29
|
+
AND pg_locks.objid = (('x'||substr(md5(:table_name || "#{table_name}"."#{primary_key}"::text), 1, 16))::bit(64) << 32)::bit(32)::int
|
14
30
|
SQL
|
31
|
+
|
32
|
+
joins(sanitize_sql_for_conditions([join_sql, { table_name: table_name }]))
|
15
33
|
end)
|
16
34
|
|
17
35
|
scope :advisory_unlocked, -> { joins_advisory_locks.where(pg_locks: { locktype: nil }) }
|
36
|
+
scope :advisory_locked, -> { joins_advisory_locks.where.not(pg_locks: { locktype: nil }) }
|
37
|
+
scope :owns_advisory_locked, -> { joins_advisory_locks.where('"pg_locks"."pid" = pg_backend_pid()') }
|
18
38
|
|
19
39
|
attr_accessor :create_with_advisory_lock
|
20
|
-
|
21
40
|
after_create -> { advisory_lock }, if: :create_with_advisory_lock
|
22
41
|
end
|
23
42
|
|
24
43
|
class_methods do
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
SQL
|
44
|
+
def with_advisory_lock(&block)
|
45
|
+
records = advisory_lock.to_a
|
46
|
+
begin
|
47
|
+
block.call(records)
|
48
|
+
ensure
|
49
|
+
records.each(&:advisory_unlock)
|
50
|
+
end
|
33
51
|
end
|
34
52
|
end
|
35
53
|
|
36
54
|
def advisory_lock
|
37
|
-
|
55
|
+
query = <<~SQL
|
56
|
+
SELECT 1 AS one
|
57
|
+
WHERE pg_try_advisory_lock(('x'||substr(md5(:table_name || :id::text), 1, 16))::bit(64)::bigint)
|
58
|
+
SQL
|
59
|
+
self.class.connection.execute(sanitize_sql_for_conditions([query, { table_name: self.class.table_name, id: send(self.class.primary_key) }])).ntuples.positive?
|
60
|
+
end
|
61
|
+
|
62
|
+
def advisory_unlock
|
63
|
+
query = <<~SQL
|
64
|
+
SELECT 1 AS one
|
65
|
+
WHERE pg_advisory_unlock(('x'||substr(md5(:table_name || :id::text), 1, 16))::bit(64)::bigint)
|
66
|
+
SQL
|
67
|
+
self.class.connection.execute(sanitize_sql_for_conditions([query, { table_name: self.class.table_name, id: send(self.class.primary_key) }])).ntuples.positive?
|
38
68
|
end
|
39
69
|
|
40
70
|
def advisory_lock!
|
@@ -45,38 +75,16 @@ module GoodJob
|
|
45
75
|
def with_advisory_lock
|
46
76
|
advisory_lock!
|
47
77
|
yield
|
48
|
-
|
49
|
-
advisory_unlock unless
|
50
|
-
raise
|
78
|
+
ensure
|
79
|
+
advisory_unlock unless $ERROR_INFO.is_a? RecordAlreadyAdvisoryLockedError
|
51
80
|
end
|
52
81
|
|
53
82
|
def advisory_locked?
|
54
|
-
self.class.
|
55
|
-
SELECT 1 as one
|
56
|
-
FROM pg_locks
|
57
|
-
WHERE
|
58
|
-
locktype = 'advisory'
|
59
|
-
AND objsubid = 1
|
60
|
-
AND classid = ('x'||substr(md5('#{id}'), 1, 16))::bit(32)::int
|
61
|
-
AND objid = (('x'||substr(md5('#{id}'), 1, 16))::bit(64) << 32)::bit(32)::int
|
62
|
-
SQL
|
83
|
+
self.class.advisory_locked.where(id: send(self.class.primary_key)).any?
|
63
84
|
end
|
64
85
|
|
65
86
|
def owns_advisory_lock?
|
66
|
-
self.class.
|
67
|
-
SELECT 1 as one
|
68
|
-
FROM pg_locks
|
69
|
-
WHERE
|
70
|
-
locktype = 'advisory'
|
71
|
-
AND objsubid = 1
|
72
|
-
AND classid = ('x'||substr(md5('#{id}'), 1, 16))::bit(32)::int
|
73
|
-
AND objid = (('x'||substr(md5('#{id}'), 1, 16))::bit(64) << 32)::bit(32)::int
|
74
|
-
AND pid = pg_backend_pid()
|
75
|
-
SQL
|
76
|
-
end
|
77
|
-
|
78
|
-
def advisory_unlock
|
79
|
-
self.class.connection.execute("SELECT pg_advisory_unlock(('x'||substr(md5('#{id}'), 1, 16))::bit(64)::bigint)").first["pg_advisory_unlock"]
|
87
|
+
self.class.owns_advisory_locked.where(id: send(self.class.primary_key)).any?
|
80
88
|
end
|
81
89
|
|
82
90
|
def advisory_unlock!
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -62,18 +62,18 @@ module GoodJob
|
|
62
62
|
|
63
63
|
def create_thread
|
64
64
|
future = Concurrent::Future.new(args: [ordered_query], executor: @pool) do |query|
|
65
|
-
|
65
|
+
good_job = nil
|
66
66
|
|
67
67
|
Rails.application.executor.wrap do
|
68
|
-
|
69
|
-
|
68
|
+
query.limit(1).with_advisory_lock do |good_jobs|
|
69
|
+
good_job = good_jobs.first
|
70
|
+
break unless good_job
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
good_job.advisory_unlock
|
72
|
+
good_job.perform
|
73
|
+
end
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
good_job
|
77
77
|
end
|
78
78
|
future.add_observer(self, :task_observer)
|
79
79
|
future.execute
|
@@ -83,9 +83,9 @@ module GoodJob
|
|
83
83
|
ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: error, time: time })
|
84
84
|
end
|
85
85
|
|
86
|
-
def task_observer(time,
|
87
|
-
ActiveSupport::Notifications.instrument("finished_job_task.good_job", {
|
88
|
-
create_thread if
|
86
|
+
def task_observer(time, performed_job, error)
|
87
|
+
ActiveSupport::Notifications.instrument("finished_job_task.good_job", { good_job: performed_job, error: error, time: time })
|
88
|
+
create_thread if performed_job
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: foreman
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: gem-release
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|