search-engine-for-typesense 30.1.8.5 → 30.1.8.7

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: 88a738bfc2dcfe706d2c67c70b4f4923859ca808a50364b4b97ec93f1ecfdc66
4
- data.tar.gz: dcb84555d2106d27c5bff18e31b0d9f20af7b2584ec2740ebe32cf4f07e842d2
3
+ metadata.gz: 77958fc8a7cb84effc7e3a4ca73b2f01e88eacc9ddead6e888c746e17b7e28d5
4
+ data.tar.gz: 74a046ef9f7cbdddeeed1c9fb7d9749d501b71300adc30bd0d2d771088aae189
5
5
  SHA512:
6
- metadata.gz: db614f2ad79a4ed16424721b06b990a6b523f98bbc0e3e054fbf7f7ad1939c9282e948c70db257b40623865337b94f88c6614845a8088b048904c2baf105762d
7
- data.tar.gz: 98d27347d7e98ee4f0380884331464e280d003af96fc5cd87c5278efca443ce88d8047f2652055e111621bbb10808f93f6fb8d9fec3000f8f475d3a73128b3b7
6
+ metadata.gz: 9e6bc4e2159d41cd66f0f4f9568d503c3b40ebbbe59be2cb6709ce008589d2e2caa3028f54f3de180a1dc9ff5c37c37cab5ba049780b4583a4974018eaf7ca39
7
+ data.tar.gz: a478abb0104cf415d31bcd069d93eb89039acff25c081423117e965fed6d6cd2ffbc859a88601988036246e4b343633e454fda25e27999013c5363e4f824d0c9
@@ -14,10 +14,20 @@ module SearchEngine
14
14
  def perform(limit: nil)
15
15
  return nil unless SearchEngine.config.postgres_outbox.enabled
16
16
 
17
+ effective_limit = limit || SearchEngine.config.postgres_outbox.batch_size
17
18
  drainer = SearchEngine::PostgresOutbox::Drainer.new
18
- return drainer.drain_once if limit.nil?
19
+ summary = drainer.drain_once(limit: effective_limit)
20
+ enqueue_continuation(limit: limit) if summary[:claimed].to_i >= effective_limit.to_i
19
21
 
20
- drainer.drain_once(limit: limit)
22
+ summary
23
+ end
24
+
25
+ private
26
+
27
+ def enqueue_continuation(limit:)
28
+ return self.class.perform_later if limit.nil?
29
+
30
+ self.class.perform_later(limit: limit)
21
31
  end
22
32
  end
23
33
  end
@@ -37,6 +37,7 @@ module SearchEngine
37
37
  @mutex = Mutex.new
38
38
  @stop_requested = false
39
39
  @thread = nil
40
+ @last_enqueue_at = nil
40
41
  end
41
42
 
42
43
  # Start the listener thread.
@@ -120,7 +121,7 @@ module SearchEngine
120
121
  next if notified || stop_requested?
121
122
 
122
123
  instrument('search_engine.postgres_outbox.listener_fallback') {}
123
- enqueue_drain
124
+ enqueue_drain(force: true)
124
125
  end
125
126
  ensure
126
127
  connection.execute("UNLISTEN #{quoted_channel}")
@@ -187,10 +188,33 @@ module SearchEngine
187
188
  end
188
189
  end
189
190
 
190
- def enqueue_drain
191
+ def enqueue_drain(force: false)
192
+ return if enqueue_throttled?(force: force)
193
+
191
194
  drain_job.perform_later
192
195
  end
193
196
 
197
+ def enqueue_throttled?(force:)
198
+ @mutex.synchronize do
199
+ now = monotonic_time
200
+ if force
201
+ @last_enqueue_at = now
202
+ next false
203
+ end
204
+
205
+ if @last_enqueue_at && (now - @last_enqueue_at) < poll_interval_s
206
+ true
207
+ else
208
+ @last_enqueue_at = now
209
+ false
210
+ end
211
+ end
212
+ end
213
+
214
+ def monotonic_time
215
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
216
+ end
217
+
194
218
  def drain_job
