@@ -93,6 +93,12 @@ defmodule Electric.Config do
9393 conn_max_requests: 50 ,
9494 ## Performance tweaks
9595 publication_alter_debounce_ms: 0 ,
96+ # allow for configuring per-process `Process.spawn_opt()`. In the form
97+ # %{process_id :: atom() => [Process.spawn_opt()]}
98+ # See `Process.flag/2`
99+ #
100+ # e.g. %{shape_log_collector: [min_heap_size: 1024 * 1024, min_bin_vheap_size: 1024 * 1024]}
101+ process_spawn_opts: % { } ,
96102 ## Misc
97103 process_registry_partitions: & Electric.Config.Defaults . process_registry_partitions / 0 ,
98104 feature_flags: if ( Mix . env ( ) == :test , do: @ known_feature_flags , else: [ ] ) ,
@@ -466,6 +472,69 @@ defmodule Electric.Config do
466472 end
467473 end
468474
475+ @ valid_spawn_opts ~w[ min_bin_vheap_size min_heap_size priority fullsweep_after message_queue_data]
476+
477+ @ doc """
478+ Parse `spawn_opts` from environment variable to keyword list suitable for passing to `GenServer.start_link/2`
479+
480+ ## Examples
481+
482+ iex> parse_spawn_opts!(~S({"shape_log_collector":{"min_heap_size":234,"min_bin_vheap_size":123,"message_queue_data":"on_heap","priority":"high","fullsweep_after":104}}))
483+ %{shape_log_collector: [fullsweep_after: 104, message_queue_data: :on_heap, min_bin_vheap_size: 123, min_heap_size: 234, priority: :high]}
484+
485+ iex> parse_spawn_opts!(~S({"shape_log_collector":{"monkey":123,"message_queue_data":"on_fire","min_bin_vheap_size":-1,"priority":"high"}}))
486+ %{shape_log_collector: [priority: :high]}
487+
488+ iex> parse_spawn_opts!("")
489+ %{}
490+
491+ iex> parse_spawn_opts!("{}")
492+ %{}
493+ """
494+ def parse_spawn_opts! ( "" ) do
495+ % { }
496+ end
497+
498+ def parse_spawn_opts! ( str ) do
499+ str
500+ |> Jason . decode! ( )
501+ |> then ( fn
502+ opts when is_map ( opts ) ->
503+ for { process_name , process_opts } <- opts , is_map ( process_opts ) , into: % { } do
504+ opts =
505+ for { opt_key , opt_val } <- process_opts ,
506+ opt_key in @ valid_spawn_opts ,
507+ key = String . to_atom ( opt_key ) ,
508+ val = validate_spawn_opt ( key , opt_val ) do
509+ { key , val }
510+ end
511+
512+ { String . to_atom ( process_name ) , opts }
513+ end
514+
515+ _invalid ->
516+ raise ArgumentError , message: "Invalid spawn opts: #{ inspect ( str ) } "
517+ end )
518+ |> tap ( fn process_spawn_opts ->
519+ if map_size ( process_spawn_opts ) > 0 do
520+ Logger . info ( "Process spawn opts: #{ inspect ( process_spawn_opts ) } " )
521+ end
522+ end )
523+ end
524+
525+ defp validate_spawn_opt ( :min_bin_vheap_size , val ) when is_integer ( val ) and val >= 0 , do: val
526+ defp validate_spawn_opt ( :min_heap_size , val ) when is_integer ( val ) and val >= 0 , do: val
527+
528+ defp validate_spawn_opt ( :priority , val ) when val in [ "low" , "normal" , "high" ] ,
529+ do: String . to_atom ( val )
530+
531+ defp validate_spawn_opt ( :fullsweep_after , val ) when is_integer ( val ) and val >= 0 , do: val
532+
533+ defp validate_spawn_opt ( :message_queue_data , val ) when val in [ "off_heap" , "on_heap" ] ,
534+ do: String . to_atom ( val )
535+
536+ defp validate_spawn_opt ( _ , _ ) , do: nil
537+
469538 @ doc false
470539 # helper function for use in doc tests
471540 def deobfuscate ( { :ok , connection_opts } ) ,
0 commit comments