@@ -49,24 +49,17 @@ defmodule Sequin.DatabasesRuntime.SlotMessageStore.State do
49
49
:"slot_message_store_state_ordered_cursors_consumer_#{ consumer . seq } "
50
50
end
51
51
52
- @ spec put_messages ( State . t ( ) , list ( message ( ) ) | Enumerable . t ( ) ) ::
53
- { :ok , State . t ( ) } | { :error , Error . t ( ) }
54
- def put_messages ( % State { } = state , messages , opts \\ [ ] ) do
52
+ @ spec validate_put_messages ( State . t ( ) , list ( message ( ) ) | Enumerable . t ( ) , keyword ( ) ) ::
53
+ { :ok , non_neg_integer ( ) } | { :error , Error . t ( ) }
54
+ def validate_put_messages ( % State { } = state , messages , opts \\ [ ] ) do
55
55
skip_limit_check? = Keyword . get ( opts , :skip_limit_check? , false )
56
56
57
- # Create new messages map while filtering out existing messages
58
- new_messages =
59
- messages
60
- |> Stream . reject ( & Map . has_key? ( state . messages , { & 1 . commit_lsn , & 1 . commit_idx } ) )
61
- |> Map . new ( & { { & 1 . commit_lsn , & 1 . commit_idx } , & 1 } )
62
-
63
- # Only count payload sizes for new messages
64
- incoming_payload_size_bytes = Enum . sum_by ( Map . values ( new_messages ) , & & 1 . payload_size_bytes )
57
+ incoming_payload_size_bytes = Enum . sum_by ( messages , & & 1 . payload_size_bytes )
65
58
66
59
bytes_exceeded? =
67
60
state . payload_size_bytes + incoming_payload_size_bytes > state . max_memory_bytes
68
61
69
- messages_exceeded? = map_size ( new_messages ) + map_size ( state . messages ) > state . setting_max_messages
62
+ messages_exceeded? = length ( messages ) + map_size ( state . messages ) > state . setting_max_messages
70
63
71
64
cond do
72
65
not skip_limit_check? and bytes_exceeded? ->
@@ -76,28 +69,41 @@ defmodule Sequin.DatabasesRuntime.SlotMessageStore.State do
76
69
{ :error , Error . invariant ( message: "Message count limit exceeded" , code: :payload_size_limit_exceeded ) }
77
70
78
71
true ->
79
- # Insert into ETS
80
- ets_keys = Enum . map ( new_messages , fn { commit_tuple , _msg } -> { commit_tuple } end )
81
-
82
- state . consumer
83
- |> ordered_cursors_table ( )
84
- |> :ets . insert ( ets_keys )
85
-
86
- ack_ids_to_cursor_tuples =
87
- Map . new ( new_messages , fn { _commit_tuple , msg } -> { msg . ack_id , { msg . commit_lsn , msg . commit_idx } } end )
88
-
89
- state =
90
- % {
91
- state
92
- | messages: Map . merge ( state . messages , new_messages ) ,
93
- ack_ids_to_cursor_tuples: Map . merge ( state . ack_ids_to_cursor_tuples , ack_ids_to_cursor_tuples ) ,
94
- payload_size_bytes: state . payload_size_bytes + incoming_payload_size_bytes
95
- }
72
+ { :ok , incoming_payload_size_bytes }
73
+ end
74
+ end
96
75
97
- { :ok , state }
76
+ @ spec put_messages ( State . t ( ) , list ( message ( ) ) | Enumerable . t ( ) , keyword ( ) ) ::
77
+ { :ok , State . t ( ) } | { :error , Error . t ( ) }
78
+ def put_messages ( % State { } = state , messages , opts \\ [ ] ) do
79
+ messages = Enum . reject ( messages , & message_exists? ( state , & 1 ) )
80
+
81
+ with { :ok , incoming_payload_size_bytes } <- validate_put_messages ( state , messages , opts ) do
82
+ # Insert into ETS
83
+ ets_keys = Enum . map ( messages , fn msg -> { { msg . commit_lsn , msg . commit_idx } } end )
84
+
85
+ state . consumer
86
+ |> ordered_cursors_table ( )
87
+ |> :ets . insert ( ets_keys )
88
+
89
+ cursor_tuples_to_messages = Map . new ( messages , fn msg -> { { msg . commit_lsn , msg . commit_idx } , msg } end )
90
+ ack_ids_to_cursor_tuples = Map . new ( messages , fn msg -> { msg . ack_id , { msg . commit_lsn , msg . commit_idx } } end )
91
+
92
+ { :ok ,
93
+ % {
94
+ state
95
+ | messages: Map . merge ( state . messages , cursor_tuples_to_messages ) ,
96
+ ack_ids_to_cursor_tuples: Map . merge ( state . ack_ids_to_cursor_tuples , ack_ids_to_cursor_tuples ) ,
97
+ payload_size_bytes: state . payload_size_bytes + incoming_payload_size_bytes
98
+ } }
98
99
end
99
100
end
100
101
102
+ @ spec message_exists? ( State . t ( ) , message ( ) ) :: boolean ( )
103
+ def message_exists? ( % State { } = state , message ) do
104
+ Map . has_key? ( state . messages , { message . commit_lsn , message . commit_idx } )
105
+ end
106
+
101
107
@ spec put_persisted_messages ( State . t ( ) , list ( message ( ) ) | Enumerable . t ( ) ) :: { :ok , State . t ( ) } | { :error , Error . t ( ) }
102
108
def put_persisted_messages ( % State { } = state , messages ) do
103
109
persisted_message_groups =
0 commit comments