What Is ParentBased Sampling in OpenTelemetry?

What Is ParentBased Sampling in OpenTelemetry?

A plain-English guide to the rule that says “follow the decision your parent already made” — and why it keeps your traces whole, your costs under control, and your teams in agreement.

Picture a school trip with forty children, one teacher at the front, and a rule that only some children may carry a camera. Suppose each child decided alone, by tossing a coin at every stop, whether to take a picture. When the trip ended, you would have a pile of photos with big gaps in them. One photo of the bus, none of the entrance, three of the lunch, and nothing of the way home. You could never rebuild the story of the day.

Now imagine a calmer rule: the teacher decides at the start of each stop whether this stop will be photographed, and everyone simply follows the teacher. Either the whole stop is recorded, or none of it is. The album is smaller, but every page in it is complete. That calmer rule is the idea behind ParentBased sampling.

Modern software works like the school trip. One customer click can travel through a dozen services, each doing a small part of the work. Recording every single click is expensive, so teams record only some of them. The big question is how the many services agree on which clicks to record. This guide answers that question from the ground up. Every new word is explained the first time it appears, so no earlier knowledge of observability is needed.

1What Is ParentBased Sampling?

The short answer first, then the words that make it clear.

ParentBased sampling is a rule inside OpenTelemetry that decides whether a piece of a trace should be kept or thrown away by looking at what the parent of that piece decided. If the parent was kept, the child is kept. If the parent was thrown away, the child is thrown away. Only when there is no parent at all, because this is the very first step of a request, does the rule fall back on a different rule that you choose yourself.

Let us unpack the vocabulary. OpenTelemetry, often shortened to OTel, is a free, open source framework, hosted by the Cloud Native Computing Foundation, that gives developers one standard way to produce telemetry: the stream of traces, metrics, and logs that describes how software behaves. A trace follows one request through all the services it touches. A trace is built from spans, and a span is one timed step of the work, such as “look up the cart” or “charge the card.” Spans form a family tree: a span that starts another span is its parent, and the new one is its child. The very first span of a trace has no parent and is called the root span.

Sampling means keeping only a portion of the traces instead of all of them. A sampler is the small piece of code that makes the keep-or-drop choice every time a new span is about to start. ParentBased is not a sampler that makes a decision from scratch. It is a wrapper: a sampler that holds other samplers inside it and chooses which one of them to ask, depending on what kind of parent the new span has.

5
Situations ParentBased can tell apart: no parent, and four kinds of parent
1
Decision per trace, made at the root and followed by everyone else
0
Half-recorded traces when every service follows the rule
Everyday Analogy

Think of a relay race with a baton. The first runner chooses whether this lap will be timed. Every runner after that sees the baton and simply obeys the mark on it: “timed” or “not timed.” Nobody in the middle of the race gets to change the mark, so the stopwatch either records the whole lap or none of it. ParentBased sampling is the rule that says “obey the mark on the baton.”

What ParentBased answers

  • Who decides? The first service in the chain decides, and everyone downstream follows.
  • What if there is no earlier service? Then the “root” rule you configured decides, such as “keep ten percent.”
  • What if the parent lives in another service? The decision arrives inside a small header, and ParentBased reads it.
  • What if the parent lives in the same program? ParentBased reads the decision straight from memory.
  • Can I override a decision? Yes, each of the five situations has its own slot, so you can change the behaviour of any one of them.

What ParentBased is not

It is not a percentage by itself. People often say “we use ParentBased sampling at ten percent,” but the ten percent actually belongs to the rule used at the root. ParentBased is the part that makes everyone else agree with that root. It is also not tail sampling, which decides after a trace has finished and can therefore keep only the slow or broken ones. ParentBased decides at the beginning, before anyone knows how the request will turn out. We will compare the two carefully in a later chapter.

i
Remember This

ParentBased does not choose how many traces to keep. It chooses who gets to choose. The root decides, and everyone else follows. That single habit is what keeps traces complete.

A Closer Look at the Word “Parent”

The word “parent” may sound heavy, but it is simply the span that was running when the new span was created. When a web server receives a request and starts a span called “handle request,” and that code then calls the database, the database call becomes a child of “handle request.” Children can have children of their own, and in this way a whole request becomes a tree. The tree can cross the boundary between programs, because a service that calls another service can pass along the identity of its current span in a message header.

Because every span has at most one parent, the family tree always leads back to a single root. This is why one decision at the root can be enough for the entire tree: the answer can be handed down from parent to child, one step at a time, like a message passed along a line of people.

2Why Sampling Exists, and Why Parents Matter

Recording everything sounds ideal until the bill arrives.

Every span costs a little: a little time to create, a little memory to hold, a little network traffic to send, and a little money to store. For a small program, these costs are invisible. For a busy shop that handles millions of requests a day, with a dozen steps per request, they become very visible. Teams therefore decide to keep only a slice of their traces. The sliced portion is usually enough to find patterns, measure speed, and investigate problems, as long as the slice is chosen carefully.

Here is a simple calculation. Imagine one million requests per day. Each request creates a trace with twelve spans, and each span takes about five hundred bytes when stored. Multiply: 1,000,000 × 12 × 500 bytes = 6,000,000,000 bytes, which is six gigabytes every day, or about 180 gigabytes in a thirty-day month. If the team keeps ten percent of the traces, the daily amount drops to 600 megabytes, and the monthly amount to about 18 gigabytes. The saving is large, and the picture of how the system behaves is still sharp.

6 GB
Trace data per day when every request is kept (1,000,000 × 12 × 500 bytes)
600 MB
Per day when ten percent of traces are kept
10×
Reduction in storage, network, and processing cost

The problem that parents solve

Now comes the subtle part. Suppose a request passes through five services, and each service decides on its own, by rolling a ten-sided die, whether to keep its piece. The chance that one service keeps its piece is one in ten. The chance that all five keep their pieces is one in ten, five times in a row: 0.1 × 0.1 × 0.1 × 0.1 × 0.1 = 0.00001, which is a thousandth of one percent. Almost every stored trace would be missing most of its steps. The few spans you did keep would be orphans, pointing at parents that were never saved. Engineers looking at such a trace would see holes where the story should be.

With ParentBased sampling, the decision is made once, at the root, and every service follows it. The chance that all five services keep their pieces is the same as the chance that the root keeps its piece: one in ten. Each stored trace is complete, and each dropped trace is dropped completely. You pay the same ten percent cost, but every trace you pay for is whole.

Everyday Analogy

Think of a jigsaw puzzle sold in a shop. If the shop chose pieces at random from a hundred puzzles, you would own a thousand pieces and finish nothing. If the shop instead sold complete puzzles, ten out of every hundred, you would own ten pictures you could actually see. ParentBased sampling sells whole puzzles.

A short history

1

Early tracing systems

Pioneering distributed tracing systems inside large companies discovered that tracing every request was too costly, so they sampled. They also discovered that the sampling decision had to be carried along with the request.

2

The “sampled” flag

Open tracing tools adopted a simple flag attached to each request that says whether the trace is being recorded. Services read the flag and obey it.

3

A shared standard

The W3C, the web standards organization, published the Trace Context standard, which defines a header that carries the trace identity and the sampled flag in the same format for every tool and vendor.

4

OpenTelemetry samplers

OpenTelemetry defined a standard sampler interface and a small family of built-in samplers, including ParentBased, so that every language behaves the same way.

5

Today

ParentBased wrapped around a ratio sampler is a widely used starting point for production tracing. Newer work on consistent probability sampling extends the idea with extra information carried in the trace state.

