riverqueue 0.13.0 → 0.14.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.
data/lib/resumable.rb CHANGED
@@ -11,6 +11,7 @@ module River
11
11
  attr_accessor :completed_step
12
12
  attr_reader :cursors
13
13
  attr_accessor :cursors_dirty
14
+ attr_reader :pending_cursor_steps
14
15
  attr_accessor :resume_matched
15
16
  attr_reader :resume_step
16
17
  attr_accessor :step_name
@@ -24,6 +25,8 @@ module River
24
25
  @step_name = nil
25
26
 
26
27
  @resume_matched = @resume_step.to_s.empty?
28
+ # Step markers may accompany stale cursors from older clients.
29
+ @pending_cursor_steps = @resume_matched ? @cursors.keys.sort : []
27
30
  end
28
31
 
29
32
  def register(name)
@@ -37,6 +40,9 @@ module River
37
40
  # Internal runtime boundary: attach progress only to attempts that did not
38
41
  # complete, matching River Go's persisted metadata format.
39
42
  def __capture_resumable_metadata!
43
+ # Keep missing cursor steps reachable when the worker restores them.
44
+ return unless @resumable_state.pending_cursor_steps.empty?
45
+
40
46
  if @resumable_state.cursors_dirty
41
47
  @metadata_updates[RESUMABLE_CURSOR_METADATA_KEY] = @resumable_state.cursors.empty? ? nil : @resumable_state.cursors.dup
42
48
  end
@@ -49,6 +55,9 @@ module River
49
55
  # Internal runtime boundary: validate that the recorded resume point still
50
56
  # exists in the worker after it has returned.
51
57
  def __finish_resumable_work!
58
+ if (pending = @resumable_state.pending_cursor_steps.first)
59
+ raise Error, "resumable cursor step #{pending.inspect} not found in worker"
60
+ end
52
61
  if @resumable_state.resume_step && !@resumable_state.resume_matched
53
62
  raise Error, "resumable step #{@resumable_state.resume_step.inspect} not found in worker"
54
63
  end
@@ -56,12 +65,22 @@ module River
56
65
 
57
66
  # Immediately checkpoints the current step and cursor progress. Wrap the
58
67
  # call in Driver#transaction alongside application writes when an atomic
59
- # checkpoint is needed.
68
+ # checkpoint is needed. When resuming cursor-only progress, reach its saved
69
+ # cursor step before checkpointing another step.
70
+ #
71
+ # Commit the transaction before the step block returns
72
+ # successfully, and let transaction and commit errors propagate out of it.
73
+ # A successful block marks the step complete even if an enclosing transaction
74
+ # later rolls back.
60
75
  def resumable_checkpoint(cursor: RESUMABLE_CURSOR_UNSET)
61
76
  step_name = @resumable_state.step_name
62
77
  raise Error, "resumable step can only be persisted inside a resumable step" unless step_name
63
78
  raise Error, "job must be running" unless row.state == JOB_STATE_RUNNING
64
79
 
80
+ if (pending = @resumable_state.pending_cursor_steps.first)
81
+ raise Error, "resumable checkpoint cannot precede saved cursor step #{pending.inspect}"
82
+ end
83
+
65
84
  cursors = @resumable_state.cursors.dup
66
85
  cursors[step_name] = JSON.parse(JSON.generate(cursor)) unless cursor.equal?(RESUMABLE_CURSOR_UNSET)
