@@ -34,6 +34,7 @@ defmodule Electric.Replication.PublicationManager do
3434 :can_alter_publication? ,
3535 :manual_table_publishing? ,
3636 :configure_tables_for_replication_fn ,
37+ :publication_refresh_period ,
3738 :shape_cache ,
3839 next_update_forced?: false
3940 ]
@@ -50,6 +51,7 @@ defmodule Electric.Replication.PublicationManager do
5051 publication_name: String . t ( ) ,
5152 db_pool: term ( ) ,
5253 configure_tables_for_replication_fn: fun ( ) ,
54+ publication_refresh_period: non_neg_integer ( ) ,
5355 shape_cache: { module ( ) , term ( ) } ,
5456 next_update_forced?: boolean ( )
5557 }
@@ -80,7 +82,8 @@ defmodule Electric.Replication.PublicationManager do
8082 required: false ,
8183 default: & Configuration . configure_publication! / 4
8284 ] ,
83- server: [ type: :any , required: false ]
85+ server: [ type: :any , required: false ] ,
86+ refresh_period: [ type: :pos_integer , required: false , default: 60_000 ]
8487 )
8588
8689 @ behaviour __MODULE__
@@ -170,10 +173,11 @@ defmodule Electric.Replication.PublicationManager do
170173 can_alter_publication?: opts . can_alter_publication? ,
171174 manual_table_publishing?: opts . manual_table_publishing? ,
172175 shape_cache: Map . get ( opts , :shape_cache , { Electric.ShapeCache , [ stack_id: opts . stack_id ] } ) ,
173- configure_tables_for_replication_fn: opts . configure_tables_for_replication_fn
176+ configure_tables_for_replication_fn: opts . configure_tables_for_replication_fn ,
177+ publication_refresh_period: opts . refresh_period
174178 }
175179
176- { :ok , state }
180+ { :ok , state , state . publication_refresh_period }
177181 end
178182
179183 @ impl true
@@ -190,7 +194,7 @@ defmodule Electric.Replication.PublicationManager do
190194 state = schedule_update_publication ( state . update_debounce_timeout , state )
191195 { :noreply , state }
192196 else
193- { :reply , :ok , state }
197+ { :reply , :ok , state , state . publication_refresh_period }
194198 end
195199 end
196200 end
@@ -208,7 +212,7 @@ defmodule Electric.Replication.PublicationManager do
208212 state = schedule_update_publication ( state . update_debounce_timeout , state )
209213 { :noreply , state }
210214 else
211- { :reply , :ok , state }
215+ { :reply , :ok , state , state . publication_refresh_period }
212216 end
213217 end
214218 end
@@ -219,19 +223,21 @@ defmodule Electric.Replication.PublicationManager do
219223 state = schedule_update_publication ( state . update_debounce_timeout , forced? , state )
220224 { :noreply , state }
221225 else
222- { :reply , :ok , state }
226+ { :reply , :ok , state , state . publication_refresh_period }
223227 end
224228 end
225229
226230 def handle_call ( { :recover_shape , shape_handle , shape } , _from , state ) do
227- if is_tracking_shape_handle? ( shape_handle , state ) do
228- Logger . debug ( "Shape already tracked: #{ inspect ( shape_handle ) } " )
229- { :reply , :ok , state }
230- else
231- state = track_shape_handle ( shape_handle , state )
232- state = update_relation_filters_for_shape ( shape , :add , state )
233- { :reply , :ok , state }
234- end
231+ state =
232+ if is_tracking_shape_handle? ( shape_handle , state ) do
233+ Logger . debug ( "Shape already tracked: #{ inspect ( shape_handle ) } " )
234+ state
235+ else
236+ state = track_shape_handle ( shape_handle , state )
237+ update_relation_filters_for_shape ( shape , :add , state )
238+ end
239+
240+ { :reply , :ok , state , state . publication_refresh_period }
235241 end
236242
237243 defguardp is_fatal ( err )
@@ -241,16 +247,27 @@ defmodule Electric.Replication.PublicationManager do
241247 @ impl true
242248 def handle_info ( :update_publication , state ) do
243249 # Clear out the timer ref
250+ if state . scheduled_updated_ref , do: Process . cancel_timer ( state . scheduled_updated_ref )
244251 state = % { state | scheduled_updated_ref: nil }
245252
246253 # Invoke the actual handler for the publication update
247- if not state . can_alter_publication? or state . manual_table_publishing? do
248- check_publication_relations ( state )
249- else
250- update_publication_state ( state )
254+ updated_state =
255+ if not state . can_alter_publication? or state . manual_table_publishing? do
256+ check_publication_relations ( state )
257+ else
258+ update_publication_state ( state )
259+ end
260+
261+ with { :ok , state } <- updated_state do
262+ # Schedule a forced refresh to happen periodically unless there's an explicit call to
263+ # update the publication that happens sooner.
264+ { :noreply , state , state . publication_refresh_period }
251265 end
252266 end
253267
268+ def handle_info ( :timeout , state ) ,
269+ do: handle_info ( :update_publication , % { state | next_update_forced?: true } )
270+
254271 defp check_publication_relations (
255272 % __MODULE__ {
256273 committed_relation_filters: committed_filters ,
@@ -260,7 +277,7 @@ defmodule Electric.Replication.PublicationManager do
260277 ) do
261278 if not forced? and filters_are_equal? ( current_filters , committed_filters ) do
262279 Logger . debug ( "No changes to publication, skipping checkup" )
263- { :noreply , reply_to_waiters ( :ok , state ) }
280+ { :ok , reply_to_waiters ( :ok , state ) }
264281 else
265282 # We cannot modify the publication, so we only check whether it is in the right state for
266283 # the set of currently active relation filters.
@@ -294,7 +311,7 @@ defmodule Electric.Replication.PublicationManager do
294311 error = % Electric.DbConfigurationError { type: reason , message: message }
295312
296313 state = reply_to_waiters ( { :error , error } , state )
297- { :noreply , % { state | next_update_forced?: false } }
314+ { :ok , % { state | next_update_forced?: false } }
298315 end
299316 end
300317 end
@@ -326,12 +343,12 @@ defmodule Electric.Replication.PublicationManager do
326343 { :error , err } when retries < @ max_retries and not is_fatal ( err ) ->
327344 Logger . warning ( "Failed to configure publication, retrying: #{ inspect ( err ) } " )
328345 state = schedule_update_publication ( @ retry_timeout , % { state | retries: retries + 1 } )
329- { :noreply , state }
346+ { :ok , state }
330347
331348 { :error , err } ->
332349 Logger . error ( "Failed to configure publication: #{ inspect ( err ) } " )
333350 state = reply_to_waiters ( { :error , err } , state )
334- { :noreply , % { state | next_update_forced?: false } }
351+ { :ok , % { state | next_update_forced?: false } }
335352 end
336353 end
337354
@@ -352,7 +369,7 @@ defmodule Electric.Replication.PublicationManager do
352369 state = reply_to_waiters ( :ok , state )
353370 committed_filters = MapSet . difference ( state . prepared_relation_filters , invalidated_relations )
354371
355- { :noreply ,
372+ { :ok ,
356373 % {
357374 state
358375 | committed_relation_filters: committed_filters ,
0 commit comments