@@ -140,7 +140,10 @@ defmodule Phoenix.Sync.Electric do
140140 @ doc false
141141 @ impl Phoenix.Sync.Adapter
142142 def children ( env , opts ) do
143- { mode , electric_opts } = electric_opts ( opts )
143+ { mode , electric_opts } =
144+ opts
145+ |> set_environment_defaults ( env )
146+ |> electric_opts ( env )
144147
145148 case mode do
146149 :disabled ->
@@ -158,7 +161,11 @@ defmodule Phoenix.Sync.Electric do
158161 @ doc false
159162 @ impl Phoenix.Sync.Adapter
160163 def plug_opts ( env , opts ) do
161- { mode , electric_opts } = electric_opts ( opts )
164+ { mode , electric_opts } =
165+ opts
166+ |> set_environment_defaults ( env )
167+ |> electric_opts ( env )
168+
162169 # don't need to validate the mode here -- it will have already been
163170 # validated by children/0 which is run at phoenix_sync startup before the
164171 # plug opts call even comes through
@@ -179,7 +186,10 @@ defmodule Phoenix.Sync.Electric do
179186 @ doc false
180187 @ impl Phoenix.Sync.Adapter
181188 def client ( env , opts ) do
182- { mode , electric_opts } = electric_opts ( opts )
189+ { mode , electric_opts } =
190+ opts
191+ |> set_environment_defaults ( env )
192+ |> electric_opts ( env )
183193
184194 case mode do
185195 mode when mode in @ client_valid_modes ->
@@ -192,40 +202,74 @@ defmodule Phoenix.Sync.Electric do
192202 end
193203 end
194204
205+ # if we want to set up per-run configuration, and avoid weird state errors in
206+ # dev and test, then we have to write them to the application config, because
207+ # `children/2`, `client/2` and `plug_opts/2` need to have consistent
208+ # configuration values.
209+ defp set_environment_defaults ( opts , :test ) do
210+ opts
211+ |> set_persistent_config ( :stack_id , fn ->
212+ "electric-stack#{ System . monotonic_time ( ) } "
213+ end )
214+ |> set_persistent_config ( :replication_stream_id , fn ->
215+ String . replace ( "phoenix_sync#{ System . monotonic_time ( ) } " , "-" , "_" )
216+ end )
217+ |> set_persistent_config ( :replication_slot_temporary? , true )
218+ end
219+
220+ defp set_environment_defaults ( opts , :dev ) do
221+ opts
222+ |> set_environment_defaults ( :prod )
223+ |> set_persistent_config ( :storage_dir , fn ->
224+ Path . join ( [ System . tmp_dir! ( ) , "phoenix-sync#{ System . monotonic_time ( ) } " ] )
225+ end )
226+ end
227+
228+ defp set_environment_defaults ( opts , _env ) do
229+ opts
230+ |> set_persistent_config ( :stack_id , "electric-embedded" )
231+ end
232+
233+ defp set_persistent_config ( opts , key , value_fun ) when is_function ( value_fun ) do
234+ Keyword . put_new_lazy ( opts , key , fn ->
235+ value = value_fun . ( )
236+ Application . put_env ( :phoenix_sync , key , value )
237+ value
238+ end )
239+ end
240+
241+ defp set_persistent_config ( opts , key , value ) do
242+ set_persistent_config ( opts , key , fn -> value end )
243+ end
244+
195245 @ doc false
196246 def electric_available? do
197247 @ electric_available?
198248 end
199249
200- defp electric_opts ( opts ) do
250+ defp electric_opts ( opts , env ) do
201251 Keyword . pop_lazy ( opts , :mode , fn ->
202- if electric_available? ( ) do
203- Logger . warning ( [
204- "missing mode configuration for :phoenix_sync. Electric is installed so assuming `embedded` mode"
205- ] )
206-
207- :embedded
208- else
209- Logger . warning ( "No `:mode` configuration for :phoenix_sync, assuming `:disabled`" )
210-
211- :disabled
212- end
252+ default_mode ( env )
213253 end )
214254 end
215255
216- defp electric_api_server ( opts ) do
217- config = electric_http_config ( opts )
256+ defp default_mode ( :test ) do
257+ :disabled
258+ end
218259
219- cond do
220- Code . ensure_loaded? ( Bandit ) ->
221- Electric.Application . api_server ( Bandit , config )
260+ if @ electric_available? do
261+ defp default_mode ( _env ) do
262+ Logger . warning ( [
263+ "missing mode configuration for :phoenix_sync. Electric is installed so assuming `embedded` mode"
264+ ] )
222265
223- Code . ensure_loaded? ( Plug.Cowboy ) ->
224- Electric.Application . api_server ( Plug.Cowboy , config )
266+ :embedded
267+ end
268+ else
269+ defp default_mode ( _env ) do
270+ Logger . warning ( "No `:mode` configuration for :phoenix_sync, assuming `:disabled`" )
225271
226- true ->
227- raise RuntimeError ,
228- message: "No HTTP server found. Please install either Bandit or Plug.Cowboy"
272+ :disabled
229273 end
230274 end
231275
@@ -236,15 +280,18 @@ defmodule Phoenix.Sync.Electric do
236280 end
237281 end
238282
239- defp plug_opts ( env , :embedded , electric_opts ) do
240- if electric_available? ( ) do
283+ if @ electric_available? do
284+ defp plug_opts ( env , :embedded , electric_opts ) do
241285 env
242286 |> core_configuration ( electric_opts )
243287 |> Electric.Application . api_plug_opts ( )
244288 |> Keyword . fetch! ( :api )
245- else
246- raise RuntimeError ,
247- message: "Configured for embedded mode but `:electric` dependency not installed"
289+ end
290+ else
291+ defp plug_opts ( _env , :embedded , _electric_opts ) do
292+ raise ArgumentError ,
293+ message:
294+ "phoenix_sync configured in `mode: :embedded` but electric not installed. Please add `:electric` to your dependencies or use `:http` mode."
248295 end
249296 end
250297
@@ -259,36 +306,71 @@ defmodule Phoenix.Sync.Electric do
259306 defp electric_children ( env , mode , opts ) do
260307 case validate_database_config ( env , mode , opts ) do
261308 { :start , db_config_fun , message } ->
262- if electric_available? ( ) do
263- db_config =
264- db_config_fun . ( )
265- |> Keyword . update! ( :connection_opts , & Electric.Utils . obfuscate_password / 1 )
309+ start_embedded ( env , mode , db_config_fun , message )
266310
267- electric_config = core_configuration ( env , db_config )
311+ :ignore ->
312+ { :ok , [ ] }
268313
269- Logger . info ( message )
314+ { :error , _ } = error ->
315+ error
316+ end
317+ end
270318
271- http_server =
272- case mode do
273- :http -> electric_api_server ( electric_config )
274- :embedded -> [ ]
275- end
319+ if @ electric_available? do
320+ defp start_embedded ( env , mode , db_config_fun , message ) do
321+ db_config =
322+ db_config_fun . ( )
323+ |> Keyword . update! ( :connection_opts , & Electric.Utils . obfuscate_password / 1 )
276324
277- { :ok ,
278- [
279- { Electric.StackSupervisor , Electric.Application . configuration ( electric_config ) }
280- | http_server
281- ] }
282- else
283- { :error ,
284- "Electric configured to start in embedded mode but :electric dependency not available" }
325+ electric_config = core_configuration ( env , db_config )
326+
327+ Logger . info ( message )
328+
329+ http_server =
330+ case mode do
331+ :http -> electric_api_server ( electric_config )
332+ : embedded -> [ ]
285333 end
286334
287- :ignore ->
288- { :ok , [ ] }
335+ { :ok ,
336+ [
337+ { Electric.StackSupervisor , Electric.Application . configuration ( electric_config ) }
338+ | http_server
339+ ] }
340+ end
289341
290- { :error , _ } = error ->
291- error
342+ defp electric_api_server ( opts ) do
343+ config = electric_http_config ( opts )
344+
345+ cond do
346+ Code . ensure_loaded? ( Bandit ) ->
347+ Electric.Application . api_server ( Bandit , config )
348+
349+ Code . ensure_loaded? ( Plug.Cowboy ) ->
350+ Electric.Application . api_server ( Plug.Cowboy , config )
351+
352+ true ->
353+ raise RuntimeError ,
354+ message: "No HTTP server found. Please install either Bandit or Plug.Cowboy"
355+ end
356+ end
357+
358+ defp electric_http_config ( opts ) do
359+ case Keyword . fetch ( opts , :http ) do
360+ { :ok , http_opts } ->
361+ opts
362+ |> then ( fn o ->
363+ if ( port = http_opts [ :port ] , do: Keyword . put ( o , :service_port , port ) , else: o )
364+ end )
365+
366+ :error ->
367+ opts
368+ end
369+ end
370+ else
371+ defp start_embedded ( _env , _mode , _db_config_fun , _message ) do
372+ { :error ,
373+ "Electric configured to start in embedded mode but :electric dependency not available" }
292374 end
293375 end
294376
@@ -299,8 +381,8 @@ defmodule Phoenix.Sync.Electric do
299381 defp core_configuration ( env , opts ) do
300382 opts
301383 |> env_defaults ( env )
302- |> overrides ( )
303384 |> stack_id ( )
385+ |> overrides ( )
304386 end
305387
306388 defp env_defaults ( opts , :dev ) do
@@ -310,24 +392,11 @@ defmodule Phoenix.Sync.Electric do
310392 # if we want to use emphemeral dir for dev storage then we have to persist
311393 # the storage_dir into the application config.
312394 opts
313- # |> Keyword.put_new(
314- # :storage_dir,
315- # Path.join(System.tmp_dir!(), "electric/shape-data#{System.monotonic_time()}")
316- # )
317- |> Keyword . put_new (
318- :storage ,
319- { Electric.ShapeCache.InMemoryStorage ,
320- table_base_name: :"electric-storage#{ opts [ :stack_id ] } " , stack_id: opts [ :stack_id ] }
321- )
322- |> Keyword . put_new (
323- :persistent_kv ,
324- { Electric.PersistentKV.Memory , :new! , [ ] }
325- )
326395 |> Keyword . put_new ( :send_cache_headers? , false )
327396 end
328397
329398 defp env_defaults ( opts , :test ) do
330- stack_id = "electric-stack#{ System . monotonic_time ( ) } "
399+ stack_id = "electric-stack"
331400
332401 opts = Keyword . put_new ( opts , :stack_id , stack_id )
333402
@@ -389,26 +458,32 @@ defmodule Phoenix.Sync.Electric do
389458 end
390459 end
391460
392- defp convert_repo_config ( repo_config ) do
393- expected_keys = Electric . connection_opts_schema ( ) |> Keyword . keys ( )
394-
395- ssl_opts =
396- case Keyword . get ( repo_config , :ssl , nil ) do
397- off when off in [ nil , false ] -> [ sslmode: :disable ]
398- true -> [ sslmode: :require ]
399- _opts -> [ ]
400- end
461+ if @ electric_available? do
462+ defp convert_repo_config ( repo_config ) do
463+ expected_keys = Electric . connection_opts_schema ( ) |> Keyword . keys ( )
464+
465+ ssl_opts =
466+ case Keyword . get ( repo_config , :ssl , nil ) do
467+ off when off in [ nil , false ] -> [ sslmode: :disable ]
468+ true -> [ sslmode: :require ]
469+ _opts -> [ ]
470+ end
401471
402- tcp_opts =
403- if :inet6 in Keyword . get ( repo_config , :socket_options , [ ] ) ,
404- do: [ ipv6: true ] ,
405- else: [ ]
472+ tcp_opts =
473+ if :inet6 in Keyword . get ( repo_config , :socket_options , [ ] ) ,
474+ do: [ ipv6: true ] ,
475+ else: [ ]
406476
407- repo_config
408- |> Keyword . take ( expected_keys )
409- |> Keyword . merge ( ssl_opts )
410- |> Keyword . merge ( tcp_opts )
411- |> Keyword . put_new ( :port , 5432 )
477+ repo_config
478+ |> Keyword . take ( expected_keys )
479+ |> Keyword . merge ( ssl_opts )
480+ |> Keyword . merge ( tcp_opts )
481+ |> Keyword . put_new ( :port , 5432 )
482+ end
483+ else
484+ defp convert_repo_config ( _repo_config ) do
485+ [ ]
486+ end
412487 end
413488
414489 defp http_mode_plug_opts ( electric_config ) do
@@ -426,19 +501,6 @@ defmodule Phoenix.Sync.Electric do
426501 end
427502 end
428503
429- defp electric_http_config ( opts ) do
430- case Keyword . fetch ( opts , :http ) do
431- { :ok , http_opts } ->
432- opts
433- |> then ( fn o ->
434- if ( port = http_opts [ :port ] , do: Keyword . put ( o , :service_port , port ) , else: o )
435- end )
436-
437- :error ->
438- opts
439- end
440- end
441-
442504 if @ electric_available? do
443505 defp configure_client ( opts , :embedded ) do
444506 Electric.Client . embedded ( opts )
0 commit comments