search-engine-for-typesense 30.1.8.11 → 30.1.8.12

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: 95396b4ac85d2dfc142b05cf38aea45e73fd551ec7acb6915da3d7df7ffde9dd
4
- data.tar.gz: 644a13a4a7ec1c8a841b19b6e8b59b88762b36db235428c13b5483a1082996c4
3
+ metadata.gz: e12c8943b56f8054dbb1cfff9c52f17f0a926664eeb77436ee711cfba06e9d0c
4
+ data.tar.gz: 833df3fbf7c7ff98576c29e218a15dea996230e1a1a624f63fdc896a3728d2ed
5
5
  SHA512:
6
- metadata.gz: a59fc89cefc6c5a320616737159e6adbeebad8dac16886dea3b9d4e25e8124028469fc62b30c1f45bf53c1e6c2addad54aee694c40bfbe14cc399a93a9732625
7
- data.tar.gz: eaa3305204a97489af5bf1d2e89062f0be63c922425061c9705b05cb44f16035dd8bc1eac7740fc8146c8a0ad38ecdeec2a3a3af564cec39188371828988d302
6
+ metadata.gz: 65dcbf97c8b91ae1f85836dcb69a955fa6cf63a52bf71063b8ebd53a8ec7c82eac0a8df211fc9e2e4fc8da0f488b5c8f3ee442dbb870d9002987c959e990f689
7
+ data.tar.gz: 78fbe84b31b3fa457144ce7c81466a1b074a25c4218705f19d4ce3875f0e7cd6fedfbd32f72658396eca255db0aee5554693c2df078590d65bd04765489a274f
@@ -27,7 +27,7 @@ module SearchEngine
27
27
  targets = delivery_targets
28
28
  return enqueue_legacy(limit: limit) if targets.empty?
29
29
 
30
- repository.materialize_deliveries!
30
+ materialize_deliveries(limit: limit)
31
31
  targets.each { |target| enqueue_target(target, limit: limit) }
32
32
  nil
33
33
  end
@@ -36,6 +36,12 @@ module SearchEngine
36
36
 
37
37
  attr_reader :repository, :targets_resolver
38
38
 
39
+ def materialize_deliveries(limit:)
40
+ return repository.materialize_deliveries! if limit.nil?
41
+
42
+ repository.materialize_deliveries!(limit: limit)
43
+ end
44
+
39
45
  def enqueue_legacy(limit:)
40
46
  return drain_job.perform_later if limit.nil?
41
47
 
@@ -111,11 +111,78 @@ module SearchEngine
111
111
 
112
112
  # Create missing delivery rows for all configured delivery targets.
113
113
  # @return [void]
114
- def materialize_deliveries!
114
+ def materialize_deliveries!(limit: SearchEngine.config.postgres_outbox.batch_size)
115
115
  targets = delivery_targets
116
116
  return if targets.empty?
117
117
 
118
- execute(<<~SQL)
118
+ rows = []
119
+ connection.transaction do
120
+ rows = select_rows(delivery_materialization_select_sql(limit.to_i, targets))
121
+ next if rows.empty?
122
+
123
+ execute(materialization_supersede_older_deliveries_sql(rows, targets))
124
+ execute(supersede_older_pending_sql(rows))
125
+ execute(delivery_materialization_insert_sql(rows, targets))
126
+ end
127
+
128
+ rows
129
+ end
130
+
131
+ private
132
+
133
+ attr_reader :target_key
134
+
135
+ def connection
136
+ @connection ||= begin
137
+ require 'active_record'
138
+ ActiveRecord::Base.connection
139
+ end
140
+ end
141
+
142
+ def claim_pending_deliveries(limit:, worker_id:)
143
+ reset_stale_delivery_processing!
144
+ rows = claim_pending_delivery_rows(limit: limit, worker_id: worker_id)
145
+
146
+ if rows.empty?
147
+ materialize_deliveries!(limit: limit)
148
+ rows = claim_pending_delivery_rows(limit: limit, worker_id: worker_id)
149
+ end
150
+
151
+ rows.map { |row| Event.new(row) }
152
+ end
153
+
154
+ def delivery_materialization_select_sql(limit, targets)
155
+ <<~SQL
156
+ WITH target(target_key, queue_name) AS (
157
+ VALUES #{delivery_target_values_sql(targets)}
158
+ ),
159
+ candidate_events AS (
160
+ SELECT outbox.*
161
+ FROM #{quoted_table} outbox
162
+ WHERE outbox.status IN ('pending', 'processing', 'failed')
163
+ AND (outbox.next_attempt_at IS NULL OR outbox.next_attempt_at <= CURRENT_TIMESTAMP)
164
+ AND EXISTS (
165
+ SELECT 1
166
+ FROM target
167
+ WHERE NOT EXISTS (
168
+ SELECT 1
169
+ FROM #{quoted_delivery_table} deliveries
170
+ WHERE deliveries.event_id = outbox.id
171
+ AND deliveries.target_key = target.target_key
172
+ )
173
+ )
174
+ ORDER BY outbox.id ASC
175
+ LIMIT #{limit}
176
+ FOR UPDATE SKIP LOCKED
177
+ )
178
+ SELECT DISTINCT ON (collection, document_id) *
179
+ FROM candidate_events
180
+ ORDER BY collection, document_id, id DESC
181
+ SQL
182
+ end
183
+
184
+ def delivery_materialization_insert_sql(rows, targets)
185
+ <<~SQL
119
186
  INSERT INTO #{quoted_delivery_table} (
