The Circuit describes a control system as an execution graph. The Circuit is defined as a graph of interconnected signal processing components. Signals flow between components through ports. As signals traverse the circuit, they get processed, stored within components or get acted upon (for example: load-throttling, rate-limit, auto-scale and so on). The Circuit is evaluated periodically to respond to changes in signal readings.
The building blocks of a circuit are components. Each component has input ports
in_ports) and output ports (
out_ports). The exact ports available are
determined by the type of component. Each port can be associated
with a signal. Components get chained to one another based on the name
of the signal.
The Signal represents a
float64 value that updates with every tick
of circuit execution. Every signal must have a name to uniquely identify it
within a circuit.
The output port on a component can emit a signal. No other port (on any component) in the circuit can emit a signal with the same name.
To receive a named signal at a component, it must be defined exactly once as an output at some component in the circuit. Once defined, a signal can be received at other components that have an input port with the same name.
The Circuit evaluates at a constant tick frequency. Each round of evaluation
is called a tick. The
evaluation_interval parameter in the policy
spec configures how often the circuit evaluates (ticks).
On every tick, each component in the circuit gets executed exactly once. Components get executed as they become ready. A component is ready if all its input signals are available.
During execution, the input signals are processed, and output signals are emitted by the component. Any looping signals are saved and consumed by the circuit in the next tick.
Circuit runtime provides highly predictable execution semantics. Any timed operations such as PromQL queries are synchronized to run on multiples of ticks. For PromQL queries, this ensures that all the queries that fire in the same tick return results together in a future tick.
Circuit execution graph can have loops. Looping signals enable expression of powerful paradigms such as integration using basic arithmetic components.
Despite loops, the execution is still performed on a directed acyclic graph. Before execution, loops are detected in the circuit. Each loop is unlinked at the component with the smallest index (in list of components). The unlinked component ports consume looping signals. A looping signal has the value of the unlinked signal from the previous tick.
The exhaustive list of the built-in components can be found in the policy reference.
Examples of built-in components include:
- Query: These components emit signals into the circuit from outside.
- PromQL: Converts results from a PromQL query into a signal.
- Signal Processors: These components transform input signal(s) into output
- Arithmetic: These components perform basic arithmetic operations on signal(s).
- Transformers: These components transform an input signal into an output
signal based on past state.
- EMA: Exponential moving average.
- Decider and Switcher: These components work in tandem to make the circuit adapt based on conditions.
- Controllers: Controllers are an essential part of a closed loop control
system. A controller takes as input a signal, a setpoint and emits the
suggested value of the control variable as output. The aim of the controller
is to make the signal achieve the setpoint.
- Gradient Controller: This controller acts on the ratio of setpoint and signal.
- Actuators: Actuators are components which act on signals and interface with external systems to perform actions such as throttling and queuing traffic, changing rate limits or auto-scaling.