i
The Core Insight

Sampling saves money only if the traces you keep are useful. A trace with missing steps costs money and tells a confusing story. Agreeing on one decision per trace is what turns saved money into useful insight.

What Could Go Wrong Without Any Sampling Rule

It is tempting to think that cost is the only reason to sample. In fact, volume can also harm the very system you are trying to observe. A program that creates, stores, and sends millions of spans competes with the business logic for processor time, memory, and network bandwidth. A tracing pipeline that cannot keep up may start refusing data, and then the spans you lose are chosen by accident, not by design. Deliberate sampling replaces accidental loss with a rule you understand and control.

There is also a human reason. Engineers who open a tracing tool and find hundreds of thousands of near-identical traces cannot see the unusual ones. A well-chosen sample reveals the typical shape of the system, and a small number of carefully chosen extra traces reveal the exceptions. Less data, chosen well, often leads to faster understanding than more data chosen carelessly.

3Core Concepts You Need First

Eight small ideas hold up the whole topic. Learn these and the rest will feel natural.

Idea 1

Sampler

A small piece of the OpenTelemetry library that is asked, each time a span is about to start, “should this one be recorded and kept?” It answers with a decision. Think of a doorman checking a guest list.

Idea 2

Sampling decision

The answer the sampler gives. There are three possible answers: drop it completely, record it but do not keep it, or record it and keep it. Most of the time only the first and last matter.

Idea 3

Sampled flag

A single yes-or-no mark stored with the span’s identity. When it is yes, the trace is kept. The flag is small enough to travel in a network header with every request.

Idea 4

Root span

The first span of a trace, with no parent. It is the only place where a fresh decision is needed, because everyone after it can follow.

Idea 5

Remote parent

A parent that lives in a different program, reached over the network. Its identity and sampled flag arrive in a request header. The word “remote” means “across a boundary,” not “far away.”

Idea 6

Local parent

A parent that lives in the same program, such as the web handler that calls the database layer in the same process. Its decision is read straight from memory.

Idea 7

Context propagation

The act of carrying the trace identity and the sampled flag from one service to the next, usually inside a request header. Without it, children cannot know what their parent decided.

Idea 8

Head sampling

Making the decision at the very beginning of a trace, before anyone knows how it will end. It is cheap and simple, and it is the family to which ParentBased belongs.

A closer look at the three possible decisions

The OpenTelemetry standard names three outcomes. Drop means the span is not even recorded: no timing, no attributes, nothing is stored, and the cost is almost zero. Record only means the span is recorded inside the program, so that local processors can look at it, but it is not marked as sampled and is normally not exported. Record and sample means the span is recorded, marked as sampled, and sent to the backend. For everyday use you can picture a simple switch with two positions: kept or dropped.

Everyday Analogy

Imagine a photographer at an event. “Drop” means the camera stays in the bag. “Record only” means the photographer takes a picture for personal use but never puts it in the album. “Record and sample” means the picture goes into the album that everyone will see.

Non-recording spans still carry identity

A dropped span is not invisible. Even when the decision is “do not record,” the program still creates a very light placeholder span that carries the trace identity and the unsampled flag. This placeholder costs almost nothing, but it allows the identity and the decision to continue to the next service. Without it, a downstream service would think it was the start of a brand-new trace and would make a brand-new decision, and the agreement would be lost.

i
A Helpful Mental Model

Every request carries a small sticky note that says either “keep this trace” or “skip this trace.” The first service writes the note. Every other service reads it, obeys it, and passes it along unchanged.

How These Ideas Fit Together

It helps to see the eight ideas as a single short story. A request arrives, and the library creates a span. Before the span starts, the sampler is asked for a decision. ParentBased looks at the parent, and if there is none, the root sampler answers. The decision becomes the sampled flag, which is stored in the span’s identity. When the program calls another program, context propagation writes that flag into a header. The next program reads the header, treats the caller as a remote parent, and follows the flag. At the end, only spans marked as sampled are exported.

Every idea in the chapter appears somewhere in that story, and each one has a single, small job. This is the real strength of the design: no part has to be clever, and the whole behaves intelligently because the small parts agree with each other.

4The Family of Built-in Samplers

ParentBased lives alongside three simple relatives. Meeting them first makes ParentBased easy to understand.

OpenTelemetry ships with a small set of samplers that every language implementation provides. Three of them make a decision on their own. The fourth, ParentBased, makes no decision of its own, but chooses among the others. This design is a classic example of building complicated behaviour from simple parts.

SamplerWhat it doesGood forWatch out for
AlwaysOnKeeps every traceDevelopment, testing, and low-traffic systemsCost grows with traffic, and a busy system can overwhelm storage
AlwaysOffKeeps no trace at allSwitching tracing off without removing the codeYou will see nothing, so use it deliberately
TraceIdRatioBasedKeeps a fixed fraction of traces, using the trace identity as a source of randomnessCutting volume to a steady percentageOn its own it ignores the parent, so different services may disagree if their ratios differ
ParentBasedFollows the parent’s decision, and uses a chosen sampler when there is no parentKeeping whole traces across many servicesTrusts whatever the parent says, which matters at the public edge

AlwaysOn and AlwaysOff

These are the simplest samplers, and they are useful as building blocks. AlwaysOn says “keep it” every time. AlwaysOff says “drop it” every time. On their own they are blunt tools, but they appear inside ParentBased as the defaults for four of its five slots, as we will see in the next chapter.

TraceIdRatioBased

This sampler keeps a fixed fraction, such as one in ten. It does not use a random number generator at the moment of the decision. Instead it looks at the trace identity, a long random number that every trace receives when it is born, and keeps the trace when that number falls inside a chosen portion of the possible range. Because the decision depends only on the trace identity, two services that use the same ratio will reach the same decision for the same trace. That is a nice property, but it only works when every service uses the same ratio, and it breaks down as soon as the ratios differ.

Everyday Analogy

Imagine a raffle where every ticket has a long number, and the rule is “tickets that end in 0 win.” Anyone, anywhere, can look at a ticket and say whether it wins, and they will always agree. But if one judge uses “ends in 0” and another uses “ends in 0 or 1,” they will disagree about some tickets. Ratio sampling is the raffle rule. ParentBased is the instruction “do not argue, accept the first judge’s result.”

ParentBased

The ParentBased sampler holds several other samplers inside it, like a switchboard. When a new span asks, “should I be kept?”, ParentBased first looks at the span’s parent, works out which of five situations applies, and hands the question to the matching inner sampler. The inner samplers can be any of the simple ones, and you can combine them freely.

i
Wrapper, Not Replacement

ParentBased does not replace ratio sampling. It wraps it. The ratio sampler is still the one that decides at the root. ParentBased only makes sure that the rest of the family follows that decision.

Choosing Among the Family

For a tiny project on one laptop, AlwaysOn is perfectly fine, because the volume is small and every trace is precious. For a switched-off environment, AlwaysOff silences tracing without removing any instrumentation. For a single service that starts every trace itself and has no neighbours, a plain ratio sampler is enough. As soon as two or more services share a request, ParentBased wrapped around a ratio sampler becomes the sensible default, because it keeps the whole chain in agreement while still limiting volume.

5The Five Slots of ParentBased

ParentBased is really five small decisions bundled into one. Each slot handles one situation.

When a new span is about to start, ParentBased asks two questions about its parent. First, does a parent exist at all? Second, if it does, was the parent kept, and did it come from another program or from the same program? The answers lead to exactly one of five slots.

