riverqueue 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/client.rb +81 -111
- data/lib/driver.rb +7 -24
- data/lib/insert_opts.rb +12 -2
- data/lib/job.rb +7 -1
- data/lib/riverqueue.rb +1 -1
- data/lib/unique_bitmask.rb +41 -0
- metadata +4 -4
- data/lib/fnv.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46f45a18aa1b8884c93295ba1b023751c826d8feeac212949e18f388f3a6b6cc
|
4
|
+
data.tar.gz: 679dde3151af410e5e8f8949d80750d68f247e300af1e6f93b43270d81dc5d40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7507d5e78f99ef0bb6cda3a91479da386bb5e66c18c40a26c98ed5aec42bb3b5425b3753e797759e4ca4117f9a01636d9784fd1b27d160af215e5e5216ac998
|
7
|
+
data.tar.gz: a65b3a772b4d85b823256fe2bebb656a816cff1ba87777c77d9393670dbdfe3be383095bb49f3446d8110a143cb1ba68b9c7530d64da44a9e9093265be9aa8ba
|
data/lib/client.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "digest"
|
2
|
-
require "fnv"
|
3
2
|
require "time"
|
4
3
|
|
5
4
|
module River
|
@@ -25,9 +24,8 @@ module River
|
|
25
24
|
# River drivers are found in separate gems like `riverqueue-sequel` to help
|
26
25
|
# minimize transient dependencies.
|
27
26
|
class Client
|
28
|
-
def initialize(driver
|
27
|
+
def initialize(driver)
|
29
28
|
@driver = driver
|
30
|
-
@advisory_lock_prefix = check_advisory_lock_prefix_bounds(advisory_lock_prefix)
|
31
29
|
@time_now_utc = -> { Time.now.utc } # for test time stubbing
|
32
30
|
end
|
33
31
|
|
@@ -73,11 +71,8 @@ module River
|
|
73
71
|
#
|
74
72
|
# Returns an instance of InsertResult.
|
75
73
|
def insert(args, insert_opts: EMPTY_INSERT_OPTS)
|
76
|
-
insert_params
|
77
|
-
|
78
|
-
job = @driver.job_insert(insert_params)
|
79
|
-
InsertResult.new(job)
|
80
|
-
end
|
74
|
+
insert_params = make_insert_params(args, insert_opts)
|
75
|
+
insert_and_check_unique_job(insert_params)
|
81
76
|
end
|
82
77
|
|
83
78
|
# Inserts many new jobs as part of a single batch operation for improved
|
@@ -122,30 +117,20 @@ module River
|
|
122
117
|
#
|
123
118
|
# See also JobArgsHash for an easy way to insert a job from a hash.
|
124
119
|
#
|
125
|
-
# Unique job insertion isn't supported with bulk insertion because it'd run
|
126
|
-
# the risk of major lock contention.
|
127
|
-
#
|
128
120
|
# Returns the number of jobs inserted.
|
129
121
|
def insert_many(args)
|
130
122
|
all_params = args.map do |arg|
|
131
123
|
if arg.is_a?(InsertManyParams)
|
132
|
-
make_insert_params(arg.args, arg.insert_opts || EMPTY_INSERT_OPTS
|
124
|
+
make_insert_params(arg.args, arg.insert_opts || EMPTY_INSERT_OPTS)
|
133
125
|
else # jobArgs
|
134
|
-
make_insert_params(arg, EMPTY_INSERT_OPTS
|
126
|
+
make_insert_params(arg, EMPTY_INSERT_OPTS)
|
135
127
|
end
|
136
128
|
end
|
137
129
|
|
138
130
|
@driver.job_insert_many(all_params)
|
139
|
-
|
140
|
-
|
141
|
-
private def check_advisory_lock_prefix_bounds(advisory_lock_prefix)
|
142
|
-
return nil if advisory_lock_prefix.nil?
|
143
|
-
|
144
|
-
# 2**32-1 is 0xffffffff (the largest number that's four bytes)
|
145
|
-
if advisory_lock_prefix < 0 || advisory_lock_prefix > 2**32 - 1
|
146
|
-
raise ArgumentError, "advisory lock prefix must fit inside four bytes"
|
131
|
+
.map do |job, unique_skipped_as_duplicate|
|
132
|
+
InsertResult.new(job, unique_skipped_as_duplicated: unique_skipped_as_duplicate)
|
147
133
|
end
|
148
|
-
advisory_lock_prefix
|
149
134
|
end
|
150
135
|
|
151
136
|
# Default states that are used during a unique insert. Can be overridden by
|
@@ -153,123 +138,101 @@ module River
|
|
153
138
|
DEFAULT_UNIQUE_STATES = [
|
154
139
|
JOB_STATE_AVAILABLE,
|
155
140
|
JOB_STATE_COMPLETED,
|
141
|
+
JOB_STATE_PENDING,
|
156
142
|
JOB_STATE_RETRYABLE,
|
157
143
|
JOB_STATE_RUNNING,
|
158
144
|
JOB_STATE_SCHEDULED
|
159
145
|
].freeze
|
160
146
|
private_constant :DEFAULT_UNIQUE_STATES
|
161
147
|
|
148
|
+
REQUIRED_UNIQUE_STATES = [
|
149
|
+
JOB_STATE_AVAILABLE,
|
150
|
+
JOB_STATE_PENDING,
|
151
|
+
JOB_STATE_RUNNING,
|
152
|
+
JOB_STATE_SCHEDULED
|
153
|
+
].freeze
|
154
|
+
private_constant :REQUIRED_UNIQUE_STATES
|
155
|
+
|
162
156
|
EMPTY_INSERT_OPTS = InsertOpts.new.freeze
|
163
157
|
private_constant :EMPTY_INSERT_OPTS
|
164
158
|
|
165
|
-
private def
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
get_params = Driver::JobGetByKindAndUniquePropertiesParam.new(kind: insert_params.kind)
|
170
|
-
unique_key = ""
|
159
|
+
private def insert_and_check_unique_job(insert_params)
|
160
|
+
job, unique_skipped_as_duplicate = @driver.job_insert(insert_params)
|
161
|
+
InsertResult.new(job, unique_skipped_as_duplicated: unique_skipped_as_duplicate)
|
162
|
+
end
|
171
163
|
|
172
|
-
|
173
|
-
|
174
|
-
# unless they're updated everywhere.
|
175
|
-
if unique_opts.by_args
|
176
|
-
any_unique_opts = true
|
177
|
-
get_params.encoded_args = insert_params.encoded_args
|
178
|
-
unique_key += "&args=#{insert_params.encoded_args}"
|
179
|
-
end
|
164
|
+
private def make_insert_params(args, insert_opts)
|
165
|
+
raise "args should respond to `#kind`" if !args.respond_to?(:kind)
|
180
166
|
|
181
|
-
|
182
|
-
|
167
|
+
# ~all objects in Ruby respond to `#to_json`, so check non-nil instead.
|
168
|
+
args_json = args.to_json
|
169
|
+
raise "args should return non-nil from `#to_json`" if !args_json
|
183
170
|
|
184
|
-
|
185
|
-
|
186
|
-
|
171
|
+
args_insert_opts = if args.respond_to?(:insert_opts)
|
172
|
+
args_with_insert_opts = args #: _JobArgsWithInsertOpts # rubocop:disable Layout/LeadingCommentSpace
|
173
|
+
args_with_insert_opts.insert_opts || EMPTY_INSERT_OPTS
|
174
|
+
else
|
175
|
+
EMPTY_INSERT_OPTS
|
187
176
|
end
|
188
177
|
|
189
|
-
|
190
|
-
any_unique_opts = true
|
191
|
-
get_params.queue = insert_params.queue
|
192
|
-
unique_key += "&queue=#{insert_params.queue}"
|
193
|
-
end
|
178
|
+
scheduled_at = insert_opts.scheduled_at || args_insert_opts.scheduled_at
|
194
179
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
180
|
+
insert_params = Driver::JobInsertParams.new(
|
181
|
+
encoded_args: args_json,
|
182
|
+
kind: args.kind,
|
183
|
+
max_attempts: insert_opts.max_attempts || args_insert_opts.max_attempts || MAX_ATTEMPTS_DEFAULT,
|
184
|
+
priority: insert_opts.priority || args_insert_opts.priority || PRIORITY_DEFAULT,
|
185
|
+
queue: insert_opts.queue || args_insert_opts.queue || QUEUE_DEFAULT,
|
186
|
+
scheduled_at: scheduled_at&.utc || Time.now,
|
187
|
+
state: scheduled_at ? JOB_STATE_SCHEDULED : JOB_STATE_AVAILABLE,
|
188
|
+
tags: validate_tags(insert_opts.tags || args_insert_opts.tags || [])
|
189
|
+
)
|
190
|
+
|
191
|
+
unique_opts = insert_opts.unique_opts || args_insert_opts.unique_opts
|
192
|
+
if unique_opts
|
193
|
+
unique_key, unique_states = make_unique_key_and_bitmask(insert_params, unique_opts)
|
194
|
+
insert_params.unique_key = unique_key
|
195
|
+
insert_params.unique_states = unique_states
|
202
196
|
end
|
197
|
+
insert_params
|
198
|
+
end
|
203
199
|
|
204
|
-
|
200
|
+
private def make_unique_key_and_bitmask(insert_params, unique_opts)
|
201
|
+
unique_key = ""
|
205
202
|
|
206
|
-
#
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
203
|
+
# It's extremely important here that this unique key format and algorithm
|
204
|
+
# match the one in the main River library _exactly_. Don't change them
|
205
|
+
# unless they're updated everywhere.
|
206
|
+
unless unique_opts.exclude_kind
|
207
|
+
unique_key += "&kind=#{insert_params.kind}"
|
211
208
|
end
|
212
209
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
lock_key = if @advisory_lock_prefix.nil?
|
219
|
-
FNV.fnv1_hash(lock_str, size: 64)
|
210
|
+
if unique_opts.by_args
|
211
|
+
parsed_args = JSON.parse(insert_params.encoded_args)
|
212
|
+
filtered_args = if unique_opts.by_args.is_a?(Array)
|
213
|
+
parsed_args.select { |k, _| unique_opts.by_args.include?(k) }
|
220
214
|
else
|
221
|
-
|
222
|
-
prefix = @advisory_lock_prefix #: Integer # rubocop:disable Layout/LeadingCommentSpace
|
223
|
-
prefix << 32 | FNV.fnv1_hash(lock_str, size: 32)
|
215
|
+
parsed_args
|
224
216
|
end
|
225
217
|
|
226
|
-
|
227
|
-
|
228
|
-
# we can use the full bigint space (including negative numbers) for the
|
229
|
-
# advisory lock.
|
230
|
-
lock_key = uint64_to_int64(lock_key)
|
231
|
-
|
232
|
-
@driver.advisory_lock(lock_key)
|
233
|
-
|
234
|
-
existing_job = @driver.job_get_by_kind_and_unique_properties(get_params)
|
235
|
-
return InsertResult.new(existing_job, unique_skipped_as_duplicated: true) if existing_job
|
236
|
-
|
237
|
-
block.call
|
218
|
+
encoded_args = JSON.generate(filtered_args.sort.to_h)
|
219
|
+
unique_key += "&args=#{encoded_args}"
|
238
220
|
end
|
239
|
-
end
|
240
221
|
|
241
|
-
|
242
|
-
|
222
|
+
if unique_opts.by_period
|
223
|
+
lower_period_bound = truncate_time(@time_now_utc.call, unique_opts.by_period).utc
|
243
224
|
|
244
|
-
|
245
|
-
|
246
|
-
raise "args should return non-nil from `#to_json`" if !args_json
|
225
|
+
unique_key += "&period=#{lower_period_bound.strftime("%FT%TZ")}"
|
226
|
+
end
|
247
227
|
|
248
|
-
|
249
|
-
|
250
|
-
args_with_insert_opts.insert_opts || EMPTY_INSERT_OPTS
|
251
|
-
else
|
252
|
-
EMPTY_INSERT_OPTS
|
228
|
+
if unique_opts.by_queue
|
229
|
+
unique_key += "&queue=#{insert_params.queue}"
|
253
230
|
end
|
254
231
|
|
255
|
-
|
256
|
-
|
232
|
+
unique_key_hash = Digest::SHA256.digest(unique_key)
|
233
|
+
unique_states = validate_unique_states(unique_opts.by_state || DEFAULT_UNIQUE_STATES)
|
257
234
|
|
258
|
-
|
259
|
-
|
260
|
-
[
|
261
|
-
Driver::JobInsertParams.new(
|
262
|
-
encoded_args: args_json,
|
263
|
-
kind: args.kind,
|
264
|
-
max_attempts: insert_opts.max_attempts || args_insert_opts.max_attempts || MAX_ATTEMPTS_DEFAULT,
|
265
|
-
priority: insert_opts.priority || args_insert_opts.priority || PRIORITY_DEFAULT,
|
266
|
-
queue: insert_opts.queue || args_insert_opts.queue || QUEUE_DEFAULT,
|
267
|
-
scheduled_at: scheduled_at&.utc, # database defaults to now
|
268
|
-
state: scheduled_at ? JOB_STATE_SCHEDULED : JOB_STATE_AVAILABLE,
|
269
|
-
tags: validate_tags(insert_opts.tags || args_insert_opts.tags)
|
270
|
-
),
|
271
|
-
unique_opts
|
272
|
-
]
|
235
|
+
[unique_key_hash, UniqueBitmask.from_states(unique_states)]
|
273
236
|
end
|
274
237
|
|
275
238
|
# Truncates the given time down to the interval. For example:
|
@@ -290,11 +253,18 @@ module River
|
|
290
253
|
private_constant :TAG_RE
|
291
254
|
|
292
255
|
private def validate_tags(tags)
|
293
|
-
tags
|
256
|
+
tags.each do |tag|
|
294
257
|
raise ArgumentError, "tags should be 255 characters or less" if tag.length > 255
|
295
258
|
raise ArgumentError, "tag should match regex #{TAG_RE.inspect}" unless TAG_RE.match(tag)
|
296
259
|
end
|
297
260
|
end
|
261
|
+
|
262
|
+
private def validate_unique_states(states)
|
263
|
+
REQUIRED_UNIQUE_STATES.each do |required_state|
|
264
|
+
raise ArgumentError, "by_state should include required state #{required_state}" unless states.include?(required_state)
|
265
|
+
end
|
266
|
+
states
|
267
|
+
end
|
298
268
|
end
|
299
269
|
|
300
270
|
# A single job to insert that's part of an #insert_many batch insert. Unlike
|
@@ -322,7 +292,7 @@ module River
|
|
322
292
|
# job matching unique property already being present.
|
323
293
|
attr_reader :unique_skipped_as_duplicated
|
324
294
|
|
325
|
-
def initialize(job, unique_skipped_as_duplicated:
|
295
|
+
def initialize(job, unique_skipped_as_duplicated:)
|
326
296
|
@job = job
|
327
297
|
@unique_skipped_as_duplicated = unique_skipped_as_duplicated
|
328
298
|
end
|
data/lib/driver.rb
CHANGED
@@ -4,29 +4,6 @@ module River
|
|
4
4
|
# considered to be for internal use only and subject to change. API stability
|
5
5
|
# is not guaranteed.
|
6
6
|
module Driver
|
7
|
-
# Parameters for looking up a job by kind and unique properties.
|
8
|
-
class JobGetByKindAndUniquePropertiesParam
|
9
|
-
attr_accessor :created_at
|
10
|
-
attr_accessor :encoded_args
|
11
|
-
attr_accessor :kind
|
12
|
-
attr_accessor :queue
|
13
|
-
attr_accessor :state
|
14
|
-
|
15
|
-
def initialize(
|
16
|
-
kind:,
|
17
|
-
created_at: nil,
|
18
|
-
encoded_args: nil,
|
19
|
-
queue: nil,
|
20
|
-
state: nil
|
21
|
-
)
|
22
|
-
self.kind = kind
|
23
|
-
self.created_at = created_at
|
24
|
-
self.encoded_args = encoded_args
|
25
|
-
self.queue = queue
|
26
|
-
self.state = state
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
7
|
# Insert parameters for a job. This is sent to underlying drivers and is meant
|
31
8
|
# for internal use only. Its interface is subject to change.
|
32
9
|
class JobInsertParams
|
@@ -38,6 +15,8 @@ module River
|
|
38
15
|
attr_accessor :scheduled_at
|
39
16
|
attr_accessor :state
|
40
17
|
attr_accessor :tags
|
18
|
+
attr_accessor :unique_key
|
19
|
+
attr_accessor :unique_states
|
41
20
|
|
42
21
|
def initialize(
|
43
22
|
encoded_args:,
|
@@ -47,7 +26,9 @@ module River
|
|
47
26
|
queue:,
|
48
27
|
scheduled_at:,
|
49
28
|
state:,
|
50
|
-
tags
|
29
|
+
tags:,
|
30
|
+
unique_key: nil,
|
31
|
+
unique_states: nil
|
51
32
|
)
|
52
33
|
self.encoded_args = encoded_args
|
53
34
|
self.kind = kind
|
@@ -57,6 +38,8 @@ module River
|
|
57
38
|
self.scheduled_at = scheduled_at
|
58
39
|
self.state = state
|
59
40
|
self.tags = tags
|
41
|
+
self.unique_key = unique_key
|
42
|
+
self.unique_states = unique_states
|
60
43
|
end
|
61
44
|
end
|
62
45
|
end
|
data/lib/insert_opts.rb
CHANGED
@@ -114,23 +114,33 @@ module River
|
|
114
114
|
# Unlike other unique options, ByState gets a default when it's not set for
|
115
115
|
# user convenience. The default is equivalent to:
|
116
116
|
#
|
117
|
-
# by_state: [River::JOB_STATE_AVAILABLE, River::JOB_STATE_COMPLETED, River::JOB_STATE_RUNNING, River::JOB_STATE_RETRYABLE, River::JOB_STATE_SCHEDULED]
|
117
|
+
# by_state: [River::JOB_STATE_AVAILABLE, River::JOB_STATE_COMPLETED, River::JOB_STATE_PENDING, River::JOB_STATE_RUNNING, River::JOB_STATE_RETRYABLE, River::JOB_STATE_SCHEDULED]
|
118
118
|
#
|
119
119
|
# With this setting, any jobs of the same kind that have been completed or
|
120
120
|
# discarded, but not yet cleaned out by the system, won't count towards the
|
121
121
|
# uniqueness of a new insert.
|
122
|
+
#
|
123
|
+
# The pending, scheduled, available, and running states are required when
|
124
|
+
# customizing this list.
|
122
125
|
attr_accessor :by_state
|
123
126
|
|
127
|
+
# Indicates that the job kind should not be considered for uniqueness. This
|
128
|
+
# is useful when you want to enforce uniqueness based on other properties
|
129
|
+
# across multiple worker types.
|
130
|
+
attr_accessor :exclude_kind
|
131
|
+
|
124
132
|
def initialize(
|
125
133
|
by_args: nil,
|
126
134
|
by_period: nil,
|
127
135
|
by_queue: nil,
|
128
|
-
by_state: nil
|
136
|
+
by_state: nil,
|
137
|
+
exclude_kind: nil
|
129
138
|
)
|
130
139
|
self.by_args = by_args
|
131
140
|
self.by_period = by_period
|
132
141
|
self.by_queue = by_queue
|
133
142
|
self.by_state = by_state
|
143
|
+
self.exclude_kind = exclude_kind
|
134
144
|
end
|
135
145
|
end
|
136
146
|
end
|
data/lib/job.rb
CHANGED
@@ -3,6 +3,7 @@ module River
|
|
3
3
|
JOB_STATE_CANCELLED = "cancelled"
|
4
4
|
JOB_STATE_COMPLETED = "completed"
|
5
5
|
JOB_STATE_DISCARDED = "discarded"
|
6
|
+
JOB_STATE_PENDING = "pending"
|
6
7
|
JOB_STATE_RETRYABLE = "retryable"
|
7
8
|
JOB_STATE_RUNNING = "running"
|
8
9
|
JOB_STATE_SCHEDULED = "scheduled"
|
@@ -113,6 +114,9 @@ module River
|
|
113
114
|
# configuration.
|
114
115
|
attr_accessor :unique_key
|
115
116
|
|
117
|
+
# A list of states that the job must be in to be considered for uniqueness.
|
118
|
+
attr_accessor :unique_states
|
119
|
+
|
116
120
|
def initialize(
|
117
121
|
id:,
|
118
122
|
args:,
|
@@ -132,7 +136,8 @@ module River
|
|
132
136
|
errors: nil,
|
133
137
|
finalized_at: nil,
|
134
138
|
tags: nil,
|
135
|
-
unique_key: nil
|
139
|
+
unique_key: nil,
|
140
|
+
unique_states: nil
|
136
141
|
)
|
137
142
|
self.id = id
|
138
143
|
self.args = args
|
@@ -151,6 +156,7 @@ module River
|
|
151
156
|
self.state = state
|
152
157
|
self.tags = tags
|
153
158
|
self.unique_key = unique_key
|
159
|
+
self.unique_states = unique_states
|
154
160
|
end
|
155
161
|
end
|
156
162
|
|
data/lib/riverqueue.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
module River
|
2
|
+
class UniqueBitmask
|
3
|
+
JOB_STATE_BIT_POSITIONS = {
|
4
|
+
::River::JOB_STATE_AVAILABLE => 7,
|
5
|
+
::River::JOB_STATE_CANCELLED => 6,
|
6
|
+
::River::JOB_STATE_COMPLETED => 5,
|
7
|
+
::River::JOB_STATE_DISCARDED => 4,
|
8
|
+
::River::JOB_STATE_PENDING => 3,
|
9
|
+
::River::JOB_STATE_RETRYABLE => 2,
|
10
|
+
::River::JOB_STATE_RUNNING => 1,
|
11
|
+
::River::JOB_STATE_SCHEDULED => 0
|
12
|
+
}.freeze
|
13
|
+
private_constant :JOB_STATE_BIT_POSITIONS
|
14
|
+
|
15
|
+
def self.from_states(states)
|
16
|
+
val = 0
|
17
|
+
|
18
|
+
states.each do |state|
|
19
|
+
bit_index = JOB_STATE_BIT_POSITIONS[state]
|
20
|
+
|
21
|
+
bit_position = 7 - (bit_index % 8)
|
22
|
+
val |= 1 << bit_position
|
23
|
+
end
|
24
|
+
|
25
|
+
format("%08b", val)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.to_states(mask)
|
29
|
+
states = [] #: Array[jobStateAll] # rubocop:disable Layout/LeadingCommentSpace
|
30
|
+
|
31
|
+
JOB_STATE_BIT_POSITIONS.each do |state, bit_index|
|
32
|
+
bit_position = 7 - (bit_index % 8)
|
33
|
+
if (mask & (1 << bit_position)) != 0
|
34
|
+
states << state
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
states.sort
|
39
|
+
end
|
40
|
+
end
|
41
|
+
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.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blake Gentry
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: River is a fast job queue for Go. Use this gem in conjunction with gems
|
15
15
|
riverqueue-activerecord or riverqueue-sequel to insert jobs in Ruby which will be
|
@@ -21,10 +21,10 @@ extra_rdoc_files: []
|
|
21
21
|
files:
|
22
22
|
- lib/client.rb
|
23
23
|
- lib/driver.rb
|
24
|
-
- lib/fnv.rb
|
25
24
|
- lib/insert_opts.rb
|
26
25
|
- lib/job.rb
|
27
26
|
- lib/riverqueue.rb
|
27
|
+
- lib/unique_bitmask.rb
|
28
28
|
homepage: https://riverqueue.com
|
29
29
|
licenses:
|
30
30
|
- LGPL-3.0-or-later
|
@@ -48,7 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '0'
|
50
50
|
requirements: []
|
51
|
-
rubygems_version: 3.
|
51
|
+
rubygems_version: 3.5.16
|
52
52
|
signing_key:
|
53
53
|
specification_version: 4
|
54
54
|
summary: River is a fast job queue for Go.
|
data/lib/fnv.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
module River
|
2
|
-
# FNV is the Fowler–Noll–Vo hash function, a simple hash that's very easy to
|
3
|
-
# implement, and hash the perfect characteristics for use with the 64 bits of
|
4
|
-
# available space in a PG advisory lock.
|
5
|
-
#
|
6
|
-
# I'm implemented it myself so that the River gem can stay dependency free
|
7
|
-
# (and because it's quite easy to do).
|
8
|
-
module FNV
|
9
|
-
def self.fnv1_hash(str, size:)
|
10
|
-
hash = OFFSET_BASIS.fetch(size)
|
11
|
-
mask = (2**size - 1).to_int # creates a mask of 1s of `size` bits long like 0xffffffff
|
12
|
-
prime = PRIME.fetch(size)
|
13
|
-
|
14
|
-
str.each_byte do |byte|
|
15
|
-
hash *= prime
|
16
|
-
hash &= mask
|
17
|
-
hash ^= byte
|
18
|
-
end
|
19
|
-
|
20
|
-
hash
|
21
|
-
end
|
22
|
-
|
23
|
-
OFFSET_BASIS = {
|
24
|
-
32 => 0x811c9dc5,
|
25
|
-
64 => 0xcbf29ce484222325
|
26
|
-
}.freeze
|
27
|
-
private_constant :OFFSET_BASIS
|
28
|
-
|
29
|
-
PRIME = {
|
30
|
-
32 => 0x01000193,
|
31
|
-
64 => 0x00000100000001B3
|
32
|
-
}.freeze
|
33
|
-
private_constant :PRIME
|
34
|
-
end
|
35
|
-
end
|