good_job 0.3.0 → 0.4.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.
- 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
|