SlotThe situationDefault inner samplerPlain meaning
RootThe span has no parentYou must choose one (for example, a ten percent ratio)This is the start of a trace, so a fresh decision is needed
Remote parent, sampledThe parent is in another program and was keptAlwaysOnThe caller kept the trace, so keep it too
Remote parent, not sampledThe parent is in another program and was droppedAlwaysOffThe caller dropped the trace, so drop it too
Local parent, sampledThe parent is in the same program and was keptAlwaysOnKeep the whole inside of the program together
Local parent, not sampledThe parent is in the same program and was droppedAlwaysOffDrop the whole inside of the program together

The defaults make perfect sense for most systems, which is why ParentBased works well even when you only configure the root slot. The four parent-aware slots simply copy the parent’s verdict, and only the root slot involves a real choice.

Why separate “remote” from “local”?

At first glance, the difference between a remote and a local parent seems unimportant, since in both cases we just follow. The reason for the split is trust. A local parent was created by your own code, inside your own program, so you can trust it completely. A remote parent arrived over the network, perhaps from a service owned by another team, or even from a stranger’s browser. You might want to treat it with care. For example, you might choose to accept a remote “kept” mark only some of the time, or to ignore remote marks entirely at your public entrance. Having separate slots lets you do this without disturbing the behaviour of the inside of your program.

Everyday Analogy

Think of a club with two kinds of guests. Members of the same club who walk in from the inside corridor are known to the staff, and their word is accepted. People arriving from the street bring a note saying “let me in, I am a friend of the manager.” Most nights the staff accept the note. At a very busy event, the staff might check the note more carefully. The remote slots are the checkpoint at the street door.

Reading the table like a story

Trace a request through three services: a gateway, a checkout service, and a payment service. The request arrives at the gateway with no earlier trace, so the gateway uses its root slot, perhaps a ten percent ratio, and decides to keep this one. The gateway calls the checkout service and passes the decision along in a header. Checkout sees a remote parent, sampled, applies AlwaysOn, and keeps its span. Inside checkout, a child span is created for a database call. Its parent is in the same program and was kept, so it uses local parent, sampled, which also keeps. Checkout then calls payment, which again sees remote parent, sampled. All the spans are kept, and the trace is whole.

Run the story again with the opposite result at the start. The gateway’s root slot decides to drop. Every other service sees remote parent, not sampled, applies AlwaysOff, and drops its spans too. The request still flows normally, and the customer notices nothing, but no span is stored. One decision at the start has silently shaped the behaviour of the entire chain.

i
Only One Slot Needs Your Attention

Most teams configure only the root slot, usually with a ratio. The other four slots keep their defaults of “follow the parent.” Change the others only when you have a specific reason, such as protecting a public entrance.

6How the Decision Travels Between Services

The sticky note on the request is a real, tiny piece of text. Seeing what it contains makes the whole mechanism feel concrete.

When one service calls another, the caller adds a few extra lines to the request, called headers. Headers are like the writing on the outside of an envelope: they are not the letter itself, but they tell the postal workers how to handle it. OpenTelemetry uses a standard header named traceparent, defined by the World Wide Web Consortium, usually called the W3C. Because it is a public standard, services written in different languages, and tools from different vendors, all understand it in the same way.

The traceparent header contains four parts separated by dashes. The first part is a version number. The second is the trace identity, a long random number that all spans of one trace share. The third is the identity of the parent span, the step that made this call. The fourth is a small set of flags, and the lowest flag is the one we care about: the sampled flag.

PartExampleMeaning
Version00The format of the header, currently always zero-zero
Trace identity0af7651916cd43dd8448eb211c80319cA 32-character code shared by every span in the trace
Parent span identityb7ad6b7169203331A 16-character code naming the span that made the call
Flags01 or 0001 means the trace is sampled and kept; 00 means it is not

When the receiving service starts its own span, its OpenTelemetry library reads this header, creates a record of the parent called a remote span context, and marks it as remote, because it came from outside. ParentBased then looks at the flags. If the last digit says one, it picks the remote parent, sampled slot. If it says zero, it picks the remote parent, not sampled slot.

sequenceDiagram
    participant U as Customer
    participant G as Gateway
    participant C as Checkout
    participant P as Payment
    U->>G: Clicks the buy button, no trace exists yet
    Note over G: Root slot checks the ratio and says keep
    G->>C: Request with traceparent flag 01
    Note over C: Remote parent sampled, so keep
    C->>P: Request with the same traceparent flag 01
    Note over P: Remote parent sampled, so keep
    P-->>C: Reply
    C-->>G: Reply
    G-->>U: Confirmation page
Figure 1: One decision at the gateway travels in a header to every later service

Explaining Figure 1

Read the diagram from top to bottom. A customer clicks the buy button, which reaches the gateway. The gateway sees no earlier trace, so it uses its root slot, which applies the ratio, and the answer is “keep.” The gateway then calls checkout and writes the sampled flag into the header. Checkout reads the flag, follows it, and passes the same flag to payment. Replies flow back along the same path. The decision was made once, written once, and read twice.

Everyday Analogy

Think of a parcel with a coloured sticker on its side. The first sorting office decides whether the parcel is “tracked” or “untracked” and sticks the matching colour on it. Every later office on the route glances at the sticker and treats the parcel the same way. Nobody has to phone the first office to ask what to do. The sticker travels with the parcel.

The companion header: tracestate

Next to traceparent, the standard defines a second header, tracestate, which lets vendors and tools attach extra notes. Newer sampling work uses it to carry a threshold value, so that services can agree not only on whether a trace is kept, but also on the probability with which it was chosen. This is useful for statistics, because it lets backends scale counts back up correctly. For everyday use you can think of tracestate as an optional margin note beside the main sticker.

What happens when no header arrives

If a request arrives without a traceparent header, the receiving service has no remote parent. It treats the new span as a root and uses its root slot. This happens for requests from browsers, mobile apps, scheduled jobs, and older services that do not yet pass headers. It is not an error, but it explains many surprises in practice: a service you expected to be in the middle of a trace appears as the start of a new one.

!
Headers Can Be Lost

Proxies, queues, and older libraries sometimes strip or fail to copy headers. When the sticky note is lost, the next service quietly starts a new trace and makes a new decision. The trace is split in two, and no error appears anywhere.

Why the Flag Is So Small

The designers of the standard kept the sampled flag to a single bit on purpose. Headers travel with every network call, and a busy system may make billions of calls a day. Anything large in a header would add up to a heavy burden on bandwidth, and anything complicated would be hard for every language and tool to implement the same way. A single bit is easy to write, easy to read, and impossible to misunderstand.

The trade-off is that one bit can only say “kept” or “not kept.” It cannot say why, or with what probability. That is exactly why the optional tracestate header exists: it lets a more advanced system add the extra detail without burdening the simple one.

7Inside the Decision: Step by Step

Every time a span is about to begin, the same short sequence of questions runs. It takes a tiny fraction of a millisecond.

The sampler is called at one precise moment: just before a span is created. At that moment the library hands the sampler a few facts: the context that holds the parent, the trace identity, the name of the span, the kind of span (for example, a server handling a request), some starting attributes, and any links to other traces. ParentBased uses only the first of these, the parent context, to choose the slot. The chosen inner sampler may use the others.

