|
2 | 2 |
|
3 | 3 | Status: **WIP** |
4 | 4 |
|
5 | | -## Nodes |
6 | | - |
7 | | -The Pipeline Engine consists of three main node types: |
8 | | - |
9 | | -- Receiver: A node that receives or scrapes telemetry data from a telemetry |
10 | | - source. |
11 | | -- Processor: A node that processes telemetry data. |
12 | | -- Exporter: A node that exports telemetry data to a telemetry sink. |
13 | | - |
14 | | -Each node can receive control messages from the Pipeline Engine to manage its |
15 | | -operation. |
16 | | - |
17 | | -```mermaid |
18 | | -graph LR |
19 | | - RecvControlIn[Control Channel] --> Receiver |
20 | | - Receiver((Receiver)) |
21 | | - Receiver --> RecvControlOut[Control Channel] |
22 | | - Receiver --> RecvDataOut[Data Channel] |
23 | | - class Receiver node |
24 | | - class RecvControlIn,RecvControlOut controlQueue |
25 | | - class RecvDataOut dataQueue |
26 | | - |
27 | | - ProcControlIn[Control Channel] --> Processor |
28 | | - ProcDataIn[Data Channel] --> Processor |
29 | | - Processor((Processor)) |
30 | | - Processor --> ProcControlOut[Control Channel] |
31 | | - Processor --> ProcDataOut[Data Channel] |
32 | | - class Processor node |
33 | | - class ProcControlIn,ProcControlOut controlQueue |
34 | | - class ProcDataIn,ProcDataOut dataQueue |
35 | | -
|
36 | | - ExControlIn[Control Channel] --> Exporter |
37 | | - ExDataIn[Data Channel] --> Exporter |
38 | | - Exporter((Exporter)) |
39 | | - Exporter --> ExControlOut[Control Channel] |
40 | | - class Exporter node |
41 | | - class ExControlIn,ExControlOut controlQueue |
42 | | - class ExDataIn,ExDataOut dataQueue |
43 | | -
|
44 | | - classDef controlQueue fill:#ffdddd,stroke:#cc0000,color:#000,stroke-width:0px,font-size:10px,padding:0px |
45 | | - classDef dataQueue fill:#ddffdd,stroke:#009900,color:#000,stroke-width:0px,font-size:10px,padding:0px |
46 | | - classDef node fill:#4a90e2,color:#ffffff,stroke-width:0px,font-size:12px |
47 | | -``` |
| 5 | +## Introduction |
| 6 | + |
| 7 | +The term pipeline is used here to represent an interconnection of nodes forming |
| 8 | +a Directed Acyclic Graph (DAG). The inputs of a pipeline are called receivers, |
| 9 | +the intermediate processing nodes are called processors, and the output nodes |
| 10 | +are referred to as exporters. |
| 11 | + |
| 12 | +Messages flowing through a pipeline are generically referred to as pdata (i.e. |
| 13 | +Pipeline Data). An OTLP message or an OTAP message are examples of pdata types. |
| 14 | +This pipeline framework is generic over pdata, which means: |
| 15 | + |
| 16 | +- It is possible to instantiate an OTLP pipeline, an OTAP pipeline, or even a |
| 17 | + pipeline designed to support another type of pdata. |
| 18 | +- It is not possible to support multiple pdata types within a single pipeline. |
| 19 | + However, a fourth type of component, called a connector, can be used to bridge |
| 20 | + two pipelines and enable interoperability between different pdata types. |
| 21 | + |
| 22 | +This terminology aligns well with the concepts introduced in the OTEL Go |
| 23 | +Collector. |
| 24 | + |
| 25 | +## Architecture |
| 26 | + |
| 27 | +A list of the design principles followed by this project can be found |
| 28 | +[here](../../docs/design-principles.md). More specifically, the pipeline engine |
| 29 | +implemented in this crate follows a share-nothing, thread-per-core approach. |
| 30 | +In particular, one instance of the pipeline engine is created per core. This |
| 31 | +engine: |
| 32 | + |
| 33 | +- Is based on a single-threaded async runtime |
| 34 | +- Avoids synchronization mechanisms whenever possible |
| 35 | +- Declares async traits as `?Send`, providing `!Send` implementations and |
| 36 | + futures whenever practical |
| 37 | +- Relies on listening sockets configured with the `SO_REUSEPORT` option, |
| 38 | + allowing the OS to handle connection load balancing |
| 39 | +- May share immutable data between cores, but ideally only within a single NUMA |
| 40 | + node |
| 41 | + |
| 42 | +These design principles focus on achieving high performance, predictability, and |
| 43 | +maintainability in an observability gateway implemented in Rust. Targeting a |
| 44 | +single-threaded async runtime reduces complexity, enhances cache locality, and |
| 45 | +lowers overhead. Favoring `!Send` futures and declaring async traits as `?Send` |
| 46 | +maximizes flexibility and allows performance gains by avoiding unnecessary |
| 47 | +synchronization. Minimizing synchronization primitives prevents contention and |
| 48 | +overhead, thus ensuring consistently low latency. Avoiding unbounded channels |
| 49 | +and data structures protects against unpredictable resource consumption, |
| 50 | +maintaining stable performance. Finally, limiting external dependencies reduces |
| 51 | +complexity, security risks, and maintenance effort, further streamlining the |
| 52 | +gateway’s operation and reliability. |
| 53 | + |
| 54 | +## Control Messages |
| 55 | + |
| 56 | +Each node in a pipeline can receive control messages, which must be handled with |
| 57 | +priority. These control messages are issued by a control entity (e.g. a pipeline |
| 58 | +engine) and are used to orchestrate the behavior of pipeline nodes. For example, |
| 59 | +configuring or reconfiguring nodes, coordinating acknowledgment mechanisms, |
| 60 | +stopping a pipeline, and more. |
| 61 | + |
| 62 | +## Testability |
| 63 | + |
| 64 | +All node types, as well as the pipeline engine itself, are designed for isolated |
| 65 | +testing. Practically, this means it's possible to test components like an OTLP |
| 66 | +Receiver independently, without needing to construct an entire pipeline. This |
| 67 | +approach facilitates rapid and precise identification of issues such as memory |
| 68 | +overconsumption, bottlenecks, or logical errors within individual nodes. |
| 69 | + |
| 70 | +The engine provides an extensive `testing` module containing utilities tailored |
| 71 | +to each pipeline component: |
| 72 | + |
| 73 | +- Defined test message types and control message counters for monitoring |
| 74 | + component behavior. |
| 75 | +- Dedicated test contexts and runtimes specifically built for receivers, |
| 76 | + processors, and exporters. |
| 77 | +- Single-threaded asynchronous runtime utilities aligned with the engine's |
| 78 | + non-Send design philosophy. |
| 79 | +- Convenient helper functions for establishing and managing communication |
| 80 | + channels between components. |
| 81 | + |
| 82 | +These utilities streamline the process of validating individual component |
| 83 | +behaviors, significantly reducing setup effort while enabling comprehensive |
| 84 | +testing. |
0 commit comments