67
86
  updates = {
@@ -117,6 +136,9 @@ module River
117
136
  private def run_resumable_step(name, cursor:, default:)
118
137
  raise Error, "resumable steps cannot be nested" if @resumable_state.step_name
119
138
  raise ArgumentError, "resumable step name must be non-empty" if name.empty?
139
+ if !cursor && @resumable_state.pending_cursor_steps.include?(name)
140
+ raise Error, "resumable cursor step #{name.inspect} requires a cursor step"
141
+ end
120
142
  @resumable_state.register(name)
121
143
 
122
144
  unless @resumable_state.resume_matched
@@ -132,6 +154,7 @@ module River
132
154
  @resumable_state.step_name = name
133
155
  begin
134
156
  value = (cursor && @resumable_state.cursors.key?(name)) ? @resumable_state.cursors[name] : default
157
+ @resumable_state.pending_cursor_steps.delete(name) if cursor
135
158
  result = cursor ? yield(value) : yield
136
159
  @resumable_state.completed_step = name
137
160
  if cursor && @resumable_state.cursors.key?(name)
data/lib/worker.rb CHANGED
@@ -12,8 +12,18 @@ module River
12
12
  #
13
13
  # With one argument, the kind is read from +worker.kind+ or
14
14
  # +worker.class.kind+. Pass a kind and worker separately to override it.
15
- # Kinds and aliases accept symbols or strings.
16
- def add(kind_or_worker, worker = nil, aliases: [])
15
+ # Alternatively, pass a kind and a block that receives a River::Job on each
16
+ # attempt. Block workers use the client's retry and timeout policies.
17
+ # Supply a worker or a block, not both. Kinds and aliases accept symbols or
18
+ # strings.
19
+ def add(kind_or_worker, worker = nil, aliases: [], &block)
20
+ if block
21
+ raise ArgumentError, "use a worker or a block, not both" unless worker.nil?
22
+ raise ArgumentError, "block workers require a string or symbol kind" unless kind_or_worker.is_a?(String) || kind_or_worker.is_a?(Symbol)
23
+
24
+ worker = BlockWorker.new(block)
25
+ end
26
+
17
27
  if worker
18
28
  kind = kind_or_worker.to_s
19
29
  else
@@ -54,11 +64,29 @@ module River
54
64
  def kinds
55
65
  @workers.keys.freeze
56
66
  end
67
+
68
+ # Adapts a registered block to the runtime's worker interface.
69
+ class BlockWorker
70
+ def initialize(work_func)
71
+ @work_func = work_func
72
+ end
73
+
74
+ def work(job)
75
+ @work_func.call(job)
76
+ end
77
+ end
78
+ private_constant :BlockWorker
57
79
  end
58
80
 
59
81
  # A job being worked, with access to its persisted row and attempt-local
60
82
  # metadata changes.
61
83
  class Job
84
+ # Internal runtime hint; persisted state determines whether completion committed.
85
+ attr_accessor :__completion_attempted
86
+
87
+ # Internal snapshot retained only after transactional completion commits.
88
+ attr_accessor :__completion_snapshot
89
+
62
90
  # Client working this job.
63
91
  attr_reader :client
64
92
 
@@ -67,6 +95,8 @@ module River
67
95
 
68
96
  def initialize(client, row)
69
97
  @client = client
98
+ @__completion_attempted = false
99
+ @__completion_snapshot = nil
70
100
  @logger = nil
71
101
  @metadata_updates = {}
72
102
  @row = row
@@ -12,10 +12,10 @@
12
12
  "postgresql/main/005_migration_unique_client.up.sql": "b760f487152c7d92102869d46b8a64dc1e2094d5675e690ffbe52a747eee8431",
13
13
  "postgresql/main/006_bulk_unique.down.sql": "726483f6e5aa7dd02cdd974cd7bf716973a8d0a97ba6dbc5dc5304aaf54c7ad7",
14
14
  "postgresql/main/006_bulk_unique.up.sql": "3b133f7ce4662d3dc8bd4a57628e0e116a300b2635a79315557aa1369849f0fb",
15
- "postgresql/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.down.sql": "9131aae235187dbdaaa822dab2a475a884e917d9af05e3c98fb95c152eaa769a",
16
- "postgresql/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.up.sql": "47ec8031b88e69004de2def5bc3109d969f71ee4c33a1e7dac2fb8c9dd19182d",
17
- "postgresql/main/008_job_id_autoincrement.down.sql": "0c3750a947d6494db07d56f5d3735a5e49a2cbfa1f7a09771227c31c8147bf70",
18
- "postgresql/main/008_job_id_autoincrement.up.sql": "0c3750a947d6494db07d56f5d3735a5e49a2cbfa1f7a09771227c31c8147bf70",
15
+ "postgresql/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.down.sql": "2771765becc5baa0a8af4406cb4d58ea6d3fb0ebdbf72a0c23ccc72da623caeb",
16
+ "postgresql/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.up.sql": "af01339fcb1687de1f768a570b6ad1e51d23300082aed60440f6a662a0e39d85",
17
+ "postgresql/main/008_job_id_autoincrement.down.sql": "678b3b02deb2f798c3fda3a73c974fc1fe48c6b88688b94f0f7700132b30626f",
18
+ "postgresql/main/008_job_id_autoincrement.up.sql": "678b3b02deb2f798c3fda3a73c974fc1fe48c6b88688b94f0f7700132b30626f",
19
19
  "sqlite/main/001_create_river_migration.down.sql": "34c87dc594bf7520bc3ae69f6f0da8d2d9a472616ab38b37e63d4e3838da06d2",
20
20
  "sqlite/main/001_create_river_migration.up.sql": "d15597cb0bb884fb0727d2a29ad8313842708b55fd561a5fe62e37aad5f34298",
21
21
  "sqlite/main/002_initial_schema.down.sql": "900508ba08d0ca3c8451eb2854cd9ab837166ef55736393524253b6228438470",
@@ -31,7 +31,7 @@
31
31
  "sqlite/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.down.sql": "55bffeb528b40dffc0cbec2f22d1463aef37a8b3d7977f83729cc505bb77c745",
32
32
  "sqlite/main/007_notification_outbox_sqlite_jsonb_and_sql_cleanup.up.sql": "441a05e1d9aa4f151877ccf725b0b0a86f27013442297d4b621c05676270d0b0",
33
33
  "sqlite/main/008_job_id_autoincrement.down.sql": "04871283fe5d4cab4ac70da28d8509aa7ba765994d528c2dcc7ebe0ee596710c",
34
- "sqlite/main/008_job_id_autoincrement.up.sql": "049c9bf615f24a326bcc31ddc87b46f76e3de11e3eea3b2b9e1358131b3f3bed"
34
+ "sqlite/main/008_job_id_autoincrement.up.sql": "3ce6d941f4c434adbd87e69089fa5e99b6047e5b51e611c9845e1d0d3c080e0f"
35
35
  },
