Foundation.ProcessRegistry (foundation v0.1.0)

Centralized process registry for Foundation layer.

Provides namespace isolation using Elixir's native Registry to enable concurrent testing and prevent naming conflicts between production and test environments.

Performance Characteristics

  • Storage: ETS-based partitioned table for high concurrent throughput
  • Partitions: 24 partitions (matches CPU cores)
  • Lookup Time: O(1) average case, < 1ms typical latency
  • Registration: Atomic operations with automatic process monitoring
  • Memory: Minimal overhead per registered process (~100 bytes)

Registry Architecture

Uses Elixir's native Registry module with optimized settings:

  • Keys: :unique - Each {namespace, service} key maps to exactly one process
  • Partitioning: CPU-optimized for concurrent access patterns
  • Monitoring: Automatic cleanup when processes terminate
  • Fault Tolerance: ETS table survives individual process crashes

Supported Namespaces

  • :production - For normal operation
  • {:test, reference()} - For test isolation with unique references

Examples

# Register a service in production
:ok = ProcessRegistry.register(:production, :config_server, self())

# Register in test namespace
test_ref = make_ref()
:ok = ProcessRegistry.register({:test, test_ref}, :config_server, self())

# Lookup services
{:ok, pid} = ProcessRegistry.lookup(:production, :config_server)

Summary

Functions

Child specification for supervision tree integration.

Cleanup all services in a test namespace.

Count the number of services in a namespace.

Get all registered services with their PIDs for a namespace.

List all services registered in a namespace.

Look up a service in the given namespace.

Register a service in the given namespace.

Check if a service is registered in a namespace.

Get registry statistics for monitoring and performance analysis.

Unregister a service from the given namespace.

Create a via tuple for GenServer registration.

Types

namespace()

@type namespace() :: :production | {:test, reference()}

registry_key()

@type registry_key() :: {namespace(), service_name()}

service_name()

@type service_name() ::
  :config_server | :event_store | :telemetry_service | :test_supervisor

Functions

child_spec(opts)

Child specification for supervision tree integration.

cleanup_test_namespace(test_ref)

@spec cleanup_test_namespace(reference()) :: :ok

Cleanup all services in a test namespace.

This is useful for test cleanup - terminates all processes registered in the given test namespace.

Parameters

  • test_ref: The test reference used in namespace

Returns

  • :ok after cleanup is complete

Examples

iex> test_ref = make_ref()
iex> # ... register services in {:test, test_ref} ...
iex> ProcessRegistry.cleanup_test_namespace(test_ref)
:ok

count_services(namespace)

@spec count_services(namespace()) :: non_neg_integer()

Count the number of services in a namespace.

Parameters

  • namespace: The namespace to count services in

Returns

  • Non-negative integer count of services

Examples

iex> ProcessRegistry.count_services(:production)
3

get_all_services(namespace)

@spec get_all_services(namespace()) :: %{required(service_name()) => pid()}

Get all registered services with their PIDs for a namespace.

Parameters

  • namespace: The namespace to get services for

Returns

  • Map of service_name => pid

Examples

iex> ProcessRegistry.get_all_services(:production)
%{
  config_server: #PID<0.123.0>,
  event_store: #PID<0.124.0>
}

list_services(namespace)

@spec list_services(namespace()) :: [service_name()]

List all services registered in a namespace.

Parameters

  • namespace: The namespace to list services for

Returns

  • List of service names registered in the namespace

Examples

iex> ProcessRegistry.list_services(:production)
[:config_server, :event_store, :telemetry_service]

iex> ProcessRegistry.list_services({:test, test_ref})
[]

lookup(namespace, service)

@spec lookup(namespace(), service_name()) :: {:ok, pid()} | :error

Look up a service in the given namespace.

Parameters

  • namespace: The namespace to search in
  • service: The service name to lookup

Returns

  • {:ok, pid} if service found
  • :error if service not found

Examples

iex> ProcessRegistry.lookup(:production, :config_server)
{:ok, #PID<0.123.0>}

iex> ProcessRegistry.lookup(:production, :nonexistent)
:error

register(namespace, service, pid)

@spec register(namespace(), service_name(), pid()) ::
  :ok | {:error, {:already_registered, pid()}}

Register a service in the given namespace.

Parameters

  • namespace: The namespace for service isolation
  • service: The service name to register
  • pid: The process PID to register

Returns

  • :ok if registration succeeds
  • {:error, {:already_registered, pid}} if name already taken

Examples

iex> ProcessRegistry.register(:production, :config_server, self())
:ok

iex> ProcessRegistry.register(:production, :config_server, self())
{:error, {:already_registered, #PID<0.123.0>}}

registered?(namespace, service)

@spec registered?(namespace(), service_name()) :: boolean()

Check if a service is registered in a namespace.

Parameters

  • namespace: The namespace to check
  • service: The service name to check

Returns

  • true if service is registered
  • false if service is not registered

Examples

iex> ProcessRegistry.registered?(namespace, :config_server)
true

stats()

@spec stats() :: %{
  total_services: non_neg_integer(),
  production_services: non_neg_integer(),
  test_namespaces: non_neg_integer(),
  partitions: pos_integer(),
  partition_count: pos_integer(),
  memory_usage_bytes: non_neg_integer(),
  ets_table_info: map()
}

Get registry statistics for monitoring and performance analysis.

Returns

  • Map with comprehensive registry statistics including:
    • Service counts by namespace type
    • Performance characteristics
    • Memory usage information
    • Partition utilization

Examples

iex> ProcessRegistry.stats()
%{
  total_services: 15,
  production_services: 3,
  test_namespaces: 4,
  partitions: 8,
  partition_count: 8,
  memory_usage_bytes: 4096,
  ets_table_info: %{...}
}

unregister(namespace, service)

@spec unregister(namespace(), service_name()) :: :ok

Unregister a service from the given namespace.

Note: This is typically not needed as Registry automatically unregisters when the process dies.

Parameters

  • namespace: The namespace containing the service
  • service: The service name to unregister

Returns

  • :ok regardless of whether service was registered

Examples

iex> ProcessRegistry.unregister(:production, :config_server)
:ok

via_tuple(namespace, service)

@spec via_tuple(namespace(), service_name()) ::
  {:via, Registry, {atom(), registry_key()}}

Create a via tuple for GenServer registration.

This is used in GenServer.start_link/3 for automatic registration.

Parameters

  • namespace: The namespace for the service
  • service: The service name

Returns

  • Via tuple for GenServer registration

Examples

iex> via = ProcessRegistry.via_tuple(:production, :config_server)
iex> GenServer.start_link(MyServer, [], name: via)