Documentation
¶
Overview ¶
Package relaynode is the API server's registry of standalone relay nodes. A platform admin registers and retires nodes; a periodic probe reads each node's GET /lifecycle; relay credentials list only the nodes that are eligible, preferring the client's region.
Index ¶
- Constants
- Variables
- type Collector
- type EligibleFunc
- type Monitor
- type Node
- type PriorProbe
- type ProbeStore
- type Prober
- type Result
- type Selection
- type Selector
- type Spec
- type Store
- func (s *Store) Create(ctx context.Context, spec Spec) (Node, error)
- func (s *Store) Eligible(ctx context.Context) ([]Node, error)
- func (s *Store) Known(ctx context.Context) ([]Node, error)
- func (s *Store) List(ctx context.Context) ([]Node, error)
- func (s *Store) RecordProbe(ctx context.Context, id int64, r Result) (prior PriorProbe, recorded bool, err error)
- func (s *Store) Retire(ctx context.Context, id int64) (Node, error)
Constants ¶
const ( StateKnown = "known" StateRetired = "retired" )
Registration states.
const ( FailureInvalidURL = "invalid lifecycle_url" FailureBlocked = "destination not allowed" FailureTimeout = "timeout" FailureConnection = "connection failed" FailureInvalidJSON = "invalid /lifecycle JSON" FailureUnknownState = "unknown lifecycle state" FailureNotRelay = "role is not relay" )
Probe failure reasons. They are stable strings, so a repeated failure can be recognised and the admin list can show why a node is unhealthy; the changing details, such as an error with an ephemeral port, go to the log.
const DefaultProbeTimeout = 3 * time.Second
DefaultProbeTimeout bounds one GET /lifecycle. It stays well under ProbeInterval so a slow node cannot stack probes.
const FreshnessWindow = 3 * ProbeInterval
FreshnessWindow is how old a node's last good probe may be before the node stops being advertised: three probe intervals.
const ProbeInterval = 10 * time.Second
ProbeInterval is how often the probe job reads every known node.
Variables ¶
var ErrDuplicate = errors.New("relaynode: a known node already uses this lifecycle_url")
ErrDuplicate is returned when a known node already uses the lifecycle URL.
var ErrInvalid = errors.New("relaynode: invalid node")
ErrInvalid marks a Spec that fails validation.
var ErrNoEligibleNodes = errors.New("relaynode: no eligible relay nodes")
ErrNoEligibleNodes is returned when no node can be advertised.
var ErrNotFound = errors.New("relaynode: node not found")
ErrNotFound is returned for an unknown node id.
Functions ¶
This section is empty.
Types ¶
type Collector ¶
type Collector struct {
// contains filtered or unexported fields
}
Collector reports each known node's eligibility and last probe result at scrape time, so an alert can see a node that serves but fails its probe. Every API server reads the same registry; aggregate with max by (node_id).
func NewCollector ¶
NewCollector returns a Collector over list, normally Store.List.
func (*Collector) Collect ¶
func (c *Collector) Collect(ch chan<- prometheus.Metric)
Collect implements prometheus.Collector. A failed read reports no nodes rather than failing the whole scrape.
func (*Collector) Describe ¶
func (c *Collector) Describe(ch chan<- *prometheus.Desc)
Describe implements prometheus.Collector.
type EligibleFunc ¶
EligibleFunc lists the nodes relay credentials may advertise.
type Monitor ¶
type Monitor struct {
// contains filtered or unexported fields
}
Monitor probes every known node and records the results. It logs a failure when the node's stored last_probe_error changes, and a recovery when a failing node succeeds. The comparison uses the result each write replaced, so the log follows the database rather than one process's memory: any API server can run the probe.
func NewMonitor ¶
func NewMonitor(store ProbeStore, prober *Prober, logger *slog.Logger) *Monitor
NewMonitor returns a Monitor.
type Node ¶
type Node struct {
ID int64 `json:"id" example:"7"`
Region string `json:"region" example:"us-east"`
TURNURLs []string `json:"turn_urls" example:"turn:relay-1.example.com:3478?transport=udp"`
LifecycleURL string `json:"lifecycle_url" example:"http://10.0.0.5:9091/lifecycle"`
State string `json:"state" enum:"known,retired" example:"known"`
// Eligible reports whether credentials currently advertise this node.
Eligible bool `json:"eligible" example:"true"`
LastProbeAt *time.Time `json:"last_probe_at" example:"2026-10-26T12:00:00Z"`
LastProbeOK bool `json:"last_probe_ok" example:"true"`
// LastProbeError is why the last probe failed, such as "timeout" or
// "status 503". nil when it succeeded or has not run.
LastProbeError *string `json:"last_probe_error" example:"timeout"`
// LastLifecycleState and LastActiveAllocations are the state and
// active_work the last probe read. nil when it read no valid /lifecycle
// document.
LastLifecycleState *string `json:"last_lifecycle_state" example:"active"`
LastActiveAllocations *int64 `json:"last_active_allocations" example:"12"`
CreatedAt time.Time `json:"created_at" example:"2026-10-26T11:00:00Z"`
UpdatedAt time.Time `json:"updated_at" example:"2026-10-26T12:00:00Z"`
}
Node is one registered relay node as the admin API reports it.
type PriorProbe ¶
PriorProbe is a node's stored probe result just before a new one replaced it.
type ProbeStore ¶
type ProbeStore interface {
Known(ctx context.Context) ([]Node, error)
RecordProbe(ctx context.Context, id int64, r Result) (prior PriorProbe, recorded bool, err error)
}
ProbeStore is the part of Store the Monitor needs.
type Prober ¶
type Prober struct {
// contains filtered or unexported fields
}
Prober reads a node's GET /lifecycle.
type Result ¶
type Result struct {
// OK is true when the node answered 2xx with a valid relay status. A
// draining node is OK; eligibility also needs State "active".
OK bool
// State is the lifecycle state read, or "" when none could be read.
State string
// ActiveWork is the active_work read, or nil when none could be read.
ActiveWork *int64
// not match the node's LIFECYCLE_TOKEN.
Unauthorized bool
// Failure is the stable reason OK is false: one of the Failure constants
// or "status <code>".
Failure string
// Detail is the underlying error or value, for the log only.
Detail string
}
Result is one probe of a node's GET /lifecycle.
type Selection ¶
type Selection struct {
URLs []string
// Region is the region served, or "" when the nodes span regions.
Region string
}
Selection is the relay URLs one credential set advertises.
type Selector ¶
type Selector struct {
// contains filtered or unexported fields
}
Selector picks the relay URLs for a credential request. Safe for concurrent use.
func NewSelector ¶
func NewSelector(eligible EligibleFunc, embedded *Node) *Selector
NewSelector returns a Selector over eligible. embedded, when non-nil, is the relay running inside this API server: it has no region, it is always eligible while the process runs, and it is served only in the cross-region fallback.
func (*Selector) Select ¶
Select returns the URLs of every eligible node in region, in shuffled node order. region is matched case-insensitively. With no eligible node in region, or no region, it falls back to every eligible node. Each node's URLs keep their registered order, so a client that tries them in order still prefers that node's first transport. When the eligible nodes cannot be read, the embedded relay is served alone if there is one.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store reads and writes the relay_nodes table. The table is platform-global, so every query runs outside a tenant context.
func (*Store) Create ¶
Create validates spec and registers a known node. It returns a wrapped ErrInvalid or ErrDuplicate for a bad request.
func (*Store) List ¶
List returns every node, retired ones included, oldest first. Eligible is computed by the database, with the same rule and clock as Eligible.
func (*Store) RecordProbe ¶
func (s *Store) RecordProbe(ctx context.Context, id int64, r Result) (prior PriorProbe, recorded bool, err error)
RecordProbe stores one probe result and returns the result it replaced, read under a row lock so concurrent probers each see the other's write. recorded is false when the node was retired since it was listed; such a row is left alone.