36
36
  "source": "riverdriver/"
37
37
  }
@@ -47,7 +47,7 @@ ALTER TABLE /* TEMPLATE: schema */river_queue
47
47
  --
48
48
  -- SQLite JSONB conversion rollback.
49
49
  --
50
- -- No-op. PostgreSQL already stores River JSON columns as jsonb.
50
+ -- No-op. Postgres already stores River JSON columns as jsonb.
51
51
 
52
52
  --
53
53
  -- Notification outbox rollback.
@@ -16,7 +16,7 @@ CREATE INDEX river_notification_topic_id_idx ON /* TEMPLATE: schema */river_noti
16
16
  --
17
17
  -- SQLite JSONB conversion.
18
18
  --
19
- -- No-op. PostgreSQL already stores River JSON columns as jsonb.
19
+ -- No-op. Postgres already stores River JSON columns as jsonb.
20
20
 
21
21
  --
22
22
  -- SQL cleanup.
@@ -1,3 +1,3 @@
1
- -- No-op. PostgreSQL sequences already prevent automatically generated job IDs
1
+ -- No-op. Postgres sequences already prevent automatically generated job IDs
2
2
  -- from being reused.
3
3
  SELECT 1;
@@ -1,3 +1,3 @@
1
- -- No-op. PostgreSQL sequences already prevent automatically generated job IDs
1
+ -- No-op. Postgres sequences already prevent automatically generated job IDs
2
2
  -- from being reused.
3
3
  SELECT 1;
@@ -1,5 +1,5 @@
1
1
  -- Rebuild river_job so automatically generated IDs are never reused after the
2
- -- job holding the largest ID is deleted. Unlike PostgreSQL sequences, SQLite's
2
+ -- job holding the largest ID is deleted. Unlike Postgres sequences, SQLite's
3
3
  -- default ROWID allocator may otherwise reuse that deleted ID.
4
4
 
5
5
  -- Rebuilding river_job would discard schema installed by River Pro. Check
data/sig/client.rbs CHANGED
@@ -15,6 +15,7 @@ module River
15
15
 
16
16
  def initialize: (_Driver driver, ?config: Config?) -> void
17
17
  def __finish_claimed_job: (JobRow, ?Exception?) -> untyped
18
+ def __insert_with_default_scheduled_at: (jobArgs, InsertOpts, Time?) -> JobInsertResult
18
19
  def __perform_job: (Integer, ?allow_scheduled: bool) -> untyped
19
20
  def __interrupt_workers: () -> untyped
