@@ -264,7 +264,7 @@ defmodule PartitionSupervisor do
264264 raise "the call to the function in :with_arguments must return a list, got: #{ inspect ( args ) } "
265265 end
266266
267- start = { __MODULE__ , :start_child , [ mod , fun , args , name , partition ] }
267+ start = { __MODULE__ , :start_child , [ mod , fun , args , partition ] }
268268 Map . merge ( map , % { id: partition , start: start , modules: modules } )
269269 end
270270
@@ -282,48 +282,112 @@ defmodule PartitionSupervisor do
282282 end
283283
284284 @ doc false
285- def start_child ( mod , fun , args , name , partition ) do
285+ def start_child ( mod , fun , args , partition ) do
286286 case apply ( mod , fun , args ) do
287287 { :ok , pid } ->
288- register_child ( name , partition , pid )
288+ register_child ( partition , pid )
289289 { :ok , pid }
290290
291291 { :ok , pid , info } ->
292- register_child ( name , partition , pid )
292+ register_child ( partition , pid )
293293 { :ok , pid , info }
294294
295295 other ->
296296 other
297297 end
298298 end
299299
300- defp register_child ( name , partition , pid ) when is_atom ( name ) do
301- :ets . insert ( name , { partition , pid } )
302- end
303-
304- defp register_child ( { :via , _ , _ } , partition , pid ) do
305- Registry . register ( @ registry , { self ( ) , partition } , pid )
300+ defp register_child ( partition , pid ) do
301+ :ets . insert ( Process . get ( :ets_table ) , { partition , pid } )
306302 end
307303
308304 @ impl true
309305 def init ( { name , partitions , children , init_opts } ) do
310- init_partitions ( name , partitions )
306+ table = init_table ( name )
307+ :ets . insert ( table , { :partitions , partitions , partitions } )
308+ Process . put ( :ets_table , table )
311309 Supervisor . init ( children , Keyword . put_new ( init_opts , :strategy , :one_for_one ) )
312310 end
313311
314- defp init_partitions ( name , partitions ) when is_atom ( name ) do
315- :ets . new ( name , [ :set , :named_table , :protected , read_concurrency: true ] )
316- :ets . insert ( name , { :partitions , partitions } )
312+ defp init_table ( name ) when is_atom ( name ) do
313+ :ets . new ( name , [ :set , :named_table , :public , read_concurrency: true ] )
317314 end
318315
319- defp init_partitions ( { :via , _ , _ } , partitions ) do
320- child_spec = { Registry , keys: :unique , name: @ registry }
316+ defp init_table ( { :via , _ , _ } ) do
317+ table = :ets . new ( __MODULE__ , [ :set , :public , read_concurrency: true ] )
318+ ensure_registry ( )
319+ Registry . register ( @ registry , self ( ) , table )
320+ table
321+ end
321322
322- if ! Process . whereis ( @ registry ) do
323- Supervisor . start_child ( :elixir_sup , child_spec )
323+ defp ensure_registry do
324+ if Process . whereis ( @ registry ) == nil do
325+ Supervisor . start_child ( :elixir_sup , { Registry , keys: :unique , name: @ registry } )
324326 end
327+ end
328+
329+ @ doc """
330+ Resizes the number of partitions in the PartitionSupervisor.
325331
326- Registry . register ( @ registry , self ( ) , partitions )
332+ This is done by starting or stopping a given number of
333+ partitions in the supervisor. All of the child specifications
334+ are kept in the `PartitionSupervisor` itself.
335+
336+ The final number of partitions cannot be less than zero and
337+ cannot be more than the number of partitions the supervisor
338+ started with.
339+ """
340+ @ doc since: "1.18.0"
341+ @ spec resize! ( name ( ) , non_neg_integer ( ) ) :: non_neg_integer ( )
342+ def resize! ( name , partitions ) when is_integer ( partitions ) do
343+ supervisor =
344+ GenServer . whereis ( name ) || exit ( { :noproc , { __MODULE__ , :resize! , [ name , partitions ] } } )
345+
346+ table = table ( name )
347+ ensure_registry ( )
348+
349+ Registry . lock ( @ registry , supervisor , fn ->
350+ case :ets . lookup ( table , :partitions ) do
351+ [ { :partitions , _current , max } ] when partitions not in 0 .. max // 1 ->
352+ raise ArgumentError ,
353+ "the number of partitions to resize to must be a number between 0 and #{ max } , got: #{ partitions } "
354+
355+ [ { :partitions , current , max } ] when partitions > current ->
356+ for id <- current .. ( partitions - 1 ) do
357+ case Supervisor . restart_child ( supervisor , id ) do
358+ { :ok , _ } ->
359+ :ok
360+
361+ { :ok , _ , _ } ->
362+ :ok
363+
364+ { :error , reason } ->
365+ raise "cannot restart partition #{ id } of PartitionSupervisor #{ inspect ( name ) } due to reason #{ inspect ( reason ) } "
366+ end
367+ end
368+
369+ :ets . insert ( table , { :partitions , partitions , max } )
370+ current
371+
372+ [ { :partitions , current , max } ] when partitions < current ->
373+ :ets . insert ( table , { :partitions , partitions , max } )
374+
375+ for id <- partitions .. ( current - 1 ) do
376+ case Supervisor . terminate_child ( supervisor , id ) do
377+ :ok ->
378+ :ok
379+
380+ { :error , reason } ->
381+ raise "cannot terminate partition #{ id } of PartitionSupervisor #{ inspect ( name ) } due to reason #{ inspect ( reason ) } "
382+ end
383+ end
384+
385+ current
386+
387+ [ { :partitions , current , _max } ] ->
388+ current
389+ end
390+ end )
327391 end
328392
329393 @ doc """
@@ -332,24 +396,27 @@ defmodule PartitionSupervisor do
332396 @ doc since: "1.14.0"
333397 @ spec partitions ( name ( ) ) :: pos_integer ( )
334398 def partitions ( name ) do
335- { _name , partitions } = name_partitions ( name )
336- partitions
399+ name |> table ( ) |> partitions ( name )
337400 end
338401
339- # For whereis_name, we want to lookup on GenServer.whereis/1
340- # just once, so we lookup the name and partitions together.
341- defp name_partitions ( name ) when is_atom ( name ) do
402+ defp partitions ( table , name ) do
342403 try do
343- { name , :ets . lookup_element ( name , :partitions , 2 ) }
404+ :ets . lookup_element ( table , :partitions , 2 )
344405 rescue
345406 _ -> exit ( { :noproc , { __MODULE__ , :partitions , [ name ] } } )
346407 end
347408 end
348409
349- defp name_partitions ( name ) when is_tuple ( name ) do
410+ defp table ( name ) when is_atom ( name ) do
411+ name
412+ end
413+
414+ # For whereis_name, we want to lookup on GenServer.whereis/1
415+ # just once, so we lookup the name and partitions together.
416+ defp table ( name ) when is_tuple ( name ) do
350417 with pid when is_pid ( pid ) <- GenServer . whereis ( name ) ,
351- [ name_partitions ] <- Registry . lookup ( @ registry , pid ) do
352- name_partitions
418+ [ { _ , table } ] <- Registry . lookup ( @ registry , pid ) do
419+ table
353420 else
354421 _ -> exit ( { :noproc , { __MODULE__ , :partitions , [ name ] } } )
355422 end
@@ -374,7 +441,7 @@ defmodule PartitionSupervisor do
374441 @ doc since: "1.14.0"
375442 @ spec which_children ( name ( ) ) :: [
376443 # Inlining [module()] | :dynamic here because :supervisor.modules() is not exported
377- { :undefined , pid | :restarting , :worker | :supervisor , [ module ( ) ] | :dynamic }
444+ { integer ( ) , pid | :restarting , :worker | :supervisor , [ module ( ) ] | :dynamic }
378445 ]
379446 def which_children ( name ) when is_atom ( name ) or elem ( name , 0 ) == :via do
380447 Supervisor . which_children ( name )
@@ -428,22 +495,17 @@ defmodule PartitionSupervisor do
428495
429496 @ doc false
430497 def whereis_name ( { name , key } ) when is_atom ( name ) or is_tuple ( name ) do
431- { name , partitions } = name_partitions ( name )
498+ table = table ( name )
499+ partitions = partitions ( table , name )
500+
501+ if partitions == 0 do
502+ raise ArgumentError , "PartitionSupervisor #{ inspect ( name ) } has zero partitions"
503+ end
432504
433505 partition =
434506 if is_integer ( key ) , do: rem ( abs ( key ) , partitions ) , else: :erlang . phash2 ( key , partitions )
435507
436- whereis_name ( name , partition )
437- end
438-
439- defp whereis_name ( name , partition ) when is_atom ( name ) do
440- :ets . lookup_element ( name , partition , 2 )
441- end
442-
443- defp whereis_name ( name , partition ) when is_pid ( name ) do
444- @ registry
445- |> Registry . values ( { name , partition } , name )
446- |> List . first ( :undefined )
508+ :ets . lookup_element ( table , partition , 2 )
447509 end
448510
449511 @ doc false
0 commit comments