riverqueue 0.7.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8aa1b2a14e085df2b2a7e79fa6c6cd5b19aeb2c893a4fd369b99e469dd5916b9
4
- data.tar.gz: 3cebd975dcadb223ecc163918b2e8bb0ce67bb671be99c6f706d3d9ec2da6507
3
+ metadata.gz: 2ff63f8dce021accd460f7009b3db5eb562f787d2df47f7de69f754a1f9d1734
4
+ data.tar.gz: cd0b0f7abacdc94d5db17a96d20c597c2e6f7de341b563cde46325459c42490e
5
5
  SHA512:
6
- metadata.gz: 868459a39a19fe9aacec7552b03148d97228ff58bdfb6b79a6e724c800cc97ef10017097a46f5132684d5d4e97ea9c788b83c813dcb6dc812835e9c0c9ec9645
7
- data.tar.gz: '05538541ee6466adc722af9d6ab4cbf4c1a081a59c9eb887089b8abea8474415d61dfa9fdf827e4863ee393874258f09e6c54b06ffac87ddc90931f346f12f0b'
6
+ metadata.gz: eaa89fce622ca310f58b5e1c76442e4284dd537469732deb0c9a37c14e1d93db1e6902f13f40bdb289976ddd2e68ec2f382b2a2c5b16d2684872491351d24c5a
7
+ data.tar.gz: 7fad9dc2cdfc7087e386f1dc0336b403bc94a86ddcddd90dc681f973f7cda9b1e46e0c4f454db35f007cab1f67819a33d7ce52cb9a01cead3ced069f30c646ea
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, advisory_lock_prefix: nil)
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, unique_opts = make_insert_params(args, insert_opts)
77
- check_unique_job(insert_params, unique_opts) do
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, is_insert_many: true).first # unique opts ignored on batch insert
124
+ make_insert_params(arg.args, arg.insert_opts || EMPTY_INSERT_OPTS)
133
125
  else # jobArgs
134
- make_insert_params(arg, EMPTY_INSERT_OPTS, is_insert_many: true).first # unique opts ignored on batch insert
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
- end
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 check_unique_job(insert_params, unique_opts, &block)
166
- return block.call if unique_opts.nil?
167
-
168
- any_unique_opts = false
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
- # It's extremely important here that this lock string format and algorithm
173
- # match the one in the main River library _exactly_. Don't change them
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
- if unique_opts.by_period
182
- lower_period_bound = truncate_time(@time_now_utc.call, unique_opts.by_period).utc
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
- any_unique_opts = true
185
- get_params.created_at = [lower_period_bound, lower_period_bound + unique_opts.by_period]
186
- unique_key += "&period=#{lower_period_bound.strftime("%FT%TZ")}"
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
- if unique_opts.by_queue
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
- if unique_opts.by_state
196
- any_unique_opts = true
197
- get_params.state = unique_opts.by_state
198
- unique_key += "&state=#{unique_opts.by_state.join(",")}"
199
- else
200
- get_params.state = DEFAULT_UNIQUE_STATES
201
- unique_key += "&state=#{DEFAULT_UNIQUE_STATES.join(",")}"
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
- return block.call unless any_unique_opts
200
+ private def make_unique_key_and_bitmask(insert_params, unique_opts)
201
+ unique_key = ""
205
202
 
206
- # fast path
207
- if !unique_opts.by_state || unique_opts.by_state.sort == DEFAULT_UNIQUE_STATES
208
- unique_key_hash = Digest::SHA256.digest(unique_key)
209
- job, unique_skipped_as_duplicate = @driver.job_insert_unique(insert_params, unique_key_hash)
210
- return InsertResult.new(job, unique_skipped_as_duplicated: unique_skipped_as_duplicate)
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
- @driver.transaction do
214
- lock_str = "unique_key"
215
- lock_str += "kind=#{insert_params.kind}"
216
- lock_str += unique_key
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.slice(*unique_opts.by_args)
220
214
  else
221
- # Steep should be able to tell that this is not nil, but it can't.
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
- # Packs a uint64 then unpacks to int64, which we need to do to keep the
227
- # value within the bounds of Postgres' bigint. Overflow is okay because
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
- private def make_insert_params(args, insert_opts, is_insert_many: false)
242
- raise "args should respond to `#kind`" if !args.respond_to?(:kind)
222
+ if unique_opts.by_period
223
+ lower_period_bound = truncate_time(insert_params.scheduled_at || @time_now_utc.call, unique_opts.by_period).utc
243
224
 
