Skip to main content
Version: development

Circuit

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.

Component

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.

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.

Circuit Runtime

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.

Looping Signals

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.

Example Components

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 signal(s).
    • Arithmetic: These components perform basic arithmetic operations on signal(s).
      • Arithmetic Combinator: This component takes two input signals and performs a basic arithmetic operation to generate an output signal.
      • Max and Min: These components take multiple input or output signals and emit the maximum or minimum of those signals.
    • 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.
  • 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.
    • Load Scheduler: Takes load multiplier as a signal which determines the proportion of tokens to accept relative to past window.
    • Rate Limiter: Take fill rate and bucket capacity as signals which determines the API rate limit for each user or label value.