solid_queue 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2af6f7c63701d0cdc2c017e4dde0ab4d73e00140402bb09df68705321c122483
4
- data.tar.gz: 82732122367e5161471f1a76a80b9d11878241fce2a9d095b71e33a2dc6dd5d8
3
+ metadata.gz: e843e842397e1d8141e0457b5b278026b71effe6ebf83d8300d2c4098db56adf
4
+ data.tar.gz: a5db37bdea8dacec796f2dcb912ab8f2a69c929487dff081d8a53fe10c20086c
5
5
  SHA512:
6
- metadata.gz: 255deba66b8fddc1df0a050642d9ef640f609a58a51020feb3aeaede5c04fe64acd64e5cc2088969a688f2ff94b423f06b955d6ad70f5a10d2b7d00dea54ce75
7
- data.tar.gz: 7d41051ef3361b1bb55d6a484e0faa5eb979bafc3d0ef99f6759c6ec1375c4587aba159880be4b2a0534d4821b68227ea4645650822243306b9bbc18947e38d8
6
+ metadata.gz: a6831f7114c24d68ae8ed7b5aebfd4d3df0cc2c7b4e0046e6275a5bf5e46f8da8f5b79e9a303ae908e3581e8b96a132776f4c1a3f55bbf550dd008541e679665
7
+ data.tar.gz: e51b90234b3a60355a96c9163bfc7d95a1f5eb95fcf7a2dc1faf553f04a7c242da2d48493ba5b6574c2dbf4b0d259cb69af1dda576475f1510a31f8325be5f8b
data/README.md CHANGED
@@ -239,7 +239,9 @@ The supervisor is in charge of managing these processes, and it responds to the
239
239
 
240
240
  When receiving a `QUIT` signal, if workers still have jobs in-flight, these will be returned to the queue when the processes are deregistered.
241
241
 
242
- If processes have no chance of cleaning up before exiting (e.g. if someone pulls a cable somewhere), in-flight jobs might remain claimed by the processes executing them. Processes send heartbeats, and the supervisor checks and prunes processes with expired heartbeats, which will release any claimed jobs back to their queues. You can configure both the frequency of heartbeats and the threshold to consider a process dead. See the section below for this.
242
+ If processes have no chance of cleaning up before exiting (e.g. if someone pulls a cable somewhere), in-flight jobs might remain claimed by the processes executing them. Processes send heartbeats, and the supervisor checks and prunes processes with expired heartbeats. Jobs that were claimed by processes with an expired heartbeat will be marked as failed with a `SolidQueue::Processes::ProcessPrunedError`. You can configure both the frequency of heartbeats and the threshold to consider a process dead. See the section below for this.
243
+
244
+ In a similar way, if a worker is terminated in any other way not initiated by the above signals (e.g. a worker is sent a `KILL` signal), jobs in progress will be marked as failed so that they can be inspected, with a `SolidQueue::Processes::Process::ProcessExitError`. Sometimes a job in particular is responsible for this, for example, if it has a memory leak and you have a mechanism to kill processes over a certain memory threshold, so this will help identifying this kind of situation.
243
245
 
244
246
 
245
247
  ### Database configuration
@@ -412,13 +414,19 @@ to your `puma.rb` configuration.
412
414
 
413
415
 
414
416
  ## Jobs and transactional integrity
415
- :warning: Having your jobs in the same ACID-compliant database as your application data enables a powerful yet sharp tool: taking advantage of transactional integrity to ensure some action in your app is not committed unless your job is also committed and viceversa, and ensuring that your job won't be enqueued until the transaction within which you're enqueuing it is committed. This can be very powerful and useful, but it can also backfire if you base some of your logic on this behaviour, and in the future, you move to another active job backend, or if you simply move Solid Queue to its own database, and suddenly the behaviour changes under you.
417
+ :warning: Having your jobs in the same ACID-compliant database as your application data enables a powerful yet sharp tool: taking advantage of transactional integrity to ensure some action in your app is not committed unless your job is also committed and vice versa, and ensuring that your job won't be enqueued until the transaction within which you're enqueuing it is committed. This can be very powerful and useful, but it can also backfire if you base some of your logic on this behaviour, and in the future, you move to another active job backend, or if you simply move Solid Queue to its own database, and suddenly the behaviour changes under you. Because this can be quite tricky and many people shouldn't need to worry about it, by default Solid Queue is configured in a different database as the main app.
416
418
 
417
- Because this can be quite tricky and many people shouldn't need to worry about it, by default Solid Queue is configured in a different database as the main app, **job enqueuing is deferred until any ongoing transaction is committed** thanks to Active Job's built-in capability to do this. This means that even if you run Solid Queue in the same DB as your app, you won't be taking advantage of this transactional integrity.
419
+ Starting from Rails 8, an option which doesn't rely on this transactional integrity and which Active Job provides is to defer the enqueueing of a job inside an Active Record transaction until that transaction successfully commits. This option can be set via the [`enqueue_after_transaction_commit`](https://edgeapi.rubyonrails.org/classes/ActiveJob/Enqueuing.html#method-c-enqueue_after_transaction_commit) class method on the job level and is by default disabled. Either it can be enabled for individual jobs or for all jobs through `ApplicationJob`:
418
420
 