flowchart TD
    A["A new span is about to start"] --> B{"Does the span have a valid parent?"}
    B -- "No" --> R["Root slot: ask the root sampler"]
    B -- "Yes" --> C{"Did the parent come from another program?"}
    C -- "Yes, remote" --> D{"Was the remote parent sampled?"}
    C -- "No, local" --> E{"Was the local parent sampled?"}
    D -- "Yes" --> F["Remote sampled slot"]
    D -- "No" --> G["Remote not sampled slot"]
    E -- "Yes" --> H["Local sampled slot"]
    E -- "No" --> I["Local not sampled slot"]
    R --> Z["Decision: drop, record only, or record and sample"]
    F --> Z
    G --> Z
    H --> Z
    I --> Z
Figure 2: ParentBased picks one of five slots, and the chosen inner sampler gives the final decision

Explaining Figure 2

The diagram shows a funnel with two questions and five exits. The first question asks whether a parent exists. If not, the root slot handles the span. If a parent does exist, the second question asks whether it was remote or local, and the third asks whether it was sampled. Each of the four answers leads to a slot, and each slot passes the question to its inner sampler. All five paths join at the bottom, where the decision is handed back to the library.

What happens after the decision

1

The library reads the decision

If the decision is “drop,” the library creates a light placeholder with the trace identity and the unsampled flag. If it is “record and sample,” it creates a full, recording span.

2

The span does its work

A recording span collects timing, attributes, events, and status. A placeholder ignores such requests almost for free, so your code can add attributes without caring whether the span is kept.

3

Child spans ask their own questions

Each new child runs the same funnel, with this span as its parent. Local children use the local slots.

4

Outgoing calls carry the flag

When the program calls another service, the propagator writes the current span’s identity and sampled flag into the outgoing header.

5

Finished spans are exported

Only spans marked as sampled are handed to the exporter and sent onward to a Collector or a backend. The rest disappear.

Why the cost is so small

Dropping early is the whole point of head sampling. When a span is dropped at its birth, the library does not allocate space for attributes, does not measure the details of timing, and never queues the span for export. For a busy service, this means that ninety percent of the tracing work vanishes when the ratio is ten percent. The remaining ten percent pays the full price, and that is the portion you actually wanted.

Everyday Analogy

Think of a security guard at a concert door with a stamp. The guard glances at the back of each visitor’s hand. Stamp present, let them through to every room. No stamp, the visitor stays in the lobby. The glance takes a second, and every room inside only needs to look for the same stamp.

i
Spans You Skip Cost Almost Nothing

Because a dropped span is a featherweight placeholder, you can keep your instrumentation generous. The sampler, not the code author, decides what is stored.

8Configuring ParentBased Sampling

Most teams set it once, through two environment settings, and never think about it again.

An environment variable is a named setting that lives outside your program, in the place where the program is started, such as a deployment file. OpenTelemetry defines two standard variables for sampling, and every language implementation understands them. This is convenient, because the platform team can set sampling for all services in the same way, without changing any application code.

Setting valueMeaning in plain words
always_onKeep every trace, ignoring the parent
always_offKeep no trace, ignoring the parent
traceidratioKeep a fixed fraction of traces, ignoring the parent
parentbased_always_onFollow the parent; keep every trace that has no parent
parentbased_always_offFollow the parent; drop every trace that has no parent
parentbased_traceidratioFollow the parent; keep a fixed fraction of traces that have no parent

The first variable, which selects the sampler, is called OTEL_TRACES_SAMPLER. The second, called OTEL_TRACES_SAMPLER_ARG, supplies a number to the sampler, such as the ratio. A ratio of 0.1 means ten percent, 0.5 means half, and 1 means all. If you set neither variable, the standard default is parentbased_always_on: follow any parent, and keep every trace that has no parent. This default is a sensible starting point for a small system, but it keeps everything that starts in your service, which becomes costly as traffic grows.

Everyday Analogy

Think of a thermostat with two dials. One dial chooses the mode, such as “heat” or “cool.” The other dial sets the temperature. The sampler variable is the mode dial, and the argument variable is the temperature dial. For everyday use, the mode is “parentbased with a ratio,” and the temperature is the ratio you can afford.

Choosing the ratio

Pick the ratio from three numbers: how many requests you receive, how large an average trace is, and how much storage and money you are willing to spend. Returning to our example of one million daily requests and 6,000 bytes per trace, a ratio of 0.1 yields about 600 megabytes per day. If your budget allows only 60 megabytes, the ratio must be 0.01, which keeps ten thousand traces per day. Ten thousand traces are usually still plenty to see the shape of ordinary behaviour, though too few to catch rare problems.

Setting the other four slots

The environment variables expose only the common combinations. If you need to give the remote or local slots a different inner sampler, you configure the ParentBased sampler directly in your language’s OpenTelemetry settings, where each of the five slots is a separate option. The slot names are the same everywhere, so what you learn in one language carries over to the others.

Where to set it

  • In the deployment template. One shared template means every new service begins with the same sampling choice.
  • In the platform’s defaults. The platform team can provide the values, and individual teams override them only with a reason.
  • Not scattered in code. Hard-coded ratios are forgotten, and are hard to change during an incident.
!
Use the Same Family Everywhere

If one service in a chain runs a plain ratio sampler while its neighbours run ParentBased, they will disagree about some traces, and broken traces will appear. Choose ParentBased as the standard and make exceptions rare and documented.

A Sample Planning Exercise

Suppose a team receives 4,000,000 requests per day, with 10 spans per trace and 500 bytes per span. Keeping everything gives 4,000,000 × 10 × 500 = 20,000,000,000 bytes, or 20 gigabytes per day. Their storage budget allows about 1 gigabyte per day. The ratio they need is 1 divided by 20, which is 0.05, or five percent. They set the sampler to parentbased_traceidratio and the argument to 0.05, and the plan is complete. They then verify after one day that about 200,000 traces were kept, because five percent of 4,000,000 is 200,000.

The exercise shows how simple the arithmetic is. Three numbers, one division, and two settings. The harder work lies in checking that the plan matches reality, which is the purpose of the monitoring chapter.

9Head Sampling, Tail Sampling, and the Collector

ParentBased decides early. Another family decides late. Many teams use both, and they must understand the difference.

Head sampling decides at the beginning of a trace, before anyone knows what will happen. It is cheap and simple, and ParentBased is its most popular form. Its weakness is that it is blind. It cannot know that a particular request will turn out slow or broken. If it drops that trace, the interesting evidence is gone.

Tail sampling decides at the end, after all the spans of a trace have arrived. It can then keep every trace that contained an error, every trace slower than a threshold, and only a small portion of the ordinary ones. It is powerful, but it costs more, because something must collect and hold every span of every trace for a short time while waiting for the trace to finish. That something is usually the OpenTelemetry Collector, a standalone program that receives telemetry, processes it, and forwards it.

FeatureHead sampling (ParentBased)Tail sampling
When it decidesAt the start of the traceAfter the trace is complete
Where it runsInside each applicationIn a Collector
Can it keep all errors?No, it cannot know yetYes
Cost to the applicationVery low, dropped spans cost almost nothingApplications still record and send everything to the Collector
Memory and compute costNegligibleConsiderable, because whole traces are buffered
ComplexityLow, one settingHigher, needs routing so all spans of a trace reach the same Collector
Typical useSteady, affordable baselineKeeping the rare, valuable traces
Everyday Analogy

Head sampling is like deciding at the start of a football match which minutes you will film. You cannot know when the goals will happen. Tail sampling is like filming the whole match and then, afterwards, cutting out only the goals and the fouls. The second gives better highlights, but it requires a large camera, a big hard drive, and an editor.

