|
32 | 32 |
|
33 | 33 | Now how does that look like in Redis? For each incoming span, we: |
34 | 34 |
|
35 | | -1. Store the span payload in a payload key: |
36 | | - "span-buf:s:{project_id:trace_id:span_id}:span_id". Each subsegment |
37 | | - gets its own key, distributed across Redis cluster nodes. |
| 35 | +1. Store the span payload in a payload key. Each subsegment gets its own key, |
| 36 | + distributed across Redis cluster nodes. |
| 37 | + a. When segment size enforcement is disabled, the key uses the parent_span_id to |
| 38 | + determine where to write span payloads to. |
| 39 | + Key: `span-buf:s:{project_id:trace_id:parent_span_id}:parent_span_id` |
| 40 | + b. When segment size enforcement is enabled, the key uses a unique salt per |
| 41 | + subsegment. This allows us to skip merging the subsegment into the parent segment |
| 42 | + and not lose any data, since the subsegment will become its own separate segment |
| 43 | + and be flushed out independently. |
| 44 | + Key: `span-buf:s:{project_id:trace_id:salt}:salt` |
38 | 45 | 2. The Lua script (add-buffer.lua) receives the span IDs and: |
39 | 46 | a. Follows redirects from parent_span_id (hashmap at |
40 | 47 | "span-buf:ssr:{project_id:trace_id}") to find the segment root. |
41 | 48 | b. Updates the redirect table so future spans can find the segment root. |
42 | 49 | c. Merges member-keys indexes and counters (ingested count, byte count) |
43 | 50 | from span IDs that were previously separate segment roots into the |
44 | 51 | current segment root. |
| 52 | + d. If segment size enforcement is enabled and the segment exceeds |
| 53 | + max_segment_bytes, detaches the subsegment into its own segment |
| 54 | + keyed by the salt. |
45 | 55 | 3. To a "global queue", we write the segment key, sorted by timeout. |
46 | 56 |
|
47 | 57 | Eventually, flushing cronjob looks at that global queue, and removes all timed |
|
58 | 68 | or using spillover topics, especially when their new partition count is lower |
59 | 69 | than the original topic. |
60 | 70 |
|
| 71 | +Segment size enforcement: |
| 72 | +
|
| 73 | +Segments can grow unboundedly as spans arrive. To prevent oversized segments from |
| 74 | +consuming excessive memory during flush, the buffer enforces a maximum byte limit |
| 75 | +per segment (controlled by `spans.buffer.max-segment-bytes` and gated behind |
| 76 | +`spans.buffer.enforce-segment-size`). |
| 77 | +
|
| 78 | +Each subsegment is assigned a unique salt (UUID). The Lua script tracks cumulative |
| 79 | +ingested bytes per segment via `span-buf:ibc` keys. If adding a subsegment would |
| 80 | +push the segment over the byte limit, the script detaches it into a new segment |
| 81 | +keyed by the salt instead of merging it into the parent. The detached segment is |
| 82 | +independently tracked and flushed. |
| 83 | +
|
| 84 | +During flush, segments that exceed `max-segment-bytes` are chunked into multiple |
| 85 | +Kafka messages to stay within downstream size limits. |
| 86 | +
|
61 | 87 | Glossary for types of keys: |
62 | 88 |
|
63 | 89 | * span-buf:s:{project_id:trace_id:span_id}:span_id -- payload keys containing span payloads, distributed across cluster nodes. |
|
0 commit comments