async-background 1.1.0 → 1.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 +14 -0
- data/lib/async/background/job.rb +75 -28
- data/lib/async/background/metrics.rb +41 -33
- data/lib/async/background/min_heap.rb +16 -12
- data/lib/async/background/queue/client.rb +11 -8
- data/lib/async/background/queue/options.rb +3 -3
- data/lib/async/background/queue/schema.rb +5 -1
- data/lib/async/background/queue/socket_notifier.rb +14 -4
- data/lib/async/background/queue/socket_waker.rb +27 -7
- data/lib/async/background/queue/sql.rb +31 -31
- data/lib/async/background/queue/store.rb +23 -7
- data/lib/async/background/runner/lifecycle.rb +131 -0
- data/lib/async/background/runner/queue_execution.rb +72 -52
- data/lib/async/background/runner/schedule.rb +4 -4
- data/lib/async/background/runner.rb +57 -126
- data/lib/async/background/runtime/notification.rb +2 -1
- data/lib/async/background/runtime/semaphore.rb +15 -11
- data/lib/async/background/runtime/task.rb +18 -17
- data/lib/async/background/runtime/task_group.rb +2 -2
- data/lib/async/background/runtime.rb +56 -53
- data/lib/async/background/scheduler.rb +4 -4
- data/lib/async/background/version.rb +1 -1
- data/lib/async/background/web/app.rb +26 -19
- data/lib/async/background/web/assets/app.css +174 -0
- data/lib/async/background/web/assets/app.js +486 -0
- data/lib/async/background/web/assets/index.html +29 -0
- data/lib/async/background/web/assets.rb +4 -697
- data/lib/async/background/web/auth.rb +3 -3
- data/lib/async/background/web/configuration.rb +15 -15
- data/lib/async/background/web/cursor.rb +14 -7
- data/lib/async/background/web/metrics_reader.rb +4 -6
- data/lib/async/background/web/response.rb +1 -1
- data/lib/async/background/web/serializer.rb +2 -2
- data/lib/async/background/web/snapshot.rb +5 -5
- data/lib/async/background/web/sql.rb +7 -7
- data/lib/async/background/web/stream.rb +52 -32
- data/lib/async/background/web.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9bd77f8af5601609ef43abb3b108ce225e0b20357e712023e2118a06c7e3496
|
|
4
|
+
data.tar.gz: a0e71bfec3a834da9d7a2df0fbd161ee42bbedff00ea1e90f62bee89d9c1edb3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f7d7bd4011648a53092257c35906face2cb05bb46c24233cd9739fd37fa07b19d6476e1ad3a433891abce6146fde4c6fcbaf7fff1a73721fe2fde0dcdde1243
|
|
7
|
+
data.tar.gz: 25070911a4a50ed9142f582bffe6f385aeb236885e417409324804b1525cdea5e30889dc92d14f15c0480386a790732cf84aab54858d73c1b0177eff65af8f9c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Refactored runner lifecycle and queue execution, runtime task coordination,
|
|
8
|
+
job option normalization, metrics access, and web request handling into
|
|
9
|
+
smaller focused components.
|
|
10
|
+
- Moved dashboard HTML, CSS, and JavaScript into separate packaged assets.
|
|
11
|
+
- No public API or queue schema changes are intended.
|
|
12
|
+
|
|
13
|
+
### Validation
|
|
14
|
+
|
|
15
|
+
- Full spec suite: 431 examples, 0 failures.
|
|
16
|
+
|
|
3
17
|
## 1.1.0
|
|
4
18
|
|
|
5
19
|
The gem no longer depends on `async`. The same worker runs under any
|
data/lib/async/background/job.rb
CHANGED
|
@@ -8,42 +8,81 @@ module Async
|
|
|
8
8
|
BACKOFFS = %i[fixed linear exponential].freeze
|
|
9
9
|
DEFAULT_JITTER_FOR = { fixed: 0.0, linear: 0.0, exponential: 0.5 }.freeze
|
|
10
10
|
|
|
11
|
+
NormalizedOptions = Data.define(:timeout, :retries, :retry_delay, :backoff, :attempt, :jitter)
|
|
12
|
+
private_constant :NormalizedOptions
|
|
13
|
+
|
|
11
14
|
Options = Data.define(:timeout, :retry, :retry_delay, :backoff, :attempt, :jitter) do
|
|
12
15
|
def initialize(timeout: DEFAULT_TIMEOUT, retry: 0, retry_delay: nil, backoff: :fixed, attempt: nil, jitter: nil)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
raise ArgumentError, "retry must be >= 0" if retries.negative?
|
|
22
|
-
raise ArgumentError, "attempt must be >= 0" if attempt && attempt.negative?
|
|
23
|
-
raise ArgumentError, "backoff must be one of #{BACKOFFS.inspect}" unless BACKOFFS.include?(backoff)
|
|
24
|
-
if retries.positive?
|
|
25
|
-
raise ArgumentError, "retry_delay is required when retry > 0" if retry_delay.nil?
|
|
26
|
-
raise ArgumentError, "retry_delay must be > 0" unless retry_delay.positive?
|
|
27
|
-
end
|
|
28
|
-
raise ArgumentError, "jitter must be in [0, 1]" if jitter && !(0.0..1.0).cover?(jitter)
|
|
29
|
-
|
|
30
|
-
super(timeout:, retry: retries, retry_delay:, backoff:, attempt:, jitter:)
|
|
16
|
+
retries = Integer(binding.local_variable_get(:retry) || 0)
|
|
17
|
+
settings = NormalizedOptions.new(
|
|
18
|
+
Integer(timeout), retries, retry_delay.nil? ? nil : Float(retry_delay), backoff.to_sym,
|
|
19
|
+
attempt.nil? ? nil : Integer(attempt), jitter.nil? ? nil : Float(jitter)
|
|
20
|
+
)
|
|
21
|
+
validate_options!(settings)
|
|
22
|
+
super(timeout: settings.timeout, retry: settings.retries, retry_delay: settings.retry_delay,
|
|
23
|
+
backoff: settings.backoff, attempt: settings.attempt, jitter: settings.jitter)
|
|
31
24
|
end
|
|
32
25
|
|
|
33
26
|
def retry? = self.retry.positive?
|
|
34
27
|
def attempts_made = attempt || 0
|
|
35
28
|
def next_attempt = attempts_made + 1
|
|
36
29
|
def with_attempt(n) = with(attempt: Integer(n))
|
|
30
|
+
|
|
37
31
|
def next_retry_delay(for_attempt = next_attempt, rng: Random)
|
|
38
|
-
raise ArgumentError,
|
|
39
|
-
raise ArgumentError,
|
|
32
|
+
raise ArgumentError, 'attempt must be > 0' unless for_attempt.positive?
|
|
33
|
+
raise ArgumentError, 'retry_delay must be configured' if retry_delay.nil?
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
apply_jitter(base_retry_delay(for_attempt), rng)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def validate_options!(settings)
|
|
41
|
+
validate_timeout!(settings.timeout)
|
|
42
|
+
validate_retry_count!(settings.retries)
|
|
43
|
+
validate_attempt!(settings.attempt)
|
|
44
|
+
validate_backoff!(settings.backoff)
|
|
45
|
+
validate_retry_delay!(settings)
|
|
46
|
+
validate_jitter!(settings.jitter)
|
|
47
|
+
end
|
|
46
48
|
|
|
49
|
+
def validate_timeout!(timeout)
|
|
50
|
+
raise ArgumentError, 'timeout must be > 0' unless timeout.positive?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_retry_count!(retries)
|
|
54
|
+
raise ArgumentError, 'retry must be >= 0' if retries.negative?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_attempt!(attempt)
|
|
58
|
+
raise ArgumentError, 'attempt must be >= 0' if attempt&.negative?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_backoff!(backoff)
|
|
62
|
+
raise ArgumentError, "backoff must be one of #{BACKOFFS.inspect}" unless BACKOFFS.include?(backoff)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validate_retry_delay!(settings)
|
|
66
|
+
return unless settings.retries.positive?
|
|
67
|
+
|
|
68
|
+
raise ArgumentError, 'retry_delay is required when retry > 0' if settings.retry_delay.nil?
|
|
69
|
+
raise ArgumentError, 'retry_delay must be > 0' unless settings.retry_delay.positive?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def validate_jitter!(jitter)
|
|
73
|
+
raise ArgumentError, 'jitter must be in [0, 1]' if jitter && !(0.0..1.0).cover?(jitter)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def base_retry_delay(for_attempt)
|
|
77
|
+
multiplier = case backoff
|
|
78
|
+
when :fixed then 1
|
|
79
|
+
when :linear then for_attempt
|
|
80
|
+
when :exponential then 2**(for_attempt - 1)
|
|
81
|
+
end
|
|
82
|
+
retry_delay * multiplier
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def apply_jitter(base, rng)
|
|
47
86
|
factor = jitter || DEFAULT_JITTER_FOR[backoff]
|
|
48
87
|
factor.zero? ? base : base * (1 + rng.rand * factor)
|
|
49
88
|
end
|
|
@@ -56,9 +95,17 @@ module Async
|
|
|
56
95
|
module ClassMethods
|
|
57
96
|
def perform_now(*args) = new.perform(*args)
|
|
58
97
|
|
|
59
|
-
def perform_async(*args, options: EMPTY_OPTIONS)
|
|
60
|
-
|
|
61
|
-
|
|
98
|
+
def perform_async(*args, options: EMPTY_OPTIONS)
|
|
99
|
+
Async::Background::Queue.enqueue(self, *args, options: options)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def perform_in(delay, *args, options: EMPTY_OPTIONS)
|
|
103
|
+
Async::Background::Queue.enqueue_in(delay, self, *args, options: options)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def perform_at(time, *args, options: EMPTY_OPTIONS)
|
|
107
|
+
Async::Background::Queue.enqueue_at(time, self, *args, options: options)
|
|
108
|
+
end
|
|
62
109
|
|
|
63
110
|
def options(**values)
|
|
64
111
|
@options = Options.new(**values).to_h.compact
|
|
@@ -29,53 +29,57 @@ module Async
|
|
|
29
29
|
|
|
30
30
|
validate_worker!(worker_index, total_workers)
|
|
31
31
|
initialize_registry!(worker_index, total_workers, shm_path)
|
|
32
|
-
rescue LoadError =>
|
|
33
|
-
mark_unavailable!(
|
|
32
|
+
rescue LoadError => e
|
|
33
|
+
mark_unavailable!(e)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def enabled? = @enabled
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
module JobEvents
|
|
39
|
+
def job_started(_entry)
|
|
40
|
+
return unless enabled?
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
increment(:total_runs)
|
|
43
|
+
increment(:active_jobs)
|
|
44
|
+
set(:last_run_at, Process.clock_gettime(Process::CLOCK_REALTIME).to_i)
|
|
45
|
+
end
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
def job_succeeded(_entry, duration)
|
|
48
|
+
return unless enabled?
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
increment(:total_successes)
|
|
51
|
+
set(:last_duration_ms, duration_to_milliseconds(duration))
|
|
52
|
+
end
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
def job_finished(entry, duration)
|
|
55
|
+
job_succeeded(entry, duration)
|
|
56
|
+
job_stopped(entry)
|
|
57
|
+
end
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
def job_failed(_entry, _error)
|
|
60
|
+
increment(:total_failures) if enabled?
|
|
61
|
+
end
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
def job_timed_out(_entry)
|
|
64
|
+
increment(:total_timeouts) if enabled?
|
|
65
|
+
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
def job_stopped(_entry)
|
|
68
|
+
decrement(:active_jobs) if enabled?
|
|
69
|
+
end
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
def job_skipped(_entry)
|
|
72
|
+
increment(:total_skips) if enabled?
|
|
73
|
+
end
|
|
72
74
|
end
|
|
75
|
+
private_constant :JobEvents
|
|
76
|
+
include JobEvents
|
|
73
77
|
|
|
74
78
|
def values
|
|
75
79
|
enabled? ? registry.values : {}
|
|
76
80
|
end
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
module Reader
|
|
79
83
|
def available?
|
|
80
84
|
load_utilization!
|
|
81
85
|
true
|
|
@@ -138,6 +142,8 @@ module Async
|
|
|
138
142
|
end
|
|
139
143
|
end
|
|
140
144
|
|
|
145
|
+
extend Reader
|
|
146
|
+
|
|
141
147
|
private
|
|
142
148
|
|
|
143
149
|
def initialize_registry!(worker_index, total_workers, path)
|
|
@@ -145,9 +151,7 @@ module Async
|
|
|
145
151
|
ensure_shm!(total_workers, path)
|
|
146
152
|
|
|
147
153
|
@registry = ::Async::Utilization::Registry.new
|
|
148
|
-
unless @registry.respond_to?(:metric)
|
|
149
|
-
raise LoadError, 'async-utilization >= 0.3 is required for metrics'
|
|
150
|
-
end
|
|
154
|
+
raise LoadError, 'async-utilization >= 0.3 is required for metrics' unless @registry.respond_to?(:metric)
|
|
151
155
|
|
|
152
156
|
attach_observer!(worker_index, path)
|
|
153
157
|
@metric_handles = SCHEMA_FIELDS.keys.to_h { |name| [name, @registry.metric(name)] }.freeze
|
|
@@ -196,7 +200,11 @@ module Async
|
|
|
196
200
|
file.flock(File::LOCK_EX)
|
|
197
201
|
file.truncate(mapped_size) if file.size < mapped_size
|
|
198
202
|
ensure
|
|
199
|
-
|
|
203
|
+
begin
|
|
204
|
+
file.flock(File::LOCK_UN)
|
|
205
|
+
rescue StandardError
|
|
206
|
+
nil
|
|
207
|
+
end
|
|
200
208
|
end
|
|
201
209
|
end
|
|
202
210
|
|
|
@@ -41,7 +41,7 @@ module Async
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def sift_up(i)
|
|
44
|
-
while i
|
|
44
|
+
while i.positive?
|
|
45
45
|
parent = (i - 1) >> 1
|
|
46
46
|
break if @data[parent].next_run_at <= @data[i].next_run_at
|
|
47
47
|
|
|
@@ -50,20 +50,24 @@ module Async
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def sift_down(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
right = left + 1
|
|
53
|
+
def sift_down(index)
|
|
54
|
+
loop do
|
|
55
|
+
child = smallest_child(index)
|
|
56
|
+
break if child == index
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
swap(index, child)
|
|
59
|
+
index = child
|
|
60
|
+
end
|
|
61
|
+
end
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
def smallest_child(index)
|
|
64
|
+
left = (index << 1) + 1
|
|
65
|
+
right = left + 1
|
|
66
|
+
smallest = index
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
smallest = left if left < @data.size && @data[left].next_run_at < @data[smallest].next_run_at
|
|
69
|
+
smallest = right if right < @data.size && @data[right].next_run_at < @data[smallest].next_run_at
|
|
70
|
+
smallest
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
def swap(a, b)
|
|
@@ -43,17 +43,20 @@ module Async
|
|
|
43
43
|
|
|
44
44
|
def enqueue(job_class, *args, options: EMPTY_OPTIONS)
|
|
45
45
|
ensure_configured!
|
|
46
|
-
default_client.push(resolve_class_name(job_class), normalized_args(args), nil,
|
|
46
|
+
default_client.push(resolve_class_name(job_class), normalized_args(args), nil,
|
|
47
|
+
options: build_options(job_class, options))
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def enqueue_in(delay, job_class, *args, options: EMPTY_OPTIONS)
|
|
50
51
|
ensure_configured!
|
|
51
|
-
default_client.push_in(delay, resolve_class_name(job_class), normalized_args(args),
|
|
52
|
+
default_client.push_in(delay, resolve_class_name(job_class), normalized_args(args),
|
|
53
|
+
options: build_options(job_class, options))
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def enqueue_at(time, job_class, *args, options: EMPTY_OPTIONS)
|
|
55
57
|
ensure_configured!
|
|
56
|
-
default_client.push_at(time, resolve_class_name(job_class), normalized_args(args),
|
|
58
|
+
default_client.push_at(time, resolve_class_name(job_class), normalized_args(args),
|
|
59
|
+
options: build_options(job_class, options))
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
private
|
|
@@ -79,7 +82,7 @@ module Async
|
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def ensure_configured!
|
|
82
|
-
raise
|
|
85
|
+
raise 'Async::Background::Queue not configured' unless default_client
|
|
83
86
|
end
|
|
84
87
|
|
|
85
88
|
def normalized_args(args) = args.empty? ? EMPTY_ARGS : args
|
|
@@ -95,10 +98,10 @@ module Async
|
|
|
95
98
|
return EMPTY_OPTIONS unless job_class.respond_to?(:resolve_options)
|
|
96
99
|
|
|
97
100
|
options = if job_class.respond_to?(:queue_options)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
job_class.queue_options
|
|
102
|
+
else
|
|
103
|
+
job_class.resolve_options
|
|
104
|
+
end
|
|
102
105
|
|
|
103
106
|
options.empty? ? EMPTY_OPTIONS : options
|
|
104
107
|
end
|
|
@@ -6,9 +6,9 @@ module Async
|
|
|
6
6
|
EMPTY_ARGS = [].freeze
|
|
7
7
|
EMPTY_OPTIONS = {}.freeze
|
|
8
8
|
|
|
9
|
-
SYNCHRONOUS_LEVELS = {normal: 'NORMAL', full: 'FULL', extra: 'EXTRA'}.freeze
|
|
9
|
+
SYNCHRONOUS_LEVELS = { normal: 'NORMAL', full: 'FULL', extra: 'EXTRA' }.freeze
|
|
10
10
|
WAL_AUTOCHECKPOINT_RANGE = 100..10_000
|
|
11
|
-
DEFAULT_STORE_OPTIONS = {mmap: true, synchronous: :normal, wal_autocheckpoint: 1_000}.freeze
|
|
11
|
+
DEFAULT_STORE_OPTIONS = { mmap: true, synchronous: :normal, wal_autocheckpoint: 1_000 }.freeze
|
|
12
12
|
DEFAULTS = DEFAULT_STORE_OPTIONS
|
|
13
13
|
MMAP_SIZE = 268_435_456
|
|
14
14
|
|
|
@@ -45,7 +45,7 @@ module Async
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def validate_mmap!(value)
|
|
48
|
-
return if
|
|
48
|
+
return if [true, false].include?(value)
|
|
49
49
|
|
|
50
50
|
raise ArgumentError, "mmap must be true or false, got #{value.inspect}"
|
|
51
51
|
end
|
|
@@ -19,7 +19,7 @@ module Async
|
|
|
19
19
|
].freeze
|
|
20
20
|
|
|
21
21
|
DEAD_WORKER_TTL = 5.0
|
|
22
|
-
WAKE_BYTE = "\x01"
|
|
22
|
+
WAKE_BYTE = "\x01"
|
|
23
23
|
|
|
24
24
|
def initialize(socket_dir:, total_workers:)
|
|
25
25
|
@socket_dir = socket_dir
|
|
@@ -64,7 +64,11 @@ module Async
|
|
|
64
64
|
begin
|
|
65
65
|
socket.write_nonblock(WAKE_BYTE)
|
|
66
66
|
ensure
|
|
67
|
-
|
|
67
|
+
begin
|
|
68
|
+
socket.close
|
|
69
|
+
rescue StandardError
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
68
72
|
end
|
|
69
73
|
true
|
|
70
74
|
rescue IO::WaitWritable
|
|
@@ -72,9 +76,15 @@ module Async
|
|
|
72
76
|
rescue *UNAVAILABLE
|
|
73
77
|
mark_dead(index, now)
|
|
74
78
|
false
|
|
75
|
-
rescue => e
|
|
79
|
+
rescue StandardError => e
|
|
76
80
|
mark_dead(index, now)
|
|
77
|
-
|
|
81
|
+
begin
|
|
82
|
+
Console.logger.warn(self) do
|
|
83
|
+
"SocketNotifier#notify_one(#{index + 1}) failed: #{e.class} #{e.message}"
|
|
84
|
+
end
|
|
85
|
+
rescue StandardError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
78
88
|
false
|
|
79
89
|
end
|
|
80
90
|
|
|
@@ -54,10 +54,18 @@ module Async
|
|
|
54
54
|
hang_up_clients
|
|
55
55
|
stop_clients
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
begin
|
|
58
|
+
@server&.close
|
|
59
|
+
rescue StandardError
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
58
62
|
@server = nil
|
|
59
63
|
@accept_task = nil
|
|
60
|
-
|
|
64
|
+
begin
|
|
65
|
+
File.unlink(@path)
|
|
66
|
+
rescue StandardError
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
private
|
|
@@ -101,7 +109,11 @@ module Async
|
|
|
101
109
|
Console.logger.warn(self) { "SocketWaker client handler error: #{e.class} #{e.message}" }
|
|
102
110
|
ensure
|
|
103
111
|
@sockets.delete(client)
|
|
104
|
-
|
|
112
|
+
begin
|
|
113
|
+
client.close
|
|
114
|
+
rescue StandardError
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
105
117
|
@notification.signal_all
|
|
106
118
|
end
|
|
107
119
|
end
|
|
@@ -119,13 +131,17 @@ module Async
|
|
|
119
131
|
def wake_accept_loop
|
|
120
132
|
return unless @server
|
|
121
133
|
|
|
122
|
-
UNIXSocket.open(@path)
|
|
134
|
+
UNIXSocket.open(@path) do |s|
|
|
135
|
+
s.write_nonblock("\x00")
|
|
136
|
+
rescue StandardError
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
123
139
|
rescue StandardError
|
|
124
140
|
nil
|
|
125
141
|
end
|
|
126
142
|
|
|
127
143
|
def hang_up_clients
|
|
128
|
-
@sockets.
|
|
144
|
+
@sockets.each_key do |socket|
|
|
129
145
|
socket.close
|
|
130
146
|
rescue StandardError
|
|
131
147
|
nil
|
|
@@ -166,11 +182,15 @@ module Async
|
|
|
166
182
|
return unless File.exist?(@path)
|
|
167
183
|
|
|
168
184
|
begin
|
|
169
|
-
UNIXSocket.open(@path
|
|
185
|
+
UNIXSocket.open(@path, &:close)
|
|
170
186
|
|
|
171
187
|
raise "Socket #{@path} is already in use by another process (worker_index conflict?)"
|
|
172
188
|
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
|
173
|
-
|
|
189
|
+
begin
|
|
190
|
+
File.unlink(@path)
|
|
191
|
+
rescue StandardError
|
|
192
|
+
nil
|
|
193
|
+
end
|
|
174
194
|
end
|
|
175
195
|
end
|
|
176
196
|
|