@@ -126,7 +126,9 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
126
126
field :batch , list ( ) | nil
127
127
field :batch_size , non_neg_integer ( )
128
128
field :batch_id , TableReader . batch_id ( ) | nil
129
- field :batch_lsn , integer ( )
129
+ # ~LSN around the time the batch was fetched. Approximate, as it doesn't correspond to
130
+ # the watermark messages emitted by this process (see TableReader.with_watermark/5 for explanation.)
131
+ field :batch_appx_lsn , non_neg_integer ( ) | nil
130
132
field :fetch_slot_lsn , ( PostgresDatabase . t ( ) , String . t ( ) -> { :ok , term ( ) } | { :error , term ( ) } )
131
133
field :fetch_batch , ( pid ( ) , PostgresDatabaseTable . t ( ) , map ( ) , Keyword . t ( ) -> { :ok , term ( ) } | { :error , term ( ) } )
132
134
field :batch_check_count , integer ( ) , default: 0
@@ -234,14 +236,14 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
234
236
time_ms = max ( System . monotonic_time ( :millisecond ) - start_time , 1 )
235
237
236
238
case res do
237
- { :ok , % { rows: [ ] , next_cursor: nil } , _lsn } ->
239
+ { :ok , % { rows: [ ] , next_cursor: nil } , _appx_lsn } ->
238
240
Logger . info ( "[TableReaderServer] Batch returned no records. Table pagination complete." )
239
241
Consumers . table_reader_finished ( state . consumer . id )
240
242
TableReader . delete_cursor ( state . consumer . active_backfill . id )
241
243
242
244
{ :stop , :normal }
243
245
244
- { :ok , % { rows: rows , next_cursor: next_cursor } , lsn } ->
246
+ { :ok , % { rows: rows , next_cursor: next_cursor } , appx_lsn } ->
245
247
Logger . debug ( "[TableReaderServer] Batch returned #{ length ( rows ) } records" )
246
248
247
249
# Record successful timing
@@ -254,7 +256,7 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
254
256
255
257
batch =
256
258
rows
257
- |> messages_from_rows ( lsn , state )
259
+ |> messages_from_rows ( state )
258
260
|> filter_messages ( state )
259
261
260
262
if batch == [ ] do
@@ -268,7 +270,7 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
268
270
| batch: batch ,
269
271
batch_id: batch_id ,
270
272
next_cursor: next_cursor ,
271
- batch_lsn: lsn
273
+ batch_appx_lsn: appx_lsn
272
274
}
273
275
274
276
{ :next_state , :await_flush , state }
@@ -332,10 +334,10 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
332
334
state_name == { :paused , :max_pending_messages } and state . count_pending_messages < state . max_pending_messages ->
333
335
{ :next_state , :fetch_batch , state , actions }
334
336
335
- state_name == :await_flush and current_slot_lsn > state . batch_lsn ->
337
+ state_name == :await_flush and current_slot_lsn > state . batch_appx_lsn ->
336
338
Logger . warning (
337
339
"[TableReaderServer] Detected stale batch #{ state . batch_id } . " <>
338
- "Batch LSN #{ state . batch_lsn } is behind slot LSN #{ current_slot_lsn } . Retrying."
340
+ "Batch LSN #{ state . batch_appx_lsn } is behind slot LSN #{ current_slot_lsn } . Retrying."
339
341
)
340
342
341
343
state = % { state | batch: nil , batch_id: nil , next_cursor: nil }
@@ -357,7 +359,7 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
357
359
358
360
def handle_event (
359
361
{ :call , from } ,
360
- { :flush_batch , % { batch_id: batch_id } = batch_info } ,
362
+ { :flush_batch , % { batch_id: batch_id , commit_lsn: commit_lsn } = batch_info } ,
361
363
_state_name ,
362
364
% State { batch_id: batch_id , batch: batch } = state
363
365
)
@@ -375,6 +377,18 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
375
377
{ :next_state , { :paused , :max_pending_messages } , state }
376
378
end
377
379
else
380
+ batch =
381
+ batch
382
+ |> Stream . with_index ( )
383
+ |> Stream . map ( fn
384
+ { % ConsumerRecord { } = record , idx } ->
385
+ % { record | commit_lsn: commit_lsn , commit_idx: idx }
386
+
387
+ { % ConsumerEvent { } = event , idx } ->
388
+ % { event | commit_lsn: commit_lsn , commit_idx: idx }
389
+ end )
390
+ |> Enum . to_list ( )
391
+
378
392
case push_batch_with_retry ( state , batch ) do
379
393
:ok ->
380
394
# Clear the batch from memory
@@ -513,20 +527,17 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
513
527
% { db_table | sort_column_attnum: sort_column_attnum ( state . consumer ) }
514
528
end
515
529
516
- defp messages_from_rows ( rows , commit_lsn , % State { } = state ) when record_messages? ( state ) do
530
+ defp messages_from_rows ( rows , % State { } = state ) when record_messages? ( state ) do
517
531
table = table ( state )
518
532
consumer = state . consumer
519
533
520
- rows
521
- |> Enum . with_index ( )
522
- |> Enum . map ( fn { row , idx } ->
534
+ Enum . map ( rows , fn row ->
523
535
data = build_record_data ( table , consumer , row )
524
536
payload_size_bytes = :erlang . external_size ( data )
525
537
538
+ # commit_lsn and commit_idx are added later
526
539
% ConsumerRecord {
527
540
consumer_id: consumer . id ,
528
- commit_lsn: commit_lsn ,
529
- commit_idx: idx ,
530
541
table_oid: table . oid ,
531
542
record_pks: record_pks ( table , row ) ,
532
543
group_id: generate_group_id ( consumer , table , row ) ,
@@ -537,20 +548,17 @@ defmodule Sequin.DatabasesRuntime.TableReaderServer do
537
548
end )
538
549
end
539
550
540
- defp messages_from_rows ( rows , commit_lsn , % State { } = state ) when event_messages? ( state ) do
551
+ defp messages_from_rows ( rows , % State { } = state ) when event_messages? ( state ) do
541
552
table = table ( state )
542
553
consumer = state . consumer
543
554
544
- rows
545
- |> Enum . with_index ( )
546
- |> Enum . map ( fn { row , idx } ->
555
+ Enum . map ( rows , fn row ->
547
556
data = build_event_data ( table , consumer , row )
548
557
payload_size_bytes = :erlang . external_size ( data )
549
558
559
+ # commit_lsn and commit_idx are added later
550
560
% ConsumerEvent {
551
561
consumer_id: consumer . id ,
552
- commit_lsn: commit_lsn ,
553
- commit_idx: idx ,
554
562
record_pks: record_pks ( table , row ) ,
555
563
group_id: generate_group_id ( consumer , table , row ) ,
556
564
table_oid: table . oid ,
0 commit comments