|
27 | 27 | defmodule Electric.ShapeCache do |
28 | 28 | use GenServer |
29 | 29 |
|
30 | | - alias Electric.Postgres.Lsn |
31 | 30 | alias Electric.Replication.LogOffset |
32 | 31 | alias Electric.Replication.ShapeLogCollector |
33 | 32 | alias Electric.ShapeCache.ShapeStatus |
34 | | - alias Electric.ShapeCache |
35 | 33 | alias Electric.Shapes |
36 | 34 | alias Electric.ShapeCache.ShapeCleaner |
37 | 35 | alias Electric.Shapes.Shape |
@@ -195,29 +193,30 @@ defmodule Electric.ShapeCache do |
195 | 193 | subscription: nil |
196 | 194 | } |
197 | 195 |
|
198 | | - {:ok, state, {:continue, :recover_shapes}} |
| 196 | + {:ok, state, {:continue, :wait_for_restore}} |
199 | 197 | end |
200 | 198 |
|
201 | 199 | @impl GenServer |
202 | | - def handle_continue(:recover_shapes, state) do |
| 200 | + def handle_continue(:wait_for_restore, state) do |
203 | 201 | start_time = System.monotonic_time() |
204 | | - {last_processed_lsn, total_recovered, total_failed_to_recover} = recover_shapes(state) |
| 202 | + |
| 203 | + total_recovered = ShapeStatus.count_shapes(state.stack_id) |
205 | 204 |
|
206 | 205 | Electric.Replication.PublicationManager.wait_for_restore(state.stack_id) |
207 | 206 |
|
208 | 207 | # Let ShapeLogCollector that it can start processing after finishing this function so that |
209 | 208 | # we're subscribed to the producer before it starts forwarding its demand. |
210 | | - ShapeLogCollector.set_last_processed_lsn(state.stack_id, last_processed_lsn) |
| 209 | + ShapeLogCollector.mark_as_ready(state.stack_id) |
211 | 210 |
|
212 | 211 | duration = System.monotonic_time() - start_time |
213 | 212 |
|
214 | 213 | Logger.notice( |
215 | | - "Consumers ready in #{System.convert_time_unit(duration, :native, :millisecond)}ms (#{total_recovered} shapes, #{total_failed_to_recover} failed to recover)" |
| 214 | + "Consumers ready in #{System.convert_time_unit(duration, :native, :millisecond)}ms (#{total_recovered} shapes)" |
216 | 215 | ) |
217 | 216 |
|
218 | 217 | Electric.Telemetry.OpenTelemetry.execute( |
219 | 218 | [:electric, :connection, :consumers_ready], |
220 | | - %{duration: duration, total: total_recovered, failed_to_recover: total_failed_to_recover}, |
| 219 | + %{duration: duration, total: total_recovered}, |
221 | 220 | %{stack_id: state.stack_id} |
222 | 221 | ) |
223 | 222 |
|
@@ -255,55 +254,6 @@ defmodule Electric.ShapeCache do |
255 | 254 | end |
256 | 255 | end |
257 | 256 |
|
258 | | - defp recover_shapes(%{stack_id: stack_id} = _state) do |
259 | | - import Electric.Postgres.Lsn, only: [is_larger: 2] |
260 | | - |
261 | | - start_time = System.monotonic_time() |
262 | | - storage = ShapeCache.Storage.for_stack(stack_id) |
263 | | - all_handles_and_shapes = ShapeStatus.list_shapes(stack_id) |
264 | | - |
265 | | - {max_lsn, total_recovered} = |
266 | | - all_handles_and_shapes |
267 | | - |> Task.async_stream( |
268 | | - fn {shape_handle, shape} -> |
269 | | - shape_storage = ShapeCache.Storage.for_shape(shape_handle, storage) |
270 | | - |
271 | | - case ShapeCache.Storage.get_current_position(shape_storage) do |
272 | | - {:ok, latest_offset, _pg_snapshot} -> |
273 | | - {shape_handle, LogOffset.extract_lsn(latest_offset)} |
274 | | - |
275 | | - {:error, reason} -> |
276 | | - Logger.error([ |
277 | | - "shape #{inspect(shape)} (#{inspect(shape_handle)})", |
278 | | - " returned error from get_current_position: #{inspect(reason)}" |
279 | | - ]) |
280 | | - |
281 | | - ShapeCleaner.remove_shape(stack_id, shape_handle) |
282 | | - |
283 | | - {shape_handle, :error} |
284 | | - end |
285 | | - end, |
286 | | - ordered: false |
287 | | - ) |
288 | | - |> Enum.reduce({Lsn.from_integer(0), 0}, fn |
289 | | - {:ok, {_handle, :error}}, acc -> acc |
290 | | - {:ok, {_handle, lsn}}, {max, recovered} when is_larger(lsn, max) -> {lsn, recovered + 1} |
291 | | - _, {max, recovered} -> {max, recovered + 1} |
292 | | - end) |
293 | | - |
294 | | - total_failed_to_recover = length(all_handles_and_shapes) - total_recovered |
295 | | - |
296 | | - duration = System.monotonic_time() - start_time |
297 | | - |
298 | | - Logger.info([ |
299 | | - "Restored LSN position #{max_lsn} in", |
300 | | - " #{System.convert_time_unit(duration, :native, :millisecond)}ms", |
301 | | - " (#{total_recovered} shapes, #{total_failed_to_recover} failed to recover)" |
302 | | - ]) |
303 | | - |
304 | | - {max_lsn, total_recovered, total_failed_to_recover} |
305 | | - end |
306 | | - |
307 | 257 | defp maybe_create_shape(shape, otel_ctx, %{stack_id: stack_id} = state) do |
308 | 258 | if shape_state = ShapeStatus.get_existing_shape(stack_id, shape) do |
309 | 259 | shape_state |
|
0 commit comments