244
- # ~all objects in Ruby respond to `#to_json`, so check non-nil instead.
245
- args_json = args.to_json
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
- args_insert_opts = if args.respond_to?(:insert_opts)
249
- args_with_insert_opts = args #: _JobArgsWithInsertOpts # rubocop:disable Layout/LeadingCommentSpace
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
- scheduled_at = insert_opts.scheduled_at || args_insert_opts.scheduled_at
256
- unique_opts = insert_opts.unique_opts || args_insert_opts.unique_opts
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
- raise ArgumentError, "unique opts can't be used with `#insert_many`" if is_insert_many && unique_opts
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&.each do |tag|
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: false)
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
@@ -72,12 +72,6 @@ module River
72
72
  # given job kind, a single instance is allowed for each combination of args
73
73
  # and queues. If either args or queue is changed on a new job, it's allowed to
74
74
  # be inserted as a new job.
75
- #
76
- # Uniquenes is checked at insert time by taking a Postgres advisory lock,
77
- # doing a look up for an equivalent row, and inserting only if none was found.
78
- # There's no database-level mechanism that guarantees jobs stay unique, so if
79
- # an equivalent row is inserted out of band (or batch inserted, where a unique
80
- # check doesn't occur), it's conceivable that duplicates could coexist.
81
75
  class UniqueOpts
82
76
  # Indicates that uniqueness should be enforced for any specific instance of
83
77
  # encoded args for a job.
@@ -114,23 +108,33 @@ module River
114
108
  # Unlike other unique options, ByState gets a default when it's not set for
115
109
  # user convenience. The default is equivalent to:
116
110
  #
117
- # by_state: [River::JOB_STATE_AVAILABLE, River::JOB_STATE_COMPLETED, River::JOB_STATE_RUNNING, River::JOB_STATE_RETRYABLE, River::JOB_STATE_SCHEDULED]
111
+ # 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
112
  #
119
113
  # With this setting, any jobs of the same kind that have been completed or
120
114
  # discarded, but not yet cleaned out by the system, won't count towards the
121
115
  # uniqueness of a new insert.
116
+ #
117
+ # The pending, scheduled, available, and running states are required when
118
+ # customizing this list.
122
119
  attr_accessor :by_state
123
120
 
121
+ # Indicates that the job kind should not be considered for uniqueness. This
122
+ # is useful when you want to enforce uniqueness based on other properties
123
+ # across multiple worker types.
124
+ attr_accessor :exclude_kind
125
+
124
126
  def initialize(
125
127
  by_args: nil,
126
128
  by_period: nil,
127
129
  by_queue: nil,
128
- by_state: nil
130
+ by_state: nil,
131
+ exclude_kind: nil
129
132
  )
130
133
  self.by_args = by_args
131
134
  self.by_period = by_period
132
135
  self.by_queue = by_queue
133
136
  self.by_state = by_state
137
+ self.exclude_kind = exclude_kind
134
138
  end
135
139
  end
136
140
  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
@@ -1,11 +1,11 @@
1
1
  require "json"
2
2
 
3
- require_relative "fnv"
4
3
  require_relative "insert_opts"
5
4
  require_relative "job"
6
5
 
7
6
  require_relative "client"
8
7
  require_relative "driver"
8
+ require_relative "unique_bitmask"
9
9
 
10
10
  module River
11
11
  end
@@ -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,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riverqueue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Gentry
8
8
  - Brandur Leach
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2024-08-31 00:00:00.000000000 Z
11
+ date: 2025-04-12 00:00:00.000000000 Z
13
12
  dependencies: []
14
13
  description: River is a fast job queue for Go. Use this gem in conjunction with gems
15
14
  riverqueue-activerecord or riverqueue-sequel to insert jobs in Ruby which will be
@@ -21,10 +20,10 @@ extra_rdoc_files: []
21
20
  files:
22
21
  - lib/client.rb
23
22
  - lib/driver.rb
24
- - lib/fnv.rb
25
23
  - lib/insert_opts.rb
26
24
  - lib/job.rb
27
25
  - lib/riverqueue.rb
26
+ - lib/unique_bitmask.rb
28
27
  homepage: https://riverqueue.com
29
28
  licenses:
30
29
  - LGPL-3.0-or-later
@@ -33,7 +32,6 @@ metadata:
33
32
  changelog_uri: https://github.com/riverqueue/riverqueue-ruby/blob/master/CHANGELOG.md
34
33
  rubygems_mfa_required: 'true'
35
34
  source_code_uri: https://github.com/riverqueue/riverqueue-ruby
36
- post_install_message:
37
35
  rdoc_options: []
38
36
  require_paths:
39
37
  - lib
@@ -48,8 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
46
  - !ruby/object:Gem::Version
49
47
  version: '0'
50
48
  requirements: []
51
- rubygems_version: 3.4.20
52
- signing_key:
49
+ rubygems_version: 3.6.2
53
50
  specification_version: 4
54
51
  summary: River is a fast job queue for Go.
55
52
  test_files: []
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