120
187
  event_id,
121
188
  target_key,
@@ -132,29 +199,19 @@ module SearchEngine
132
199
  0,
133
200
  CURRENT_TIMESTAMP,
134
201
  CURRENT_TIMESTAMP
135
- FROM #{quoted_table} outbox
202
+ FROM (
203
+ VALUES #{materialization_event_values_sql(rows)}
204
+ ) AS selected_events(event_id)
205
+ INNER JOIN #{quoted_table} outbox
206
+ ON outbox.id = selected_events.event_id
136
207
  CROSS JOIN (
137
208
  VALUES #{delivery_target_values_sql(targets)}
138
209
  ) AS target(target_key, queue_name)
139
- WHERE outbox.status IN ('pending', 'processing', 'failed')
140
210
  ON CONFLICT (event_id, target_key) DO NOTHING
141
211
  SQL
142
212
  end
143
213
 
144
- private
145
-
146
- attr_reader :target_key
147
-
148
- def connection
149
- @connection ||= begin
150
- require 'active_record'
151
- ActiveRecord::Base.connection
152
- end
153
- end
154
-
155
- def claim_pending_deliveries(limit:, worker_id:)
156
- materialize_deliveries!
157
- reset_stale_delivery_processing!
214
+ def claim_pending_delivery_rows(limit:, worker_id:)
158
215
  rows = []
159
216
 
160
217
  connection.transaction do
@@ -164,7 +221,7 @@ module SearchEngine
164
221
  execute(delivery_claim_update_sql(delivery_ids, worker_id)) unless delivery_ids.empty?
165
222
  end
166
223
 
167
- rows.map { |row| Event.new(row) }
224
+ rows
168
225
  end
169
226
 
170
227
  def reset_stale_delivery_processing!
@@ -271,6 +328,46 @@ module SearchEngine
271
328
  SQL
272
329
  end
273
330
 
331
+ def materialization_supersede_older_deliveries_sql(rows, targets)
332
+ <<~SQL
333
+ WITH updated_deliveries AS (
334
+ UPDATE #{quoted_delivery_table} older_deliveries
335
+ SET status = 'superseded',
336
+ processed_at = CURRENT_TIMESTAMP,
337
+ locked_at = NULL,
338
+ locked_by = NULL,
339
+ updated_at = CURRENT_TIMESTAMP
340
+ FROM #{quoted_table} older_events,
341
+ (
342
+ VALUES #{coalesce_values_sql(rows)}
343
+ ) AS latest(collection, document_id, id),
344
+ (
345
+ VALUES #{delivery_target_values_sql(targets)}
346
+ ) AS target(target_key, queue_name)
347
+ WHERE older_deliveries.event_id = older_events.id
348
+ AND older_deliveries.status = 'pending'
349
+ AND older_deliveries.target_key = target.target_key
350
+ AND older_events.collection = latest.collection
351
+ AND older_events.document_id = latest.document_id
352
+ AND older_events.id < latest.id
353
+ RETURNING older_deliveries.event_id
354
+ ),
355
+ aggregate AS (
356
+ #{event_status_aggregate_sql('SELECT event_id FROM updated_deliveries')}
357
+ )
358
+ UPDATE #{quoted_table} events
359
+ SET status = aggregate.status,
360
+ processed_at = CASE
361
+ WHEN aggregate.status IN ('processed', 'superseded') THEN CURRENT_TIMESTAMP
362
+ ELSE NULL
363
+ END,
364
+ last_error = aggregate.last_error,
365
+ updated_at = CURRENT_TIMESTAMP
366
+ FROM aggregate
367
+ WHERE events.id = aggregate.event_id
368
+ SQL
369
+ end
370
+
274
371
  def supersede_older_pending_sql(rows)
275
372
  <<~SQL
276
373
  UPDATE #{quoted_table} older
@@ -468,6 +565,12 @@ module SearchEngine
468
565
  end.join(', ')
469
566
  end
470
567
 
568
+ def materialization_event_values_sql(rows)
569
+ rows.map do |row|
570
+ "(#{quote(row_value(row, :id))})"
571
+ end.join(', ')
572
+ end
573
+
471
574
  def quoted_table
472
575
  connection.quote_table_name(SearchEngine.config.postgres_outbox.table_name)
473
576
  end
@@ -3,5 +3,5 @@
3
3
  module SearchEngine
4
4
  # Current gem version.
5
5
  # @return [String]
6
- VERSION = '30.1.8.11'
6
+ VERSION = '30.1.8.12'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search-engine-for-typesense
3
3
  version: !ruby/object:Gem::Version
4
- version: 30.1.8.11
4
+ version: 30.1.8.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shkoda