flowchart LR
    A["Application with ParentBased head sampling"] -->|"Only kept traces are sent"| B["Collector: load balancing by trace identity"]
    B --> C["Collector: tail sampling policies"]
    C -->|"Errors and slow traces"| D["Backend storage"]
    C -->|"Small portion of ordinary traces"| D
    C -.->|"The rest are dropped"| E["Discarded"]
Figure 3: A two-stage design, with cheap head sampling in the application and selective tail sampling in the Collector

Explaining Figure 3

The diagram shows a two-stage filter. In the first stage, applications use ParentBased with a ratio to cut the volume sharply. The traces that survive travel to a Collector. A load-balancing step makes sure that all spans with the same trace identity reach the same Collector instance, because a tail sampler can only judge a trace if it sees all of it. In the second stage, rules inspect each complete trace and keep the errors and the slow ones, plus a small share of the ordinary ones. Everything else is discarded.

!
Head Sampling Limits Tail Sampling

A tail sampler can only choose among the traces that reach it. If head sampling already dropped a trace at ten percent, the Collector can never bring it back. Teams that want to catch every error usually keep a high head ratio, or no head sampling, and rely on the Collector to do the thinning.

Choosing between them

Use ParentBased head sampling alone when traffic is moderate, errors are common enough to appear in a sample, and simplicity matters. Add a tail sampler when you must reliably capture rare failures and unusual slowness, and you can afford to run the extra Collector layer. Whichever you choose, keep the rule simple enough that a new engineer can explain it in a minute.

Why All Spans of a Trace Must Meet

A tail sampler works like a judge who must hear the whole case before giving a verdict. If half of the spans of a trace arrive at one Collector and the other half at another, neither Collector can see the whole trace, and each may reach a different verdict. That would recreate the broken-trace problem at a new layer. To avoid this, a load-balancing step sends all spans with the same trace identity to the same Collector instance, usually by using the trace identity as a routing key.

The tail sampler also needs patience. It waits for a short time after the first span arrives, to let the rest of the spans catch up, and only then applies its rules. Setting this wait too short produces decisions on unfinished traces, and setting it too long increases memory use. Choose a wait that slightly exceeds the longest ordinary request in your system.

10Sampling and Your Numbers: The Metrics Trap

Sampled traces are a gift for debugging and a trap for counting.

Many tools can compute handy numbers from spans: how many requests arrived, how many failed, how long they took. These are often called RED metrics, for rate, errors, and duration. The trap is simple. If the numbers are computed from the spans that survived sampling, they describe only the survivors. With a ten percent ratio, a service that really handled 1,000,000 requests will appear to have handled about 100,000. Dashboards that show request rate will be ten times too low, and alerts that depend on them will be wrong.

1,000,000
Requests that really happened in a day
100,000
Traces kept at a ten percent ratio
10%
Share of the true rate a naive dashboard would show

Three safe ways to count

  • Use metrics that do not depend on tracing. Many OpenTelemetry instrumentation libraries record request counts and durations as real metrics, which are updated for every request, whether or not the trace is kept.
  • Compute span metrics before sampling. If you derive metrics from spans in a Collector, place that step before any sampler, so it sees every span. This requires that applications send all spans to the Collector, which costs more network and compute.
  • Scale the counts back up. If every trace was kept with the same known probability, a backend can multiply counts by the inverse of that probability. This works only when the probability is known and uniform, which is why tracestate-based probability information is becoming popular.
Everyday Analogy

Imagine you want to know how many people walk through a shopping centre, but you only count those who stop at a photo booth. The booth gives you lovely pictures, but if you report its count as the footfall, the real number will be far larger. Use a door counter for footfall and use the photos for stories.

Why changing ratios makes it worse

If different services use different ratios, or if the ratio changes during the day, scaling counts back up becomes unreliable. A dashboard that adjusts for ten percent will be wrong when the ratio is changed to five percent during an incident. This is another reason to keep counting separate from sampling: counters should be exact, and traces should be a representative sample.

i
A Simple Rule

Use metrics to answer “how many?” and traces to answer “why?” When each tool does its own job, sampling can be as aggressive as your budget demands without damaging your charts.

A Worked Illustration of the Trap

Imagine an alert that fires when failed requests exceed 200 per hour. In reality, the service sees 600 failures in one bad hour, which is well above the line. But the alert is computed from sampled spans at five percent, so it sees only about 30 failures, and the alert stays silent. The incident goes unnoticed for hours. The fix is not to raise the ratio, but to move the alert to a real error counter that sees every request.

The reverse mistake also happens. Teams sometimes try to compensate by multiplying every sampled count by twenty. That works while the ratio is exactly five percent and every service agrees, but it quietly breaks the day someone changes the ratio. Exact counters have no such weakness.

11Security: Who Is Allowed to Write the Sticky Note?

ParentBased trusts the parent. At your public front door, that trust deserves a second thought.

Inside your own network, all services are yours, and believing the parent is perfectly reasonable. At the public edge, where requests come from browsers, mobile apps, and partner systems, the picture changes. Anyone who can send a request can also send a traceparent header, and anyone can write a one into the sampled flag. A service that blindly follows a remote parent will then record and export every such request.

Consider the numbers. Normally your gateway keeps ten percent of 1,000,000 daily requests, which is 100,000 traces. Now suppose an outsider, by mistake or by design, sends 500,000 extra requests with the sampled flag set to one. The gateway obeys the flag, so it keeps all of them: 100,000 + 500,000 = 600,000 traces, six times the planned volume. Your storage bill, your network use, and your Collector load all rise by the same factor, and the cause is invisible unless you look for it.

flowchart TD
    X["Request from the public internet"] --> Q{"Does it carry a sampled flag?"}
    Q -- "Trusted internal caller" --> T["Follow the parent as usual"]
    Q -- "Untrusted outside caller" --> I["Ignore the incoming flag and treat the span as a new root"]
    I --> RS["Root sampler applies your own ratio"]
    T --> K["Normal ParentBased behaviour"]
    RS --> K
Figure 4: At the public edge, outside flags are ignored and your own ratio decides

Explaining Figure 4

The diagram shows a checkpoint at the public door. A request from a trusted internal caller is handled as usual, and its flag is followed. A request from an untrusted outsider has its flag ignored, and the gateway starts a fresh trace under its own root rule. The result is that only your own ratio decides how many traces are kept, and outsiders cannot raise or lower it.

ThreatWhat an outsider could doProtection
Forced samplingMark every request as sampled to inflate your costsAt the edge, give both remote slots your own ratio sampler, or ignore incoming trace headers
EvasionMark requests as not sampled so unusual activity leaves no traceApply the same edge rule, and keep access logs and security logs independent of tracing
Trace identity guessingReuse chosen trace identities to confuse investigatorsGenerate new identities at the edge for outside requests
Header stuffingSend very large or malformed headers to waste resourcesLimit header sizes at the gateway and reject malformed values
Information leakRead trace identities from replies and learn about internal structureDo not echo internal trace headers back to external clients unless needed
Everyday Analogy

Think of a restaurant where a waiter will serve any table that waves a “VIP” card. Inside the staff room, that is fine. At the street door, a stranger could print a thousand VIP cards and fill the dining room. A sensible manager checks the cards at the door, and treats strangers by the normal rule.

A balanced approach

You do not need to ignore outside flags everywhere. A reasonable pattern is to ignore them at the single public entrance, where every request is unknown, and follow them everywhere inside, where every caller is yours. Some teams also allow a trusted mobile app or a partner to continue its own trace by recognising an agreed marker, and apply the ratio to the rest.

!
Tracing Is Not a Security Log