419
- If you prefer to change this, you can set [`config.active_job.enqueue_after_transaction_commit`](https://edgeguides.rubyonrails.org/configuring.html#config-active-job-enqueue-after-transaction-commit) to `never`. You can also set this on a per-job basis.
421
+ ```ruby
422
+ class ApplicationJob < ActiveJob::Base
423
+ self.enqueue_after_transaction_commit = true
424
+ end
425
+ ```
426
+
427
+ Using this option, you can also use Solid Queue in the same database as your app but not rely on transactional integrity.
420
428
 
421
- If you set that to `never` but still want to make sure you're not inadvertently on transactional integrity, you can make sure that:
429
+ If you don't set this option but still want to make sure you're not inadvertently on transactional integrity, you can make sure that:
422
430
  - Your jobs relying on specific data are always enqueued on [`after_commit` callbacks](https://guides.rubyonrails.org/active_record_callbacks.html#after-commit-and-after-rollback) or otherwise from a place where you're certain that whatever data the job will use has been committed to the database before the job is enqueued.
423
431
  - Or, you configure a different database for Solid Queue, even if it's the same as your app, ensuring that a different connection on the thread handling requests or running jobs for your app will be used to enqueue jobs. For example:
424
432
 
@@ -433,6 +441,7 @@ If you set that to `never` but still want to make sure you're not inadvertently
433
441
  config.solid_queue.connects_to = { database: { writing: :primary, reading: :replica } }
434
442
  ```
435
443
 
444
+
436
445
  ## Recurring tasks
437
446
 
438
447
  Solid Queue supports defining recurring tasks that run at specific times in the future, on a regular basis like cron jobs. These are managed by the scheduler process and are defined in their own configuration file. By default, the file is located in `config/recurring.yml`, but you can set a different path using the environment variable `SOLID_QUEUE_RECURRING_SCHEDULE` or by using the `--recurring_schedule_file` option with `bin/jobs`, like this:
@@ -40,6 +40,19 @@ module SolidQueue
40
40
  @size ||= ReadyExecution.queued_as(name).count
41
41
  end
42
42
 
43
+ def latency
44
+ @latency ||= begin
45
+ now = Time.current
46
+ oldest_enqueued_at = ReadyExecution.queued_as(name).minimum(:created_at) || now
47
+
48
+ (now - oldest_enqueued_at).to_i
49
+ end
50
+ end
51
+
52
+ def human_latency
53
+ ActiveSupport::Duration.build(latency).inspect
54
+ end
55
+
43
56
  def ==(queue)
44
57
  name == queue.name
45
58
  end
@@ -11,9 +11,11 @@ class SolidQueue::InstallGenerator < Rails::Generators::Base
11
11
  chmod "bin/jobs", 0755 & ~File.umask, verbose: false
12
12
  end
13
13
 
14
- def configure_active_job_adapter
15
- gsub_file Pathname(destination_root).join("config/environments/production.rb"),
16
- /(# )?config\.active_job\.queue_adapter\s+=.*/,
14
+ def configure_adapter_and_database
15
+ pathname = Pathname(destination_root).join("config/environments/production.rb")
16
+
17
+ gsub_file pathname, /\n\s*config\.solid_queue\.connects_to\s+=.*\n/, "\n", verbose: false
18
+ gsub_file pathname, /(# )?config\.active_job\.queue_adapter\s+=.*\n/,
17
19
  "config.active_job.queue_adapter = :solid_queue\n" +
18
20
  " config.solid_queue.connects_to = { database: { writing: :queue } }\n"
19
21
  end
@@ -7,31 +7,23 @@ module SolidQueue::Processes
7
7
  end
8
8
 
9
9
  private
10
- SELF_PIPE_BLOCK_SIZE = 11
11
10
 
12
11
  def interrupt
13
- self_pipe[:writer].write_nonblock(".")
14
- rescue Errno::EAGAIN, Errno::EINTR
15
- # Ignore writes that would block and retry
16
- # if another signal arrived while writing
17
- retry
12
+ queue << true
18
13
  end
19
14
 
20
15
  def interruptible_sleep(time)
21
- if time > 0 && self_pipe[:reader].wait_readable(time)
22
- loop { self_pipe[:reader].read_nonblock(SELF_PIPE_BLOCK_SIZE) }
23
- end
24
- rescue Errno::EAGAIN, Errno::EINTR
16
+ # Invoking from the main thread can result in a 35% slowdown (at least when running the test suite).
17
+ # Using some form of Async (Futures) addresses this performance issue.
18
+ Concurrent::Promises.future(time) do |timeout|
19
+ if timeout > 0 && queue.pop(timeout:)
20
+ queue.clear
21
+ end
22
+ end.value
25
23
  end
26
24
 
27
- # Self-pipe for signal-handling (http://cr.yp.to/docs/selfpipe.html)
28
- def self_pipe
29
- @self_pipe ||= create_self_pipe
30
- end
31
-
32
- def create_self_pipe
33
- reader, writer = IO.pipe
34
- { reader: reader, writer: writer }
25
+ def queue
26
+ @queue ||= Queue.new
35
27
  end
36
28
  end
37
29
  end
@@ -41,6 +41,7 @@ module SolidQueue
41
41
 
42
42
  private
43
43
  def persist_tasks
44
+ SolidQueue::RecurringTask.static.where.not(key: task_keys).delete_all
44
45
  SolidQueue::RecurringTask.create_or_update_all configured_tasks
45
46
  end
46
47
 
@@ -1,3 +1,3 @@
1
1
  module SolidQueue
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rosa Gutierrez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-16 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: debug
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '1.9'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '1.9'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: mocha
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +192,34 @@ dependencies:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rdoc
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: logger
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
195
223
  description: Database-backed Active Job backend.
196
224
  email:
197
225
  - rosa@37signals.com