195
219
  @drain_job ||= SearchEngine::PostgresOutbox::DrainJob
196
220
  end
@@ -22,6 +22,7 @@ module SearchEngine
22
22
  connection.transaction do
23
23
  rows = select_rows(claim_select_sql(limit.to_i))
24
24
  ids = rows.map { |row| row_value(row, :id) }
25
+ execute(supersede_older_pending_sql(rows)) unless rows.empty?
25
26
  execute(claim_update_sql(ids, worker_id)) unless ids.empty?
26
27
  end
27
28
 
@@ -103,12 +104,30 @@ module SearchEngine
103
104
 
104
105
  def claim_select_sql(limit)
105
106
  <<~SQL
106
- SELECT *
107
- FROM #{quoted_table}
108
- WHERE status = 'pending'
109
- AND (next_attempt_at IS NULL OR next_attempt_at <= CURRENT_TIMESTAMP)
110
- ORDER BY id ASC
111
- LIMIT #{limit}
107
+ WITH ranked_pending AS (
108
+ SELECT id,
109
+ ROW_NUMBER() OVER (
110
+ PARTITION BY collection, document_id
111
+ ORDER BY id DESC
112
+ ) AS row_number
113
+ FROM #{quoted_table}
114
+ WHERE status = 'pending'
115
+ ),
116
+ latest_due AS (
117
+ SELECT outbox.id
118
+ FROM #{quoted_table} outbox
119
+ INNER JOIN ranked_pending
120
+ ON ranked_pending.id = outbox.id
121
+ WHERE ranked_pending.row_number = 1
122
+ AND (outbox.next_attempt_at IS NULL OR outbox.next_attempt_at <= CURRENT_TIMESTAMP)
123
+ ORDER BY outbox.id ASC
124
+ LIMIT #{limit}
125
+ )
126
+ SELECT outbox.*
127
+ FROM #{quoted_table} outbox
128
+ INNER JOIN latest_due
129
+ ON latest_due.id = outbox.id
130
+ ORDER BY outbox.id ASC
112
131
  FOR UPDATE SKIP LOCKED
113
132
  SQL
114
133
  end
@@ -124,6 +143,24 @@ module SearchEngine
124
143
  SQL
125
144
  end
126
145
 
146
+ def supersede_older_pending_sql(rows)
147
+ <<~SQL
148
+ UPDATE #{quoted_table} older
149
+ SET status = 'superseded',
150
+ processed_at = CURRENT_TIMESTAMP,
151
+ locked_at = NULL,
152
+ locked_by = NULL,
153
+ updated_at = CURRENT_TIMESTAMP
154
+ FROM (
155
+ VALUES #{coalesce_values_sql(rows)}
156
+ ) AS latest(collection, document_id, id)
157
+ WHERE older.status = 'pending'
158
+ AND older.collection = latest.collection
159
+ AND older.document_id = latest.document_id
160
+ AND older.id < latest.id
161
+ SQL
162
+ end
163
+
127
164
  def update_status!(event_ids, status, extra:)
128
165
  ids = Array(event_ids).compact
129
166
  return if ids.empty?
@@ -154,6 +191,16 @@ module SearchEngine
154
191
  ids.map { |id| quote(id) }.join(', ')
155
192
  end
156
193
 
194
+ def coalesce_values_sql(rows)
195
+ rows.map do |row|
196
+ collection = row_value(row, :collection)
197
+ document_id = row_value(row, :document_id)
198
+ id = row_value(row, :id)
199
+
200
+ "(#{quote(collection)}, #{quote(document_id)}, #{quote(id)})"
201
+ end.join(', ')
202
+ end
203
+
157
204
  def quoted_table
158
205
  connection.quote_table_name(SearchEngine.config.postgres_outbox.table_name)
159
206
  end
@@ -3,5 +3,5 @@
3
3
  module SearchEngine
4
4
  # Current gem version.
5
5
  # @return [String]
6
- VERSION = '30.1.8.5'
6
+ VERSION = '30.1.8.7'
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.5
4
+ version: 30.1.8.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shkoda