patient_http-sidekiq 1.0.1 → 1.1.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 +19 -0
- data/VERSION +1 -1
- data/lib/patient_http/sidekiq/callback_worker.rb +16 -15
- data/lib/patient_http/sidekiq/configuration.rb +1 -1
- data/lib/patient_http/sidekiq/request_worker.rb +25 -15
- data/lib/patient_http/sidekiq/stats.rb +1 -1
- data/lib/patient_http/sidekiq/task_handler.rb +20 -0
- data/lib/patient_http/sidekiq/task_monitor.rb +49 -6
- data/lib/patient_http/sidekiq/task_monitor_thread.rb +4 -5
- data/lib/patient_http/sidekiq.rb +34 -15
- data/patient_http-sidekiq.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b97f976999ea59ac067153a7698579accb7e7a9941171adbb31e1270ff044fc8
|
|
4
|
+
data.tar.gz: bf45c48c1afaae62826c66f8a2a8f5cd82379b40edc0c9d34faad1c856cfbcd2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bff1df1ca4265615ec56aedce59ef87a08f70344f31324e432e231d114c7b04139424a29cf510d8aac6cc0dad2643149cf1f4b17c194066e3fe13deea4a8989b
|
|
7
|
+
data.tar.gz: 2fb165723aecdd602a638f81a3a2804ff3951e1b3db74c4dc1afbc5a6e32420adc9728fbe06d503bd424616fd5c665faf8cb93b74bbcde2b2193681d0c6f3651
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 1.1.0
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `PatientHttp::Sidekiq.configure` now sets the built configuration as the `PatientHttp.default_configuration` so that secrets registered at the module level with `PatientHttp.register_secret` are applied to the configuration the processor runs with, regardless of boot order. This requires patient_http 1.3.0 or newer.
|
|
12
|
+
|
|
13
|
+
## 1.0.2
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Crash recovery now recovers requests from crashed processes. Previously a crashed process's ID remained in the process registry indefinitely, permanently excluding its in-flight requests from orphan detection unless the Sidekiq Web UI happened to prune it.
|
|
18
|
+
- Fixed calls to nonexistent `Sidekiq.testing?` in error handlers, which would raise `NoMethodError` and permanently kill the task monitor thread on the first transient Redis error.
|
|
19
|
+
- Externally stored request payloads are no longer deleted as soon as the request is submitted to the processor. They are now retained until the request completes so that Sidekiq retries, shutdown re-enqueues, and crash recovery can still fetch them. Dead `RequestWorker` jobs clean up their stored payloads when retries are exhausted.
|
|
20
|
+
- `CallbackWorker` no longer deletes externally stored payloads when the callback raises, so Sidekiq retries can fetch the payload and re-run the callback.
|
|
21
|
+
- Fixed dead job payload cleanup in `CallbackWorker`, which silently failed by calling a nonexistent method.
|
|
22
|
+
- `PatientHttp::Sidekiq.configure` and `reset_configuration!` now reset the memoized external storage so it picks up the new configuration.
|
|
23
|
+
- Heartbeat updates now refresh the TTL on the inflight tracking keys so they cannot expire while long-running requests are still in flight.
|
|
24
|
+
- Fixed race conditions that could create duplicate processors or task monitor threads from concurrent lifecycle calls; starting while the processor is draining no longer replaces it.
|
|
25
|
+
|
|
7
26
|
## 1.0.1
|
|
8
27
|
|
|
9
28
|
### Fixed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0
|
|
1
|
+
1.1.0
|
|
@@ -53,7 +53,7 @@ module PatientHttp
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
begin
|
|
56
|
-
|
|
56
|
+
Sidekiq.external_storage.delete(data)
|
|
57
57
|
rescue => e
|
|
58
58
|
PatientHttp::Sidekiq.configuration.logger&.warn(
|
|
59
59
|
"[PatientHttp::Sidekiq] Failed to delete stored payload for dead job: #{e.class.name} #{e.message}".strip
|
|
@@ -75,21 +75,22 @@ module PatientHttp
|
|
|
75
75
|
actual_data = ref_data ? Sidekiq.external_storage.fetch(data) : data
|
|
76
76
|
actual_data = Sidekiq.decrypt(actual_data)
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
raise ArgumentError, "Unknown result_type: #{result_type}"
|
|
89
|
-
end
|
|
90
|
-
ensure
|
|
91
|
-
Sidekiq.external_storage.delete(ref_data) if ref_data
|
|
78
|
+
if result_type == "response"
|
|
79
|
+
response = PatientHttp::Response.load(actual_data)
|
|
80
|
+
PatientHttp::Sidekiq.invoke_completion_callbacks(response)
|
|
81
|
+
callback_service.on_complete(response)
|
|
82
|
+
elsif result_type == "error"
|
|
83
|
+
error = PatientHttp::Error.load(actual_data)
|
|
84
|
+
PatientHttp::Sidekiq.invoke_error_callbacks(error)
|
|
85
|
+
callback_service.on_error(error)
|
|
86
|
+
else
|
|
87
|
+
raise ArgumentError, "Unknown result_type: #{result_type}"
|
|
92
88
|
end
|
|
89
|
+
|
|
90
|
+
# Only delete the stored payload after the callback succeeds so that
|
|
91
|
+
# Sidekiq retries can still fetch it. Failed jobs are cleaned up by
|
|
92
|
+
# the sidekiq_retries_exhausted hook.
|
|
93
|
+
Sidekiq.external_storage.delete(ref_data) if ref_data
|
|
93
94
|
end
|
|
94
95
|
end
|
|
95
96
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module PatientHttp
|
|
4
4
|
module Sidekiq
|
|
5
|
-
# Configuration for the Sidekiq
|
|
5
|
+
# Configuration for the Sidekiq integration.
|
|
6
6
|
#
|
|
7
7
|
# Wraps PatientHttp::Configuration with Sidekiq-aware defaults and adds
|
|
8
8
|
# Sidekiq-specific options like worker queue/retry settings.
|
|
@@ -15,6 +15,17 @@ module PatientHttp
|
|
|
15
15
|
class RequestWorker
|
|
16
16
|
include ::Sidekiq::Job
|
|
17
17
|
|
|
18
|
+
# Clean up the externally stored request payload when the job exhausts all
|
|
19
|
+
# retries. The payload is normally deleted by TaskHandler when the request
|
|
20
|
+
# completes, so this only fires for requests that never made it that far.
|
|
21
|
+
sidekiq_retries_exhausted do |job, _exception|
|
|
22
|
+
Sidekiq.external_storage.delete(job["args"][0])
|
|
23
|
+
rescue => e
|
|
24
|
+
PatientHttp::Sidekiq.configuration.logger&.warn(
|
|
25
|
+
"[PatientHttp::Sidekiq] Failed to delete stored payload for dead job: #{e.class.name} #{e.message}".strip
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
# Perform the HTTP request.
|
|
19
30
|
#
|
|
20
31
|
# @param data [Hash] Request data (possibly a storage reference) with keys:
|
|
@@ -26,31 +37,30 @@ module PatientHttp
|
|
|
26
37
|
# - "max_redirects" [Integer, nil] Maximum redirects to follow
|
|
27
38
|
# @param callback_service_name [String] Fully qualified callback service class name
|
|
28
39
|
# @param raise_error_responses [Boolean, nil] Whether to treat non-2xx responses as errors;
|
|
29
|
-
#
|
|
40
|
+
# nil is treated as false
|
|
30
41
|
# @param callback_args [Hash, nil] Arguments to pass to the callback
|
|
31
42
|
# @param request_id [String, nil] Unique request ID for tracking
|
|
32
43
|
# @return [void]
|
|
33
44
|
def perform(data, callback_service_name, raise_error_responses, callback_args, request_id)
|
|
34
45
|
# Fetch from external storage if needed
|
|
35
|
-
|
|
36
|
-
actual_data = ref_data ? Sidekiq.external_storage.fetch(data) : data
|
|
46
|
+
actual_data = PatientHttp::ExternalStorage.storage_ref?(data) ? Sidekiq.external_storage.fetch(data) : data
|
|
37
47
|
actual_data = Sidekiq.decrypt(actual_data)
|
|
38
48
|
|
|
39
49
|
request = PatientHttp::Request.load(actual_data)
|
|
40
50
|
sidekiq_job = Sidekiq::Context.current_job
|
|
41
51
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
# The stored payload must not be deleted here: this job hash is re-pushed
|
|
53
|
+
# for Sidekiq retries (e.g. MaxCapacityError), processor shutdown retries,
|
|
54
|
+
# and crash recovery, all of which need to fetch the payload again.
|
|
55
|
+
# TaskHandler deletes it when the request completes.
|
|
56
|
+
RequestExecutor.execute(
|
|
57
|
+
request,
|
|
58
|
+
callback: callback_service_name,
|
|
59
|
+
raise_error_responses: raise_error_responses,
|
|
60
|
+
callback_args: callback_args,
|
|
61
|
+
sidekiq_job: sidekiq_job,
|
|
62
|
+
request_id: request_id
|
|
63
|
+
)
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
@@ -29,6 +29,7 @@ module PatientHttp
|
|
|
29
29
|
def on_complete(response, callback)
|
|
30
30
|
data = store_if_needed(response.as_json)
|
|
31
31
|
CallbackWorker.perform_async(data, "response", callback)
|
|
32
|
+
delete_stored_request_payload
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Trigger the error callback with the error.
|
|
@@ -42,6 +43,7 @@ module PatientHttp
|
|
|
42
43
|
def on_error(error, callback)
|
|
43
44
|
data = store_if_needed(error.as_json)
|
|
44
45
|
CallbackWorker.perform_async(data, "error", callback)
|
|
46
|
+
delete_stored_request_payload
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# Re-enqueue the original Sidekiq job for retry.
|
|
@@ -67,6 +69,24 @@ module PatientHttp
|
|
|
67
69
|
|
|
68
70
|
private
|
|
69
71
|
|
|
72
|
+
# Delete the externally stored request payload once the request has
|
|
73
|
+
# completed. Until then the payload must remain fetchable because the
|
|
74
|
+
# Sidekiq job hash referencing it can be re-pushed by Sidekiq retries,
|
|
75
|
+
# processor shutdown retries, and crash recovery. Only applies to
|
|
76
|
+
# RequestWorker jobs; other job types own their own arguments.
|
|
77
|
+
def delete_stored_request_payload
|
|
78
|
+
return unless @sidekiq_job["class"] == RequestWorker.name
|
|
79
|
+
|
|
80
|
+
data = @sidekiq_job["args"]&.first
|
|
81
|
+
return unless PatientHttp::ExternalStorage.storage_ref?(data)
|
|
82
|
+
|
|
83
|
+
PatientHttp::Sidekiq.external_storage.delete(data)
|
|
84
|
+
rescue => e
|
|
85
|
+
PatientHttp::Sidekiq.configuration.logger&.warn(
|
|
86
|
+
"[PatientHttp::Sidekiq] Failed to delete stored request payload: #{e.class.name} #{e.message}".strip
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
70
90
|
def store_if_needed(data)
|
|
71
91
|
encrypted = Sidekiq.encrypt(data)
|
|
72
92
|
external_storage = PatientHttp::Sidekiq.external_storage
|
|
@@ -226,6 +226,11 @@ module PatientHttp
|
|
|
226
226
|
task_ids.each do |task_id|
|
|
227
227
|
pipeline.call("ZADD", INFLIGHT_INDEX_KEY, "XX", timestamp_ms, full_task_id(task_id))
|
|
228
228
|
end
|
|
229
|
+
# Keep the inflight keys alive while requests are still in flight;
|
|
230
|
+
# otherwise they only get their TTL refreshed when new requests
|
|
231
|
+
# are registered.
|
|
232
|
+
pipeline.call("EXPIRE", INFLIGHT_INDEX_KEY, inflight_ttl)
|
|
233
|
+
pipeline.call("EXPIRE", INFLIGHT_JOBS_KEY, inflight_ttl)
|
|
229
234
|
end
|
|
230
235
|
end
|
|
231
236
|
end
|
|
@@ -401,10 +406,8 @@ module PatientHttp
|
|
|
401
406
|
orphaned_request_ids_by_process = all_orphaned_request_ids.group_by do |request_id|
|
|
402
407
|
request_id.split("/", 2).first
|
|
403
408
|
end
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
end
|
|
407
|
-
orphaned_request_ids = orphaned_request_ids_by_process.except(*all_process_ids).values.flatten
|
|
409
|
+
live_process_ids = prune_stale_processes(orphaned_request_ids_by_process.keys)
|
|
410
|
+
orphaned_request_ids = orphaned_request_ids_by_process.except(*live_process_ids).values.flatten
|
|
408
411
|
|
|
409
412
|
return [] if orphaned_request_ids.empty?
|
|
410
413
|
|
|
@@ -416,6 +419,42 @@ module PatientHttp
|
|
|
416
419
|
orphaned_request_ids.zip(job_payloads).reject { |_id, payload| payload.nil? }
|
|
417
420
|
end
|
|
418
421
|
|
|
422
|
+
# Determine which of the given process IDs belong to live processes,
|
|
423
|
+
# removing dead ones from the process set.
|
|
424
|
+
#
|
|
425
|
+
# Membership in the process set alone doesn't prove liveness: a crashed
|
|
426
|
+
# process never removes itself from the set. A process is only considered
|
|
427
|
+
# live if its max_connections key (refreshed on every heartbeat with a
|
|
428
|
+
# short TTL) still exists. Stale members are removed from the set so
|
|
429
|
+
# their inflight requests can be recovered.
|
|
430
|
+
#
|
|
431
|
+
# @param process_ids [Array<String>] candidate process IDs
|
|
432
|
+
#
|
|
433
|
+
# @return [Array<String>] the subset of process IDs that are live
|
|
434
|
+
def prune_stale_processes(process_ids)
|
|
435
|
+
registered_ids = ::Sidekiq.redis do |redis|
|
|
436
|
+
redis.smembers(PROCESS_SET_KEY)
|
|
437
|
+
end
|
|
438
|
+
candidates = process_ids & registered_ids
|
|
439
|
+
return [] if candidates.empty?
|
|
440
|
+
|
|
441
|
+
max_connection_values = ::Sidekiq.redis do |redis|
|
|
442
|
+
redis.mget(*candidates.map { |process_id| max_connections_key_for(process_id) })
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
stale_process_ids, live_process_ids = candidates.zip(max_connection_values)
|
|
446
|
+
.partition { |_process_id, max_conn| max_conn.nil? }
|
|
447
|
+
.map { |pairs| pairs.map(&:first) }
|
|
448
|
+
|
|
449
|
+
unless stale_process_ids.empty?
|
|
450
|
+
::Sidekiq.redis do |redis|
|
|
451
|
+
redis.srem(PROCESS_SET_KEY, stale_process_ids)
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
live_process_ids
|
|
456
|
+
end
|
|
457
|
+
|
|
419
458
|
# Re-enqueue all orphaned jobs.
|
|
420
459
|
#
|
|
421
460
|
# @param orphaned_requests [Array<Array(String, String)>] array of [request_id, job_payload] pairs
|
|
@@ -503,7 +542,7 @@ module PatientHttp
|
|
|
503
542
|
# @return [Integer] TTL in seconds
|
|
504
543
|
def inflight_ttl
|
|
505
544
|
# Set to 3x the orphan threshold, with a minimum of 1 hour
|
|
506
|
-
[config.orphan_threshold * 3, 3600].max
|
|
545
|
+
[config.orphan_threshold * 3, 3600].max.round
|
|
507
546
|
end
|
|
508
547
|
|
|
509
548
|
# Calculate the TTL for the garbage collection lock.
|
|
@@ -535,7 +574,11 @@ module PatientHttp
|
|
|
535
574
|
end
|
|
536
575
|
|
|
537
576
|
def max_connections_key
|
|
538
|
-
|
|
577
|
+
max_connections_key_for(@lock_identifier)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def max_connections_key_for(process_id)
|
|
581
|
+
"#{PROCESS_SET_KEY}:#{process_id}:max_connections"
|
|
539
582
|
end
|
|
540
583
|
end
|
|
541
584
|
end
|
|
@@ -35,8 +35,7 @@ module PatientHttp
|
|
|
35
35
|
#
|
|
36
36
|
# @return [void]
|
|
37
37
|
def start
|
|
38
|
-
return
|
|
39
|
-
@running.make_true
|
|
38
|
+
return unless @running.make_true
|
|
40
39
|
@stop_signal.reset
|
|
41
40
|
|
|
42
41
|
@task_monitor.ping_process
|
|
@@ -46,7 +45,7 @@ module PatientHttp
|
|
|
46
45
|
rescue => e
|
|
47
46
|
# Log error but don't crash
|
|
48
47
|
@config.logger&.error("[PatientHttp::Sidekiq] Monitor error: #{e.message}\n#{e.backtrace.join("\n")}")
|
|
49
|
-
raise if
|
|
48
|
+
raise if PatientHttp.testing?
|
|
50
49
|
end
|
|
51
50
|
|
|
52
51
|
@thread.name = "patient-http-monitor"
|
|
@@ -120,7 +119,7 @@ module PatientHttp
|
|
|
120
119
|
@config.logger&.debug("[PatientHttp::Sidekiq] Updated heartbeats for #{request_ids.size} inflight requests")
|
|
121
120
|
rescue => e
|
|
122
121
|
@config.logger&.error("[PatientHttp::Sidekiq] Failed to update heartbeats: #{e.class} - #{e.message}")
|
|
123
|
-
raise if
|
|
122
|
+
raise if PatientHttp.testing?
|
|
124
123
|
end
|
|
125
124
|
|
|
126
125
|
# Attempt to acquire GC lock and clean up orphaned requests.
|
|
@@ -147,7 +146,7 @@ module PatientHttp
|
|
|
147
146
|
end
|
|
148
147
|
rescue => e
|
|
149
148
|
@config.logger&.error("[PatientHttp::Sidekiq] Garbage collection failed: #{e.class} - #{e.message}")
|
|
150
|
-
raise if
|
|
149
|
+
raise if PatientHttp.testing?
|
|
151
150
|
end
|
|
152
151
|
end
|
|
153
152
|
end
|
data/lib/patient_http/sidekiq.rb
CHANGED
|
@@ -87,18 +87,26 @@ module PatientHttp
|
|
|
87
87
|
@after_error_callbacks = []
|
|
88
88
|
@external_storage = nil
|
|
89
89
|
@request_handler = nil
|
|
90
|
+
@lifecycle_mutex = Mutex.new
|
|
90
91
|
|
|
91
92
|
class << self
|
|
92
93
|
attr_writer :configuration
|
|
93
94
|
|
|
94
|
-
# Configure the gem with a block
|
|
95
|
+
# Configure the gem with a block. The built configuration is also set as the
|
|
96
|
+
# `PatientHttp.default_configuration` so that secrets registered at the module
|
|
97
|
+
# level with `PatientHttp.register_secret` are applied to the configuration the
|
|
98
|
+
# processor runs with, regardless of boot order.
|
|
99
|
+
#
|
|
95
100
|
# @yield [Configuration] the configuration object
|
|
96
101
|
# @return [Configuration]
|
|
97
102
|
def configure
|
|
98
103
|
configuration = Configuration.new
|
|
99
104
|
yield(configuration) if block_given?
|
|
100
|
-
register_handler
|
|
101
105
|
@configuration = configuration
|
|
106
|
+
@external_storage = nil
|
|
107
|
+
register_handler
|
|
108
|
+
PatientHttp.default_configuration = configuration
|
|
109
|
+
configuration
|
|
102
110
|
end
|
|
103
111
|
|
|
104
112
|
# Ensure configuration is initialized
|
|
@@ -111,6 +119,7 @@ module PatientHttp
|
|
|
111
119
|
# @return [Configuration]
|
|
112
120
|
def reset_configuration!
|
|
113
121
|
@configuration = nil
|
|
122
|
+
@external_storage = nil
|
|
114
123
|
configuration
|
|
115
124
|
end
|
|
116
125
|
|
|
@@ -234,6 +243,8 @@ module PatientHttp
|
|
|
234
243
|
|
|
235
244
|
# Register Sidekiq as the request handler for processing HTTP requests. This is called
|
|
236
245
|
# automatically when the processor starts or you call PatientHttp::Sidekiq.configure.
|
|
246
|
+
#
|
|
247
|
+
# @return [void]
|
|
237
248
|
def register_handler
|
|
238
249
|
@request_handler ||= lambda do |request:, callback:, raise_error_responses:, callback_args:|
|
|
239
250
|
execute(
|
|
@@ -251,14 +262,16 @@ module PatientHttp
|
|
|
251
262
|
#
|
|
252
263
|
# @return [void]
|
|
253
264
|
def start
|
|
254
|
-
|
|
265
|
+
@lifecycle_mutex.synchronize do
|
|
266
|
+
return if @processor && !@processor.stopped?
|
|
255
267
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
@processor = PatientHttp::Processor.new(configuration)
|
|
269
|
+
@processor.observe(ProcessorObserver.new(@processor))
|
|
270
|
+
configuration.observers.each do |observer|
|
|
271
|
+
@processor.observe(observer)
|
|
272
|
+
end
|
|
273
|
+
@processor.start
|
|
260
274
|
end
|
|
261
|
-
@processor.start
|
|
262
275
|
|
|
263
276
|
register_handler
|
|
264
277
|
end
|
|
@@ -267,9 +280,11 @@ module PatientHttp
|
|
|
267
280
|
#
|
|
268
281
|
# @return [void]
|
|
269
282
|
def quiet
|
|
270
|
-
|
|
283
|
+
@lifecycle_mutex.synchronize do
|
|
284
|
+
return unless running?
|
|
271
285
|
|
|
272
|
-
|
|
286
|
+
@processor.drain
|
|
287
|
+
end
|
|
273
288
|
end
|
|
274
289
|
|
|
275
290
|
# Stop the processor gracefully
|
|
@@ -281,10 +296,12 @@ module PatientHttp
|
|
|
281
296
|
PatientHttp.unregister_handler(@request_handler)
|
|
282
297
|
end
|
|
283
298
|
|
|
284
|
-
|
|
299
|
+
@lifecycle_mutex.synchronize do
|
|
300
|
+
return unless @processor
|
|
285
301
|
|
|
286
|
-
|
|
287
|
-
|
|
302
|
+
@processor.stop(timeout: timeout)
|
|
303
|
+
@processor = nil
|
|
304
|
+
end
|
|
288
305
|
end
|
|
289
306
|
|
|
290
307
|
# Reset all state (useful for testing)
|
|
@@ -292,8 +309,10 @@ module PatientHttp
|
|
|
292
309
|
# @return [void]
|
|
293
310
|
# @api private
|
|
294
311
|
def reset!
|
|
295
|
-
@
|
|
296
|
-
|
|
312
|
+
@lifecycle_mutex.synchronize do
|
|
313
|
+
@processor&.stop(timeout: 0)
|
|
314
|
+
@processor = nil
|
|
315
|
+
end
|
|
297
316
|
@configuration = nil
|
|
298
317
|
@external_storage = nil
|
|
299
318
|
@after_completion_callbacks = []
|
|
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
|
|
|
40
40
|
spec.required_ruby_version = ">= 3.2"
|
|
41
41
|
|
|
42
42
|
spec.add_dependency "sidekiq", ">= 7.0"
|
|
43
|
-
spec.add_dependency "patient_http"
|
|
43
|
+
spec.add_dependency "patient_http", ">= 1.3.0"
|
|
44
44
|
|
|
45
45
|
spec.add_development_dependency "bundler"
|
|
46
46
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: patient_http-sidekiq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
32
|
+
version: 1.3.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
39
|
+
version: 1.3.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: bundler
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|