Because sampled traces are incomplete by design, never rely on them as the only record of who did what. Keep separate, complete audit logs for anything that matters for security or compliance.

Protecting Without Breaking Collaboration

Ignoring outside flags has a small cost: a customer or partner who wants to follow one request across both companies cannot have their trace joined to yours automatically. If this matters, agree on a controlled arrangement, such as accepting flags from a short list of known partners, or recording the partner’s trace identity as an ordinary attribute so that engineers can search for it later, even though the two traces stay separate.

Another safe option is a rate limit. The gateway can accept outside flags, but only up to a fixed number of forced traces per minute. Anything above the limit is handled by your own ratio. This keeps cooperation possible and still caps the worst-case cost, which makes it a favourite compromise in larger organizations.

12Mixed Fleets and Broken Traces

Real companies are never uniform. Old services, new services, and borrowed services all meet in one request.

In theory, every service uses ParentBased, every service passes headers, and every trace is whole. In practice, a company grows over years, and its services differ in age, language, and care. A few common mismatches cause most of the broken traces that engineers complain about.

flowchart LR
    A["Service A: ParentBased, keeps the trace"] -->|"Header passed"| B["Service B: ParentBased, keeps the span"]
    B -->|"Header lost by an old proxy"| C["Service C: sees no parent and starts a new root"]
    C --> D["Service D: follows service C"]
    A -.->|"Result"| R["Two partial traces instead of one whole trace"]
Figure 5: A lost header splits one request into two unrelated traces

Explaining Figure 5

Services A and B behave perfectly, and the first two spans of the request are kept together. Between B and C, an old proxy drops the header. Service C sees a request without a parent and starts a brand-new trace with its own decision. Service D faithfully follows C. The backend now holds two separate, partial traces for a single customer click, and nothing tells the engineer they belong together. If C’s fresh decision is “drop,” the second half of the story is missing entirely.

SymptomLikely causeWhat to do
Traces that stop halfwayA hop that does not pass the header, such as an old proxy or message queueUpgrade or configure the hop to copy trace headers
Spans whose parent never appearsOne service uses AlwaysOn and keeps spans of traces others droppedSwitch that service to ParentBased
Far more traces than expectedSeveral services start their own roots, or an outsider forces samplingCheck which services use a root slot, and protect the edge
Far fewer traces than expectedA service uses AlwaysOff or a tiny ratio at its root, and everyone followsReview the root slot of the first service in each chain
Different ratios in different placesTeams chose ratios independentlyAgree on a standard and set it in the shared template
Asynchronous work with no traceMessages do not carry headers through queuesAdd trace context to message attributes, or use span links
Everyday Analogy

Imagine a chain of postal workers, each of whom reads the coloured sticker and passes the parcel on. If one worker peels off the sticker, the next worker sees a parcel with no instructions and decides afresh. The parcel still arrives, but the tracking record is cut in two.

Queues and background work

Messages sent through queues need special care. A message placed on a queue by one service may be picked up by another service seconds or hours later. Unless the trace context is stored with the message, the receiving service starts a new root. A common solution is to put the context into the message’s attributes. When that is not possible, or when one message triggers many consumers, a span link can connect the new trace to the old one without making it a child.

i
Test the Whole Chain

After adding a new service, send a single test request through the full path with the sampled flag set, and confirm that every service produced a span in one trace. A two-minute test finds most header problems before customers do.

Prevention Is Cheaper Than Detection

Most mixed-fleet problems appear during growth, when a new service or a new library version enters the system. A short checklist for every new service prevents the majority of them. Confirm that the service uses the standard propagation format. Confirm that it uses ParentBased. Confirm that every proxy, load balancer, and queue between it and its neighbours copies the trace headers. Confirm that its first test request produces one whole trace.

When these four checks are part of the standard launch process, the broken-trace complaints that engineers once raised every month shrink to a few each year, and each one is usually traceable to a single missing header on a single hop.

13Monitoring Your Sampling

Sampling fails quietly. Nothing crashes when a ratio is wrong; the pictures simply become less trustworthy.

If a service starts dropping everything, the application keeps working and customers notice nothing. The only symptom is that traces stop appearing, and someone, some day, cannot find what they need during an incident. Because the failure is silent, you must look for it on purpose. A short routine of checks can keep sampling healthy.

What to watchWhat it tells youTypical action
Traces kept per day, compared with requests per dayThe real kept ratio, which should match what you configuredIf it drifts, look for services that start their own roots or an outsider forcing sampling
Share of spans whose parent is missingHow many traces are brokenFind the hop that loses headers, or the service that ignores the parent
Spans exported per serviceWhether any service exports far more or far less than its peersCheck that service’s sampler settings
Export queue size and dropped-span counters in the SDKWhether kept spans are being lost on the way to the CollectorIncrease capacity, or reduce the ratio
Collector refused or dropped span countersWhether the pipeline is overloadedScale the Collector, or add head sampling upstream
Trace size, in spans per traceWhether instrumentation has become too chattyRemove low-value spans
Storage cost per dayWhether the budget is on trackAdjust the ratio

The most useful number: trace completeness

If you track only one number, make it trace completeness: the share of stored traces that have no missing parents. In a healthy ParentBased setup, completeness should be very high, close to one hundred percent. A sudden fall means that somewhere a header is being lost or a service has changed its sampler. Because completeness is a ratio, it stays meaningful even when the sampling rate changes.

Everyday Analogy

Think of a library that lends out books in sets, such as a whole encyclopedia. A good librarian does not only count how many books left the shelf. She also checks that every set that left is complete, because a set with missing volumes is frustrating for the reader. Trace completeness is the librarian’s check for sets.

Check after every change

1

Deploy a new service or change a setting

Every new service and every changed ratio is a chance for agreement to break.

2

Send a test request that is flagged as sampled

Use a request that you can recognise, so you can find its trace easily.

3

Look for one whole trace

Confirm that every service in the path appears in the same trace.

4

Send a test request flagged as not sampled

Confirm that nothing at all appears. A leak here means some service is ignoring its parent.

5

Compare the volume with the plan

After a day of traffic, check that the number of kept traces matches what the ratio predicts.

i
Make the Check Automatic

Put the two test requests into your deployment pipeline. A thirty-second automatic test can prevent months of confusing, half-empty traces.

14Benefits, Drawbacks, and Trade-offs

Every design has a price. An honest view helps you decide when ParentBased is the right tool.

Advantages

  • Complete traces. Either the whole trace is kept or none of it is, so stored traces are always useful.
  • Cheap. Dropped spans cost almost nothing, because the decision happens before the work.
  • Simple. One setting, usually in the deployment template, covers every service.
  • Predictable cost. The volume is a known fraction of the traffic.
  • Standard. Every language implementation behaves the same way, so teams share one mental model.
  • Flexible. Five slots allow special handling for trust boundaries.

Disadvantages

  • Blind to outcomes. It cannot know that a request will fail or be slow, so rare problems can be dropped.
  • Trusts the parent. Unprotected, an outsider can force or suppress sampling.
  • Depends on propagation. A lost header splits a trace and creates a new decision.
  • Distorts counts. Metrics derived from kept spans undercount reality.
  • Hard to change mid-trace. Once the decision is made at the root, later services cannot upgrade it.
  • Uniform treatment. Important and unimportant requests are sampled at the same rate unless you add rules.

Common trade-offs in plain language

