Packages
snakepit
0.8.6
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
High-performance pooler and session manager for external language integrations. Supports Python, Node.js, Ruby, and more with gRPC streaming, session management, and production-ready process cleanup.
Current section
Files
Jump to
Current section
Files
lib/snakepit/telemetry/naming.ex
defmodule Snakepit.Telemetry.Naming do
@moduledoc """
Event catalog and naming validation for Snakepit telemetry.
This module ensures atom safety by maintaining a curated catalog of all
valid telemetry events and measurement keys. Python-originated events
must pass through this module to prevent arbitrary atom creation.
## Python Event Catalog
`python_event_catalog/0` lists the event strings emitted by `snakepit_bridge`
and the measurement keys they are expected to use. When adding a new Python
telemetry event, update that catalog and the allowlist in
`snakepit_bridge.telemetry.stream` together so both languages agree on the
schema.
"""
# Layer 1: Infrastructure Events (Elixir-originated)
@pool_events [
:initialized,
:status,
:queue_enqueued,
:queue_dequeued,
:queue_timeout,
:worker_spawn_started,
:worker_spawned,
:worker_spawn_failed,
:worker_terminated,
:worker_restarted
]
@session_events [
:created,
:destroyed,
:affinity_assigned,
:affinity_broken
]
# Layer 2: Python Execution Events (Python-originated, folded by Elixir)
@python_events [
:call_start,
:call_stop,
:call_exception,
:memory_sampled,
:cpu_sampled,
:gc_completed,
:error_occurred
]
# Layer 3: gRPC Bridge Events (Elixir-originated)
@grpc_events [
:call_start,
:call_stop,
:call_exception,
:stream_opened,
:stream_message,
:stream_closed,
:connection_established,
:connection_lost,
:connection_reconnected
]
@python_event_catalog [
%{
name: "python.call.start",
event: [:snakepit, :python, :call, :start],
measurements: [:count]
},
%{
name: "python.call.stop",
event: [:snakepit, :python, :call, :stop],
measurements: [:duration, :count]
},
%{
name: "python.call.exception",
event: [:snakepit, :python, :call, :exception],
measurements: [:count]
},
%{
name: "python.memory.sampled",
event: [:snakepit, :python, :memory, :sampled],
measurements: [:rss_bytes, :vms_bytes]
},
%{
name: "python.cpu.sampled",
event: [:snakepit, :python, :cpu, :sampled],
measurements: [:cpu_percent]
},
%{
name: "python.gc.completed",
event: [:snakepit, :python, :gc, :completed],
measurements: [:count, :generation]
},
%{
name: "python.error.occurred",
event: [:snakepit, :python, :error, :occurred],
measurements: [:count]
},
%{
name: "tool.execution.start",
event: [:snakepit, :python, :tool, :execution, :start],
measurements: [:count]
},
%{
name: "tool.execution.stop",
event: [:snakepit, :python, :tool, :execution, :stop],
measurements: [:duration, :count]
},
%{
name: "tool.execution.exception",
event: [:snakepit, :python, :tool, :execution, :exception],
measurements: [:count]
},
%{
name: "tool.result_size",
event: [:snakepit, :python, :tool, :result_size],
measurements: [:message_size]
}
]
# Valid measurement keys (atom-safe)
@measurement_keys [
:duration,
:duration_ms,
:bytes,
:system_time,
:queue_depth,
:queue_time,
:available_workers,
:busy_workers,
:total_workers,
:worker_count,
:lifetime,
:total_commands,
:command_count,
:downtime,
:restart_count,
:retry_count,
:request_size,
:response_size,
:message_size,
:sequence_number,
:message_count,
:network_time,
:uptime,
:call_count,
:affinity_duration,
:commands_with_affinity,
:rss_bytes,
:vms_bytes,
:cpu_percent,
:collected,
:generation,
:latency_ms,
:count,
:python_pid
]
@doc """
Convert Python event parts to a valid Elixir telemetry event name.
Returns `{:ok, event_name}` if the parts map to a known event,
`{:error, reason}` otherwise.
## Examples
iex> Snakepit.Telemetry.Naming.from_parts(["python", "call", "start"])
{:ok, [:snakepit, :python, :call, :start]}
iex> Snakepit.Telemetry.Naming.from_parts(["unknown", "event"])
{:error, :unknown_event}
"""
def from_parts(["python", "call", "start"]) do
{:ok, [:snakepit, :python, :call, :start]}
end
def from_parts(["python", "call", "stop"]) do
{:ok, [:snakepit, :python, :call, :stop]}
end
def from_parts(["python", "call", "exception"]) do
{:ok, [:snakepit, :python, :call, :exception]}
end
def from_parts(["python", "memory", "sampled"]) do
{:ok, [:snakepit, :python, :memory, :sampled]}
end
def from_parts(["python", "cpu", "sampled"]) do
{:ok, [:snakepit, :python, :cpu, :sampled]}
end
def from_parts(["python", "gc", "completed"]) do
{:ok, [:snakepit, :python, :gc, :completed]}
end
def from_parts(["python", "error", "occurred"]) do
{:ok, [:snakepit, :python, :error, :occurred]}
end
def from_parts(["tool", "execution", action]) when action in ["start", "stop", "exception"] do
{:ok, [:snakepit, :python, :tool, :execution, String.to_existing_atom(action)]}
rescue
ArgumentError -> {:error, :invalid_atom}
end
def from_parts(["tool", "result_size"]) do
{:ok, [:snakepit, :python, :tool, :result_size]}
end
def from_parts(parts) when is_list(parts) do
{:error, :unknown_event}
end
@doc """
Validate a measurement key and convert to atom if it's in the allowlist.
## Examples
iex> Snakepit.Telemetry.Naming.measurement_key("duration")
{:ok, :duration}
iex> Snakepit.Telemetry.Naming.measurement_key("unknown_key")
{:error, :unknown_measurement_key}
"""
def measurement_key(key) when is_binary(key) do
atom_key = String.to_existing_atom(key)
if atom_key in @measurement_keys do
{:ok, atom_key}
else
{:error, :unknown_measurement_key}
end
rescue
ArgumentError -> {:error, :invalid_atom}
end
def measurement_key(key) when is_atom(key) do
if key in @measurement_keys do
{:ok, key}
else
{:error, :unknown_measurement_key}
end
end
@doc """
Get all valid pool events.
"""
def pool_events, do: @pool_events
@doc """
Get all valid session events.
"""
def session_events, do: @session_events
@doc """
Get all valid Python events.
"""
def python_events, do: @python_events
@doc """
Return the catalog describing Python event names, their telemetry atoms, and expected measurements.
"""
def python_event_catalog, do: @python_event_catalog
@doc """
Get all valid gRPC events.
"""
def grpc_events, do: @grpc_events
@doc """
Get all valid measurement keys.
"""
def measurement_keys, do: @measurement_keys
@doc """
Build an event name from components.
## Examples
iex> Snakepit.Telemetry.Naming.event(:pool, :worker, :spawned)
[:snakepit, :pool, :worker, :spawned]
"""
def event(component, resource, action) do
[:snakepit, component, resource, action]
end
@doc """
Build a pool event name.
"""
def pool_event(:initialized), do: [:snakepit, :pool, :initialized]
def pool_event(:status), do: [:snakepit, :pool, :status]
def pool_event(:queue_enqueued), do: [:snakepit, :pool, :queue, :enqueued]
def pool_event(:queue_dequeued), do: [:snakepit, :pool, :queue, :dequeued]
def pool_event(:queue_timeout), do: [:snakepit, :pool, :queue, :timeout]
def pool_event(:worker_spawn_started), do: [:snakepit, :pool, :worker, :spawn_started]
def pool_event(:worker_spawned), do: [:snakepit, :pool, :worker, :spawned]
def pool_event(:worker_spawn_failed), do: [:snakepit, :pool, :worker, :spawn_failed]
def pool_event(:worker_terminated), do: [:snakepit, :pool, :worker, :terminated]
def pool_event(:worker_restarted), do: [:snakepit, :pool, :worker, :restarted]
@doc """
Build a session event name.
"""
def session_event(action) when action in @session_events do
case action do
:created -> [:snakepit, :session, :created]
:destroyed -> [:snakepit, :session, :destroyed]
:affinity_assigned -> [:snakepit, :session, :affinity, :assigned]
:affinity_broken -> [:snakepit, :session, :affinity, :broken]
end
end
@doc """
Build a Python event name.
"""
def python_event(action) when action in @python_events do
case action do
:call_start -> [:snakepit, :python, :call, :start]
:call_stop -> [:snakepit, :python, :call, :stop]
:call_exception -> [:snakepit, :python, :call, :exception]
:memory_sampled -> [:snakepit, :python, :memory, :sampled]
:cpu_sampled -> [:snakepit, :python, :cpu, :sampled]
:gc_completed -> [:snakepit, :python, :gc, :completed]
:error_occurred -> [:snakepit, :python, :error, :occurred]
end
end
@doc """
Build a gRPC event name.
"""
def grpc_event(resource, action) when action in @grpc_events do
[:snakepit, :grpc, resource, action]
end
end