search-engine-for-typesense 30.1.6.3 → 30.1.6.5
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/lib/search_engine/base/index_maintenance/cleanup.rb +12 -12
- data/lib/search_engine/base/index_maintenance/lifecycle.rb +63 -35
- data/lib/search_engine/base/index_maintenance/schema.rb +21 -15
- data/lib/search_engine/base/index_maintenance.rb +110 -90
- data/lib/search_engine/cascade.rb +16 -15
- data/lib/search_engine/cli/doctor.rb +2 -4
- data/lib/search_engine/config.rb +5 -1
- data/lib/search_engine/indexer/bulk_import.rb +142 -118
- data/lib/search_engine/indexer.rb +9 -4
- data/lib/search_engine/interruptible_pool.rb +39 -0
- data/lib/search_engine/logging/batch_line.rb +89 -0
- data/lib/search_engine/logging/color.rb +18 -1
- data/lib/search_engine/logging/cursor_guard.rb +52 -0
- data/lib/search_engine/logging/live_renderer.rb +366 -0
- data/lib/search_engine/logging/spinner.rb +106 -0
- data/lib/search_engine/logging/step_line.rb +170 -0
- data/lib/search_engine/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 933b8b58a98c81e8e6b72f3083cfcf2ee1de5ea9f709d4c10aea5d09dba59d99
|
|
4
|
+
data.tar.gz: 626111c57bc0ee533d21938d7a218c13b80cc23aa27827731994a968f97de672
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e4456f56ddd18865793f0d61890967ba53e50eac9d2b573362800ed0f509aad397bc9233b4f4a0d308e5a9dadfc0a0aac9e9ca946516b638f5eee0a4728fa3b
|
|
7
|
+
data.tar.gz: 80133e73075e0047968ed9afb3d7756eb5cf47aadeb1c1bbb73d332ee2d44c61e0cc6e6fb5b8932f23ab280f8259319c36d16c288e7d3817a0fdc3844fcde24a
|
|
@@ -45,18 +45,19 @@ module SearchEngine
|
|
|
45
45
|
def cleanup(into: nil, partition: nil, clear_cache: false)
|
|
46
46
|
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
47
47
|
puts
|
|
48
|
-
puts(%(>>>>>> Cleanup Collection "#{logical}"))
|
|
48
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cleanup Collection "#{logical}")))
|
|
49
49
|
|
|
50
50
|
filters = SearchEngine::StaleRules.compile_filters(self, partition: partition)
|
|
51
51
|
filters.compact!
|
|
52
52
|
filters.reject! { |f| f.to_s.strip.empty? }
|
|
53
|
+
step = SearchEngine::Logging::StepLine.new('Cleanup')
|
|
53
54
|
if filters.empty?
|
|
54
|
-
|
|
55
|
+
step.skip('no stale configuration')
|
|
55
56
|
return 0
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
merged_filter = SearchEngine::StaleRules.merge_filters(filters)
|
|
59
|
-
|
|
60
|
+
step.update("filter=#{merged_filter.inspect}")
|
|
60
61
|
|
|
61
62
|
deleted = SearchEngine::Deletion.delete_by(
|
|
62
63
|
klass: self,
|
|
@@ -65,25 +66,24 @@ module SearchEngine
|
|
|
65
66
|
partition: partition
|
|
66
67
|
)
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
step.finish("deleted=#{deleted}")
|
|
69
70
|
deleted
|
|
70
71
|
rescue StandardError => error
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)
|
|
72
|
+
err_msg = "Cleanup — error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
73
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
74
74
|
0
|
|
75
75
|
ensure
|
|
76
|
+
step&.close
|
|
76
77
|
if clear_cache
|
|
77
78
|
begin
|
|
78
|
-
puts(
|
|
79
|
+
puts("Cleanup — #{SearchEngine::Logging::Color.bold('cache clear')}")
|
|
79
80
|
SearchEngine::Cache.clear
|
|
80
81
|
rescue StandardError => error
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
82
|
+
err_msg = "Cleanup — cache clear error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
83
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
|
-
puts(%(>>>>>> Cleanup Done))
|
|
86
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cleanup Done)))
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
private
|
|
@@ -17,7 +17,7 @@ module SearchEngine
|
|
|
17
17
|
def index_collection(partition: nil, client: nil, pre: nil, force_rebuild: false)
|
|
18
18
|
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
19
19
|
puts
|
|
20
|
-
puts(%(>>>>>> Indexing Collection "#{logical}"))
|
|
20
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Indexing Collection "#{logical}")))
|
|
21
21
|
client_obj = client || SearchEngine.client
|
|
22
22
|
|
|
23
23
|
result = if partition.nil?
|
|
@@ -56,7 +56,8 @@ module SearchEngine
|
|
|
56
56
|
|
|
57
57
|
diff = SearchEngine::Schema.diff(self, client: client)[:diff] || {}
|
|
58
58
|
missing = __se_schema_missing?(diff)
|
|
59
|
-
|
|
59
|
+
step = SearchEngine::Logging::StepLine.new('Presence')
|
|
60
|
+
missing ? step.finish_warn('missing') : step.finish('present')
|
|
60
61
|
|
|
61
62
|
applied, indexed_inside_apply = __se_full_apply_if_missing(client, missing)
|
|
62
63
|
drift = __se_full_check_drift(diff, missing, force_rebuild)
|
|
@@ -75,72 +76,90 @@ module SearchEngine
|
|
|
75
76
|
def __se_full_apply_if_missing(client, missing)
|
|
76
77
|
applied = false
|
|
77
78
|
indexed_inside_apply = false
|
|
79
|
+
step = SearchEngine::Logging::StepLine.new('Schema')
|
|
78
80
|
if missing
|
|
79
|
-
|
|
81
|
+
step.update('creating')
|
|
80
82
|
SearchEngine::Schema.apply!(self, client: client) do |new_physical|
|
|
83
|
+
step.yield_line!
|
|
81
84
|
indexed_inside_apply = __se_index_partitions!(into: new_physical)
|
|
82
85
|
end
|
|
83
86
|
applied = true
|
|
84
|
-
|
|
87
|
+
step.finish('created')
|
|
85
88
|
else
|
|
86
|
-
|
|
89
|
+
step.skip('collection present')
|
|
87
90
|
end
|
|
88
91
|
[applied, indexed_inside_apply]
|
|
92
|
+
ensure
|
|
93
|
+
step&.close
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def __se_full_check_drift(diff, missing, force_rebuild)
|
|
97
|
+
step = SearchEngine::Logging::StepLine.new('Schema Status')
|
|
92
98
|
unless missing
|
|
93
|
-
|
|
99
|
+
step.update('checking')
|
|
94
100
|
drift = __se_schema_drift?(diff)
|
|
95
101
|
if force_rebuild && !drift
|
|
96
|
-
|
|
102
|
+
step.finish_warn('force_rebuild')
|
|
97
103
|
return true
|
|
98
104
|
end
|
|
99
|
-
|
|
105
|
+
drift ? step.finish_warn('drift') : step.finish('in_sync')
|
|
100
106
|
return drift
|
|
101
107
|
end
|
|
102
|
-
|
|
108
|
+
step.skip('just created')
|
|
103
109
|
false
|
|
110
|
+
ensure
|
|
111
|
+
step&.close
|
|
104
112
|
end
|
|
105
113
|
|
|
106
114
|
def __se_full_apply_if_drift(client, drift, applied, indexed_inside_apply, force_rebuild)
|
|
115
|
+
step = SearchEngine::Logging::StepLine.new('Schema Apply')
|
|
107
116
|
if drift
|
|
108
|
-
|
|
117
|
+
step.update('applying')
|
|
109
118
|
SearchEngine::Schema.apply!(self, client: client, force_rebuild: force_rebuild) do |new_physical|
|
|
119
|
+
step.yield_line!
|
|
110
120
|
indexed_inside_apply = __se_index_partitions!(into: new_physical)
|
|
111
121
|
end
|
|
112
122
|
applied = true
|
|
113
|
-
|
|
123
|
+
step.finish('applied')
|
|
114
124
|
else
|
|
115
|
-
|
|
125
|
+
step.skip
|
|
116
126
|
end
|
|
117
127
|
[applied, indexed_inside_apply]
|
|
128
|
+
ensure
|
|
129
|
+
step&.close
|
|
118
130
|
end
|
|
119
131
|
|
|
120
132
|
def __se_full_indexation(applied, indexed_inside_apply)
|
|
121
133
|
result = nil
|
|
134
|
+
step = SearchEngine::Logging::StepLine.new('Indexing')
|
|
122
135
|
if applied && indexed_inside_apply
|
|
123
|
-
|
|
136
|
+
step.skip('performed during schema apply')
|
|
124
137
|
result = indexed_inside_apply if indexed_inside_apply.is_a?(Hash)
|
|
125
138
|
else
|
|
126
|
-
|
|
139
|
+
step.update('indexing')
|
|
140
|
+
step.yield_line!
|
|
127
141
|
result = __se_index_partitions!(into: nil)
|
|
128
|
-
|
|
142
|
+
step.finish('done')
|
|
129
143
|
end
|
|
130
144
|
|
|
131
145
|
cascade_ok = result.is_a?(Hash) ? result[:status] == :ok : false
|
|
132
146
|
__se_cascade_after_indexation!(context: :full) if cascade_ok
|
|
133
147
|
result
|
|
148
|
+
ensure
|
|
149
|
+
step&.close
|
|
134
150
|
end
|
|
135
151
|
|
|
136
152
|
def __se_full_retention(applied, logical, client)
|
|
153
|
+
step = SearchEngine::Logging::StepLine.new('Retention')
|
|
137
154
|
if applied
|
|
138
|
-
|
|
155
|
+
step.skip('handled by schema apply')
|
|
139
156
|
else
|
|
140
|
-
|
|
157
|
+
step.update('cleaning')
|
|
141
158
|
dropped = __se_retention_cleanup!(logical: logical, client: client)
|
|
142
|
-
|
|
159
|
+
step.finish("dropped=#{dropped.inspect}")
|
|
143
160
|
end
|
|
161
|
+
ensure
|
|
162
|
+
step&.close
|
|
144
163
|
end
|
|
145
164
|
|
|
146
165
|
def __se_index_partial(partition:, client:, pre: nil)
|
|
@@ -149,70 +168,76 @@ module SearchEngine
|
|
|
149
168
|
diff = diff_res[:diff] || {}
|
|
150
169
|
|
|
151
170
|
missing = __se_schema_missing?(diff)
|
|
152
|
-
|
|
171
|
+
step = SearchEngine::Logging::StepLine.new('Presence')
|
|
153
172
|
if missing
|
|
154
|
-
|
|
173
|
+
step.finish_warn('missing — collection not present, exit early')
|
|
155
174
|
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
156
175
|
sample_error: 'Collection not present' }
|
|
157
176
|
end
|
|
177
|
+
step.finish('present')
|
|
158
178
|
|
|
159
|
-
|
|
179
|
+
step = SearchEngine::Logging::StepLine.new('Schema Status')
|
|
180
|
+
step.update('checking')
|
|
160
181
|
drift = __se_schema_drift?(diff)
|
|
161
182
|
if drift
|
|
162
|
-
|
|
183
|
+
step.finish_warn('drift — exit early (run full indexing)')
|
|
163
184
|
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
164
185
|
sample_error: 'Schema drift detected' }
|
|
165
186
|
end
|
|
166
|
-
|
|
187
|
+
step.finish('in_sync')
|
|
167
188
|
|
|
168
189
|
__se_preflight_dependencies!(mode: pre, client: client) if pre
|
|
169
190
|
|
|
170
|
-
|
|
191
|
+
step = SearchEngine::Logging::StepLine.new('Partial Indexing')
|
|
192
|
+
step.update('indexing')
|
|
193
|
+
step.yield_line!
|
|
171
194
|
summaries = []
|
|
172
195
|
partitions.each do |p|
|
|
173
196
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: p, into: nil)
|
|
174
197
|
summaries << summary
|
|
175
198
|
puts(SearchEngine::Logging::PartitionProgress.line(p, summary))
|
|
176
199
|
end
|
|
177
|
-
|
|
200
|
+
step.finish('done')
|
|
178
201
|
|
|
179
202
|
result = __se_build_index_result(summaries)
|
|
180
203
|
__se_cascade_after_indexation!(context: :full) if result[:status] == :ok
|
|
181
204
|
result
|
|
205
|
+
ensure
|
|
206
|
+
step&.close
|
|
182
207
|
end
|
|
183
208
|
|
|
184
209
|
# rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
185
210
|
def __se_cascade_after_indexation!(context: :full)
|
|
186
211
|
if SearchEngine::Instrumentation.context&.[](:bulk_suppress_cascade)
|
|
187
212
|
puts
|
|
188
|
-
puts('>>>>>> Cascade Referencers — suppressed (bulk)')
|
|
213
|
+
puts(SearchEngine::Logging::Color.dim('>>>>>> Cascade Referencers — suppressed (bulk)'))
|
|
189
214
|
return
|
|
190
215
|
end
|
|
191
216
|
puts
|
|
192
|
-
puts(%(>>>>>> Cascade Referencers))
|
|
217
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cascade Referencers)))
|
|
193
218
|
results = SearchEngine::Cascade.cascade_reindex!(source: self, ids: nil, context: context)
|
|
194
219
|
outcomes = Array(results[:outcomes])
|
|
195
220
|
if outcomes.empty?
|
|
196
|
-
puts(' none')
|
|
221
|
+
puts(SearchEngine::Logging::Color.dim(' none'))
|
|
197
222
|
else
|
|
198
223
|
outcomes.each do |o|
|
|
199
224
|
coll = o[:collection] || o['collection']
|
|
200
225
|
mode = (o[:mode] || o['mode']).to_s
|
|
201
226
|
case mode
|
|
202
227
|
when 'partial'
|
|
203
|
-
puts(%( Referencer "#{coll}" → partial reindex))
|
|
228
|
+
puts(%( Referencer "#{coll}" → #{SearchEngine::Logging::Color.apply('partial reindex', :green)}))
|
|
204
229
|
when 'full'
|
|
205
|
-
puts(%( Referencer "#{coll}" → full reindex))
|
|
230
|
+
puts(%( Referencer "#{coll}" → #{SearchEngine::Logging::Color.apply('full reindex', :green)}))
|
|
206
231
|
when 'skipped_unregistered'
|
|
207
|
-
puts(%( Referencer "#{coll}" → skipped (unregistered)))
|
|
232
|
+
puts(SearchEngine::Logging::Color.dim(%( Referencer "#{coll}" → skipped (unregistered))))
|
|
208
233
|
when 'skipped_cycle'
|
|
209
|
-
puts(%( Referencer "#{coll}" → skipped (cycle)))
|
|
234
|
+
puts(SearchEngine::Logging::Color.dim(%( Referencer "#{coll}" → skipped (cycle))))
|
|
210
235
|
else
|
|
211
236
|
puts(%( Referencer "#{coll}" → #{mode}))
|
|
212
237
|
end
|
|
213
238
|
end
|
|
214
239
|
end
|
|
215
|
-
puts('>>>>>> Cascade Done')
|
|
240
|
+
puts(SearchEngine::Logging::Color.header('>>>>>> Cascade Done'))
|
|
216
241
|
rescue StandardError => error
|
|
217
242
|
base = "Cascade — error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
218
243
|
if error.respond_to?(:status) || error.respond_to?(:body)
|
|
@@ -233,9 +258,12 @@ module SearchEngine
|
|
|
233
258
|
rescue StandardError
|
|
234
259
|
nil
|
|
235
260
|
end
|
|
236
|
-
|
|
261
|
+
err_parts = [base]
|
|
262
|
+
err_parts << "status=#{status}" if status
|
|
263
|
+
err_parts << "body=#{body_preview}" if body_preview
|
|
264
|
+
warn(SearchEngine::Logging::Color.apply(err_parts.compact.join(' '), :red))
|
|
237
265
|
else
|
|
238
|
-
warn(base)
|
|
266
|
+
warn(SearchEngine::Logging::Color.apply(base, :red))
|
|
239
267
|
end
|
|
240
268
|
end
|
|
241
269
|
# rubocop:enable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
@@ -27,23 +27,24 @@ module SearchEngine
|
|
|
27
27
|
|
|
28
28
|
def update_collection!
|
|
29
29
|
client = SearchEngine.client
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
step = SearchEngine::Logging::StepLine.new('Update Collection')
|
|
31
|
+
step.update('analyzing diff')
|
|
32
32
|
updated = SearchEngine::Schema.update!(self, client: client)
|
|
33
33
|
|
|
34
34
|
if updated
|
|
35
|
-
|
|
35
|
+
step.finish('updated in-place (PATCH)')
|
|
36
36
|
else
|
|
37
|
-
|
|
37
|
+
step.skip('no changes or incompatible')
|
|
38
38
|
end
|
|
39
39
|
updated
|
|
40
|
+
ensure
|
|
41
|
+
step&.close
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
def drop_collection!
|
|
43
45
|
client = SearchEngine.client
|
|
44
46
|
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
45
47
|
|
|
46
|
-
# Resolve alias with a safer timeout for control-plane operations
|
|
47
48
|
alias_target = client.resolve_alias(logical, timeout_ms: 10_000)
|
|
48
49
|
physical = if alias_target && !alias_target.to_s.strip.empty?
|
|
49
50
|
alias_target.to_s
|
|
@@ -52,19 +53,21 @@ module SearchEngine
|
|
|
52
53
|
live ? logical : nil
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
step = SearchEngine::Logging::StepLine.new('Drop Collection')
|
|
55
57
|
if physical.nil?
|
|
56
|
-
|
|
58
|
+
step.skip('not present')
|
|
57
59
|
return
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
puts
|
|
61
|
-
puts(%(>>>>>> Dropping Collection "#{logical}"))
|
|
62
|
-
|
|
63
|
-
# Use an extended timeout to accommodate large collection drops
|
|
63
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Dropping Collection "#{logical}")))
|
|
64
|
+
step.update("dropping (logical=#{logical} physical=#{physical})")
|
|
64
65
|
client.delete_collection(physical, timeout_ms: 60_000)
|
|
65
|
-
|
|
66
|
-
puts(%(>>>>>> Dropped Collection "#{logical}"))
|
|
66
|
+
step.finish('done')
|
|
67
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Dropped Collection "#{logical}")))
|
|
67
68
|
nil
|
|
69
|
+
ensure
|
|
70
|
+
step&.close
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
def recreate_collection!
|
|
@@ -79,18 +82,21 @@ module SearchEngine
|
|
|
79
82
|
live ? logical : nil
|
|
80
83
|
end
|
|
81
84
|
|
|
85
|
+
step = SearchEngine::Logging::StepLine.new('Recreate Collection')
|
|
82
86
|
if physical
|
|
83
|
-
|
|
87
|
+
step.update("dropping existing (logical=#{logical} physical=#{physical})")
|
|
84
88
|
client.delete_collection(physical)
|
|
85
89
|
else
|
|
86
|
-
|
|
90
|
+
step.update("creating (logical=#{logical})")
|
|
87
91
|
end
|
|
88
92
|
|
|
89
93
|
schema = SearchEngine::Schema.compile(self)
|
|
90
|
-
|
|
94
|
+
step.update("creating with schema (logical=#{logical})")
|
|
91
95
|
client.create_collection(schema)
|
|
92
|
-
|
|
96
|
+
step.finish('done')
|
|
93
97
|
nil
|
|
98
|
+
ensure
|
|
99
|
+
step&.close
|
|
94
100
|
end
|
|
95
101
|
|
|
96
102
|
def __se_retention_cleanup!(_logical:, _client:)
|