ChoiceWhat you gainWhat it costs
High ratio, such as fifty percentMore detail and a better chance to catch rare eventsHigher storage and network cost
Low ratio, such as one percentVery low costRare errors almost never appear
Follow outside flags at the edgeCustomers and partners can continue their own tracesExposure to forced or suppressed sampling
Ignore outside flags at the edgeFull control of volumeTraces from outside callers start fresh and cannot be joined to the caller’s side
Head sampling onlyLow cost and low complexityBlind to outcomes
Head plus tail samplingKeeps the valuable tracesAn extra Collector tier, more memory, and more operations work
Same ratio everywhereSimple to reason aboutCannot treat critical paths differently without extra rules
i
A Fair Rule of Thumb

Begin with ParentBased and a modest ratio at the first service of every chain. Add tail sampling only when you can name a specific class of trace, such as errors, that head sampling is missing and that you cannot afford to lose.

When ParentBased Is Not Enough

ParentBased is an excellent baseline, but some goals need more. If your business must investigate every failed payment, a fixed ratio will never be enough, because failures are rare and the ratio treats them like everything else. If you operate a large batch system where a few giant traces matter more than millions of small ones, a uniform ratio wastes budget on the unimportant. In both cases, keep ParentBased as the foundation and add rules on top, such as tail sampling for errors or special handling for important endpoints.

15Good Habits and Design Patterns

Patterns are tried-and-true recipes. Habits are the small routines that keep sampling clean, cheap, and honest.

Pattern

One standard, set in the template

Choose ParentBased with a ratio as the company default and put it into the shared deployment template, so every new service starts correct.

Pattern

Root at the edge

Let the public gateway be the only place that makes fresh decisions for outside traffic, using your own ratio, and ignore incoming flags there.

Pattern

Follow inside

Behind the gateway, every service follows its parent with the default slots. No inside service needs its own ratio.

Pattern

Counters that never sample

Count requests, errors, and durations with real metrics that see every request, so dashboards stay exact however aggressively you sample traces.

Pattern

Cheap head, smart tail

Combine a generous head ratio with a Collector tail sampler that keeps every error and slow request, plus a small share of ordinary ones.

Pattern

Debug override

Provide a safe, audited way to force sampling for a single test request, so engineers can trace one problem without raising the global ratio.

Habits that pay off

  • Use ParentBased everywhere. Make exceptions rare, written down, and reviewed.
  • Configure from outside the code. Environment variables keep sampling visible and easy to change.
  • Pass headers through every hop. Proxies, queues, and background jobs must copy the trace context.
  • Protect the front door. Decide deliberately how to treat flags that arrive from outsiders.
  • Keep counting separate. Never base request counts on sampled spans.
  • Measure completeness. Treat a fall in complete traces as an alert, not a curiosity.
  • Review the ratio each quarter. Traffic and budgets change, and the ratio should follow.
  • Document the standard. A one-page guide with a diagram makes the right way the easy way.

A gentle path to adopting ParentBased

1

Audit what exists

List the sampler used by each service, and find services that use a plain ratio or AlwaysOn.

2

Verify header passing

Make sure every hop, including proxies and queues, copies the trace context.

3

Switch services to ParentBased

Change inside services first, then the gateway, and watch completeness after each step.

4

Set the root ratio at the edge

Choose the ratio from your traffic, trace size, and budget, and protect the edge from outside flags.

5

Add tail sampling if needed

Only after the basics are stable, add a Collector tier for errors and slow traces.

i
Make the Easy Path the Right Path

If the correct sampling setup is one line in a shared template, everyone will use it. If it needs a long document and three approvals, people will quietly invent their own.

Keeping the Standard Alive

Writing a standard is the easy part. Keeping it alive through years of change is harder. Teams reorganize, languages are added, and libraries are upgraded, and every change is a chance for someone to invent a private sampling rule. Give the standard an owner, publish it where engineers actually look, and include a worked example. When the standard changes, update the shared template first, so that new releases pick up the change in the ordinary course of work instead of through a long manual campaign.

16Common Mistakes, Anti-patterns, and Myths

Most sampling problems come from the same few mistakes. Knowing them in advance saves a lot of confusion.

AP-1The Independent Coin FlipperVery common

What happens: Each service uses a plain ratio sampler and ignores its parent, so each makes its own choice.

Why it hurts: With five services at ten percent, the chance of a complete trace can fall toward one in a hundred thousand, and stored traces are full of holes.

Better approach: Wrap the ratio sampler in ParentBased, so only the first service rolls the die.

AP-2The Trusting Front DoorCostly

What happens: The public gateway follows whatever sampled flag arrives from the internet.

Why it hurts: Outsiders can multiply your tracing bill, or hide their activity by marking requests as unsampled.

Better approach: At the edge, give the remote slots your own ratio sampler, or ignore incoming trace headers.

AP-3Counting With SamplesMisleading

What happens: Request rates and error rates are computed from the spans that survived sampling.

Why it hurts: Dashboards show only a fraction of reality, and alerts fire or stay silent for the wrong reasons.

Better approach: Use real metrics that see every request, or compute span metrics before any sampler.

AP-4The Lost Sticky NoteQuietly harmful

What happens: A proxy, queue, or old library fails to pass the trace header.

Why it hurts: The next service starts a new trace, and one request becomes two unrelated, incomplete traces.

Better approach: Test the full path after every change, and track the share of spans with missing parents.

Myths worth clearing up

MythReality
“ParentBased is a percentage.”ParentBased is a wrapper. The percentage belongs to the root sampler inside it.
“Dropped spans still cost a lot.”A dropped span is a featherweight placeholder and costs almost nothing.
“ParentBased keeps all the errors.”It decides before the outcome is known, so it keeps errors at the same rate as everything else.
“A child can upgrade a dropped trace.”With default slots, a child follows its parent. Once the root drops, the rest drops.
“Ratio sampling and ParentBased are alternatives.”They are partners. ParentBased usually wraps a ratio sampler.
“Tail sampling replaces head sampling.”It can only choose among traces that reach it, and it needs more resources. Many teams use both.
“Sampling affects the customer.”Sampling affects only telemetry. Requests are processed identically whether or not a trace is kept.

17A Worked Example: The Bazaar Shop

Let us follow an imaginary online shop from a costly, confusing setup to a calm, affordable one, using simple numbers.

The shop, called Bazaar, receives 2,000,000 requests per day. Each request creates a trace with 15 spans of about 400 bytes. Four services take part: a gateway, a checkout service, a payment service, and an inventory service. At first, every service uses AlwaysOn, so everything is kept.

Step 1: count the cost of keeping everything

Multiply the numbers: 2,000,000 requests × 15 spans × 400 bytes = 12,000,000,000 bytes, which is 12 gigabytes per day, or 360 gigabytes in a thirty-day month. The team decides that this is too much and sets a target of about five percent.

12 GB
Per day with everything kept (2,000,000 × 15 × 400 bytes)
600 MB
Per day at a five percent ratio (100,000 traces)
20×
Reduction in trace volume

Step 2: a first mistake

One team changes only its own service to a five percent ratio and ignores the parent. The gateway still keeps everything. Now the gateway sends the flag 01 to checkout, but checkout drops 95 percent of the time anyway. The result is a flood of orphan fragments: gateway spans without matching checkout spans. The team sees storage fall a little, and traces fill with holes.

Step 3: the fix

ServiceRoot slotRemote slotsLocal slotsWhy
GatewayFive percent ratioFive percent ratio, so outside flags are ignoredFollow the parentThe only place that makes fresh decisions, and a protected front door
CheckoutFive percent ratio, rarely usedFollow the parentFollow the parentTrusts the gateway
PaymentFive percent ratio, rarely usedFollow the parentFollow the parentTrusts its caller
InventoryFive percent ratio, rarely usedFollow the parentFollow the parentTrusts its caller

