Skip to content

Commit

Permalink
Merge pull request #94 from lightstep/jmacd/sampler
Browse files Browse the repository at this point in the history
Introduce Satellite sampler package
  • Loading branch information
jmacd authored Sep 23, 2024
2 parents 39afee9 + 04978d1 commit 1b48943
Show file tree
Hide file tree
Showing 25 changed files with 2,994 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ on:
- "**"
paths:
- "arrow/**"
- "lightstep/**"
push:
branches:
- main
paths:
- "arrow/**"
- "lightstep/**"

# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
Expand Down
103 changes: 103 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Lightstep Satellite Sampler

This package contains an OpenTelemetry processor for an OpenTelemetry
traces pipeline that makes sampling decisions consistent with the
legacy Lightstep Satellite. This component enables a slow transition
from Lightstep Satellites to OpenTelemetry Collectors without
simultaneously changing sampling algorithms.

## Recommended usage

This component supports operating a mixture of Lightstep Satellites
and OpenTelemetry Collectors with consistent probability sampling.
Here is a recommended sequence of steps for performing a migratation
to OpenTelemetry Collectors for Lightstep Satellite users.

### Build a custom OpenTelemetry Collector

This component is provided as a standalone component, meant for
incorporating into a custom build of the OpenTelemetry Collector using
the [OpenTelemetry Collector
builder](https://opentelemetry.io/docs/collector/custom-collector/)
tool. In your Collector's build configuration, add the following
processor component:

```
- gomod: github.com/lightstep/otel-collector-charts/lightstep/processor/satellitesamplerprocessor VERSIONTAG
```

where `VERSIONTAG` corresponds with the targetted OpenTelemetry
Collector release version. At the time of this writing, the version
tag is `v0.109.0`.

Users are advised to include the OpenTelemetry Probabilistic Sampler
processor in their build, to complete this transition. For example:

```
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.109.0
```

Follow the usual steps to build your collector (e.g., `builder
--config build.yaml`).

### Configure the sampler

You will need to know the sampling probability configured with
Lightstep Satellites, in percentage terms. Say the Lightstep
Satellite is configured with 10% sampling (i.e., 1-in-10).

Edit OpenTelemetry Collector configuration to include a
`satellitesatempler` block. In the following example, the OTel-Arrow
receiver and exporter are configured with `satellitesampler` with 10%
sampling and [concurrent batch
processor](https://github.com/open-telemetry/otel-arrow/blob/main/collector/processor/concurrentbatchprocessor/README.md).

```
exporters:
otelarrow:
...
receivers:
otelarrow:
...
processors:
satellitesampler:
percent: 10
concurrentbatch:
service:
pipelines:
traces:
receivers: [otelarrow]
processors: [satellitesampler, concurrentbatch]
exporters: [otelarrow]
```

Collectors with this configuration may be deployed alongside a pool of
Lightstep Satellites sampling and the resulting traces will be
complete.

### Migrate to the OpenTelemetry Probabilistic Sampler

After decomissioning Lightstep Satellites and replacing them with
OpenTelemetry Collectors, users are advised to migrate to an
OpenTelemetry Collector processor with equivalent functionality, the
[Probabilistic Sampler Processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/probabilisticsamplerprocessor/README.md).

A change of sampling configuration, either to change algorithm or to
change probability, typically results in broken traces. Users are
advised to plan accordingly and make a quick transition between
samplers, with only a brief, planned period of broken traces.

Redeploy the pool of Collectors with the Probabilistic Sampler
processor configured instead of the Satellite sampler processor. Make
this transition quickly, if possible, because traces will be
potentially incomplete as long as both samplers being used.

```
processors:
probabilisticsampler:
mode: equalizing
sampling_percentage: 10
```

The "equalizing" mode is recommended, see that [component's
documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/probabilisticsamplerprocessor/README.md#equalizing).
31 changes: 31 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright ServiceNow, Inc
// SPDX-License-Identifier: Apache-2.0

package satellitesamplerprocessor

import (
"errors"
"fmt"
)

var (
errInvalidPercent = errors.New("sampling percent must be in (0, 100]")
)

// Config defines configuration for Lightstep's "classic" Satellite sampler.
type Config struct {
// Percent in the range (0, 100]. Defaults to 100.
//
// Note that satellites began supporting percent-based
// sampling configuration at release 2022-04-28_17-39-22Z.
// When OneInN is set instead, use the formula `100.0 /
// float64(OneInN)`.
Percent float64 `mapstructure:"percent"`
}

func (c *Config) Validate() error {
if c.Percent <= 0 || c.Percent > 100 {
return fmt.Errorf("%w: %v", errInvalidPercent, c.Percent)
}
return nil
}
25 changes: 25 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright ServiceNow, Inc
// SPDX-License-Identifier: Apache-2.0

package satellitesamplerprocessor

import (
"testing"

"github.com/stretchr/testify/require"
)

func testConfig(pct float64) *Config {
return &Config{Percent: pct}
}

func TestConfigValidate(t *testing.T) {
require.Error(t, testConfig(-1).Validate())
require.Error(t, testConfig(0).Validate())
require.Error(t, testConfig(101).Validate())

require.NoError(t, testConfig(1).Validate())
require.NoError(t, testConfig(50).Validate())
require.NoError(t, testConfig(99).Validate())
require.NoError(t, testConfig(100).Validate())
}
46 changes: 46 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright ServiceNow, Inc
// SPDX-License-Identifier: Apache-2.0

package satellitesamplerprocessor

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/processor"
"go.opentelemetry.io/collector/processor/processorhelper"
)

const (
typeStr = "satellitesampler"
stability = component.StabilityLevelStable
)

func NewFactory() processor.Factory {
return processor.NewFactory(
component.MustNewType(typeStr),
createDefaultConfig,
processor.WithTraces(createTracesProcessorHelper, stability),
)
}

func createTracesProcessorHelper(ctx context.Context, set processor.Settings, cfg component.Config, nextConsumer consumer.Traces) (processor.Traces, error) {
tp, err := createTracesProcessor(ctx, set, cfg, nextConsumer)
if err != nil {
return nil, err
}
return processorhelper.NewTracesProcessor(
ctx,
set,
cfg,
nextConsumer,
tp.processTraces,
processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}))
}

func createDefaultConfig() component.Config {
return &Config{
Percent: 100,
}
}
15 changes: 15 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright ServiceNow, Inc
// SPDX-License-Identifier: Apache-2.0

package satellitesamplerprocessor

import (
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
)

func TestDefaultConfig(t *testing.T) {
require.Equal(t, component.Config(testConfig(100)), createDefaultConfig())
}
37 changes: 37 additions & 0 deletions lightstep/processor/satellitesamplerprocessor/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module github.com/lightstep/otel-collector-charts/lightstep/processor/satellitesamplerprocessor

go 1.22.6

require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/collector/component v0.109.0
go.opentelemetry.io/collector/consumer v0.109.0
go.opentelemetry.io/collector/consumer/consumertest v0.109.0
go.opentelemetry.io/collector/pdata v1.15.0
go.opentelemetry.io/collector/processor v0.109.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
google.golang.org/protobuf v1.34.2
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/collector v0.109.0 // indirect
go.opentelemetry.io/collector/config/configtelemetry v0.109.0 // indirect
go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0 // indirect
go.opentelemetry.io/collector/pdata/pprofile v0.109.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect
google.golang.org/grpc v1.66.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 1b48943

Please sign in to comment.