zizq 0.2.0 → 0.3.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/README.md +19 -0
- data/lib/zizq/active_job_config.rb +39 -18
- data/lib/zizq/bulk_enqueue.rb +1 -1
- data/lib/zizq/client.rb +205 -7
- data/lib/zizq/crontab.rb +263 -0
- data/lib/zizq/crontab_builder.rb +52 -0
- data/lib/zizq/crontab_entry.rb +182 -0
- data/lib/zizq/crontab_entry_builder.rb +133 -0
- data/lib/zizq/enqueue_with.rb +1 -1
- data/lib/zizq/job.rb +6 -2
- data/lib/zizq/query.rb +2 -2
- data/lib/zizq/resources/cron_entry.rb +27 -0
- data/lib/zizq/resources/cron_group.rb +23 -0
- data/lib/zizq/resources/job.rb +4 -36
- data/lib/zizq/resources/job_template.rb +46 -0
- data/lib/zizq/resources.rb +3 -0
- data/lib/zizq/version.rb +1 -1
- data/lib/zizq.rb +108 -20
- data/sig/generated/zizq/active_job_config.rbs +19 -8
- data/sig/generated/zizq/bulk_enqueue.rbs +2 -2
- data/sig/generated/zizq/client.rbs +115 -0
- data/sig/generated/zizq/crontab.rbs +141 -0
- data/sig/generated/zizq/crontab_builder.rbs +35 -0
- data/sig/generated/zizq/crontab_entry.rbs +98 -0
- data/sig/generated/zizq/crontab_entry_builder.rbs +92 -0
- data/sig/generated/zizq/enqueue_with.rbs +2 -2
- data/sig/generated/zizq/query.rbs +4 -4
- data/sig/generated/zizq/resources/cron_entry.rbs +29 -0
- data/sig/generated/zizq/resources/cron_group.rbs +21 -0
- data/sig/generated/zizq/resources/job.rbs +4 -26
- data/sig/generated/zizq/resources/job_template.rbs +31 -0
- data/sig/generated/zizq.rbs +78 -4
- data/sig/zizq.rbs +24 -17
- metadata +20 -6
data/lib/zizq/crontab.rb
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# Represents a Crontab schedule defined on the Zizq server.
|
|
9
|
+
#
|
|
10
|
+
# This requires a Pro license on the Zizq server.
|
|
11
|
+
#
|
|
12
|
+
# The actual data is lazily fetched when first accessed.
|
|
13
|
+
#
|
|
14
|
+
# Crontabs are used to define collections of recurring jobs that run on a
|
|
15
|
+
# specified schedule, such as at 2am on every Monday. Each entry on the
|
|
16
|
+
# Crontab is a single job enqueue, which the Zizq server automatically
|
|
17
|
+
# triggers at the correct point in time. Zizq uses standard Cron expression
|
|
18
|
+
# syntax (with support for seconds via 6-fields) to define entries.
|
|
19
|
+
#
|
|
20
|
+
# Entire schedules, and individual entries on a schedule, can be paused and
|
|
21
|
+
# resumed.
|
|
22
|
+
#
|
|
23
|
+
# By default schedules operate in the system time zone of the Zizq server
|
|
24
|
+
# but an explicit IANA timezone name can be specified when defining the
|
|
25
|
+
# Crontab.
|
|
26
|
+
class Crontab
|
|
27
|
+
# The name of the cron group that this schedule is backed by.
|
|
28
|
+
attr_reader :name #: String
|
|
29
|
+
|
|
30
|
+
# True if this schedule is paused.
|
|
31
|
+
#
|
|
32
|
+
# When paused, the scheduler continues to run but does not enqueue any jobs
|
|
33
|
+
# and only advances the timer.
|
|
34
|
+
attr_writer :paused #: bool?
|
|
35
|
+
|
|
36
|
+
# Initialize the Crontab with the given group name.
|
|
37
|
+
#
|
|
38
|
+
# @rbs name: String
|
|
39
|
+
def initialize(name)
|
|
40
|
+
@building = false #: bool
|
|
41
|
+
@materialized = false #: bool
|
|
42
|
+
@name = name #: String
|
|
43
|
+
@entries = {} #: Hash[String, Zizq::CrontabEntry]
|
|
44
|
+
@paused = false #: bool?
|
|
45
|
+
@paused_at = nil #: Float?
|
|
46
|
+
@resumed_at = nil #: Float?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Fetch data from the Zizq server if not already fetched.
|
|
50
|
+
#
|
|
51
|
+
# Once fetched, this method becomes a no-op, unless #clear is called to
|
|
52
|
+
# remove the fetched data.
|
|
53
|
+
def materialize #: () -> self
|
|
54
|
+
unless @building || @materialized
|
|
55
|
+
materialize_with(Zizq.client.get_cron_group(name))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Clear materialized data that was fetched from the Zizq server.
|
|
62
|
+
#
|
|
63
|
+
# This triggers a refetch when the data is next accessed.
|
|
64
|
+
def clear #: () -> self
|
|
65
|
+
@entries = {}
|
|
66
|
+
@paused = nil
|
|
67
|
+
@paused_at = nil
|
|
68
|
+
@resumed_at = nil
|
|
69
|
+
@materialized = false
|
|
70
|
+
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Delete this entire Crontab schedule and its entries.
|
|
75
|
+
def delete! #: () -> void
|
|
76
|
+
Zizq.client.delete_cron_group(name)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Pause this entire Crontab schedule.
|
|
80
|
+
#
|
|
81
|
+
# All entries will stop enqueueing jobs, but the server continues to
|
|
82
|
+
# advance the schedule until it is resumed.
|
|
83
|
+
def pause! #: () -> void
|
|
84
|
+
materialize_with(Zizq.client.update_cron_group(name, paused: true))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Resume this Crontab schedule if it is currently paused.
|
|
88
|
+
#
|
|
89
|
+
# Individual entries that are paused will remain paused.
|
|
90
|
+
def resume! #: () -> void
|
|
91
|
+
materialize_with(Zizq.client.update_cron_group(name, paused: false))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Check if this schedule is currently paused.
|
|
95
|
+
def paused #: () -> bool?
|
|
96
|
+
materialize
|
|
97
|
+
@paused
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Check if this schedule is currently paused.
|
|
101
|
+
#
|
|
102
|
+
# Alias of #paused.
|
|
103
|
+
def paused? = paused #: () -> bool?
|
|
104
|
+
|
|
105
|
+
# Return the timestamp at which this Crontab schedule was last paused.
|
|
106
|
+
def paused_at #: () -> Float?
|
|
107
|
+
materialize
|
|
108
|
+
@paused_at
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Return the timestamp at which this Crontab schedule was last resumed.
|
|
112
|
+
def resumed_at #: () -> Float?
|
|
113
|
+
materialize
|
|
114
|
+
@resumed_at
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Return a Hash of Zizq::CrontabEntry instances keyed by their names.
|
|
118
|
+
#
|
|
119
|
+
# Each entry specifies the cron expression at which it executes,
|
|
120
|
+
# information about when it last/next enqueued a job, and details of the
|
|
121
|
+
# job that the entry enqueues.
|
|
122
|
+
def entries #: () -> Hash[String, Zizq::CrontabEntry]
|
|
123
|
+
materialize
|
|
124
|
+
@entries
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Redefine (replace) this Crontab schedule with another.
|
|
128
|
+
#
|
|
129
|
+
# This is equivalent to calling `Zizq.define_crontab` and is idempotent
|
|
130
|
+
# when given the same schedule more than once.
|
|
131
|
+
#
|
|
132
|
+
# @rbs ?timezone: String?
|
|
133
|
+
# @rbs ?paused: bool?
|
|
134
|
+
# @rbs &block: (Zizq::CrontabBuilder) -> void
|
|
135
|
+
# @rbs return: self
|
|
136
|
+
def redefine(timezone: nil, paused: nil, &block)
|
|
137
|
+
@building = true
|
|
138
|
+
|
|
139
|
+
yield CrontabBuilder.new(self, timezone:, paused:)
|
|
140
|
+
|
|
141
|
+
materialize_with(
|
|
142
|
+
Zizq.client.replace_cron_group(
|
|
143
|
+
name,
|
|
144
|
+
paused:,
|
|
145
|
+
entries: entries.values.map(&:to_params),
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
@building = false
|
|
150
|
+
|
|
151
|
+
self
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Return a handle for the specified Zizq::CrontabEntry.
|
|
155
|
+
#
|
|
156
|
+
# The entry can be paused or resumed is isolation, can be deleted entirely
|
|
157
|
+
# or can be redefined (replaced) with another entry.
|
|
158
|
+
#
|
|
159
|
+
# @rbs name: String
|
|
160
|
+
# @rbs return: Zizq::CrontabEntry
|
|
161
|
+
def entry(name)
|
|
162
|
+
materialize
|
|
163
|
+
entries.fetch(name) do
|
|
164
|
+
entry = materialize_entry_with(
|
|
165
|
+
Zizq.client.get_cron_group_entry(self.name, name),
|
|
166
|
+
)
|
|
167
|
+
entries[name] = entry
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Define (or redefine) an entry with this Crontab schedule.
|
|
172
|
+
#
|
|
173
|
+
# Defining the same entry more than once is idempotent. If the entry does
|
|
174
|
+
# not exist, it is added to the schedule. If the entry already exists, it
|
|
175
|
+
# replaces the current entry.
|
|
176
|
+
#
|
|
177
|
+
# The return value is a Zizq::CrontabEntryBuilder instance, on which the
|
|
178
|
+
# caller must call one of the enqueue methods (`enqueue`, `enqueue_raw`,
|
|
179
|
+
# optionally chained onto `enqueue_with`, exactly the same as a regular job
|
|
180
|
+
# enqueue).
|
|
181
|
+
#
|
|
182
|
+
# All enqueue options are supported *except* `delay` and `ready_at` which
|
|
183
|
+
# make no sense for recurring jobs.
|
|
184
|
+
#
|
|
185
|
+
# Bulk enqueues are not supported.
|
|
186
|
+
#
|
|
187
|
+
# crontab.define_entry(
|
|
188
|
+
# "refresh_data_warehose",
|
|
189
|
+
# "*/15 * * * *",
|
|
190
|
+
# ).enqueue(RefreshDataWarehoseJob, incremental: true)
|
|
191
|
+
#
|
|
192
|
+
# @rbs name: String
|
|
193
|
+
# @rbs expression: String
|
|
194
|
+
# @rbs timezone: String?
|
|
195
|
+
# @rbs paused: bool?
|
|
196
|
+
# @rbs return: Zizq::CrontabEntryBuilder
|
|
197
|
+
def define_entry(name, expression, timezone: nil, paused: nil)
|
|
198
|
+
CrontabEntryBuilder.new(self, name, expression, timezone:, paused:) do |e|
|
|
199
|
+
entry = materialize_entry_with(
|
|
200
|
+
Zizq.client.replace_cron_group_entry(
|
|
201
|
+
self.name,
|
|
202
|
+
name,
|
|
203
|
+
expression: e.expression,
|
|
204
|
+
job: e.job.to_enqueue_params,
|
|
205
|
+
timezone: e.timezone,
|
|
206
|
+
paused: e.paused,
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
materialize # in case this was the first entry operation
|
|
211
|
+
|
|
212
|
+
entry
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
private
|
|
217
|
+
|
|
218
|
+
# @rbs result: Zizq::Resources::CronGroup
|
|
219
|
+
# @rbs return: self
|
|
220
|
+
def materialize_with(result)
|
|
221
|
+
@paused = result.paused?
|
|
222
|
+
@paused_at = result.paused_at
|
|
223
|
+
@resumed_at = result.resumed_at
|
|
224
|
+
|
|
225
|
+
@entries = result.entries.map do |entry|
|
|
226
|
+
[
|
|
227
|
+
entry.name,
|
|
228
|
+
materialize_entry_with(entry),
|
|
229
|
+
]
|
|
230
|
+
end.to_h
|
|
231
|
+
|
|
232
|
+
@materialized = true
|
|
233
|
+
|
|
234
|
+
self
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# @rbs result: Zizq::Resources::CronEntry
|
|
238
|
+
# @rbs return: Zizq::CrontabEntry
|
|
239
|
+
def materialize_entry_with(result)
|
|
240
|
+
CrontabEntry.new(
|
|
241
|
+
self,
|
|
242
|
+
result.name,
|
|
243
|
+
result.expression,
|
|
244
|
+
job: EnqueueRequest.new(
|
|
245
|
+
type: result.job.type,
|
|
246
|
+
queue: result.job.queue,
|
|
247
|
+
priority: result.job.priority,
|
|
248
|
+
payload: result.job.payload,
|
|
249
|
+
retry_limit: result.job.retry_limit,
|
|
250
|
+
backoff: result.job.backoff,
|
|
251
|
+
retention: result.job.retention,
|
|
252
|
+
unique_key: result.job.unique_key,
|
|
253
|
+
unique_while: result.job.unique_while,
|
|
254
|
+
),
|
|
255
|
+
paused: result.paused?,
|
|
256
|
+
paused_at: result.paused_at,
|
|
257
|
+
resumed_at: result.resumed_at,
|
|
258
|
+
last_enqueue_at: result.last_enqueue_at,
|
|
259
|
+
next_enqueue_at: result.next_enqueue_at,
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# Builder used to configure a Zizq::Crontab instance.
|
|
9
|
+
#
|
|
10
|
+
# Instances of this class are returned from `Zizq.define_crontab`. See
|
|
11
|
+
# documentation for that method for usage.
|
|
12
|
+
class CrontabBuilder
|
|
13
|
+
# The Crontab instance that this builder configures.
|
|
14
|
+
attr_reader :target #: Zizq::Crontab
|
|
15
|
+
|
|
16
|
+
# Optional timezone to be applied to all entries by default.
|
|
17
|
+
attr_accessor :timezone #: String?
|
|
18
|
+
|
|
19
|
+
# Initialize the builder with the given Crontab instance.
|
|
20
|
+
#
|
|
21
|
+
# @rbs target: Zizq::Crontab
|
|
22
|
+
# @rbs timezone: String?
|
|
23
|
+
# @rbs paused: bool?
|
|
24
|
+
def initialize(target, timezone: nil, paused: nil)
|
|
25
|
+
@target = target.clear
|
|
26
|
+
@timezone = timezone
|
|
27
|
+
|
|
28
|
+
target.paused = paused
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Add or replace an entry on the schedule
|
|
32
|
+
#
|
|
33
|
+
# If no entry with the given name exists, it is added to schedule. If an
|
|
34
|
+
# entry with the same name exist, this entry replaces that entry. If the
|
|
35
|
+
# entry is the same as the original, the result is idempotent.
|
|
36
|
+
#
|
|
37
|
+
# @rbs name: String
|
|
38
|
+
# @rbs expression: String
|
|
39
|
+
# @rbs timezone: String?
|
|
40
|
+
# @rbs paused: bool?
|
|
41
|
+
# @rbs return: Zizq::CrontabEntryBuilder
|
|
42
|
+
def define_entry(name, expression, timezone: self.timezone, paused: nil)
|
|
43
|
+
CrontabEntryBuilder.new(
|
|
44
|
+
target,
|
|
45
|
+
name,
|
|
46
|
+
expression,
|
|
47
|
+
timezone:,
|
|
48
|
+
paused:,
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# Represents a single entry within a Crontab schedule.
|
|
9
|
+
#
|
|
10
|
+
# Each entry specifies the cron expression at which it executes,
|
|
11
|
+
# information about when it last/next enqueued a job, and details of the
|
|
12
|
+
# job that the entry enqueues.
|
|
13
|
+
#
|
|
14
|
+
# Entries can be paused or resumed, and can be deleted or redefined in place.
|
|
15
|
+
class CrontabEntry
|
|
16
|
+
# The Crontab schedule to which this entry belongs.
|
|
17
|
+
attr_reader :crontab #: Zizq::Crontab
|
|
18
|
+
|
|
19
|
+
# The name of this entry within the schedule.
|
|
20
|
+
attr_reader :name #: String
|
|
21
|
+
|
|
22
|
+
# The cron expression used to define the schedule for the entry.
|
|
23
|
+
#
|
|
24
|
+
# Both standard 5-field and enhanced 6-field cron (with seconds) are
|
|
25
|
+
# supported, along with @daily, @weekly etc.
|
|
26
|
+
attr_reader :expression #: String
|
|
27
|
+
|
|
28
|
+
# The timezone in which the schedule entry is processed.
|
|
29
|
+
#
|
|
30
|
+
# Defaults to the Zizq server timezone unless specified.
|
|
31
|
+
attr_reader :timezone #: String?
|
|
32
|
+
|
|
33
|
+
# Parameters that will be used to enqueue jobs each time the schedule fires.
|
|
34
|
+
#
|
|
35
|
+
# These are the same parameters as those used to enqueue jobs normally.
|
|
36
|
+
attr_reader :job #: EnqueueRequest
|
|
37
|
+
|
|
38
|
+
# True if this entry is currrently paused.
|
|
39
|
+
attr_reader :paused #: bool?
|
|
40
|
+
|
|
41
|
+
# The timestamp at which this entry was last paused.
|
|
42
|
+
attr_reader :paused_at #: Float?
|
|
43
|
+
|
|
44
|
+
# The timestamp at which this entry was last resumed.
|
|
45
|
+
attr_reader :resumed_at #: Float?
|
|
46
|
+
|
|
47
|
+
# The timestamp at which a job was last enqueued for this entry.
|
|
48
|
+
attr_reader :last_enqueue_at #: Float?
|
|
49
|
+
#
|
|
50
|
+
# The timestamp at which the next job will be enqueued for this entry.
|
|
51
|
+
attr_reader :next_enqueue_at #: Float?
|
|
52
|
+
|
|
53
|
+
# Initialize the entry with all configured parameters.
|
|
54
|
+
#
|
|
55
|
+
# @rbs crontab: Zizq::Crontab
|
|
56
|
+
# @rbs name: String
|
|
57
|
+
# @rbs expression: String
|
|
58
|
+
# @rbs timezone: String?
|
|
59
|
+
# @rbs job: EnqueueRequest
|
|
60
|
+
# @rbs paused: bool?
|
|
61
|
+
# @rbs paused_at: Float?
|
|
62
|
+
# @rbs resumed_at: Float?
|
|
63
|
+
# @rbs last_enqueue_at: Float?
|
|
64
|
+
# @rbs next_enqueue_at: Float?
|
|
65
|
+
def initialize(crontab,
|
|
66
|
+
name,
|
|
67
|
+
expression,
|
|
68
|
+
job:,
|
|
69
|
+
timezone: nil,
|
|
70
|
+
paused: nil,
|
|
71
|
+
paused_at: nil,
|
|
72
|
+
resumed_at: nil,
|
|
73
|
+
last_enqueue_at: nil,
|
|
74
|
+
next_enqueue_at: nil)
|
|
75
|
+
@crontab = crontab
|
|
76
|
+
@name = name
|
|
77
|
+
@expression = expression
|
|
78
|
+
@timezone = timezone
|
|
79
|
+
@job = job
|
|
80
|
+
@paused = paused
|
|
81
|
+
@paused_at = paused_at
|
|
82
|
+
@resumed_at = resumed_at
|
|
83
|
+
@last_enqueue_at = last_enqueue_at
|
|
84
|
+
@next_enqueue_at = next_enqueue_at
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Replace this entry with another.
|
|
88
|
+
#
|
|
89
|
+
# This is equivalent to calling `crontab.define_entry` with the same name.
|
|
90
|
+
#
|
|
91
|
+
# @rbs expression: String
|
|
92
|
+
# @rbs timezone: String?
|
|
93
|
+
# @rbs paused: bool?
|
|
94
|
+
# @rbs return: Zizq::CrontabEntryBuilder
|
|
95
|
+
def redefine(expression, timezone: nil, paused: nil)
|
|
96
|
+
CrontabEntryBuilder.new(crontab, name, expression, timezone:, paused:) do |e|
|
|
97
|
+
materialize_with(
|
|
98
|
+
Zizq.client.replace_cron_group_entry(
|
|
99
|
+
crontab.name,
|
|
100
|
+
name,
|
|
101
|
+
expression: e.expression,
|
|
102
|
+
job: e.job.to_enqueue_params,
|
|
103
|
+
timezone: e.timezone,
|
|
104
|
+
paused: e.paused,
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Delete the entry from the schedule.
|
|
111
|
+
def delete! #: () -> void
|
|
112
|
+
Zizq.client.delete_cron_group_entry(crontab.name, name)
|
|
113
|
+
crontab.entries.delete(name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Pause the entry within the schedule.
|
|
117
|
+
#
|
|
118
|
+
# This is independent of the paused state of the Crontab itself.
|
|
119
|
+
def pause! #: () -> void
|
|
120
|
+
materialize_with(
|
|
121
|
+
Zizq.client.update_cron_group_entry(
|
|
122
|
+
crontab.name,
|
|
123
|
+
name,
|
|
124
|
+
paused: true,
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Resume this entry if it is currently paused.
|
|
130
|
+
#
|
|
131
|
+
# If the parent Crontab itself is paused, the entry will still not enqueue
|
|
132
|
+
# jobs until the Crontab is resumed.
|
|
133
|
+
def resume! #: () -> void
|
|
134
|
+
materialize_with(
|
|
135
|
+
Zizq.client.update_cron_group_entry(
|
|
136
|
+
crontab.name,
|
|
137
|
+
name,
|
|
138
|
+
paused: false,
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @private
|
|
144
|
+
# @rbs return: Zizq::cron_entry_params
|
|
145
|
+
def to_params
|
|
146
|
+
{
|
|
147
|
+
name:,
|
|
148
|
+
expression:,
|
|
149
|
+
timezone:,
|
|
150
|
+
job: job.to_enqueue_params,
|
|
151
|
+
paused:,
|
|
152
|
+
}.compact #: Zizq::cron_entry_params
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# @rbs result: Zizq::Resources::CronEntry
|
|
158
|
+
# @rbs return: self
|
|
159
|
+
def materialize_with(result)
|
|
160
|
+
@expression = result.expression
|
|
161
|
+
@timezone = result.timezone
|
|
162
|
+
@paused = result.paused?
|
|
163
|
+
@paused_at = result.paused_at
|
|
164
|
+
@resumed_at = result.resumed_at
|
|
165
|
+
@last_enqueue_at = result.last_enqueue_at
|
|
166
|
+
@next_enqueue_at = result.next_enqueue_at
|
|
167
|
+
@job = EnqueueRequest.new(
|
|
168
|
+
type: result.job.type,
|
|
169
|
+
queue: result.job.queue,
|
|
170
|
+
priority: result.job.priority,
|
|
171
|
+
payload: result.job.payload,
|
|
172
|
+
retry_limit: result.job.retry_limit,
|
|
173
|
+
backoff: result.job.backoff,
|
|
174
|
+
retention: result.job.retention,
|
|
175
|
+
unique_key: result.job.unique_key,
|
|
176
|
+
unique_while: result.job.unique_while,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
self
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
# Builder class used to define individual entries within a Crontab schedule.
|
|
9
|
+
#
|
|
10
|
+
# This is used internally by `Zizq.define_crontab`. See documentation for
|
|
11
|
+
# that method for full details.
|
|
12
|
+
#
|
|
13
|
+
# Callers *must* call one of the enqueue methods to complete the build
|
|
14
|
+
# process.
|
|
15
|
+
class CrontabEntryBuilder
|
|
16
|
+
# The Crontab instance onto which entries are applied.
|
|
17
|
+
attr_reader :target #: Zizq::Crontab
|
|
18
|
+
|
|
19
|
+
# The name of the entry being built.
|
|
20
|
+
attr_reader :name #: String
|
|
21
|
+
|
|
22
|
+
# The cron expression for the entry.
|
|
23
|
+
attr_reader :expression #: String
|
|
24
|
+
|
|
25
|
+
# Optional timezone for the entry.
|
|
26
|
+
#
|
|
27
|
+
# Defaults to the Zizq server timezone when not specified.
|
|
28
|
+
attr_reader :timezone #: String?
|
|
29
|
+
|
|
30
|
+
# True if this entry will be paused.
|
|
31
|
+
attr_reader :paused #: bool?
|
|
32
|
+
|
|
33
|
+
# Callback through which the built entry is passed before being added to
|
|
34
|
+
# the Crontab schedule.
|
|
35
|
+
#
|
|
36
|
+
# The callback receives the Zizq::CrontabEntry instance and may return an
|
|
37
|
+
# alternative instance to be used after it has done any processing on the
|
|
38
|
+
# entry.
|
|
39
|
+
attr_reader :callback #: ^(Zizq::CrontabEntry) -> Zizq::CrontabEntry
|
|
40
|
+
|
|
41
|
+
# Initialize the builder with the given inputs.
|
|
42
|
+
#
|
|
43
|
+
# @rbs target: Zizq::Crontab
|
|
44
|
+
# @rbs name: String
|
|
45
|
+
# @rbs expression: String
|
|
46
|
+
# @rbs timezone: String?
|
|
47
|
+
# @rbs paused: bool?
|
|
48
|
+
# @rbs ?&block: (Zizq::CrontabEntry) -> Zizq::CrontabEntry
|
|
49
|
+
def initialize(target,
|
|
50
|
+
name,
|
|
51
|
+
expression,
|
|
52
|
+
timezone: nil,
|
|
53
|
+
paused: nil,
|
|
54
|
+
&block)
|
|
55
|
+
@target = target
|
|
56
|
+
@name = name
|
|
57
|
+
@expression = expression
|
|
58
|
+
@timezone = timezone
|
|
59
|
+
@paused = paused
|
|
60
|
+
@callback = block || :itself.to_proc
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Enqueue a Zizq::Job or ActiveJob class using Zizq::ActiveJobConfig via
|
|
64
|
+
# this entry.
|
|
65
|
+
#
|
|
66
|
+
# @rbs job_class: Class & Zizq::JobConfig
|
|
67
|
+
# @rbs args: Array[untyped]
|
|
68
|
+
# @rbs kwargs: Hash[Symbol, untyped]
|
|
69
|
+
# @rbs &block: ?(EnqueueRequest) -> void
|
|
70
|
+
# @rbs return: void
|
|
71
|
+
def enqueue(job_class, *args, **kwargs, &block)
|
|
72
|
+
push_entry(Zizq.build_enqueue_request(job_class, *args, **kwargs, &block))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Process a raw job enqueue for this entry.
|
|
76
|
+
#
|
|
77
|
+
# This is used for low-level or cross-language support.
|
|
78
|
+
#
|
|
79
|
+
# @rbs queue: String
|
|
80
|
+
# @rbs type: String
|
|
81
|
+
# @rbs payload: untyped
|
|
82
|
+
# @rbs priority: Integer?
|
|
83
|
+
# @rbs ready_at: Zizq::to_f?
|
|
84
|
+
# @rbs retry_limit: Integer?
|
|
85
|
+
# @rbs backoff: Zizq::backoff?
|
|
86
|
+
# @rbs retention: Zizq::retention?
|
|
87
|
+
# @rbs unique_key: String?
|
|
88
|
+
# @rbs unique_while: Zizq::unique_scope?
|
|
89
|
+
# @rbs return: void
|
|
90
|
+
def enqueue_raw(queue:, type:, payload:, **opts)
|
|
91
|
+
push_entry(EnqueueRequest.new(queue:, type:, payload:, **opts))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Bulk enqueues are not supported via cron.
|
|
95
|
+
#
|
|
96
|
+
# @rbs &block: (BulkEnqueue) -> void
|
|
97
|
+
# @rbs return: self
|
|
98
|
+
def enqueue_bulk(&block)
|
|
99
|
+
raise NotImplementedError, 'bulk enqueues are not supported via cron'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Provide common fields to be used when enqueueing a job.
|
|
103
|
+
#
|
|
104
|
+
# @rbs overrides: Hash[Symbol, untyped]
|
|
105
|
+
# @rbs return: EnqueueWith
|
|
106
|
+
def enqueue_with(**overrides)
|
|
107
|
+
EnqueueWith.new(self, overrides)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# @rbs job: EnqueueRequest
|
|
113
|
+
# @rbs return: untyped
|
|
114
|
+
def push_entry(req)
|
|
115
|
+
if req.ready_at || req.delay
|
|
116
|
+
raise ArgumentError, 'delayed job are not permitted via cron'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
entry = callback.call(
|
|
120
|
+
CrontabEntry.new(
|
|
121
|
+
target,
|
|
122
|
+
name,
|
|
123
|
+
expression,
|
|
124
|
+
job: req,
|
|
125
|
+
timezone:,
|
|
126
|
+
paused:,
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
target.entries[entry.name] = entry
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
data/lib/zizq/enqueue_with.rb
CHANGED
|
@@ -68,7 +68,7 @@ module Zizq
|
|
|
68
68
|
# Enqueue a job class via the underlying target, applying the scoped
|
|
69
69
|
# overrides before invoking any caller-supplied block.
|
|
70
70
|
#
|
|
71
|
-
# @rbs job_class: Class & Zizq::
|
|
71
|
+
# @rbs job_class: Class & Zizq::JobConfig
|
|
72
72
|
# @rbs args: Array[untyped]
|
|
73
73
|
# @rbs kwargs: Hash[Symbol, untyped]
|
|
74
74
|
# @rbs &block: ?(EnqueueRequest) -> void
|
data/lib/zizq/job.rb
CHANGED
|
@@ -46,11 +46,15 @@ module Zizq
|
|
|
46
46
|
def self.call(job)
|
|
47
47
|
job_class = Object.const_get(job.type)
|
|
48
48
|
|
|
49
|
-
unless
|
|
49
|
+
unless (
|
|
50
|
+
job_class.is_a?(Class) &&
|
|
51
|
+
job_class.include?(Zizq::Job) &&
|
|
52
|
+
job_class.is_a?(Zizq::JobConfig)
|
|
53
|
+
)
|
|
50
54
|
raise "#{job.type} does not include Zizq::Job"
|
|
51
55
|
end
|
|
52
56
|
|
|
53
|
-
zizq_job_class = job_class #: Zizq::job_class
|
|
57
|
+
zizq_job_class = job_class #: (Class & Zizq::JobConfig & Zizq::job_class)
|
|
54
58
|
instance = zizq_job_class.new
|
|
55
59
|
instance.set_zizq_job(job)
|
|
56
60
|
|
data/lib/zizq/query.rb
CHANGED
|
@@ -161,7 +161,7 @@ module Zizq
|
|
|
161
161
|
# Sets the type filter to the class name and adds a jq payload filter
|
|
162
162
|
# for an exact match of the serialized arguments.
|
|
163
163
|
#
|
|
164
|
-
# @rbs job_class: Zizq::
|
|
164
|
+
# @rbs job_class: Class && Zizq::JobConfig
|
|
165
165
|
# @rbs *args: untyped
|
|
166
166
|
# @rbs **kwargs: untyped
|
|
167
167
|
# @rbs return: Query
|
|
@@ -179,7 +179,7 @@ module Zizq
|
|
|
179
179
|
# The job class must include `Zizq::Job` or for Active Job classes must
|
|
180
180
|
# extend `Zizq::ActiveJobConfig`.
|
|
181
181
|
#
|
|
182
|
-
# @rbs job_class: Zizq::
|
|
182
|
+
# @rbs job_class: Class & Zizq::JobConfig
|
|
183
183
|
# @rbs *args: untyped
|
|
184
184
|
# @rbs **kwargs: untyped
|
|
185
185
|
# @rbs return: Query
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright (c) 2026 Chris Corbyn <chris@zizq.io>
|
|
2
|
+
# Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
# rbs_inline: enabled
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
module Zizq
|
|
8
|
+
module Resources
|
|
9
|
+
# Typed wrapper around a cron entry response hash.
|
|
10
|
+
class CronEntry < Resource
|
|
11
|
+
def name = @data["name"] #: () -> String
|
|
12
|
+
def expression = @data["expression"] #: () -> String
|
|
13
|
+
def timezone = @data["timezone"] #: () -> String?
|
|
14
|
+
def paused = @data["paused"] #: () -> bool
|
|
15
|
+
def paused? = paused #: () -> bool
|
|
16
|
+
def paused_at = ms_to_seconds(@data["paused_at"]) #: () -> Float?
|
|
17
|
+
def resumed_at = ms_to_seconds(@data["resumed_at"]) #: () -> Float?
|
|
18
|
+
def next_enqueue_at = ms_to_seconds(@data["next_enqueue_at"]) #: () -> Float?
|
|
19
|
+
def last_enqueue_at = ms_to_seconds(@data["last_enqueue_at"]) #: () -> Float?
|
|
20
|
+
|
|
21
|
+
# Returns the job template for this entry.
|
|
22
|
+
def job #: () -> JobTemplate
|
|
23
|
+
JobTemplate.new(client, @data["job"])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|