mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-31 23:58:03 +08:00
vendor: update buildkit to 2f99651
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
332
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/connection/connection.go
generated
vendored
332
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/connection/connection.go
generated
vendored
@@ -1,332 +0,0 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package connection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type Connection struct {
|
||||
// Ensure pointer is 64-bit aligned for atomic operations on both 32 and 64 bit machines.
|
||||
lastConnectErrPtr unsafe.Pointer
|
||||
|
||||
// mu protects the Connection as it is accessed by the
|
||||
// exporter goroutines and background Connection goroutine
|
||||
mu sync.Mutex
|
||||
cc *grpc.ClientConn
|
||||
|
||||
// these fields are read-only after constructor is finished
|
||||
cfg otlpconfig.Config
|
||||
SCfg otlpconfig.SignalConfig
|
||||
requestFunc retry.RequestFunc
|
||||
metadata metadata.MD
|
||||
newConnectionHandler func(cc *grpc.ClientConn)
|
||||
|
||||
// these channels are created once
|
||||
disconnectedCh chan bool
|
||||
backgroundConnectionDoneCh chan struct{}
|
||||
stopCh chan struct{}
|
||||
|
||||
// this is for tests, so they can replace the closing
|
||||
// routine without a worry of modifying some global variable
|
||||
// or changing it back to original after the test is done
|
||||
closeBackgroundConnectionDoneCh func(ch chan struct{})
|
||||
}
|
||||
|
||||
func NewConnection(cfg otlpconfig.Config, sCfg otlpconfig.SignalConfig, handler func(cc *grpc.ClientConn)) *Connection {
|
||||
c := new(Connection)
|
||||
c.newConnectionHandler = handler
|
||||
c.cfg = cfg
|
||||
c.requestFunc = cfg.RetryConfig.RequestFunc(evaluate)
|
||||
c.SCfg = sCfg
|
||||
if len(c.SCfg.Headers) > 0 {
|
||||
c.metadata = metadata.New(c.SCfg.Headers)
|
||||
}
|
||||
c.closeBackgroundConnectionDoneCh = func(ch chan struct{}) {
|
||||
close(ch)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Connection) StartConnection(ctx context.Context) error {
|
||||
c.stopCh = make(chan struct{})
|
||||
c.disconnectedCh = make(chan bool, 1)
|
||||
c.backgroundConnectionDoneCh = make(chan struct{})
|
||||
|
||||
if err := c.connect(ctx); err == nil {
|
||||
c.setStateConnected()
|
||||
} else {
|
||||
c.SetStateDisconnected(err)
|
||||
}
|
||||
go c.indefiniteBackgroundConnection()
|
||||
|
||||
// TODO: proper error handling when initializing connections.
|
||||
// We can report permanent errors, e.g., invalid settings.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connection) LastConnectError() error {
|
||||
errPtr := (*error)(atomic.LoadPointer(&c.lastConnectErrPtr))
|
||||
if errPtr == nil {
|
||||
return nil
|
||||
}
|
||||
return *errPtr
|
||||
}
|
||||
|
||||
func (c *Connection) saveLastConnectError(err error) {
|
||||
var errPtr *error
|
||||
if err != nil {
|
||||
errPtr = &err
|
||||
}
|
||||
atomic.StorePointer(&c.lastConnectErrPtr, unsafe.Pointer(errPtr))
|
||||
}
|
||||
|
||||
func (c *Connection) SetStateDisconnected(err error) {
|
||||
c.saveLastConnectError(err)
|
||||
select {
|
||||
case c.disconnectedCh <- true:
|
||||
default:
|
||||
}
|
||||
c.newConnectionHandler(nil)
|
||||
}
|
||||
|
||||
func (c *Connection) setStateConnected() {
|
||||
c.saveLastConnectError(nil)
|
||||
}
|
||||
|
||||
func (c *Connection) Connected() bool {
|
||||
return c.LastConnectError() == nil
|
||||
}
|
||||
|
||||
const defaultConnReattemptPeriod = 10 * time.Second
|
||||
|
||||
func (c *Connection) indefiniteBackgroundConnection() {
|
||||
defer func() {
|
||||
c.closeBackgroundConnectionDoneCh(c.backgroundConnectionDoneCh)
|
||||
}()
|
||||
|
||||
connReattemptPeriod := c.cfg.ReconnectionPeriod
|
||||
if connReattemptPeriod <= 0 {
|
||||
connReattemptPeriod = defaultConnReattemptPeriod
|
||||
}
|
||||
|
||||
// No strong seeding required, nano time can
|
||||
// already help with pseudo uniqueness.
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024)))
|
||||
|
||||
// maxJitterNanos: 70% of the connectionReattemptPeriod
|
||||
maxJitterNanos := int64(0.7 * float64(connReattemptPeriod))
|
||||
|
||||
for {
|
||||
// Otherwise these will be the normal scenarios to enable
|
||||
// reconnection if we trip out.
|
||||
// 1. If we've stopped, return entirely
|
||||
// 2. Otherwise block until we are disconnected, and
|
||||
// then retry connecting
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
|
||||
case <-c.disconnectedCh:
|
||||
// Quickly check if we haven't stopped at the
|
||||
// same time.
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
// Normal scenario that we'll wait for
|
||||
}
|
||||
|
||||
if err := c.connect(context.Background()); err == nil {
|
||||
c.setStateConnected()
|
||||
} else {
|
||||
// this code is unreachable in most cases
|
||||
// c.connect does not establish Connection
|
||||
c.SetStateDisconnected(err)
|
||||
}
|
||||
|
||||
// Apply some jitter to avoid lockstep retrials of other
|
||||
// collector-exporters. Lockstep retrials could result in an
|
||||
// innocent DDOS, by clogging the machine's resources and network.
|
||||
jitter := time.Duration(rng.Int63n(maxJitterNanos))
|
||||
select {
|
||||
case <-c.stopCh:
|
||||
return
|
||||
case <-time.After(connReattemptPeriod + jitter):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) connect(ctx context.Context) error {
|
||||
cc, err := c.dialToCollector(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.setConnection(cc)
|
||||
c.newConnectionHandler(cc)
|
||||
return nil
|
||||
}
|
||||
|
||||
// setConnection sets cc as the client Connection and returns true if
|
||||
// the Connection state changed.
|
||||
func (c *Connection) setConnection(cc *grpc.ClientConn) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// If previous clientConn is same as the current then just return.
|
||||
// This doesn't happen right now as this func is only called with new ClientConn.
|
||||
// It is more about future-proofing.
|
||||
if c.cc == cc {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the previous clientConn was non-nil, close it
|
||||
if c.cc != nil {
|
||||
_ = c.cc.Close()
|
||||
}
|
||||
c.cc = cc
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Connection) dialToCollector(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
dialOpts := []grpc.DialOption{}
|
||||
if c.cfg.ServiceConfig != "" {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(c.cfg.ServiceConfig))
|
||||
}
|
||||
if c.SCfg.GRPCCredentials != nil {
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(c.SCfg.GRPCCredentials))
|
||||
} else if c.SCfg.Insecure {
|
||||
dialOpts = append(dialOpts, grpc.WithInsecure())
|
||||
}
|
||||
if c.SCfg.Compression == otlpconfig.GzipCompression {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if len(c.cfg.DialOptions) != 0 {
|
||||
dialOpts = append(dialOpts, c.cfg.DialOptions...)
|
||||
}
|
||||
|
||||
ctx, cancel := c.ContextWithStop(ctx)
|
||||
defer cancel()
|
||||
ctx = c.ContextWithMetadata(ctx)
|
||||
return grpc.DialContext(ctx, c.SCfg.Endpoint, dialOpts...)
|
||||
}
|
||||
|
||||
func (c *Connection) ContextWithMetadata(ctx context.Context) context.Context {
|
||||
if c.metadata.Len() > 0 {
|
||||
return metadata.NewOutgoingContext(ctx, c.metadata)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *Connection) Shutdown(ctx context.Context) error {
|
||||
close(c.stopCh)
|
||||
// Ensure that the backgroundConnector returns
|
||||
select {
|
||||
case <-c.backgroundConnectionDoneCh:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
cc := c.cc
|
||||
c.cc = nil
|
||||
c.mu.Unlock()
|
||||
|
||||
if cc != nil {
|
||||
return cc.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
// Unify the parent context Done signal with the Connection's
|
||||
// stop channel.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func(ctx context.Context, cancel context.CancelFunc) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Nothing to do, either cancelled or deadline
|
||||
// happened.
|
||||
case <-c.stopCh:
|
||||
cancel()
|
||||
}
|
||||
}(ctx, cancel)
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func (c *Connection) DoRequest(ctx context.Context, fn func(context.Context) error) error {
|
||||
ctx, cancel := c.ContextWithStop(ctx)
|
||||
defer cancel()
|
||||
return c.requestFunc(ctx, func(ctx context.Context) error {
|
||||
err := fn(ctx)
|
||||
// nil is converted to OK.
|
||||
if status.Code(err) == codes.OK {
|
||||
// Success.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// evaluate returns if err is retry-able and a duration to wait for if an
|
||||
// explicit throttle time is included in err.
|
||||
func evaluate(err error) (bool, time.Duration) {
|
||||
s := status.Convert(err)
|
||||
switch s.Code() {
|
||||
case codes.Canceled,
|
||||
codes.DeadlineExceeded,
|
||||
codes.ResourceExhausted,
|
||||
codes.Aborted,
|
||||
codes.OutOfRange,
|
||||
codes.Unavailable,
|
||||
codes.DataLoss:
|
||||
return true, throttleDelay(s)
|
||||
}
|
||||
|
||||
// Not a retry-able error.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// throttleDelay returns a duration to wait for if an explicit throttle time
|
||||
// is included in the response status.
|
||||
func throttleDelay(status *status.Status) time.Duration {
|
||||
for _, detail := range status.Details() {
|
||||
if t, ok := detail.(*errdetails.RetryInfo); ok {
|
||||
return t.RetryDelay.AsDuration()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package otlpconfig
|
||||
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -28,24 +28,17 @@ import (
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
var httpSchemeRegexp = regexp.MustCompile(`(?i)^(http://|https://)`)
|
||||
var DefaultEnvOptionsReader = EnvOptionsReader{
|
||||
GetEnv: os.Getenv,
|
||||
ReadFile: ioutil.ReadFile,
|
||||
}
|
||||
|
||||
func ApplyGRPCEnvConfigs(cfg *Config) {
|
||||
e := EnvOptionsReader{
|
||||
GetEnv: os.Getenv,
|
||||
ReadFile: ioutil.ReadFile,
|
||||
}
|
||||
|
||||
e.ApplyGRPCEnvConfigs(cfg)
|
||||
DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
|
||||
}
|
||||
|
||||
func ApplyHTTPEnvConfigs(cfg *Config) {
|
||||
e := EnvOptionsReader{
|
||||
GetEnv: os.Getenv,
|
||||
ReadFile: ioutil.ReadFile,
|
||||
}
|
||||
|
||||
e.ApplyHTTPEnvConfigs(cfg)
|
||||
DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
|
||||
}
|
||||
|
||||
type EnvOptionsReader struct {
|
||||
@@ -71,23 +64,55 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
|
||||
var opts []GenericOption
|
||||
|
||||
// Endpoint
|
||||
if v, ok := e.getEnvValue("ENDPOINT"); ok {
|
||||
if isInsecureEndpoint(v) {
|
||||
opts = append(opts, WithInsecure())
|
||||
} else {
|
||||
opts = append(opts, WithSecure())
|
||||
}
|
||||
|
||||
opts = append(opts, WithEndpoint(trimSchema(v)))
|
||||
}
|
||||
if v, ok := e.getEnvValue("TRACES_ENDPOINT"); ok {
|
||||
if isInsecureEndpoint(v) {
|
||||
opts = append(opts, WithInsecure())
|
||||
} else {
|
||||
opts = append(opts, WithSecure())
|
||||
u, err := url.Parse(v)
|
||||
// Ignore invalid values.
|
||||
if err == nil {
|
||||
// This is used to set the scheme for OTLP/HTTP.
|
||||
if insecureSchema(u.Scheme) {
|
||||
opts = append(opts, WithInsecure())
|
||||
} else {
|
||||
opts = append(opts, WithSecure())
|
||||
}
|
||||
opts = append(opts, newSplitOption(func(cfg *Config) {
|
||||
cfg.Traces.Endpoint = u.Host
|
||||
// For endpoint URLs for OTLP/HTTP per-signal variables, the
|
||||
// URL MUST be used as-is without any modification. The only
|
||||
// exception is that if an URL contains no path part, the root
|
||||
// path / MUST be used.
|
||||
path := u.Path
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
cfg.Traces.URLPath = path
|
||||
}, func(cfg *Config) {
|
||||
// For OTLP/gRPC endpoints, this is the target to which the
|
||||
// exporter is going to send telemetry.
|
||||
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
|
||||
}))
|
||||
}
|
||||
} else if v, ok = e.getEnvValue("ENDPOINT"); ok {
|
||||
u, err := url.Parse(v)
|
||||
// Ignore invalid values.
|
||||
if err == nil {
|
||||
// This is used to set the scheme for OTLP/HTTP.
|
||||
if insecureSchema(u.Scheme) {
|
||||
opts = append(opts, WithInsecure())
|
||||
} else {
|
||||
opts = append(opts, WithSecure())
|
||||
}
|
||||
opts = append(opts, newSplitOption(func(cfg *Config) {
|
||||
cfg.Traces.Endpoint = u.Host
|
||||
// For OTLP/HTTP endpoint URLs without a per-signal
|
||||
// configuration, the passed endpoint is used as a base URL
|
||||
// and the signals are sent to these paths relative to that.
|
||||
cfg.Traces.URLPath = path.Join(u.Path, DefaultTracesPath)
|
||||
}, func(cfg *Config) {
|
||||
// For OTLP/gRPC endpoints, this is the target to which the
|
||||
// exporter is going to send telemetry.
|
||||
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
|
||||
}))
|
||||
}
|
||||
|
||||
opts = append(opts, WithEndpoint(trimSchema(v)))
|
||||
}
|
||||
|
||||
// Certificate File
|
||||
@@ -136,12 +161,13 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
|
||||
return opts
|
||||
}
|
||||
|
||||
func isInsecureEndpoint(endpoint string) bool {
|
||||
return strings.HasPrefix(strings.ToLower(endpoint), "http://") || strings.HasPrefix(strings.ToLower(endpoint), "unix://")
|
||||
}
|
||||
|
||||
func trimSchema(endpoint string) string {
|
||||
return httpSchemeRegexp.ReplaceAllString(endpoint, "")
|
||||
func insecureSchema(schema string) bool {
|
||||
switch strings.ToLower(schema) {
|
||||
case "http", "unix":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// getEnvValue gets an OTLP environment variable value of the specified key using the GetEnv function.
|
||||
|
45
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig/options.go
generated
vendored
45
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig/options.go
generated
vendored
@@ -20,9 +20,11 @@ import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding/gzip"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/internal/retry"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,6 +60,7 @@ type (
|
||||
ReconnectionPeriod time.Duration
|
||||
ServiceConfig string
|
||||
DialOptions []grpc.DialOption
|
||||
GRPCConn *grpc.ClientConn
|
||||
}
|
||||
)
|
||||
|
||||
@@ -75,6 +78,46 @@ func NewDefaultConfig() Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// NewGRPCConfig returns a new Config with all settings applied from opts and
|
||||
// any unset setting using the default gRPC config values.
|
||||
func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
cfg := NewDefaultConfig()
|
||||
ApplyGRPCEnvConfigs(&cfg)
|
||||
for _, opt := range opts {
|
||||
opt.ApplyGRPCOption(&cfg)
|
||||
}
|
||||
|
||||
if cfg.ServiceConfig != "" {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultServiceConfig(cfg.ServiceConfig))
|
||||
}
|
||||
// Priroritize GRPCCredentials over Insecure (passing both is an error).
|
||||
if cfg.Traces.GRPCCredentials != nil {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(cfg.Traces.GRPCCredentials))
|
||||
} else if cfg.Traces.Insecure {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithInsecure())
|
||||
} else {
|
||||
// Default to using the host's root CA.
|
||||
creds := credentials.NewTLS(nil)
|
||||
cfg.Traces.GRPCCredentials = creds
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
if cfg.Traces.Compression == GzipCompression {
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))
|
||||
}
|
||||
if len(cfg.DialOptions) != 0 {
|
||||
cfg.DialOptions = append(cfg.DialOptions, cfg.DialOptions...)
|
||||
}
|
||||
if cfg.ReconnectionPeriod != 0 {
|
||||
p := grpc.ConnectParams{
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: cfg.ReconnectionPeriod,
|
||||
}
|
||||
cfg.DialOptions = append(cfg.DialOptions, grpc.WithConnectParams(p))
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type (
|
||||
// GenericOption applies an option to the HTTP or gRPC driver.
|
||||
GenericOption interface {
|
||||
|
2
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig/tls.go
generated
vendored
2
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig/tls.go
generated
vendored
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package otlpconfig
|
||||
package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig"
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
130
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry/retry.go
generated
vendored
130
vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry/retry.go
generated
vendored
@@ -1,130 +0,0 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
)
|
||||
|
||||
// DefaultConfig are the recommended defaults to use.
|
||||
var DefaultConfig = Config{
|
||||
Enabled: true,
|
||||
InitialInterval: 5 * time.Second,
|
||||
MaxInterval: 30 * time.Second,
|
||||
MaxElapsedTime: time.Minute,
|
||||
}
|
||||
|
||||
// Config defines configuration for retrying batches in case of export failure
|
||||
// using an exponential backoff.
|
||||
type Config struct {
|
||||
// Enabled indicates whether to not retry sending batches in case of
|
||||
// export failure.
|
||||
Enabled bool
|
||||
// InitialInterval the time to wait after the first failure before
|
||||
// retrying.
|
||||
InitialInterval time.Duration
|
||||
// MaxInterval is the upper bound on backoff interval. Once this value is
|
||||
// reached the delay between consecutive retries will always be
|
||||
// `MaxInterval`.
|
||||
MaxInterval time.Duration
|
||||
// MaxElapsedTime is the maximum amount of time (including retries) spent
|
||||
// trying to send a request/batch. Once this value is reached, the data
|
||||
// is discarded.
|
||||
MaxElapsedTime time.Duration
|
||||
}
|
||||
|
||||
// RequestFunc wraps a request with retry logic.
|
||||
type RequestFunc func(context.Context, func(context.Context) error) error
|
||||
|
||||
// EvaluateFunc returns if an error is retry-able and if an explicit throttle
|
||||
// duration should be honored that was included in the error.
|
||||
type EvaluateFunc func(error) (bool, time.Duration)
|
||||
|
||||
func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc {
|
||||
if !c.Enabled {
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Do not use NewExponentialBackOff since it calls Reset and the code here
|
||||
// must call Reset after changing the InitialInterval (this saves an
|
||||
// unnecessary call to Now).
|
||||
b := &backoff.ExponentialBackOff{
|
||||
InitialInterval: c.InitialInterval,
|
||||
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
||||
Multiplier: backoff.DefaultMultiplier,
|
||||
MaxInterval: c.MaxInterval,
|
||||
MaxElapsedTime: c.MaxElapsedTime,
|
||||
Stop: backoff.Stop,
|
||||
Clock: backoff.SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context) error) error {
|
||||
for {
|
||||
err := fn(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
retryable, throttle := evaluate(err)
|
||||
if !retryable {
|
||||
return err
|
||||
}
|
||||
|
||||
bOff := b.NextBackOff()
|
||||
if bOff == backoff.Stop {
|
||||
return fmt.Errorf("max retry time elapsed: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the greater of the backoff or throttle delay.
|
||||
var delay time.Duration
|
||||
if bOff > throttle {
|
||||
delay = bOff
|
||||
} else {
|
||||
elapsed := b.GetElapsedTime()
|
||||
if b.MaxElapsedTime != 0 && elapsed+throttle > b.MaxElapsedTime {
|
||||
return fmt.Errorf("max retry time would elapse: %w", err)
|
||||
}
|
||||
delay = throttle
|
||||
}
|
||||
|
||||
if err := waitFunc(ctx, delay); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override for testing.
|
||||
var waitFunc = wait
|
||||
|
||||
func wait(ctx context.Context, delay time.Duration) error {
|
||||
timer := time.NewTimer(delay)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -12,13 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tracetransform
|
||||
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
// KeyValues transforms a slice of attribute KeyValues into OTLP key-values.
|
||||
|
@@ -12,12 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tracetransform
|
||||
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
||||
|
||||
import (
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
func InstrumentationLibrary(il instrumentation.Library) *commonpb.InstrumentationLibrary {
|
||||
|
@@ -12,12 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tracetransform
|
||||
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
||||
|
||||
import (
|
||||
resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
)
|
||||
|
||||
// Resource transforms a Resource into an OTLP Resource.
|
||||
|
@@ -12,16 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tracetransform
|
||||
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
Reference in New Issue
Block a user