The same setting, ParentBased with a five percent root ratio, is placed in the shared template for all four. Only the gateway’s remote slots differ, because only the gateway faces the public. After the change, 100,000 traces per day are kept, each one complete from the gateway to inventory, and the volume is about 600 megabytes per day.

Step 4: the forced-sampling surprise

A partner’s old script sends 1,000,000 extra requests per day, each with the sampled flag set. Had the gateway followed outside flags, the day would hold 100,000 + 1,000,000 = 1,100,000 traces. At 15 spans of 400 bytes, that is 6,600,000,000 bytes, or 6.6 gigabytes, eleven times the plan. Because the gateway ignores outside flags and applies its own five percent, the partner’s traffic adds only 5 percent of 1,000,000, which is 50,000 traces. The bill grows a little, not eleven times.

Step 5: the errors problem

About 0.5 percent of requests fail, which is 10,000 failures per day. With five percent head sampling, only about 500 of those failures are kept. That is enough to see that something is wrong, but not enough to investigate a rare cause. The team adds a Collector with a tail sampler that keeps every trace containing an error. To make it work, the head ratio is raised to twenty-five percent, which is 500,000 traces per day, and the tail sampler thins the ordinary ones back down. After tail sampling, all 10,000 failures are kept plus a small share of ordinary traces, and the final stored volume stays near the original budget.

Everyday Analogy

It is like a newspaper photographer at a festival. At first she photographs everything and runs out of film. Then she shoots every tenth moment and misses the best ones. The final plan is to shoot generously, and then let an editor choose the best pictures and a small number of typical ones. The editor is the tail sampler.

i
What This Example Teaches

The saving came from one clear rule, not from clever tuning: the gateway decides, everyone follows, and outsiders do not get a vote. Everything else was small, careful adjustment.

What the Bazaar Team Learned Afterwards

Looking back, the team noticed that their most valuable decisions were also their simplest. They used one standard setting for every service. They gave the gateway a special rule because it faced the public. They kept counting separate from tracing, so their dashboards stayed exact. And they added tail sampling only after they could name a concrete gap: the shortage of error traces.

They also learned to treat the first day after any change as a checking day. They compared the number of kept traces with the plan, looked at trace completeness, and sent one flagged test request and one unflagged test request through the full path. Over several months, these small habits turned what had once been a source of surprise bills and half-empty traces into a quiet, predictable part of the platform.

18Real-World Scenarios

Three short stories show how the rule behaves in everyday situations.

Scenario 1: The half-empty trace

An engineer opens a slow trace and sees the gateway and checkout spans, but no payment spans. The payment service uses a plain one-in-ten ratio sampler that ignores the parent, so it discarded the span even though the gateway had kept the trace. Switching payment to ParentBased fixes it. Within a day, traces show all three services. The lesson: one service that ignores the parent can spoil the work of all the others.

Scenario 2: The sudden bill

On a Tuesday, the tracing bill triples. The platform team compares kept traces with requests and finds that the kept ratio has risen from five percent to nearly fifteen percent, all from one external address. A customer’s integration is sending the sampled flag on every call. The team changes the gateway’s remote slots to use its own ratio. The next morning the kept ratio is back to five percent, and no customer notices anything.

Scenario 3: The vanished queue worker

Orders are placed by a web service, and a separate worker prints invoices later through a queue. Engineers cannot find invoice spans in any order trace. The queue messages do not carry trace context, so each worker starts a new root and mostly drops it. The team adds the trace context to message attributes. Now the worker spans appear inside the order traces, and a dropped order stays dropped all the way through the worker.

i
The Pattern Behind the Stories

In every story, the cure was to restore agreement: one decision, passed along faithfully, and followed by all. ParentBased works beautifully when nothing interrupts that chain.

19Frequently Asked Questions

Q1What is ParentBased sampling in one sentence?
It is a sampler that follows the keep-or-drop decision of a span’s parent, and uses a separate rule only when there is no parent, so that whole traces are either fully kept or fully dropped.
Q2What is the default sampler in OpenTelemetry?
The standard default is ParentBased with AlwaysOn at the root. It follows any parent, and keeps every trace that begins in your service. It is a fine start, but expensive at high traffic, so most production systems set a ratio.
Q3Does ParentBased have its own percentage?
No. The percentage belongs to the root sampler inside it, usually a trace-identity ratio sampler. ParentBased only makes sure that the rest of the chain follows that choice.
Q4What are the five slots?
Root, remote parent sampled, remote parent not sampled, local parent sampled, and local parent not sampled. By default, the four parent slots copy the parent’s verdict, and the root slot is the one you choose.
Q5What is the difference between remote and local parents?
A remote parent lives in another program and arrives through a header. A local parent lives in the same program. The split exists so you can treat outside callers with more care than your own code.
Q6If a trace is dropped, does the request still work?
Yes. Sampling affects only the telemetry, never the business logic. A dropped request is handled exactly like a kept one.
Q7Can ParentBased guarantee that every error is kept?
No. It decides before the outcome is known. To keep every error, add a tail sampler in a Collector, and keep the head ratio high enough that error traces reach it.
Q8Why do I see traces that start in the middle?
Most often a header was lost by a proxy, queue, or older library, so the next service started a new root. It can also happen when a caller does not use OpenTelemetry at all.
Q9Should my public gateway follow incoming flags?
Usually not. At a public edge, flags come from unknown parties, so many teams ignore them and apply their own ratio. Inside the network, following the parent is the right choice.
Q10Do I need ParentBased in every service?
Yes, or at least every service that can be in the middle of a chain. A single service that ignores the parent can create orphan spans or drop parts of kept traces.
Q11Will sampling make my request counts wrong?
Only if you compute counts from sampled spans. Use real metrics that see every request, or compute span metrics before any sampler.
Q12How do I pick the ratio?
Start from your daily requests, the average trace size, and your storage budget. Choose a ratio that fits the budget, then check whether it still shows enough traces to answer your questions. Review it each quarter.
Q13Can I change the ratio without restarting?
With the basic environment variables, a change normally needs a restart. Some systems offer remote sampling configuration that updates samplers while the program runs. Check what your language and platform support.

20Summary and Key Takeaways

ParentBased sampling is a small idea with a large effect: one decision, made once, followed by everyone.

We began with a school trip where every child decided alone whether to take a photo, and ended up with an album full of holes. We saw how a single decision made by the first service and carried in a tiny header keeps traces whole. We looked at the five slots, how the flag travels, how to configure it, how it relates to tail sampling and metrics, where it must be guarded, and how to monitor it. The cure for nearly every sampling problem is the same: restore agreement, protect the front door, count with real metrics, and check completeness regularly.

Key Takeaways

  • ParentBased follows the parent. It keeps or drops a span according to its parent’s decision, and uses a root sampler only when there is no parent.
  • The percentage lives in the root. ParentBased wraps a ratio sampler; it is not a percentage itself.
  • Five slots, two worth knowing. Most teams only set the root slot and keep the four parent-following defaults.
  • The decision travels in a header. The W3C traceparent flag carries the choice from service to service; lost headers split traces.
  • Guard the front door. At the public edge, ignore outside flags so that strangers cannot inflate costs or hide activity.
  • Do not count with samples. Use real metrics for rates and errors, and traces for explanations.
  • Head is blind, tail is smart. Add a Collector tail sampler when you must keep every error, and measure trace completeness to know your setup is healthy.