good_job 4.1.0 → 4.1.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 +4 -4
- data/CHANGELOG.md +41 -0
- data/app/charts/good_job/performance_index_chart.rb +1 -1
- data/app/charts/good_job/performance_show_chart.rb +1 -1
- data/app/controllers/good_job/application_controller.rb +1 -1
- data/app/controllers/good_job/frontends_controller.rb +6 -2
- data/app/controllers/good_job/performance_controller.rb +1 -1
- data/app/frontend/good_job/icons.svg +79 -0
- data/app/frontend/good_job/style.css +5 -0
- data/app/helpers/good_job/icons_helper.rb +8 -5
- data/app/models/concerns/good_job/advisory_lockable.rb +17 -7
- data/app/models/concerns/good_job/reportable.rb +8 -12
- data/app/models/good_job/batch.rb +10 -5
- data/app/models/good_job/batch_record.rb +18 -15
- data/app/models/good_job/discrete_execution.rb +6 -59
- data/app/models/good_job/execution.rb +59 -4
- data/app/models/good_job/execution_result.rb +6 -6
- data/app/models/good_job/job.rb +567 -12
- data/app/views/good_job/batches/_jobs.erb +1 -1
- data/app/views/good_job/batches/_table.erb +1 -1
- data/app/views/good_job/jobs/index.html.erb +1 -1
- data/app/views/layouts/good_job/application.html.erb +7 -7
- data/config/brakeman.ignore +75 -0
- data/config/locales/de.yml +49 -49
- data/config/locales/es.yml +14 -14
- data/config/routes.rb +3 -3
- data/lib/good_job/active_job_extensions/concurrency.rb +105 -98
- data/lib/good_job/adapter/inline_buffer.rb +73 -0
- data/lib/good_job/adapter.rb +59 -53
- data/lib/good_job/configuration.rb +3 -4
- data/lib/good_job/cron_manager.rb +1 -3
- data/lib/good_job/current_thread.rb +4 -4
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +6 -5
- metadata +6 -20
- data/app/models/good_job/base_execution.rb +0 -605
- data/app/views/good_job/shared/icons/_arrow_clockwise.html.erb +0 -5
- data/app/views/good_job/shared/icons/_check.html.erb +0 -5
- data/app/views/good_job/shared/icons/_circle_half.html.erb +0 -4
- data/app/views/good_job/shared/icons/_clock.html.erb +0 -5
- data/app/views/good_job/shared/icons/_dash_circle.html.erb +0 -5
- data/app/views/good_job/shared/icons/_dots.html.erb +0 -3
- data/app/views/good_job/shared/icons/_eject.html.erb +0 -4
- data/app/views/good_job/shared/icons/_exclamation.html.erb +0 -5
- data/app/views/good_job/shared/icons/_globe.html.erb +0 -3
- data/app/views/good_job/shared/icons/_info.html.erb +0 -4
- data/app/views/good_job/shared/icons/_moon_stars_fill.html.erb +0 -5
- data/app/views/good_job/shared/icons/_pause.html.erb +0 -4
- data/app/views/good_job/shared/icons/_play.html.erb +0 -4
- data/app/views/good_job/shared/icons/_skip_forward.html.erb +0 -4
- data/app/views/good_job/shared/icons/_stop.html.erb +0 -4
- data/app/views/good_job/shared/icons/_sun_fill.html.erb +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 660e78528305c41b14c2926d9747037a091b57f952acd14246af99180991b434
|
|
4
|
+
data.tar.gz: 8bc407843b2d17fbeceb36507eb2e06177460f1bc4b8b4133560e1c4daa6ce3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8847a962731fec2c13683d3948e3ae51c0730bbcdd81853dfa3d6c2168bb05105ae2cf4c8f073a9c133d3f634473bdfaf58fefc08b07a0bbb8f51686e4b62ff0
|
|
7
|
+
data.tar.gz: c89b843399fe9bdc0d492c89ce1f481da62201689cc0f15ada8d73135308be1ee23295e54bae1fa586d1c9a50e2ca62a072784a4ac0fb9a5be3ee83ed374b1d7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.1.1](https://github.com/bensheldon/good_job/tree/v4.1.1) (2024-07-31)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.99.2...v4.1.1)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Convert Concurrency extension to use transaction-level \(xact\) advisory locks [\#1439](https://github.com/bensheldon/good_job/pull/1439) ([bensheldon](https://github.com/bensheldon))
|
|
10
|
+
|
|
11
|
+
**Fixed bugs:**
|
|
12
|
+
|
|
13
|
+
- Fix N+1 on dashboard batches index page [\#1442](https://github.com/bensheldon/good_job/pull/1442) ([bensheldon](https://github.com/bensheldon))
|
|
14
|
+
- Remove duplicate word on batches show page [\#1441](https://github.com/bensheldon/good_job/pull/1441) ([Earlopain](https://github.com/Earlopain))
|
|
15
|
+
- Ensure remaining inline jobs are unlocked when one job raises in Adapter\#enqueue\_all [\#1438](https://github.com/bensheldon/good_job/pull/1438) ([bensheldon](https://github.com/bensheldon))
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- Using Good Job to track all ActiveJob executions, even those with `perform_now` [\#1448](https://github.com/bensheldon/good_job/issues/1448)
|
|
20
|
+
- RuntimeError when running good\_job executable after updating v3 -\> v4 [\#1445](https://github.com/bensheldon/good_job/issues/1445)
|
|
21
|
+
- 2.99 -\> 3.0.2 migration not applying cleanly from a fresh DB [\#1435](https://github.com/bensheldon/good_job/issues/1435)
|
|
22
|
+
- \[Enhance\] Enhance performance via counter cache [\#1375](https://github.com/bensheldon/good_job/issues/1375)
|
|
23
|
+
- Change how svg images are inserted into partials [\#1364](https://github.com/bensheldon/good_job/issues/1364)
|
|
24
|
+
|
|
25
|
+
**Merged pull requests:**
|
|
26
|
+
|
|
27
|
+
- Improve some Spanish transcriptions [\#1452](https://github.com/bensheldon/good_job/pull/1452) ([sebastian-cloudnonic](https://github.com/sebastian-cloudnonic))
|
|
28
|
+
- All running jobs now have `performed_at` set so use that in status query; fix flaky test that took advisory lock in `before` block [\#1444](https://github.com/bensheldon/good_job/pull/1444) ([bensheldon](https://github.com/bensheldon))
|
|
29
|
+
- Handle empty asset format in Frontends controller [\#1443](https://github.com/bensheldon/good_job/pull/1443) ([bensheldon](https://github.com/bensheldon))
|
|
30
|
+
- Update development dependencies, Ruby 3.3.4 [\#1437](https://github.com/bensheldon/good_job/pull/1437) ([bensheldon](https://github.com/bensheldon))
|
|
31
|
+
- Refactor inline adapter to enable deferred execution after enqueue to allow batch-callbacks to use transaction-based advisory lock [\#1433](https://github.com/bensheldon/good_job/pull/1433) ([bensheldon](https://github.com/bensheldon))
|
|
32
|
+
- German translation pass [\#1432](https://github.com/bensheldon/good_job/pull/1432) ([Earlopain](https://github.com/Earlopain))
|
|
33
|
+
- Add Brakeman to linters [\#1431](https://github.com/bensheldon/good_job/pull/1431) ([bensheldon](https://github.com/bensheldon))
|
|
34
|
+
- Remove references to and ignore `good_jobs.retried_good_job_id` column [\#1430](https://github.com/bensheldon/good_job/pull/1430) ([bensheldon](https://github.com/bensheldon))
|
|
35
|
+
- Refactor Concurrency extension for Rails 6.1+ compatibility [\#1429](https://github.com/bensheldon/good_job/pull/1429) ([bensheldon](https://github.com/bensheldon))
|
|
36
|
+
- Use svg `use` for svg icons [\#1428](https://github.com/bensheldon/good_job/pull/1428) ([Earlopain](https://github.com/Earlopain))
|
|
37
|
+
- Replace references to "Discrete" executions with simply Executions; deprecate `GoodJob::DiscreteExecution` [\#1427](https://github.com/bensheldon/good_job/pull/1427) ([bensheldon](https://github.com/bensheldon))
|
|
38
|
+
- Refactor Adapter to reference jobs, not executions [\#1426](https://github.com/bensheldon/good_job/pull/1426) ([bensheldon](https://github.com/bensheldon))
|
|
39
|
+
|
|
40
|
+
## [v1.99.2](https://github.com/bensheldon/good_job/tree/v1.99.2) (2024-07-18)
|
|
41
|
+
|
|
42
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.1.0...v1.99.2)
|
|
43
|
+
|
|
3
44
|
## [v4.1.0](https://github.com/bensheldon/good_job/tree/v4.1.0) (2024-07-16)
|
|
4
45
|
|
|
5
46
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.99.1...v4.1.0)
|
|
@@ -18,7 +18,7 @@ module GoodJob
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def data
|
|
21
|
-
table_name = GoodJob::
|
|
21
|
+
table_name = GoodJob::Execution.table_name
|
|
22
22
|
|
|
23
23
|
interval_entries = BUCKET_INTERVALS.map { "interval '#{_1}'" }.join(",")
|
|
24
24
|
sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
|
|
@@ -7,7 +7,7 @@ module GoodJob
|
|
|
7
7
|
around_action :use_good_job_locale
|
|
8
8
|
|
|
9
9
|
content_security_policy do |policy|
|
|
10
|
-
policy.default_src(:
|
|
10
|
+
policy.default_src(:self) if policy.default_src(*policy.default_src).blank?
|
|
11
11
|
policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank?
|
|
12
12
|
policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank?
|
|
13
13
|
policy.font_src(:self) if policy.font_src(*policy.font_src).blank?
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module GoodJob
|
|
4
4
|
class FrontendsController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
|
5
|
+
protect_from_forgery with: :exception
|
|
5
6
|
skip_after_action :verify_same_origin_request, raise: false
|
|
6
7
|
|
|
7
8
|
STATIC_ASSETS = {
|
|
@@ -15,6 +16,9 @@ module GoodJob
|
|
|
15
16
|
es_module_shims: GoodJob::Engine.root.join("app", "frontend", "good_job", "vendor", "es_module_shims.js"),
|
|
16
17
|
rails_ujs: GoodJob::Engine.root.join("app", "frontend", "good_job", "vendor", "rails_ujs.js"),
|
|
17
18
|
},
|
|
19
|
+
svg: {
|
|
20
|
+
icons: GoodJob::Engine.root.join("app", "frontend", "good_job", "icons.svg"),
|
|
21
|
+
},
|
|
18
22
|
}.freeze
|
|
19
23
|
|
|
20
24
|
MODULE_OVERRIDES = {
|
|
@@ -34,13 +38,13 @@ module GoodJob
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def static
|
|
37
|
-
render file: STATIC_ASSETS.dig(params[:format]
|
|
41
|
+
render file: STATIC_ASSETS.dig(params[:format]&.to_sym, params[:id]&.to_sym) || raise(ActionController::RoutingError, 'Not Found')
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
def module
|
|
41
45
|
raise(ActionController::RoutingError, 'Not Found') if params[:format] != "js"
|
|
42
46
|
|
|
43
|
-
render file: self.class.js_modules[params[:
|
|
47
|
+
render file: self.class.js_modules[params[:id]&.to_sym] || raise(ActionController::RoutingError, 'Not Found')
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<!-- https://icons.getbootstrap.com/icons/arrow-clockwise/ -->
|
|
4
|
+
<symbol id="arrow_clockwise" viewBox="0 0 16 16">
|
|
5
|
+
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
|
6
|
+
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
|
7
|
+
</symbol>
|
|
8
|
+
<!-- https://icons.getbootstrap.com/icons/check-circle/ -->
|
|
9
|
+
<symbol id="check" viewBox="0 0 16 16">
|
|
10
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
11
|
+
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
|
|
12
|
+
</symbol>
|
|
13
|
+
<!-- https://icons.getbootstrap.com/icons/circle-half/ -->
|
|
14
|
+
<symbol id="circle_half" viewBox="0 0 16 16">
|
|
15
|
+
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
|
16
|
+
</symbol>
|
|
17
|
+
<!-- https://icons.getbootstrap.com/icons/clock/ -->
|
|
18
|
+
<symbol id="clock" viewBox="0 0 16 16">
|
|
19
|
+
<path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" />
|
|
20
|
+
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" />
|
|
21
|
+
</symbol>
|
|
22
|
+
<!-- https://icons.getbootstrap.com/icons/dash-circle/ -->
|
|
23
|
+
<symbol id="dash_circle" viewBox="0 0 16 16">
|
|
24
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
25
|
+
<path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" />
|
|
26
|
+
</symbol>
|
|
27
|
+
<!-- https://icons.getbootstrap.com/icons/three-dots/ -->
|
|
28
|
+
<symbol id="dots" viewBox="0 0 16 16">
|
|
29
|
+
<path d="M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />
|
|
30
|
+
</symbol>
|
|
31
|
+
<!-- https://icons.getbootstrap.com/icons/eject/ -->
|
|
32
|
+
<symbol id="eject" viewBox="0 0 16 16">
|
|
33
|
+
<path d="M7.27 1.047a1 1 0 0 1 1.46 0l6.345 6.77c.6.638.146 1.683-.73 1.683H1.656C.78 9.5.326 8.455.926 7.816L7.27 1.047zM14.346 8.5 8 1.731 1.654 8.5h12.692zM.5 11.5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1v-1zm14 0h-13v1h13v-1z" />
|
|
34
|
+
</symbol>
|
|
35
|
+
<!-- https://icons.getbootstrap.com/icons/exclamation-circle/ -->
|
|
36
|
+
<symbol id="exclamation" viewBox="0 0 16 16">
|
|
37
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
38
|
+
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
|
39
|
+
</symbol>
|
|
40
|
+
<!-- https://icons.getbootstrap.com/icons/globe/ -->
|
|
41
|
+
<symbol id="globe" viewBox="0 0 16 16">
|
|
42
|
+
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z" />
|
|
43
|
+
</symbol>
|
|
44
|
+
<!-- https://icons.getbootstrap.com/icons/info-circle/ -->
|
|
45
|
+
<symbol id="info" viewBox="0 0 16 16">
|
|
46
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
47
|
+
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
|
|
48
|
+
</symbol>
|
|
49
|
+
<!-- https://icons.getbootstrap.com/icons/moon-stars-fill/ -->
|
|
50
|
+
<symbol id="moon_stars_fill" viewBox="0 0 16 16">
|
|
51
|
+
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
|
|
52
|
+
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
|
|
53
|
+
</symbol>
|
|
54
|
+
<!-- https://icons.getbootstrap.com/icons/pause-btn/ -->
|
|
55
|
+
<symbol id="pause" viewBox="0 0 16 16">
|
|
56
|
+
<path d="M6 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5z" />
|
|
57
|
+
</symbol>
|
|
58
|
+
<!-- https://icons.getbootstrap.com/icons/play/ -->
|
|
59
|
+
<symbol id="play" viewBox="0 0 16 16">
|
|
60
|
+
<path d="M10.804 8 5 4.633v6.734L10.804 8zm.792-.696a.802.802 0 0 1 0 1.392l-6.363 3.692C4.713 12.69 4 12.345 4 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692z" />
|
|
61
|
+
</symbol>
|
|
62
|
+
<!-- https://icons.getbootstrap.com/icons/skip-forward/ -->
|
|
63
|
+
<symbol id="skip_forward" viewBox="0 0 16 16">
|
|
64
|
+
<path d="M15.5 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V8.752l-6.267 3.636c-.52.302-1.233-.043-1.233-.696v-2.94l-6.267 3.636C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696L7.5 7.248v-2.94c0-.653.713-.998 1.233-.696L15 7.248V4a.5.5 0 0 1 .5-.5zM1 4.633v6.734L6.804 8 1 4.633zm7.5 0v6.734L14.304 8 8.5 4.633z" />
|
|
65
|
+
</symbol>
|
|
66
|
+
<!-- https://icons.getbootstrap.com/icons/stop/ -->
|
|
67
|
+
<symbol id="stop" viewBox="0 0 16 16">
|
|
68
|
+
<path d="M3.5 5A1.5 1.5 0 0 1 5 3.5h6A1.5 1.5 0 0 1 12.5 5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5zM5 4.5a.5.5 0 0 0-.5.5v6a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5V5a.5.5 0 0 0-.5-.5H5z" />
|
|
69
|
+
</symbol>
|
|
70
|
+
<!-- https://icons.getbootstrap.com/icons/sun-fill/ -->
|
|
71
|
+
<symbol id="sun_fill" viewBox="0 0 16 16">
|
|
72
|
+
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
|
|
73
|
+
</symbol>
|
|
74
|
+
<!-- https://icons.getbootstrap.com/icons/trash/ -->
|
|
75
|
+
<symbol id="trash" viewBox="0 0 16 16">
|
|
76
|
+
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
|
|
77
|
+
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" />
|
|
78
|
+
</symbol>
|
|
79
|
+
</svg>
|
|
@@ -31,11 +31,14 @@ module GoodJob
|
|
|
31
31
|
content_tag :span, icon, **options
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def render_icon(name, **options)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
def render_icon(name, class: nil, **options)
|
|
35
|
+
tag.svg(viewBox: "0 0 16 16", class: "svg-icon #{binding.local_variable_get(:class)}", **options) do
|
|
36
|
+
tag.use(fill: "currentColor", href: "#{icons_path}##{name}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def icons_path
|
|
41
|
+
@_icons_path ||= frontend_static_path(:icons, format: :svg, locale: nil)
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
end
|
|
@@ -49,8 +49,8 @@ module GoodJob
|
|
|
49
49
|
|
|
50
50
|
lock_condition = "#{function}(('x' || substr(md5(#{connection.quote(table_name)} || '-' || #{connection.quote_table_name(cte_table.name)}.#{connection.quote_column_name(column)}::text), 1, 16))::bit(64)::bigint)"
|
|
51
51
|
query = cte_table.project(cte_table[:id])
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
.with(composed_cte)
|
|
53
|
+
.where(defined?(Arel::Nodes::BoundSqlLiteral) ? Arel::Nodes::BoundSqlLiteral.new(lock_condition, [], {}) : Arel::Nodes::SqlLiteral.new(lock_condition))
|
|
54
54
|
|
|
55
55
|
limit = original_query.arel.ast.limit
|
|
56
56
|
query.limit = limit.value if limit.present?
|
|
@@ -174,8 +174,11 @@ module GoodJob
|
|
|
174
174
|
if unlock_session
|
|
175
175
|
advisory_unlock_session
|
|
176
176
|
else
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
unlock_function = advisory_unlockable_function(function)
|
|
178
|
+
if unlock_function
|
|
179
|
+
records.each do |record|
|
|
180
|
+
record.advisory_unlock(key: record.lockable_column_key(column: column), function: unlock_function)
|
|
181
|
+
end
|
|
179
182
|
end
|
|
180
183
|
end
|
|
181
184
|
end
|
|
@@ -209,7 +212,8 @@ module GoodJob
|
|
|
209
212
|
begin
|
|
210
213
|
yield
|
|
211
214
|
ensure
|
|
212
|
-
|
|
215
|
+
unlock_function = advisory_unlockable_function(function)
|
|
216
|
+
advisory_unlock_key(key, function: unlock_function) if unlock_function
|
|
213
217
|
end
|
|
214
218
|
end
|
|
215
219
|
|
|
@@ -220,6 +224,9 @@ module GoodJob
|
|
|
220
224
|
# @param function [String, Symbol] Postgres Advisory Lock function name to use
|
|
221
225
|
# @return [Boolean] whether the lock was released.
|
|
222
226
|
def advisory_unlock_key(key, function: advisory_unlockable_function)
|
|
227
|
+
raise ArgumentError, "Cannot unlock transactional locks" if function.include? "_xact_"
|
|
228
|
+
raise ArgumentError, "No unlock function provide" if function.blank?
|
|
229
|
+
|
|
223
230
|
query = <<~SQL.squish
|
|
224
231
|
SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
|
|
225
232
|
SQL
|
|
@@ -284,6 +291,8 @@ module GoodJob
|
|
|
284
291
|
# @param function [String, Symbol] name of advisory lock or unlock function
|
|
285
292
|
# @return [Boolean]
|
|
286
293
|
def advisory_unlockable_function(function = advisory_lockable_function)
|
|
294
|
+
return nil if function.include? "_xact_" # Cannot unlock transactional locks
|
|
295
|
+
|
|
287
296
|
function.to_s.sub("_lock", "_unlock").sub("_try_", "_")
|
|
288
297
|
end
|
|
289
298
|
|
|
@@ -358,7 +367,8 @@ module GoodJob
|
|
|
358
367
|
begin
|
|
359
368
|
yield
|
|
360
369
|
ensure
|
|
361
|
-
|
|
370
|
+
unlock_function = self.class.advisory_unlockable_function(function)
|
|
371
|
+
advisory_unlock(key: key, function: unlock_function) if unlock_function
|
|
362
372
|
end
|
|
363
373
|
end
|
|
364
374
|
|
|
@@ -403,7 +413,7 @@ module GoodJob
|
|
|
403
413
|
# @param key [String, Symbol] Key to lock against
|
|
404
414
|
# @param function [String, Symbol] Postgres Advisory Lock function name to use
|
|
405
415
|
# @return [void]
|
|
406
|
-
def advisory_unlock!(key: lockable_key, function: self.class.advisory_unlockable_function
|
|
416
|
+
def advisory_unlock!(key: lockable_key, function: self.class.advisory_unlockable_function)
|
|
407
417
|
advisory_unlock(key: key, function: function) while advisory_locked?
|
|
408
418
|
end
|
|
409
419
|
|
|
@@ -16,27 +16,23 @@ module GoodJob
|
|
|
16
16
|
# @return [Symbol]
|
|
17
17
|
def status
|
|
18
18
|
if finished_at.present?
|
|
19
|
-
if error.present?
|
|
20
|
-
:retried
|
|
21
|
-
elsif error.present? && retried_good_job_id.nil?
|
|
19
|
+
if error.present?
|
|
22
20
|
:discarded
|
|
23
21
|
else
|
|
24
22
|
:succeeded
|
|
25
23
|
end
|
|
26
|
-
elsif
|
|
27
|
-
if serialized_params.fetch('executions', 0) > 1
|
|
28
|
-
:retried
|
|
29
|
-
else
|
|
30
|
-
:scheduled
|
|
31
|
-
end
|
|
32
|
-
elsif running?
|
|
24
|
+
elsif performed_at.present?
|
|
33
25
|
:running
|
|
34
|
-
|
|
26
|
+
elsif (scheduled_at || created_at) <= DateTime.current
|
|
35
27
|
:queued
|
|
28
|
+
elsif error.present?
|
|
29
|
+
:retried
|
|
30
|
+
else
|
|
31
|
+
:scheduled
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
34
|
|
|
39
|
-
# The last relevant timestamp for this
|
|
35
|
+
# The last relevant timestamp for this job
|
|
40
36
|
def last_status_at
|
|
41
37
|
finished_at || performed_at || scheduled_at || created_at
|
|
42
38
|
end
|
|
@@ -102,12 +102,17 @@ module GoodJob
|
|
|
102
102
|
active_jobs = add(active_jobs, &block)
|
|
103
103
|
|
|
104
104
|
Rails.application.executor.wrap do
|
|
105
|
-
|
|
106
|
-
record.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
buffer = GoodJob::Adapter::InlineBuffer.capture do
|
|
106
|
+
record.transaction do
|
|
107
|
+
record.with_advisory_lock(function: "pg_advisory_xact_lock") do
|
|
108
|
+
record.update!(enqueued_at: Time.current)
|
|
109
|
+
|
|
110
|
+
# During inline execution, this could enqueue and execute further jobs
|
|
111
|
+
record._continue_discard_or_finish(lock: false)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
110
114
|
end
|
|
115
|
+
buffer.call
|
|
111
116
|
end
|
|
112
117
|
|
|
113
118
|
active_jobs
|
|
@@ -53,22 +53,25 @@ module GoodJob
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def _continue_discard_or_finish(execution = nil, lock: true)
|
|
56
|
-
execution_discarded = execution && execution.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
execution_discarded = execution && execution.finished_at.present? && execution.error.present?
|
|
57
|
+
buffer = GoodJob::Adapter::InlineBuffer.capture do
|
|
58
|
+
advisory_lock_maybe(lock) do
|
|
59
|
+
Batch.within_thread(batch_id: nil, batch_callback_id: id) do
|
|
60
|
+
reload
|
|
61
|
+
if execution_discarded && !discarded_at
|
|
62
|
+
update(discarded_at: Time.current)
|
|
63
|
+
on_discard.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :discard }) if on_discard.present?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if enqueued_at && !finished_at && jobs.where(finished_at: nil).count.zero?
|
|
67
|
+
update(finished_at: Time.current)
|
|
68
|
+
on_success.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :success }) if !discarded_at && on_success.present?
|
|
69
|
+
on_finish.constantize.set(priority: callback_priority, queue: callback_queue_name).perform_later(to_batch, { event: :finish }) if on_finish.present?
|
|
70
|
+
end
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
end
|
|
74
|
+
buffer.call
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
class PropertySerializer
|
|
@@ -100,9 +103,9 @@ module GoodJob
|
|
|
100
103
|
|
|
101
104
|
private
|
|
102
105
|
|
|
103
|
-
def
|
|
106
|
+
def advisory_lock_maybe(value, &block)
|
|
104
107
|
if value
|
|
105
|
-
with_advisory_lock(function: "
|
|
108
|
+
transaction { with_advisory_lock(function: "pg_advisory_xact_lock", &block) }
|
|
106
109
|
else
|
|
107
110
|
yield
|
|
108
111
|
end
|
|
@@ -1,63 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module GoodJob
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
self.table_name = 'good_job_executions'
|
|
8
|
-
self.implicit_order_column = 'created_at'
|
|
9
|
-
|
|
10
|
-
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', inverse_of: :discrete_executions, optional: true
|
|
11
|
-
|
|
12
|
-
scope :finished, -> { where.not(finished_at: nil) }
|
|
13
|
-
|
|
14
|
-
alias_attribute :performed_at, :created_at
|
|
15
|
-
|
|
16
|
-
# TODO: Remove when support for Rails 6.1 is dropped
|
|
17
|
-
attribute :duration, :interval if ActiveJob.version.canonical_segments.take(2) == [6, 1]
|
|
18
|
-
|
|
19
|
-
def number
|
|
20
|
-
serialized_params.fetch('executions', 0) + 1
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Time between when this job was expected to run and when it started running
|
|
24
|
-
def queue_latency
|
|
25
|
-
created_at - scheduled_at
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Monotonic time between when this job started and finished
|
|
29
|
-
def runtime_latency
|
|
30
|
-
duration
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def last_status_at
|
|
34
|
-
finished_at || created_at
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def status
|
|
38
|
-
if finished_at.present?
|
|
39
|
-
if error.present? && job.finished_at.present?
|
|
40
|
-
:discarded
|
|
41
|
-
elsif error.present?
|
|
42
|
-
:retried
|
|
43
|
-
else
|
|
44
|
-
:succeeded
|
|
45
|
-
end
|
|
46
|
-
else
|
|
47
|
-
:running
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def display_serialized_params
|
|
52
|
-
serialized_params.merge({
|
|
53
|
-
_good_job_execution: attributes.except('serialized_params'),
|
|
54
|
-
})
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def filtered_error_backtrace
|
|
58
|
-
Rails.backtrace_cleaner.clean(error_backtrace || [])
|
|
59
|
-
end
|
|
3
|
+
module GoodJob
|
|
4
|
+
# Deprecated, use +Execution+ instead.
|
|
5
|
+
class DiscreteExecution < Execution
|
|
60
6
|
end
|
|
61
|
-
end
|
|
62
7
|
|
|
63
|
-
ActiveSupport
|
|
8
|
+
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
|
9
|
+
deprecate_constant :DiscreteExecution, 'Execution', deprecator: GoodJob.deprecator
|
|
10
|
+
end
|
|
@@ -1,8 +1,63 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module GoodJob
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
module GoodJob # :nodoc:
|
|
4
|
+
class Execution < BaseRecord
|
|
5
|
+
include ErrorEvents
|
|
6
|
+
|
|
7
|
+
self.table_name = 'good_job_executions'
|
|
8
|
+
self.implicit_order_column = 'created_at'
|
|
9
|
+
|
|
10
|
+
belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'id', inverse_of: :executions, optional: true
|
|
11
|
+
|
|
12
|
+
scope :finished, -> { where.not(finished_at: nil) }
|
|
13
|
+
|
|
14
|
+
alias_attribute :performed_at, :created_at
|
|
15
|
+
|
|
16
|
+
# TODO: Remove when support for Rails 6.1 is dropped
|
|
17
|
+
attribute :duration, :interval if ActiveJob.version.canonical_segments.take(2) == [6, 1]
|
|
18
|
+
|
|
19
|
+
def number
|
|
20
|
+
serialized_params.fetch('executions', 0) + 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Time between when this job was expected to run and when it started running
|
|
24
|
+
def queue_latency
|
|
25
|
+
created_at - scheduled_at
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Monotonic time between when this job started and finished
|
|
29
|
+
def runtime_latency
|
|
30
|
+
duration
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def last_status_at
|
|
34
|
+
finished_at || created_at
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def status
|
|
38
|
+
if finished_at.present?
|
|
39
|
+
if error.present? && job.finished_at.present?
|
|
40
|
+
:discarded
|
|
41
|
+
elsif error.present?
|
|
42
|
+
:retried
|
|
43
|
+
else
|
|
44
|
+
:succeeded
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
:running
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def display_serialized_params
|
|
52
|
+
serialized_params.merge({
|
|
53
|
+
_good_job_execution: attributes.except('serialized_params'),
|
|
54
|
+
})
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def filtered_error_backtrace
|
|
58
|
+
Rails.backtrace_cleaner.clean(error_backtrace || [])
|
|
59
|
+
end
|
|
7
60
|
end
|
|
8
61
|
end
|
|
62
|
+
|
|
63
|
+
ActiveSupport.run_load_hooks(:good_job_execution, GoodJob::Execution)
|
|
@@ -13,22 +13,22 @@ module GoodJob
|
|
|
13
13
|
attr_reader :error_event
|
|
14
14
|
# @return [Boolean, nil]
|
|
15
15
|
attr_reader :unexecutable
|
|
16
|
-
# @return [GoodJob::
|
|
17
|
-
attr_reader :
|
|
16
|
+
# @return [GoodJob::Job, nil]
|
|
17
|
+
attr_reader :retried_job
|
|
18
18
|
|
|
19
19
|
# @param value [Object, nil]
|
|
20
20
|
# @param handled_error [Exception, nil]
|
|
21
21
|
# @param unhandled_error [Exception, nil]
|
|
22
22
|
# @param error_event [String, nil]
|
|
23
23
|
# @param unexecutable [Boolean, nil]
|
|
24
|
-
# @param
|
|
25
|
-
def initialize(value:, handled_error: nil, unhandled_error: nil, error_event: nil, unexecutable: nil,
|
|
24
|
+
# @param retried_job [GoodJob::Job, nil]
|
|
25
|
+
def initialize(value:, handled_error: nil, unhandled_error: nil, error_event: nil, unexecutable: nil, retried_job: nil)
|
|
26
26
|
@value = value
|
|
27
27
|
@handled_error = handled_error
|
|
28
28
|
@unhandled_error = unhandled_error
|
|
29
29
|
@error_event = error_event
|
|
30
30
|
@unexecutable = unexecutable
|
|
31
|
-
@
|
|
31
|
+
@retried_job = retried_job
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# @return [Boolean]
|
|
@@ -38,7 +38,7 @@ module GoodJob
|
|
|
38
38
|
|
|
39
39
|
# @return [Boolean]
|
|
40
40
|
def retried?
|
|
41
|
-
|
|
41
|
+
retried_job.present?
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
end
|