A layer that isn't your app — collector agents and the gateway pool
A collector tier takes the jobs no application should do — stamping where the span ran, stripping anything sensitive, absorbing bursts, fanning out to more than one backend — and the whole-trace decision has to live in its central pool, because only a pool can be routed to.
The exporter ships batches to an endpoint we have not yet named. Something is on the other end of that connection, and it is doing more than writing rows.
Scene 11
A layer that isn't your app
- Watch
- Try it
- Predict
- Capture
What is actually on the other end of the connection the application ships its batches to? Watch one span travel there. It leaves payments knowing what it did but not where it ran; a program on the same machine stamps its pod, its node and its region; a central pool strips a card number out of it before anything leaves the cluster; and the same span is then written to two different places from one pipeline, without payments ever learning there are two. That layer — not your application, not your storage — is a collector, and none of those four jobs belonged in the application.
Highlighted lines are the ones running in the diagram right now.
def pipeline(batch):if memory_used() > soft_limit:return refuse(batch, retryable=True)batch = enrich(batch, resource_of(self.host))batch = redact(batch)batch = regroup_into_batches(batch)batch = tail_sample(batch) # regroups by trace idfor dest in self.destinations:dest.send(batch)
# one per machine; each app exports to localhostdef receives(self):return union(p.spans for p in processes_on(self.node))def can_decide(self, trace_id):held = self.buffer[trace_id]return held == every_span_of(trace_id)def resource_of(self, host):return lookup(host) # k8s.pod, host.name, cloud.region
# a scaled pool of interchangeable boxesdef route(span):i = consistent_hash(span.trace_id) % len(pool)return pool[i]def receives(self):return [s for s in arriving if route(s) is self]def can_decide(self, trace_id):held = self.buffer[trace_id]return held == every_span_of(trace_id)
Where this sits in Build a distributed tracing system (Jaeger / Zipkin style)
Scene 11 of 17, in the Pipeline act — Off the hot path, then a tier that isn't your app.. A collector tier takes the jobs no application should do: stamping where a span ran, stripping anything sensitive, absorbing bursts, fanning out. Tail decisions live in its pool, because only a pool can be routed to.
Up next. The gateway pool is the last place a trace's spans are together in memory. Then they are written down — under what key, and what has to happen before you can read a whole trace back?
All 17 scenes in Build a distributed tracing system (Jaeger / Zipkin style) · Every curriculum