20
21
  def __runtime_healthy?: () -> bool
@@ -24,6 +25,7 @@ module River
24
25
  ?scheduled_at: Time?, ?state: jobStateInitialInput?, ?tags: Array[String]?, ?unique_opts: UniqueOpts?) -> JobInsertResult
25
26
  def insert_many: (Array[jobArgs | InsertManyParams]) -> Array[JobInsertResult]
26
27
  def job_cancel: (Integer) -> JobRow
28
+ def job_complete_tx: (Job) -> JobRow
27
29
  def job_delete: (Integer) -> JobRow
28
30
  def job_delete_many: (?JobListParams?, ?after: untyped, ?after_id: untyped,
29
31
  ?ids: untyped, ?kinds: untyped, ?limit: untyped, ?metadata: untyped, ?priorities: untyped,
@@ -46,6 +48,7 @@ module River
46
48
  def queue_remove: (String | Symbol) -> self
47
49
  def queue_resume: (String | Symbol) -> bool
48
50
  def queue_update: (String | Symbol, metadata: Hash[untyped, untyped]) -> Queue
51
+ def request_resign: () -> nil
49
52
  def start: () -> self
50
53
  def started?: () -> bool
51
54
  def stop: (?wait: bool) -> self
@@ -60,7 +63,7 @@ module River
60
63
  TAG_RE: Regexp
61
64
 
62
65
  private def insert_and_check_unique_job: (Driver::JobInsertParams) -> JobInsertResult
63
- private def make_insert_params: (jobArgs, InsertOpts) -> Driver::JobInsertParams
66
+ private def make_insert_params: (jobArgs, InsertOpts, ?Time?) -> Driver::JobInsertParams
64
67
  private def make_unique_key_and_bitmask: (Driver::JobInsertParams, UniqueOpts) -> [String, String]
65
68
  private def run_insert_plugins: (Array[Driver::JobInsertParams]) { () -> Array[JobInsertResult] } -> Array[JobInsertResult]
66
69
  private def truncate_time: (Time, Integer | Rational) -> Time
data/sig/driver.rbs CHANGED
@@ -1,6 +1,8 @@
1
1
  module River
2
2
  interface _Driver
3
+ def after_commit: () { () -> void } -> void
3
4
  def respond_to?: (Symbol, ?bool) -> bool
5
+ def in_transaction?: () -> bool
4
6
  def init_driver: () -> void
5
7
  def migration_backend: () -> Symbol
6
8
  def migration_connection: [A] () { (untyped) -> A } -> A
@@ -13,11 +15,11 @@ module River
13
15
  def job_get_available: (queue: String, max: Integer, attempted_by: String, ?kinds: Array[String]?, ?now: Time) -> Array[JobRow]
14
16
  def job_claim: (id: Integer, attempted_by: String, ?allow_scheduled: bool, ?now: Time) -> JobRow?
15
17
  def job_get_cancelled_ids: (Array[Integer]) -> Array[Integer]
16
- def job_complete: (id: Integer, finalized_at: Time, ?metadata: Hash[untyped, untyped]?, ?now: Time) -> (JobRow | :cancelled)?
18
+ def job_complete: (id: Integer, finalized_at: Time, ?expected_job: JobRow?, ?metadata: Hash[untyped, untyped]?, ?now: Time) -> (JobRow | :cancelled)?
17
19
  def job_get_by_kind_and_unique_properties: (Driver::JobGetByKindAndUniquePropertiesParam) -> JobRow?
18
20
  def job_insert: (Driver::JobInsertParams) -> [JobRow, bool]
19
21
  def job_insert_many: (Array[Driver::JobInsertParams]) -> Array[[JobRow, bool]]
20
- def job_metadata_merge: (Integer, Hash[untyped, untyped]) -> JobRow?
22
+ def job_metadata_merge: (Integer, Hash[untyped, untyped], ?JobRow?) -> JobRow?
21
23
  def job_rescue_stuck: (**untyped) -> Integer
22
24
  def job_retry: (Integer, ?now: Time) -> JobRow?
23
25
  def job_schedule: (?now: Time, ?max: Integer) -> Integer
@@ -26,13 +28,17 @@ module River
26
28
  def leader_acquire: (String, ?ttl: Integer, ?now: Time) -> bool
27
29
  def leader_release: (String) -> untyped
28
30
  def leader_renew: (String, ?ttl: Integer, ?now: Time) -> bool
31
+ def normalize_time: (Time) -> Time
29
32
  def notification_delete_before: (horizon: Time, ?max: Integer) -> Integer
33
+ def notification_listener: () -> untyped
34
+ def request_resign: () -> nil
30
35
  def queue_get: (String) -> Queue?
31
36
  def queue_list: (?max: Integer) -> Array[Queue]
32
37
  def queue_pause: (String, ?now: Time) -> Array[Queue]
33
38
  def queue_resume: (String, ?now: Time) -> Array[Queue]
34
39
  def queue_update: (String, metadata: Hash[untyped, untyped], ?now: Time) -> Queue?
35
40
  def queue_upsert: (String, ?metadata: Hash[untyped, untyped], ?now: Time) -> Queue?
41
+ def time_precision: () -> Rational
36
42
  def transaction: [T] () { () -> T } -> T
37
43
 
38
44
  # this set of methods is used only in tests
@@ -56,6 +62,25 @@ module River
56
62
  def finish: (JobRow) -> JobRow
57
63
  end
58
64
 
65
+ module NotificationListener
66
+ TOPICS: Array[String]
67
+ class Postgres
68
+ @connection: untyped
69
+ @channels: Hash[String, String]
70
+ def initialize: (untyped, String) -> void
71
+ def close: () -> untyped
72
+ def poll: (Float | Integer) -> Array[untyped]
73
+ end
74
+
75
+ class SQLite
76
+ @query: untyped
77
+ @last_id: Integer
78
+ def initialize: (untyped) -> void
79
+ def close: () -> nil
80
+ def poll: (Float | Integer) -> Array[untyped]
81
+ end
82
+ end
83
+
59
84
  class PostgresCapabilities
60
85
  attr_reader supports_listen_notify: bool
61
86
  attr_reader unique_insert_mode: :metadata_nonce | :returning_old | :xmax
@@ -103,10 +128,11 @@ module River
103
128
  def runtime_decode_postgres_job: (untyped) -> JobRow
104
129
  @postgres_capabilities_cache: PostgresCapabilities::Cache
105
130
  def init_driver: () -> void
131
+ def normalize_time: (Time) -> Time
106
132
  def postgres_capabilities: () -> PostgresCapabilities
107
133
  def postgres_notify_insert: (Array[JobInsertParams]) -> void
108
134
  def job_get_cancelled_ids: (Array[Integer]) -> Array[Integer]
109
- def job_complete: (id: untyped, finalized_at: untyped, ?metadata: untyped, ?now: untyped) -> untyped
135
+ def job_complete: (id: untyped, finalized_at: untyped, ?expected_job: untyped, ?metadata: untyped, ?now: untyped) -> untyped
110
136
  def job_cancel: (untyped, ?now: untyped) -> untyped
111
137
  def job_delete: (untyped) -> untyped
112
138
  def job_delete_finalized: (retention: untyped, ?now: untyped, ?max: untyped) -> untyped
@@ -116,7 +142,7 @@ module River
116
142
  def job_claim: (id: untyped, attempted_by: untyped, ?allow_scheduled: untyped, ?now: untyped) -> untyped
117
143
  private def runtime_claim_jobs: (untyped, untyped, untyped, untyped) -> untyped
118
144
  def job_list: (?untyped) -> untyped
119
- def job_metadata_merge: (untyped, untyped) -> untyped
145
+ def job_metadata_merge: (untyped, untyped, ?untyped) -> untyped
120
146
  def job_rescue_stuck: (horizon: untyped, retry_policy: untyped, ?now: untyped, ?max: untyped, ?logger: untyped, ?rescue_if: untyped) -> untyped
121
147
  def job_retry: (untyped, ?now: untyped) -> untyped
122
148
  def job_schedule: (?now: untyped, ?max: untyped) -> untyped
@@ -126,6 +152,8 @@ module River
126
152
  def leader_release: (untyped) -> untyped
127
153
  def leader_renew: (untyped, ?ttl: untyped, ?now: untyped) -> untyped
128
154
  def notification_delete_before: (horizon: untyped, ?max: untyped) -> untyped
155
+ def notification_listener: () -> untyped
156
+ def request_resign: () -> nil
129
157
  def queue_get: (untyped) -> untyped
130
158
  def queue_list: (?max: untyped) -> untyped
131
159
  def queue_pause: (untyped, ?now: untyped) -> untyped
@@ -133,6 +161,8 @@ module River
133
161
  def queue_update: (untyped, metadata: untyped, ?now: untyped) -> untyped
134
162
  def queue_upsert: (untyped, ?metadata: untyped, ?now: untyped) -> untyped
135
163
 
164
+ def time_precision: () -> Rational
165
+
136
166
  private
137
167
 
138
168
  def job_get_by_id: (untyped) -> untyped
@@ -142,14 +172,18 @@ module River
142
172
  def runtime_cursor_clause: (untyped) -> String
143
173
  def runtime_execute: (String) -> untyped
144
174
  def runtime_in_clause: (untyped, untyped) -> untyped
175
+ def runtime_job_attempt_predicate: (untyped) -> String
145
176
  def runtime_job_list_without_params: () -> untyped
146
177
  def runtime_job_list: (JobListParams, ?for_delete: bool) -> Array[JobRow]
147
178
  def runtime_job_rows: (String) -> untyped
148
179
  def runtime_json: (untyped) -> untyped
180
+ def runtime_legacy_subtransactions?: () -> bool
149
181
  def runtime_merge_metadata: (untyped) -> untyped
150
182
  def runtime_metadata_equals: (untyped, untyped) -> untyped
151
183
  def runtime_nullable_time: (untyped) -> untyped
152
184
  def runtime_notify: (String, untyped) -> untyped
185
+ def notification_query: (String) -> untyped
186
+ def runtime_notification_connection: () -> untyped
153
187
  def sqlite_insert_nonces: (Array[JobInsertParams]) -> Array[String]
154
188
  def runtime_parse_json: (untyped) -> untyped
155
189
  def runtime_parse_time: (untyped) -> untyped
@@ -160,10 +194,11 @@ module River
160
194
  def runtime_quote: (untyped) -> String
161
195
  def runtime_rescue_retry: (untyped, untyped, untyped, untyped, untyped) -> Time
162
196
  def runtime_returning_ids: (untyped) -> untyped
197
+ def runtime_schedule_postgres: (Time, Integer) -> Array[String]
198
+ def runtime_schedule_sqlite: (Time, Integer) -> Array[String]
163
199
  def runtime_state: (untyped) -> untyped
164
200
  def runtime_tag_contains: (untyped) -> untyped
165
201
  def runtime_time: (untyped) -> untyped
166
- def runtime_unique_violation_class: () -> singleton(Exception)
167
202
  def runtime_update_value: (untyped, untyped) -> untyped
168
203
  def runtime_value: (untyped, untyped) -> untyped
169
204
  def transaction: () { () -> untyped } -> untyped
data/sig/runtime.rbs CHANGED
@@ -77,21 +77,32 @@ module River
77
77
  @workers: Hash[String, untyped]
78
78
  def initialize: () -> void
79
79
  def add: (untyped, ?untyped, ?aliases: Array[untyped]) -> self
80
+ | (String | Symbol, ?aliases: Array[untyped]) { (Job) -> untyped } -> self
80
81
  def []: (String | Symbol) -> untyped
81
82
  def fetch: (String | Symbol) -> untyped
82
83
  | (String | Symbol, untyped) -> untyped
83
84
  | (String | Symbol) { (String) -> untyped } -> untyped
84
85
  def include?: (String | Symbol) -> bool
85
86
  def kinds: () -> Array[String]
87
+
88
+ class BlockWorker
89
+ @work_func: ^(Job) -> untyped
90
+ def initialize: (^(Job) -> untyped) -> void
91
+ def work: (Job) -> untyped
92
+ end
86
93
  end
87
94
 
88
95
  class Job
89
96
  RESUMABLE_CURSOR_UNSET: Object
97
+ @__completion_attempted: bool
98
+ @__completion_snapshot: JobRow?
90
99
  @client: Client
91
100
  @logger: untyped
92
101
  @metadata_updates: Hash[String, untyped]
93
102
  @resumable_state: ResumableState
94
103
  @row: JobRow
104
+ attr_accessor __completion_attempted: bool
105
+ attr_accessor __completion_snapshot: JobRow?
95
106
  attr_reader client: Client
96
107
  attr_reader row: JobRow
97
108
  def initialize: (Client, JobRow) -> void
@@ -126,6 +137,7 @@ module River
126
137
  attr_accessor completed_step: String?
127
138
  attr_reader cursors: Hash[String, untyped]
128
139
  attr_accessor cursors_dirty: bool
140
+ attr_reader pending_cursor_steps: Array[String]
129
141
  attr_accessor resume_matched: bool
130
142
  attr_reader resume_step: String?
131
143
  attr_accessor step_name: String?
@@ -152,15 +164,30 @@ module River
152
164
  def resolved_fetch_poll_interval: (Config) -> Float
153
165
  end
154
166
 
167
+ class DefaultClientID
168
+ HOSTNAME_MAX_LENGTH: Integer
169
+ @mutex: Mutex
170
+ @state: Hash[Integer, String]?
171
+ def initialize: () -> void
172
+ def value: () -> String
173
+
174
+ private
175
+
176
+ def generate: (Integer) -> String
177
+ end
178
+
155
179
  class Config
180
+ @poll_only: bool
181
+ attr_reader poll_only: bool
156
182
  @cancelled_job_retention_period: Float?
157
183
  @completed_job_retention_period: Float?
184
+ @default_id: DefaultClientID
158
185
  @discarded_job_retention_period: Float?
159
186
  @error_handler: untyped
160
187
  @fetch_cooldown: Float
161
188
  @fetch_only_known_kinds: bool
162
189
  @fetch_poll_interval: Float
163
- @id: String
190
+ @id: String?
164
191
  @job_timeout: Float?
165
192
  @leader_election_disabled: bool
166
193
  @logger: untyped
@@ -178,7 +205,6 @@ module River
178
205
  attr_reader fetch_cooldown: Float
179
206
  attr_reader fetch_only_known_kinds: bool
180
207
  attr_reader fetch_poll_interval: Float
181
- attr_reader id: String
182
208
  attr_reader job_timeout: Float?
183
209
  attr_reader leader_election_disabled: bool
184
210
  attr_reader logger: untyped
@@ -191,13 +217,16 @@ module River
191
217
 
192
218
  def initialize: (?queues: untyped, ?workers: untyped, ?id: untyped,
193
219
  ?fetch_cooldown: untyped, ?fetch_only_known_kinds: bool, ?fetch_poll_interval: untyped, ?job_timeout: untyped,
194
- ?leader_election_disabled: bool,
220
+ ?leader_election_disabled: bool, ?poll_only: bool,
195
221
  ?retry_policy: untyped, ?error_handler: untyped, ?plugins: untyped,
196
222
  ?maintenance_services: untyped, ?periodic_jobs: untyped,
197
223
  ?cancelled_job_retention_period: untyped, ?completed_job_retention_period: untyped,
198
224
  ?discarded_job_retention_period: untyped, ?logger: untyped) -> void
225
+ def id: () -> String
199
226
  def with: (**untyped) -> Config
200
227
 
228
+ def adopt_default_id: (DefaultClientID) -> void
229
+
201
230
  private
202
231
 
203
232
  def normalize_queues: (untyped) -> Hash[String, QueueConfig]
@@ -308,6 +337,7 @@ module River
308
337
  @next_handle: Integer
309
338
  @wake: untyped
310
339
  def initialize: (Array[PeriodicJob], wake: untyped, ?enabled: bool) -> void
340
+ def __due_with_times: (Time) ?{ (PeriodicJob, StandardError) -> void } -> Array[[PeriodicJob, Time]]
311
341
  def add: (PeriodicJob) -> Integer
312
342
  def add_many: (Array[PeriodicJob]) -> Array[Integer]
313
343
  def clear: () -> self
@@ -334,13 +364,22 @@ module River
334
364
  @condition: ConditionVariable
335
365
  @config: Config
336
366
  @driver: _Driver
367
+ @fetching_queues: Hash[String, Array[Integer]]
368
+ @leader: bool
369
+ @maintenance_condition: ConditionVariable
370
+ @maintenance_generation: Integer
337
371
  @maintenance_thread: Thread?
338
372
  @mutex: Mutex
339
- @periodic_jobs: PeriodicJobBundle
373
+ @notification_thread: Thread?
374
+ @notifications_disabled: bool
340
375
  @performing: bool?
376
+ @periodic_jobs: PeriodicJobBundle
341
377
  @producer_threads: Hash[String, Thread]
342
378
  @queue_configs: Hash[String, QueueConfig]
379
+ @queue_generations: Hash[String, Integer]
380
+ @queue_paused: Hash[String, bool]
343
381
  @removed_queues: Hash[String, bool]
382
+ @resign_requested: bool
344
383
  @running: Hash[Integer, untyped]
345
384
  @started: bool
346
385
  @stop_requested: bool
@@ -373,6 +412,7 @@ module River
373
412
  def finish_failed: (JobRow, Job, Exception, JobTiming, ?cancelled: bool, ?worker: untyped) -> untyped
374
413
  def finish_interrupted: (JobRow, Job, JobTiming) -> untyped
375
414
  def finish_snoozed: (JobRow, Job, JobSnoozeError, JobTiming) -> untyped
415
+ def finish_transactional_completion: (Job, JobTiming) -> bool
376
416
  def finish_work: (Integer) -> untyped
377
417
  def invoke_plugins: (Symbol, *untyped) -> Array[untyped]
378
418
  def invoke_worker: (untyped, Job) -> untyped
@@ -381,22 +421,30 @@ module River
381
421
  def monotonic_now: () -> Float
382
422
  def next_retry: (JobRow, Exception, Time, ?worker: untyped) -> Time
383
423
  def next_snooze_count: (untyped) -> Integer
424
+ def notification_loop: (untyped) -> untyped
425
+ def notifications_stopping?: () -> bool
384
426
  def retry_allowed?: (untyped, Job, Exception) -> untyped
385
427
  def perform_work: (untyped, Job) -> untyped
386
428
  def periodic_jobs_changed: () -> untyped
387
429
  def producer_loop: (String, QueueConfig) -> untyped
388
430
  def publish: (Symbol, JobRow, JobTiming) -> untyped
389
431
  def queue_stopping?: (String) -> bool
432
+ def receive_notification: (untyped, untyped) -> untyped
390
433
  def remove_subscription: (Subscription) -> Subscription?
391
434
  def rescue_job?: (JobRow, Time) -> bool
392
435
  def resolve_worker: (String) -> untyped
393
436
  def run_periodic: (Time) -> untyped
394
437
  def running_count: (String) -> Integer
395
438
  def start_maintenance: () -> untyped
439
+ def start_notifications: () -> untyped
396
440
  def start_producer: (String, QueueConfig) -> untyped
397
441
  def stopping?: () -> bool
398
442
  def wait: (Float | Integer) -> untyped
443
+ def wait_for_maintenance: (Float | Integer, Integer) -> untyped
444
+ def wait_for_queue: (String, Float | Integer, Integer) -> untyped
399
445
  def wait_for_running_jobs: (String, Float | Integer) -> untyped
446
+ def wake_maintenance: () -> untyped
447
+ def wake_queue: (String) -> untyped
400
448
  def worker_timeout: (untyped, Job) -> Float?
401
449
  end
402
450
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riverqueue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Gentry
@@ -91,7 +91,7 @@ dependencies:
91
91
  - !ruby/object:Gem::Version
92
92
  version: '1000'
93
93
  description: Insert and work River jobs in Ruby using the same schema and state machine
94
- as River's Go client. Use with riverqueue-activerecord or riverqueue-sequel.
94
+ as other River clients. Use with riverqueue-activerecord or riverqueue-sequel.
95
95
  email: brandur@brandur.org
96
96
  executables:
97
97
  - river
@@ -113,6 +113,7 @@ files:
113
113
  - lib/config.rb
114
114
  - lib/driver.rb
115
115
  - lib/driver/job_row_decoder.rb
116
+ - lib/driver/notification_listener.rb
116
117
  - lib/driver/postgres_capabilities.rb
117
118
  - lib/driver/runtime.rb
118
119
  - lib/errors.rb
@@ -205,5 +206,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
206
  requirements: []
206
207
  rubygems_version: 4.0.9
207
208
  specification_version: 4
208
- summary: A fast, reliable job queue for Ruby backed by PostgreSQL or SQLite.
209
+ summary: A fast, reliable job queue for Ruby backed by Postgres or SQLite.
209
210
  test_files: []