good_job 4.1.0 → 4.2.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 +71 -0
- data/README.md +10 -10
- 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/batches_controller.rb +6 -0
- 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/error_events.rb +2 -2
- data/app/models/concerns/good_job/reportable.rb +8 -12
- data/app/models/good_job/batch.rb +31 -9
- data/app/models/good_job/batch_record.rb +19 -20
- 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 +543 -12
- data/app/models/good_job/process.rb +14 -3
- data/app/views/good_job/batches/_jobs.erb +1 -1
- data/app/views/good_job/batches/_table.erb +7 -1
- data/app/views/good_job/batches/show.html.erb +8 -0
- 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 +54 -49
- data/config/locales/en.yml +5 -0
- data/config/locales/es.yml +19 -14
- data/config/locales/fr.yml +5 -0
- data/config/locales/it.yml +5 -0
- data/config/locales/ja.yml +10 -5
- data/config/locales/ko.yml +9 -4
- data/config/locales/nl.yml +5 -0
- data/config/locales/pt-BR.yml +5 -0
- data/config/locales/ru.yml +5 -0
- data/config/locales/tr.yml +5 -0
- data/config/locales/uk.yml +6 -1
- data/config/routes.rb +8 -4
- data/lib/good_job/active_job_extensions/concurrency.rb +109 -98
- data/lib/good_job/adapter/inline_buffer.rb +73 -0
- data/lib/good_job/adapter.rb +59 -53
- data/lib/good_job/capsule_tracker.rb +2 -2
- data/lib/good_job/configuration.rb +13 -12
- data/lib/good_job/cron_manager.rb +1 -3
- data/lib/good_job/current_thread.rb +4 -4
- data/lib/good_job/notifier/process_heartbeat.rb +3 -2
- 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: 6b8f73f25f420a1b95841f757ef3eae435fc19e442f157bae03af0e751d619a2
|
4
|
+
data.tar.gz: e6737d5a487ed8d43c0c9dc078b72c075784cfb00889835e63b8ca2ba8788333
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd113e9ed755d014fdbd324bd7aaf62837e384176412d7eeeb4bf277fbd7ed9cb5eba82a39dc4f1e54f4753e635f8c8076194f45d1de461f92301864081cf08f
|
7
|
+
data.tar.gz: 9e028ead209f0467bfe8e2d323ebfe812503ef8310a792270604b525f4aeb5429b7857fbf644bfbc600e14593c0545cf0e628f309aabb8276ca34c6f3ae98367
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,76 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v4.2.0](https://github.com/bensheldon/good_job/tree/v4.2.0) (2024-08-16)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.1.1...v4.2.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Add retry functionality for batches [\#1456](https://github.com/bensheldon/good_job/pull/1456) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- respect perform\_throttle even if perform\_limit is provided [\#1470](https://github.com/bensheldon/good_job/pull/1470) ([doits](https://github.com/doits))
|
14
|
+
- Do not use advisory lock on heartbeat in production [\#1451](https://github.com/bensheldon/good_job/pull/1451) ([bensheldon](https://github.com/bensheldon))
|
15
|
+
|
16
|
+
**Closed issues:**
|
17
|
+
|
18
|
+
- `perform_limit` and `perform_throttle` don't work both [\#1469](https://github.com/bensheldon/good_job/issues/1469)
|
19
|
+
- Edge rails changes breaking binding commits [\#1466](https://github.com/bensheldon/good_job/issues/1466)
|
20
|
+
- Dynamic creation of CronEntries [\#1457](https://github.com/bensheldon/good_job/issues/1457)
|
21
|
+
- Batch callback job not enqueued after success of retried job [\#1450](https://github.com/bensheldon/good_job/issues/1450)
|
22
|
+
|
23
|
+
**Merged pull requests:**
|
24
|
+
|
25
|
+
- Update cron documentation to remove confusion about multiple processes [\#1467](https://github.com/bensheldon/good_job/pull/1467) ([bensheldon](https://github.com/bensheldon))
|
26
|
+
- Update compatibility matrix, remove compatibility code [\#1465](https://github.com/bensheldon/good_job/pull/1465) ([Earlopain](https://github.com/Earlopain))
|
27
|
+
- Run tests with warnings enabled [\#1462](https://github.com/bensheldon/good_job/pull/1462) ([Earlopain](https://github.com/Earlopain))
|
28
|
+
- Add appraisal for Rails 7.2; bracket lowest PG version instead of enumerating all [\#1460](https://github.com/bensheldon/good_job/pull/1460) ([bensheldon](https://github.com/bensheldon))
|
29
|
+
- Fix a few method redefinition warnings [\#1459](https://github.com/bensheldon/good_job/pull/1459) ([Earlopain](https://github.com/Earlopain))
|
30
|
+
- Bump rexml from 3.3.2 to 3.3.3 [\#1455](https://github.com/bensheldon/good_job/pull/1455) ([dependabot[bot]](https://github.com/apps/dependabot))
|
31
|
+
- Remove `smaller_number_is_higher_priority` option from v4 [\#1453](https://github.com/bensheldon/good_job/pull/1453) ([bensheldon](https://github.com/bensheldon))
|
32
|
+
|
33
|
+
## [v4.1.1](https://github.com/bensheldon/good_job/tree/v4.1.1) (2024-07-31)
|
34
|
+
|
35
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.99.2...v4.1.1)
|
36
|
+
|
37
|
+
**Implemented enhancements:**
|
38
|
+
|
39
|
+
- Convert Concurrency extension to use transaction-level \(xact\) advisory locks [\#1439](https://github.com/bensheldon/good_job/pull/1439) ([bensheldon](https://github.com/bensheldon))
|
40
|
+
|
41
|
+
**Fixed bugs:**
|
42
|
+
|
43
|
+
- Fix N+1 on dashboard batches index page [\#1442](https://github.com/bensheldon/good_job/pull/1442) ([bensheldon](https://github.com/bensheldon))
|
44
|
+
- Remove duplicate word on batches show page [\#1441](https://github.com/bensheldon/good_job/pull/1441) ([Earlopain](https://github.com/Earlopain))
|
45
|
+
- 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))
|
46
|
+
|
47
|
+
**Closed issues:**
|
48
|
+
|
49
|
+
- Using Good Job to track all ActiveJob executions, even those with `perform_now` [\#1448](https://github.com/bensheldon/good_job/issues/1448)
|
50
|
+
- RuntimeError when running good\_job executable after updating v3 -\> v4 [\#1445](https://github.com/bensheldon/good_job/issues/1445)
|
51
|
+
- 2.99 -\> 3.0.2 migration not applying cleanly from a fresh DB [\#1435](https://github.com/bensheldon/good_job/issues/1435)
|
52
|
+
- \[Enhance\] Enhance performance via counter cache [\#1375](https://github.com/bensheldon/good_job/issues/1375)
|
53
|
+
- Change how svg images are inserted into partials [\#1364](https://github.com/bensheldon/good_job/issues/1364)
|
54
|
+
|
55
|
+
**Merged pull requests:**
|
56
|
+
|
57
|
+
- Improve some Spanish transcriptions [\#1452](https://github.com/bensheldon/good_job/pull/1452) ([sebastian-cloudnonic](https://github.com/sebastian-cloudnonic))
|
58
|
+
- 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))
|
59
|
+
- Handle empty asset format in Frontends controller [\#1443](https://github.com/bensheldon/good_job/pull/1443) ([bensheldon](https://github.com/bensheldon))
|
60
|
+
- Update development dependencies, Ruby 3.3.4 [\#1437](https://github.com/bensheldon/good_job/pull/1437) ([bensheldon](https://github.com/bensheldon))
|
61
|
+
- 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))
|
62
|
+
- German translation pass [\#1432](https://github.com/bensheldon/good_job/pull/1432) ([Earlopain](https://github.com/Earlopain))
|
63
|
+
- Add Brakeman to linters [\#1431](https://github.com/bensheldon/good_job/pull/1431) ([bensheldon](https://github.com/bensheldon))
|
64
|
+
- 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))
|
65
|
+
- Refactor Concurrency extension for Rails 6.1+ compatibility [\#1429](https://github.com/bensheldon/good_job/pull/1429) ([bensheldon](https://github.com/bensheldon))
|
66
|
+
- Use svg `use` for svg icons [\#1428](https://github.com/bensheldon/good_job/pull/1428) ([Earlopain](https://github.com/Earlopain))
|
67
|
+
- 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))
|
68
|
+
- Refactor Adapter to reference jobs, not executions [\#1426](https://github.com/bensheldon/good_job/pull/1426) ([bensheldon](https://github.com/bensheldon))
|
69
|
+
|
70
|
+
## [v1.99.2](https://github.com/bensheldon/good_job/tree/v1.99.2) (2024-07-18)
|
71
|
+
|
72
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.1.0...v1.99.2)
|
73
|
+
|
3
74
|
## [v4.1.0](https://github.com/bensheldon/good_job/tree/v4.1.0) (2024-07-16)
|
4
75
|
|
5
76
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.99.1...v4.1.0)
|
data/README.md
CHANGED
@@ -164,8 +164,8 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
|
|
164
164
|
|
165
165
|
## Compatibility
|
166
166
|
|
167
|
-
- **Ruby on Rails:** 6.
|
168
|
-
- **Ruby:** Ruby
|
167
|
+
- **Ruby on Rails:** 6.1+
|
168
|
+
- **Ruby:** Ruby 3.0+. JRuby 9.4+
|
169
169
|
- **Postgres:** 10.0+
|
170
170
|
|
171
171
|
## Configuration
|
@@ -307,7 +307,7 @@ Available configuration options are:
|
|
307
307
|
- `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
|
308
308
|
- `logger` ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger` (Default: `Rails.logger`).
|
309
309
|
- `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
|
310
|
-
- `
|
310
|
+
- `advisory_lock_heartbeat` (boolean) whether to use an advisory lock for the purpose of determining whether an execeution process is active. (Default `true` in Development; `false` in other environments)
|
311
311
|
- `retry_on_unhandled_error` (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `false`)
|
312
312
|
- `on_thread_error` (proc, lambda, or callable) will be called when there is an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake. Example:
|
313
313
|
|
@@ -499,7 +499,9 @@ As a second example, you may wish to show a link to a log aggregator next to eac
|
|
499
499
|
|
500
500
|
### Job priority
|
501
501
|
|
502
|
-
|
502
|
+
Smaller `priority` values have higher priority and run first (default: `0`), in accordance with [Active Job's definition of priority](https://github.com/rails/rails/blob/e17faead4f2aff28da079d50f02ea5b015322d5b/activejob/lib/active_job/core.rb#L22).
|
503
|
+
|
504
|
+
Prior to GoodJob v4, this was reversed: higher priority numbers ran first in all versions of GoodJob v3.x and below. When migrating from v3 to v4, new behavior can be opted into by setting `config.good_job.smaller_number_is_higher_priority = true` in your GoodJob initializer or `application.rb`.
|
503
505
|
|
504
506
|
### Labelled jobs
|
505
507
|
|
@@ -611,9 +613,7 @@ GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry
|
|
611
613
|
|
612
614
|
GoodJob can enqueue Active Job jobs on a recurring basis that can be used as a replacement for cron.
|
613
615
|
|
614
|
-
Cron-style jobs can be
|
615
|
-
|
616
|
-
GoodJob's cron uses unique indexes to ensure that only a single job is enqueued at the given time interval. In order for this to work, GoodJob must preserve cron-created job records; these records will be automatically deleted like any other preserved record.
|
616
|
+
Cron-style jobs can be enequeued by any GoodJob process (e.g., CLI or `:async` execution mode) that has `config.good_job.enable_cron` set to `true`. Enabling cron on multiple processes will not enqueue duplicate jobs; GoodJob's cron uses unique indexes to ensure that only a single job is enqueued for a given time interval. In order for this to work, GoodJob must preserve cron-created job records; these records will be automatically deleted like any other preserved record.
|
617
617
|
|
618
618
|
Cron-format is parsed by the [`fugit`](https://github.com/floraison/fugit) gem, which has support for seconds-level resolution (e.g. `* * * * * *`) and natural language parsing (e.g. `every second`).
|
619
619
|
|
@@ -622,8 +622,8 @@ If you use the [Dashboard](#dashboard) the scheduled tasks can be viewed in the
|
|
622
622
|
```ruby
|
623
623
|
# config/environments/application.rb or a specific environment e.g. production.rb
|
624
624
|
|
625
|
-
# Enable cron in this process
|
626
|
-
config.good_job.enable_cron =
|
625
|
+
# Enable cron enqueuing in this process
|
626
|
+
config.good_job.enable_cron = true
|
627
627
|
|
628
628
|
# Configure cron with a hash that has a unique key for each recurring job
|
629
629
|
config.good_job.cron = {
|
@@ -890,7 +890,7 @@ To upgrade:
|
|
890
890
|
|
891
891
|
Notable changes:
|
892
892
|
|
893
|
-
- Only supports Rails 6.1+, CRuby 3.0+ and JRuby 9.4
|
893
|
+
- Only supports Rails 6.1+, CRuby 3.0+ and JRuby 9.4+. Rails 6.0 is no longer supported. CRuby 2.6 and 2.7 are no longer supported. JRuby 9.3 is no longer supported.
|
894
894
|
- Changes job `priority` to give smaller numbers higher priority (default: `0`), in accordance with Active Job's definition of priority.
|
895
895
|
- Enqueues and executes jobs via the `GoodJob::Job` model instead of `GoodJob::Execution`
|
896
896
|
- Setting `config.good_job.cleanup_interval_jobs`, `GOOD_JOB_CLEANUP_INTERVAL_JOBS`, `config.good_job.cleanup_interval_seconds`, or `GOOD_JOB_CLEANUP_INTERVAL_SECONDS` to `nil` or `""` no longer disables count- or time-based cleanups. Set to `false` to disable, or `-1` to run a cleanup after every job execution.
|
@@ -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
|
|
@@ -15,9 +15,9 @@ module GoodJob
|
|
15
15
|
discarded: 5,
|
16
16
|
}
|
17
17
|
if Gem::Version.new(Rails.version) >= Gem::Version.new('7.1.0.a')
|
18
|
-
enum :error_event, error_event_enum, validate: { allow_nil: true }
|
18
|
+
enum :error_event, error_event_enum, validate: { allow_nil: true }, scopes: false
|
19
19
|
else
|
20
|
-
enum error_event: error_event_enum
|
20
|
+
enum error_event: error_event_enum, _scopes: false
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -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
|
@@ -92,22 +92,29 @@ module GoodJob
|
|
92
92
|
if record.new_record?
|
93
93
|
record.save!
|
94
94
|
else
|
95
|
-
record.
|
96
|
-
record.
|
97
|
-
|
98
|
-
|
95
|
+
record.transaction do
|
96
|
+
record.with_advisory_lock(function: "pg_advisory_xact_lock") do
|
97
|
+
record.enqueued_at_will_change!
|
98
|
+
record.finished_at_will_change!
|
99
|
+
record.update!(enqueued_at: nil, finished_at: nil)
|
100
|
+
end
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
104
|
active_jobs = add(active_jobs, &block)
|
103
105
|
|
104
106
|
Rails.application.executor.wrap do
|
105
|
-
|
106
|
-
record.
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
buffer = GoodJob::Adapter::InlineBuffer.capture do
|
108
|
+
record.transaction do
|
109
|
+
record.with_advisory_lock(function: "pg_advisory_xact_lock") do
|
110
|
+
record.update!(enqueued_at: Time.current)
|
111
|
+
|
112
|
+
# During inline execution, this could enqueue and execute further jobs
|
113
|
+
record._continue_discard_or_finish(lock: false)
|
114
|
+
end
|
115
|
+
end
|
110
116
|
end
|
117
|
+
buffer.call
|
111
118
|
end
|
112
119
|
|
113
120
|
active_jobs
|
@@ -130,6 +137,21 @@ module GoodJob
|
|
130
137
|
buffer.active_jobs
|
131
138
|
end
|
132
139
|
|
140
|
+
def retry
|
141
|
+
Rails.application.executor.wrap do
|
142
|
+
buffer = GoodJob::Adapter::InlineBuffer.capture do
|
143
|
+
record.transaction do
|
144
|
+
record.with_advisory_lock(function: "pg_advisory_xact_lock") do
|
145
|
+
record.update!(discarded_at: nil, finished_at: nil)
|
146
|
+
record.jobs.discarded.each(&:retry_job)
|
147
|
+
record._continue_discard_or_finish(lock: false)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
buffer.call
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
133
155
|
def active_jobs
|
134
156
|
record.jobs.map(&:active_job)
|
135
157
|
end
|
@@ -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
|
@@ -81,11 +84,7 @@ module GoodJob
|
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
84
|
-
if Rails.gem_version < Gem::Version.new('
|
85
|
-
# serialize does not yet take a default value, must set via Attributes API
|
86
|
-
attribute :serialized_properties, :json, default: -> { {} }
|
87
|
-
serialize :serialized_properties, PropertySerializer
|
88
|
-
elsif Rails.gem_version < Gem::Version.new('7.1.0.alpha')
|
87
|
+
if Rails.gem_version < Gem::Version.new('7.1.0.alpha')
|
89
88
|
serialize :serialized_properties, PropertySerializer, default: -> { {} }
|
90
89
|
else
|
91
90
|
serialize :serialized_properties, coder: PropertySerializer, default: -> { {} }
|
@@ -100,9 +99,9 @@ module GoodJob
|
|
100
99
|
|
101
100
|
private
|
102
101
|
|
103
|
-
def
|
102
|
+
def advisory_lock_maybe(value, &block)
|
104
103
|
if value
|
105
|
-
with_advisory_lock(function: "
|
104
|
+
transaction { with_advisory_lock(function: "pg_advisory_xact_lock", &block) }
|
106
105
|
else
|
107
106
|
yield
|
108
107
|
end
|