async-background 1.0.2 → 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.
@@ -9,79 +9,73 @@ module Async
9
9
  ROLLBACK = 'ROLLBACK'.freeze
10
10
  QUERY_ONLY = 'PRAGMA query_only = ON'.freeze
11
11
  BUSY_TIMEOUT = 'PRAGMA busy_timeout = 2000'.freeze
12
+ BASE_COLUMNS = %w[id class_name args options].freeze
12
13
 
13
- OVERVIEW_EXECUTING = "SELECT COUNT(*) FROM jobs WHERE status = 'running' AND started_at IS NOT NULL".freeze
14
- OVERVIEW_CLAIMED = "SELECT COUNT(*) FROM jobs WHERE status = 'running' AND started_at IS NULL".freeze
15
- OVERVIEW_PENDING = "SELECT COUNT(*) FROM jobs WHERE status = 'pending'".freeze
16
- OVERVIEW_DONE = "SELECT COUNT(*) FROM jobs WHERE status = 'done'".freeze
17
- OVERVIEW_FAILED = "SELECT COUNT(*) FROM jobs WHERE status = 'failed'".freeze
18
- OVERVIEW_NEXT_PENDING = "SELECT MIN(run_at) FROM jobs WHERE status = 'pending'".freeze
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
- EXECUTING = <<~SQL.freeze
21
- SELECT id, class_name, args, options, started_at, locked_by, locked_at
22
- FROM jobs
23
- WHERE status = 'running' AND started_at IS NOT NULL
24
- ORDER BY started_at, id
25
- LIMIT ?
26
- SQL
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
- CLAIMED = <<~SQL.freeze
29
- SELECT id, class_name, args, options, locked_at, locked_by
30
- FROM jobs
31
- WHERE status = 'running' AND started_at IS NULL
32
- ORDER BY locked_at, id
33
- LIMIT ?
34
- SQL
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
- DONE = <<~SQL.freeze
37
- SELECT id, class_name, args, options, finished_at, duration_ms
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
- DONE_AFTER = <<~SQL.freeze
45
- SELECT id, class_name, args, options, finished_at, duration_ms
46
- FROM jobs
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
- FAILED = <<~SQL.freeze
53
- SELECT id, class_name, args, options, finished_at, duration_ms,
54
- last_error_class, last_error_message
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
- FAILED_AFTER = <<~SQL.freeze
62
- SELECT id, class_name, args, options, finished_at, duration_ms,
63
- last_error_class, last_error_message
64
- FROM jobs
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
- PENDING = <<~SQL.freeze
71
- SELECT id, class_name, args, options, created_at, run_at
72
- FROM jobs
73
- WHERE status = 'pending'
74
- ORDER BY run_at, id
75
- LIMIT ?
76
- SQL
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
- PENDING_AFTER = <<~SQL.freeze
79
- SELECT id, class_name, args, options, created_at, run_at
80
- FROM jobs
81
- WHERE status = 'pending' AND (run_at, id) > (?, ?)
82
- ORDER BY run_at, id
83
- LIMIT ?
84
- SQL
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
@@ -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.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Hajdarov
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-08-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: async
13
+ name: console
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '2.0'
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: '2.0'
25
+ version: '1.0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: console
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: fugit
41
+ name: base64
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '1.0'
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: '1.0'
53
+ version: '0.2'
55
54
  - !ruby/object:Gem::Dependency
56
- name: base64
55
+ name: async
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
58
  - - "~>"
60
59
  - !ruby/object:Gem::Version
61
- version: '0.2'
62
- type: :runtime
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.2'
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 top of Async. Single
132
- event loop with min-heap timer, skip-overlapping execution, jitter, monotonic clock
133
- intervals, semaphore concurrency control, and deterministic worker sharding. Designed
134
- for Falcon but works with any Async-based application.
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: []
@@ -159,6 +159,12 @@ files:
159
159
  - lib/async/background/runner.rb
160
160
  - lib/async/background/runner/queue_execution.rb
161
161
  - lib/async/background/runner/schedule.rb
162
+ - lib/async/background/runtime.rb
163
+ - lib/async/background/runtime/notification.rb
164
+ - lib/async/background/runtime/semaphore.rb
165
+ - lib/async/background/runtime/task.rb
166
+ - lib/async/background/runtime/task_group.rb
167
+ - lib/async/background/scheduler.rb
162
168
  - lib/async/background/version.rb
163
169
  - lib/async/background/web.rb
164
170
  - lib/async/background/web/app.rb
@@ -183,7 +189,6 @@ metadata:
183
189
  source_code_uri: https://github.com/roman-haidarov/async-background
184
190
  changelog_uri: https://github.com/roman-haidarov/async-background/blob/main/CHANGELOG.md
185
191
  bug_tracker_uri: https://github.com/roman-haidarov/async-background/issues
186
- post_install_message:
187
192
  rdoc_options: []
188
193
  require_paths:
189
194
  - lib
@@ -198,8 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
203
  - !ruby/object:Gem::Version
199
204
  version: '0'
200
205
  requirements: []
201
- rubygems_version: 3.4.22
202
- signing_key:
206
+ rubygems_version: 3.6.7
203
207
  specification_version: 4
204
- summary: Lightweight heap-based cron/interval scheduler for Async.
208
+ summary: Lightweight heap-based cron/interval scheduler for any Fiber scheduler.
205
209
  test_files: []