@@ -41,7 +41,8 @@ defmodule Electric.Shapes.Consumer.Materializer do
4141 :ok
4242 def new_changes ( state , changes , opts \\ [ ] ) do
4343 commit? = Keyword . get ( opts , :commit , true )
44- GenServer . call ( name ( state ) , { :new_changes , changes , commit? } , :infinity )
44+ xid = Keyword . get ( opts , :xid )
45+ GenServer . call ( name ( state ) , { :new_changes , changes , xid , commit? } , :infinity )
4546 end
4647
4748 def wait_until_ready ( state ) do
@@ -205,23 +206,23 @@ defmodule Electric.Shapes.Consumer.Materializer do
205206 { :reply , :ok , state }
206207 end
207208
208- def handle_call ( { :new_changes , { range_start , range_end } , commit? } , _from , state ) do
209+ def handle_call ( { :new_changes , { range_start , range_end } , xid , commit? } , _from , state ) do
209210 stack_storage = Storage . for_stack ( state . stack_id )
210211 storage = Storage . for_shape ( state . shape_handle , stack_storage )
211212
212213 state =
213214 Storage . get_log_stream ( range_start , range_end , storage )
214215 |> decode_json_stream ( )
215- |> apply_and_accumulate_events ( state )
216+ |> apply_and_accumulate_events ( xid , state )
216217 |> maybe_flush_pending_events ( commit? )
217218
218219 { :reply , :ok , state }
219220 end
220221
221- def handle_call ( { :new_changes , changes , commit? } , _from , state ) when is_list ( changes ) do
222+ def handle_call ( { :new_changes , changes , xid , commit? } , _from , state ) when is_list ( changes ) do
222223 state =
223224 changes
224- |> apply_and_accumulate_events ( state )
225+ |> apply_and_accumulate_events ( xid , state )
225226 |> maybe_flush_pending_events ( commit? )
226227
227228 { :reply , :ok , state }
@@ -343,21 +344,21 @@ defmodule Electric.Shapes.Consumer.Materializer do
343344 }
344345 end
345346
346- % { "headers" => % { "event" => "move-out" , "patterns" => patterns } } ->
347+ % { "headers" => % { "event" => "move-out" , "patterns" => patterns } = headers } ->
347348 patterns =
348349 Enum . map ( patterns , fn % { "pos" => pos , "value" => value } ->
349350 % { pos: pos , value: value }
350351 end )
351352
352- % { headers: % { event: "move-out" , patterns: patterns } }
353+ % { headers: % { event: "move-out" , patterns: patterns , txids: Map . get ( headers , "txids" , [ ] ) } }
353354
354- % { "headers" => % { "event" => "move-in" , "patterns" => patterns } } ->
355+ % { "headers" => % { "event" => "move-in" , "patterns" => patterns } = headers } ->
355356 patterns =
356357 Enum . map ( patterns , fn % { "pos" => pos , "value" => value } ->
357358 % { pos: pos , value: value }
358359 end )
359360
360- % { headers: % { event: "move-in" , patterns: patterns } }
361+ % { headers: % { event: "move-in" , patterns: patterns , txids: Map . get ( headers , "txids" , [ ] ) } }
361362 end )
362363 end
363364
@@ -390,16 +391,28 @@ defmodule Electric.Shapes.Consumer.Materializer do
390391 Eval.Env . const_to_pg_string ( Eval.Env . new ( ) , value , type )
391392 end
392393
393- defp apply_and_accumulate_events ( changes , state ) do
394+ defp apply_and_accumulate_events ( changes , xid , state ) do
394395 { state , events } = apply_changes ( changes , state )
396+ events = with_txids ( events , xid )
395397 % { state | pending_events: merge_events ( state . pending_events , events ) }
396398 end
397399
400+ defp with_txids ( events , _xid ) when events == % { } , do: events
401+
402+ defp with_txids ( events , xid ) do
403+ Map . put ( events , :txids , xid_set ( xid ) )
404+ end
405+
406+ defp xid_set ( nil ) , do: MapSet . new ( )
407+ defp xid_set ( xid ) when is_integer ( xid ) and xid > 0 , do: MapSet . new ( [ xid ] )
408+
398409 defp maybe_flush_pending_events ( state , true ) do
399410 events =
400411 cancel_matching_move_events ( state . pending_events )
401412
402413 if events != % { } do
414+ events = finalize_txids ( events )
415+
403416 for pid <- state . subscribers do
404417 send ( pid , { :materializer_changes , state . shape_handle , events } )
405418 end
@@ -412,13 +425,19 @@ defmodule Electric.Shapes.Consumer.Materializer do
412425
413426 defp maybe_flush_pending_events ( state , _commit? ) , do: state
414427
428+ defp finalize_txids ( events ) do
429+ Map . update ( events , :txids , [ ] , & Enum . sort ( & 1 ) )
430+ end
431+
415432 defp merge_events ( pending , new ) when pending == % { } , do: new
416433 defp merge_events ( pending , new ) when new == % { } , do: pending
417434
418435 defp merge_events ( pending , new ) do
419436 % {
420437 move_in: Map . get ( new , :move_in , [ ] ) ++ Map . get ( pending , :move_in , [ ] ) ,
421- move_out: Map . get ( new , :move_out , [ ] ) ++ Map . get ( pending , :move_out , [ ] )
438+ move_out: Map . get ( new , :move_out , [ ] ) ++ Map . get ( pending , :move_out , [ ] ) ,
439+ txids:
440+ MapSet . union ( Map . get ( pending , :txids , MapSet . new ( ) ) , Map . get ( new , :txids , MapSet . new ( ) ) )
422441 }
423442 end
424443
@@ -433,7 +452,11 @@ defmodule Electric.Shapes.Consumer.Materializer do
433452 defp cancel_matching_move_events ( events ) do
434453 ins = events |> Map . get ( :move_in , [ ] ) |> Enum . sort_by ( fn { v , _ } -> v end )
435454 outs = events |> Map . get ( :move_out , [ ] ) |> Enum . sort_by ( fn { v , _ } -> v end )
436- cancel_sorted_pairs ( ins , outs , % { move_in: [ ] , move_out: [ ] } )
455+
456+ case cancel_sorted_pairs ( ins , outs , % { move_in: [ ] , move_out: [ ] } ) do
457+ empty when empty == % { } -> empty
458+ result -> Map . put ( result , :txids , Map . get ( events , :txids , MapSet . new ( ) ) )
459+ end
437460 end
438461
439462 defp cancel_sorted_pairs ( [ { v , _ } | ins ] , [ { v , _ } | outs ] , acc ) ,
0 commit comments