async-background 1.0.2 → 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 +81 -0
- data/README.md +36 -15
- data/async-background.gemspec +13 -7
- 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 +19 -15
- data/lib/async/background/queue/socket_notifier.rb +14 -4
- data/lib/async/background/queue/socket_waker.rb +139 -56
- data/lib/async/background/queue/sql.rb +31 -31
- data/lib/async/background/queue/store.rb +44 -45
- data/lib/async/background/runner/lifecycle.rb +131 -0
- data/lib/async/background/runner/queue_execution.rb +101 -57
- data/lib/async/background/runner/schedule.rb +20 -17
- data/lib/async/background/runner.rb +98 -77
- data/lib/async/background/runtime/notification.rb +42 -0
- data/lib/async/background/runtime/semaphore.rb +151 -0
- data/lib/async/background/runtime/task.rb +146 -0
- data/lib/async/background/runtime/task_group.rb +81 -0
- data/lib/async/background/runtime.rb +246 -0
- data/lib/async/background/scheduler.rb +145 -0
- data/lib/async/background/version.rb +1 -1
- data/lib/async/background/web/app.rb +43 -40
- 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 +40 -93
- data/lib/async/background/web/cursor.rb +22 -26
- data/lib/async/background/web/event_hub.rb +12 -7
- data/lib/async/background/web/metrics_reader.rb +28 -36
- data/lib/async/background/web/response.rb +39 -51
- data/lib/async/background/web/serializer.rb +23 -63
- data/lib/async/background/web/snapshot.rb +35 -81
- data/lib/async/background/web/sql.rb +63 -69
- data/lib/async/background/web/stream.rb +52 -32
- data/lib/async/background/web.rb +1 -1
- data/lib/async/background.rb +1 -2
- metadata +30 -22
|
@@ -13,6 +13,20 @@ module Async
|
|
|
13
13
|
include Clock
|
|
14
14
|
|
|
15
15
|
CacheEntry = Data.define(:value, :created_at)
|
|
16
|
+
RAW_COLUMNS = { 'args' => :args_raw, 'options' => :options_raw }.freeze
|
|
17
|
+
|
|
18
|
+
ROW_KEYS = SQL::EXTRA_COLUMNS.to_h do |status, extra|
|
|
19
|
+
columns = SQL::BASE_COLUMNS + extra
|
|
20
|
+
[status, columns.map { |column| RAW_COLUMNS.fetch(column) { column.to_sym } }.freeze]
|
|
21
|
+
end.freeze
|
|
22
|
+
|
|
23
|
+
PAGING = {
|
|
24
|
+
executing: [SQL::EXECUTING, nil, nil].freeze,
|
|
25
|
+
claimed: [SQL::CLAIMED, nil, nil].freeze,
|
|
26
|
+
done: [SQL::DONE, SQL::DONE_AFTER, %i[finished_at id].freeze].freeze,
|
|
27
|
+
failed: [SQL::FAILED, SQL::FAILED_AFTER, %i[finished_at id].freeze].freeze,
|
|
28
|
+
pending: [SQL::PENDING, SQL::PENDING_AFTER, %i[run_at id].freeze].freeze
|
|
29
|
+
}.freeze
|
|
16
30
|
|
|
17
31
|
def initialize(path:, counts_cache_ttl:)
|
|
18
32
|
@path = path
|
|
@@ -65,26 +79,23 @@ module Async
|
|
|
65
79
|
end
|
|
66
80
|
|
|
67
81
|
def executing(limit:)
|
|
68
|
-
|
|
82
|
+
list(:executing, limit: limit)
|
|
69
83
|
end
|
|
70
84
|
|
|
71
85
|
def claimed(limit:)
|
|
72
|
-
|
|
86
|
+
list(:claimed, limit: limit)
|
|
73
87
|
end
|
|
74
88
|
|
|
75
89
|
def recent_done(limit:, cursor: nil)
|
|
76
|
-
|
|
77
|
-
read_rows(sql, binds).map { |row| done_row(row) }
|
|
90
|
+
list(:done, limit: limit, cursor: cursor)
|
|
78
91
|
end
|
|
79
92
|
|
|
80
93
|
def recent_failed(limit:, cursor: nil)
|
|
81
|
-
|
|
82
|
-
read_rows(sql, binds).map { |row| failed_row(row) }
|
|
94
|
+
list(:failed, limit: limit, cursor: cursor)
|
|
83
95
|
end
|
|
84
96
|
|
|
85
97
|
def pending(limit:, cursor: nil)
|
|
86
|
-
|
|
87
|
-
read_rows(sql, binds).map { |row| pending_row(row) }
|
|
98
|
+
list(:pending, limit: limit, cursor: cursor)
|
|
88
99
|
end
|
|
89
100
|
|
|
90
101
|
private
|
|
@@ -98,12 +109,12 @@ module Async
|
|
|
98
109
|
SQLite3::Database.new(database_uri, uri: true)
|
|
99
110
|
rescue LoadError
|
|
100
111
|
raise
|
|
101
|
-
rescue StandardError =>
|
|
102
|
-
raise UnavailableError, "cannot open queue database: #{
|
|
112
|
+
rescue StandardError => e
|
|
113
|
+
raise UnavailableError, "cannot open queue database: #{e.message}"
|
|
103
114
|
end
|
|
104
115
|
|
|
105
116
|
def database_uri
|
|
106
|
-
path = URI::
|
|
117
|
+
path = URI::RFC2396_PARSER.escape(File.expand_path(@path)).gsub('?', '%3F')
|
|
107
118
|
"file:#{path}?mode=ro"
|
|
108
119
|
end
|
|
109
120
|
|
|
@@ -153,10 +164,10 @@ module Async
|
|
|
153
164
|
{
|
|
154
165
|
counts: {
|
|
155
166
|
executing: db.get_first_value(SQL::OVERVIEW_EXECUTING).to_i,
|
|
156
|
-
claimed:
|
|
157
|
-
pending:
|
|
158
|
-
done:
|
|
159
|
-
failed:
|
|
167
|
+
claimed: db.get_first_value(SQL::OVERVIEW_CLAIMED).to_i,
|
|
168
|
+
pending: db.get_first_value(SQL::OVERVIEW_PENDING).to_i,
|
|
169
|
+
done: db.get_first_value(SQL::OVERVIEW_DONE).to_i,
|
|
170
|
+
failed: db.get_first_value(SQL::OVERVIEW_FAILED).to_i
|
|
160
171
|
}.freeze,
|
|
161
172
|
next_pending_run_at: db.get_first_value(SQL::OVERVIEW_NEXT_PENDING),
|
|
162
173
|
data_version: db.get_first_value(Queue::SQL::DATA_VERSION).to_i,
|
|
@@ -164,81 +175,24 @@ module Async
|
|
|
164
175
|
}
|
|
165
176
|
end
|
|
166
177
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def pending_query(limit, cursor)
|
|
174
|
-
return [SQL::PENDING, [limit]] unless cursor
|
|
175
|
-
|
|
176
|
-
[SQL::PENDING_AFTER, [cursor.fetch(:run_at), cursor.fetch(:id), limit]]
|
|
178
|
+
def list(status, limit:, cursor: nil)
|
|
179
|
+
sql, binds = page_query(status, limit, cursor)
|
|
180
|
+
keys = ROW_KEYS.fetch(status)
|
|
181
|
+
read_rows(sql, binds).map { |row| keys.zip(row).to_h }
|
|
177
182
|
end
|
|
178
183
|
|
|
179
|
-
def
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
class_name: row[1],
|
|
183
|
-
args_raw: row[2],
|
|
184
|
-
options_raw: row[3],
|
|
185
|
-
started_at: row[4],
|
|
186
|
-
locked_by: row[5],
|
|
187
|
-
locked_at: row[6]
|
|
188
|
-
}
|
|
189
|
-
end
|
|
184
|
+
def page_query(status, limit, cursor)
|
|
185
|
+
first_sql, seek_sql, cursor_keys = PAGING.fetch(status)
|
|
186
|
+
return [first_sql, [limit]] unless cursor
|
|
190
187
|
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
id: row[0],
|
|
194
|
-
class_name: row[1],
|
|
195
|
-
args_raw: row[2],
|
|
196
|
-
options_raw: row[3],
|
|
197
|
-
locked_at: row[4],
|
|
198
|
-
locked_by: row[5]
|
|
199
|
-
}
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def done_row(row)
|
|
203
|
-
{
|
|
204
|
-
id: row[0],
|
|
205
|
-
class_name: row[1],
|
|
206
|
-
args_raw: row[2],
|
|
207
|
-
options_raw: row[3],
|
|
208
|
-
finished_at: row[4],
|
|
209
|
-
duration_ms: row[5]
|
|
210
|
-
}
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def failed_row(row)
|
|
214
|
-
{
|
|
215
|
-
id: row[0],
|
|
216
|
-
class_name: row[1],
|
|
217
|
-
args_raw: row[2],
|
|
218
|
-
options_raw: row[3],
|
|
219
|
-
finished_at: row[4],
|
|
220
|
-
duration_ms: row[5],
|
|
221
|
-
last_error_class: row[6],
|
|
222
|
-
last_error_message: row[7]
|
|
223
|
-
}
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def pending_row(row)
|
|
227
|
-
{
|
|
228
|
-
id: row[0],
|
|
229
|
-
class_name: row[1],
|
|
230
|
-
args_raw: row[2],
|
|
231
|
-
options_raw: row[3],
|
|
232
|
-
created_at: row[4],
|
|
233
|
-
run_at: row[5]
|
|
234
|
-
}
|
|
188
|
+
[seek_sql, cursor_keys.map { |key| cursor.fetch(key) } << limit]
|
|
235
189
|
end
|
|
236
190
|
|
|
237
191
|
def require_sqlite3
|
|
238
192
|
require 'sqlite3'
|
|
239
193
|
rescue LoadError
|
|
240
194
|
raise LoadError,
|
|
241
|
-
|
|
195
|
+
'sqlite3 gem is required for Async::Background::Web. ' \
|
|
242
196
|
"Add `gem 'sqlite3', '~> 2.0'` to your Gemfile."
|
|
243
197
|
end
|
|
244
198
|
end
|
|
@@ -4,84 +4,78 @@ module Async
|
|
|
4
4
|
module Background
|
|
5
5
|
module Web
|
|
6
6
|
module SQL
|
|
7
|
-
BEGIN_READ_TRANSACTION = 'BEGIN'
|
|
8
|
-
COMMIT = 'COMMIT'
|
|
9
|
-
ROLLBACK = 'ROLLBACK'
|
|
10
|
-
QUERY_ONLY = 'PRAGMA query_only = ON'
|
|
11
|
-
BUSY_TIMEOUT = 'PRAGMA busy_timeout = 2000'
|
|
7
|
+
BEGIN_READ_TRANSACTION = 'BEGIN'
|
|
8
|
+
COMMIT = 'COMMIT'
|
|
9
|
+
ROLLBACK = 'ROLLBACK'
|
|
10
|
+
QUERY_ONLY = 'PRAGMA query_only = ON'
|
|
11
|
+
BUSY_TIMEOUT = 'PRAGMA busy_timeout = 2000'
|
|
12
|
+
BASE_COLUMNS = %w[id class_name args options].freeze
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
STATUS_PREDICATE = {
|
|
15
|
+
executing: "status = 'running' AND started_at IS NOT NULL",
|
|
16
|
+
claimed: "status = 'running' AND started_at IS NULL",
|
|
17
|
+
pending: "status = 'pending'",
|
|
18
|
+
done: "status = 'done'",
|
|
19
|
+
failed: "status = 'failed'"
|
|
20
|
+
}.freeze
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
EXTRA_COLUMNS = {
|
|
23
|
+
executing: %w[started_at locked_by locked_at],
|
|
24
|
+
claimed: %w[locked_at locked_by],
|
|
25
|
+
pending: %w[created_at run_at],
|
|
26
|
+
done: %w[finished_at duration_ms],
|
|
27
|
+
failed: %w[finished_at duration_ms last_error_class last_error_message]
|
|
28
|
+
}.freeze
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
KEYSET = {
|
|
31
|
+
executing: ['started_at', :asc],
|
|
32
|
+
claimed: ['locked_at', :asc],
|
|
33
|
+
pending: ['run_at', :asc],
|
|
34
|
+
done: ['finished_at', :desc],
|
|
35
|
+
failed: ['finished_at', :desc]
|
|
36
|
+
}.freeze
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
FROM jobs
|
|
39
|
-
WHERE status = 'done'
|
|
40
|
-
ORDER BY finished_at DESC, id DESC
|
|
41
|
-
LIMIT ?
|
|
42
|
-
SQL
|
|
38
|
+
ORDER_SQL = { asc: 'ORDER BY %<column>s, id', desc: 'ORDER BY %<column>s DESC, id DESC' }.freeze
|
|
39
|
+
SEEK_SQL = { asc: '(%<column>s, id) > (?, ?)', desc: '(%<column>s, id) < (?, ?)' }.freeze
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
WHERE status = 'done' AND (finished_at, id) < (?, ?)
|
|
48
|
-
ORDER BY finished_at DESC, id DESC
|
|
49
|
-
LIMIT ?
|
|
50
|
-
SQL
|
|
41
|
+
def self.columns_for(status)
|
|
42
|
+
(BASE_COLUMNS + EXTRA_COLUMNS.fetch(status)).join(', ')
|
|
43
|
+
end
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
SELECT
|
|
54
|
-
|
|
55
|
-
FROM jobs
|
|
56
|
-
WHERE status = 'failed'
|
|
57
|
-
ORDER BY finished_at DESC, id DESC
|
|
58
|
-
LIMIT ?
|
|
59
|
-
SQL
|
|
45
|
+
def self.count_query(status)
|
|
46
|
+
"SELECT COUNT(*) FROM jobs WHERE #{STATUS_PREDICATE.fetch(status)}".freeze
|
|
47
|
+
end
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
WHERE status = 'failed' AND (finished_at, id) < (?, ?)
|
|
66
|
-
ORDER BY finished_at DESC, id DESC
|
|
67
|
-
LIMIT ?
|
|
68
|
-
SQL
|
|
49
|
+
def self.list_query(status, seek: false)
|
|
50
|
+
column, direction = KEYSET.fetch(status)
|
|
51
|
+
predicate = STATUS_PREDICATE.fetch(status)
|
|
52
|
+
predicate += " AND #{format(SEEK_SQL.fetch(direction), column: column)}" if seek
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
<<~SQL.freeze
|
|
55
|
+
SELECT #{columns_for(status)}
|
|
56
|
+
FROM jobs
|
|
57
|
+
WHERE #{predicate}
|
|
58
|
+
#{format(ORDER_SQL.fetch(direction), column: column)}
|
|
59
|
+
LIMIT ?
|
|
60
|
+
SQL
|
|
61
|
+
end
|
|
77
62
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
63
|
+
OVERVIEW_EXECUTING = count_query(:executing)
|
|
64
|
+
OVERVIEW_CLAIMED = count_query(:claimed)
|
|
65
|
+
OVERVIEW_PENDING = count_query(:pending)
|
|
66
|
+
OVERVIEW_DONE = count_query(:done)
|
|
67
|
+
OVERVIEW_FAILED = count_query(:failed)
|
|
68
|
+
OVERVIEW_NEXT_PENDING = "SELECT MIN(run_at) FROM jobs WHERE #{STATUS_PREDICATE.fetch(:pending)}".freeze
|
|
69
|
+
|
|
70
|
+
EXECUTING = list_query(:executing)
|
|
71
|
+
CLAIMED = list_query(:claimed)
|
|
72
|
+
DONE = list_query(:done)
|
|
73
|
+
FAILED = list_query(:failed)
|
|
74
|
+
PENDING = list_query(:pending)
|
|
75
|
+
|
|
76
|
+
DONE_AFTER = list_query(:done, seek: true)
|
|
77
|
+
FAILED_AFTER = list_query(:failed, seek: true)
|
|
78
|
+
PENDING_AFTER = list_query(:pending, seek: true)
|
|
85
79
|
end
|
|
86
80
|
end
|
|
87
81
|
end
|
|
@@ -26,41 +26,13 @@ module Async
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
yield frame
|
|
29
|
-
|
|
30
|
-
unavailable_announced = false
|
|
31
|
-
|
|
32
|
-
loop do
|
|
33
|
-
sleep_for_poll
|
|
34
|
-
|
|
35
|
-
begin
|
|
36
|
-
new_version = @hub.current_version
|
|
37
|
-
|
|
38
|
-
if new_version != version
|
|
39
|
-
version = new_version
|
|
40
|
-
yield @hub.frame_for(version)
|
|
41
|
-
last_yield = monotonic_now
|
|
42
|
-
elsif (monotonic_now - last_yield) >= @heartbeat_seconds
|
|
43
|
-
yield EventHub::HEARTBEAT_FRAME
|
|
44
|
-
last_yield = monotonic_now
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
unavailable_announced = false
|
|
48
|
-
rescue ClosedError
|
|
49
|
-
break
|
|
50
|
-
rescue UnavailableError
|
|
51
|
-
unless unavailable_announced
|
|
52
|
-
yield EventHub::UNAVAILABLE_FRAME
|
|
53
|
-
unavailable_announced = true
|
|
54
|
-
last_yield = monotonic_now
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
29
|
+
stream_updates(version, monotonic_now) { |event| yield event }
|
|
58
30
|
rescue Errno::EPIPE, Errno::ECONNRESET, IOError
|
|
59
31
|
nil
|
|
60
|
-
rescue StandardError =>
|
|
32
|
+
rescue StandardError => e
|
|
61
33
|
@logger&.error(
|
|
62
|
-
|
|
63
|
-
"#{
|
|
34
|
+
'[async-background-web] SSE stream terminated: ' \
|
|
35
|
+
"#{e.class}: #{e.message}"
|
|
64
36
|
)
|
|
65
37
|
nil
|
|
66
38
|
end
|
|
@@ -76,6 +48,54 @@ module Async
|
|
|
76
48
|
def sleep_for_poll
|
|
77
49
|
sleep(@poll_seconds)
|
|
78
50
|
end
|
|
51
|
+
|
|
52
|
+
def stream_updates(version, last_yield)
|
|
53
|
+
unavailable_announced = false
|
|
54
|
+
loop do
|
|
55
|
+
sleep_for_poll
|
|
56
|
+
state = next_state(version, last_yield, unavailable_announced) { |event| yield event }
|
|
57
|
+
break unless state
|
|
58
|
+
|
|
59
|
+
version, last_yield, unavailable_announced = state
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def next_state(version, last_yield, unavailable_announced)
|
|
64
|
+
current_version = @hub.current_version
|
|
65
|
+
return updated_state(current_version) { |event| yield event } if current_version != version
|
|
66
|
+
return heartbeat_state(version) { |event| yield event } if heartbeat_due?(last_yield)
|
|
67
|
+
|
|
68
|
+
[version, last_yield, false]
|
|
69
|
+
rescue ClosedError
|
|
70
|
+
nil
|
|
71
|
+
rescue UnavailableError
|
|
72
|
+
announce_unavailable(version, last_yield, unavailable_announced) { |event| yield event }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def updated_state(version)
|
|
76
|
+
yield @hub.frame_for(version)
|
|
77
|
+
[version, monotonic_now, false]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def heartbeat_state(version)
|
|
81
|
+
yield EventHub::HEARTBEAT_FRAME
|
|
82
|
+
[version, monotonic_now, false]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def heartbeat_due?(last_yield)
|
|
86
|
+
(monotonic_now - last_yield) >= @heartbeat_seconds
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def announce_unavailable(version, last_yield, announced)
|
|
90
|
+
return [version, last_yield, true] if announced
|
|
91
|
+
|
|
92
|
+
yield_unavailable { |event| yield event }
|
|
93
|
+
[version, monotonic_now, true]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def yield_unavailable
|
|
97
|
+
yield EventHub::UNAVAILABLE_FRAME
|
|
98
|
+
end
|
|
79
99
|
end
|
|
80
100
|
end
|
|
81
101
|
end
|
data/lib/async/background/web.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Async
|
|
|
37
37
|
|
|
38
38
|
def configuration
|
|
39
39
|
@configuration or raise NotConfiguredError,
|
|
40
|
-
|
|
40
|
+
'Async::Background::Web is not configured. Call Async::Background::Web.configure.'
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def reset!
|
data/lib/async/background.rb
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'async'
|
|
4
|
-
require 'async/semaphore'
|
|
5
3
|
require 'console'
|
|
6
4
|
require 'fugit'
|
|
7
5
|
require 'tmpdir'
|
|
8
6
|
|
|
9
7
|
require_relative 'background/version'
|
|
8
|
+
require_relative 'background/runtime'
|
|
10
9
|
require_relative 'background/clock'
|
|
11
10
|
require_relative 'background/min_heap'
|
|
12
11
|
require_relative 'background/entry'
|
metadata
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-background
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Hajdarov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
13
|
+
name: console
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '1.0'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '1.0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
27
|
+
name: fugit
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
30
|
- - "~>"
|
|
@@ -39,33 +38,33 @@ dependencies:
|
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '1.0'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
41
|
+
name: base64
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
44
43
|
requirements:
|
|
45
44
|
- - "~>"
|
|
46
45
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
46
|
+
version: '0.2'
|
|
48
47
|
type: :runtime
|
|
49
48
|
prerelease: false
|
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
50
|
requirements:
|
|
52
51
|
- - "~>"
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
53
|
+
version: '0.2'
|
|
55
54
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
55
|
+
name: async
|
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
|
58
57
|
requirements:
|
|
59
58
|
- - "~>"
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0
|
|
62
|
-
type: :
|
|
60
|
+
version: '2.0'
|
|
61
|
+
type: :development
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
65
|
- - "~>"
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0
|
|
67
|
+
version: '2.0'
|
|
69
68
|
- !ruby/object:Gem::Dependency
|
|
70
69
|
name: rake
|
|
71
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -128,10 +127,11 @@ dependencies:
|
|
|
128
127
|
- - "<"
|
|
129
128
|
- !ruby/object:Gem::Version
|
|
130
129
|
version: '0.5'
|
|
131
|
-
description: A production-grade lightweight scheduler built on
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
description: 'A production-grade lightweight background job scheduler built on the
|
|
131
|
+
Fiber::Scheduler interface itself, not on any one implementation: it runs under
|
|
132
|
+
Async/Falcon, Itsi, or anything else that installs a scheduler. Single event loop
|
|
133
|
+
with min-heap timer, skip-overlapping execution, jitter, monotonic clock intervals,
|
|
134
|
+
semaphore concurrency control, and deterministic worker sharding.'
|
|
135
135
|
email:
|
|
136
136
|
- romnhajdarov@gmail.com
|
|
137
137
|
executables: []
|
|
@@ -157,12 +157,22 @@ files:
|
|
|
157
157
|
- lib/async/background/queue/sql.rb
|
|
158
158
|
- lib/async/background/queue/store.rb
|
|
159
159
|
- lib/async/background/runner.rb
|
|
160
|
+
- lib/async/background/runner/lifecycle.rb
|
|
160
161
|
- lib/async/background/runner/queue_execution.rb
|
|
161
162
|
- lib/async/background/runner/schedule.rb
|
|
163
|
+
- lib/async/background/runtime.rb
|
|
164
|
+
- lib/async/background/runtime/notification.rb
|
|
165
|
+
- lib/async/background/runtime/semaphore.rb
|
|
166
|
+
- lib/async/background/runtime/task.rb
|
|
167
|
+
- lib/async/background/runtime/task_group.rb
|
|
168
|
+
- lib/async/background/scheduler.rb
|
|
162
169
|
- lib/async/background/version.rb
|
|
163
170
|
- lib/async/background/web.rb
|
|
164
171
|
- lib/async/background/web/app.rb
|
|
165
172
|
- lib/async/background/web/assets.rb
|
|
173
|
+
- lib/async/background/web/assets/app.css
|
|
174
|
+
- lib/async/background/web/assets/app.js
|
|
175
|
+
- lib/async/background/web/assets/index.html
|
|
166
176
|
- lib/async/background/web/auth.rb
|
|
167
177
|
- lib/async/background/web/configuration.rb
|
|
168
178
|
- lib/async/background/web/cursor.rb
|
|
@@ -183,7 +193,6 @@ metadata:
|
|
|
183
193
|
source_code_uri: https://github.com/roman-haidarov/async-background
|
|
184
194
|
changelog_uri: https://github.com/roman-haidarov/async-background/blob/main/CHANGELOG.md
|
|
185
195
|
bug_tracker_uri: https://github.com/roman-haidarov/async-background/issues
|
|
186
|
-
post_install_message:
|
|
187
196
|
rdoc_options: []
|
|
188
197
|
require_paths:
|
|
189
198
|
- lib
|
|
@@ -198,8 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
198
207
|
- !ruby/object:Gem::Version
|
|
199
208
|
version: '0'
|
|
200
209
|
requirements: []
|
|
201
|
-
rubygems_version: 3.
|
|
202
|
-
signing_key:
|
|
210
|
+
rubygems_version: 3.6.7
|
|
203
211
|
specification_version: 4
|
|
204
|
-
summary: Lightweight heap-based cron/interval scheduler for
|
|
212
|
+
summary: Lightweight heap-based cron/interval scheduler for any Fiber scheduler.
|
|
205
213
|
test_files: []
|