Skip to content

Commit 88d9117

Browse files
committed
Add ability to pass context into openfeature provider to support cancellation; pins of go sdk to 1a0d39ea7e4f
1 parent 26c62ab commit 88d9117

File tree

3 files changed

+98
-21
lines changed

3 files changed

+98
-21
lines changed

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/json-iterator/go v1.1.12
2424
github.com/klauspost/compress v1.18.0
2525
github.com/minio/simdjson-go v0.4.5
26-
github.com/open-feature/go-sdk v1.16.0
26+
github.com/open-feature/go-sdk v1.16.1-0.20251030122235-1a0d39ea7e4f
2727
github.com/puzpuzpuz/xsync/v3 v3.5.1
2828
github.com/quasilyte/go-ruleguard/dsl v0.3.22
2929
github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3
@@ -37,11 +37,11 @@ require (
3737
go.uber.org/goleak v1.3.0
3838
go.yaml.in/yaml/v3 v3.0.4
3939
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
40-
golang.org/x/mod v0.27.0
40+
golang.org/x/mod v0.28.0
4141
golang.org/x/sync v0.17.0
42-
golang.org/x/sys v0.35.0
42+
golang.org/x/sys v0.36.0
4343
golang.org/x/time v0.12.0
44-
golang.org/x/tools v0.36.0
44+
golang.org/x/tools v0.37.0
4545
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
4646
google.golang.org/grpc v1.75.0
4747
google.golang.org/protobuf v1.36.7
@@ -96,8 +96,8 @@ require (
9696
go.uber.org/mock v0.6.0 // indirect
9797
go.uber.org/multierr v1.11.0 // indirect
9898
go.uber.org/zap v1.27.0 // indirect
99-
golang.org/x/net v0.43.0 // indirect
100-
golang.org/x/text v0.29.0 // indirect
99+
golang.org/x/net v0.44.0 // indirect
100+
golang.org/x/text v0.30.0 // indirect
101101
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
102102
gopkg.in/ini.v1 v1.67.0 // indirect
103103
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd
111111
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
112112
github.com/open-feature/go-sdk v1.16.0 h1:5NCHYv5slvNBIZhYXAzAufo0OI59OACZ5tczVqSE+Tg=
113113
github.com/open-feature/go-sdk v1.16.0/go.mod h1:EIF40QcoYT1VbQkMPy2ZJH4kvZeY+qGUXAorzSWgKSo=
114+
github.com/open-feature/go-sdk v1.16.1-0.20251030122235-1a0d39ea7e4f h1:LECR8thRHyrC16SqGXa96+aEFQchcZheYkfrXyewQL4=
115+
github.com/open-feature/go-sdk v1.16.1-0.20251030122235-1a0d39ea7e4f/go.mod h1:lPxPSu1UnZ4E3dCxZi5gV3et2ACi8O8P+zsTGVsDZUw=
114116
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0 h1:iPei+89a2EK4LuN4HeIRzZNE6XxCyrKfBKG3BkK/ViU=
115117
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0/go.mod h1:asV77TgnGfc7A+a9jggdsnlLlW5dnJT8RroVuf5slko=
116118
github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.133.0 h1:4ca2pM3+xDMB9H3UnhjAiNg7EpIydZ7HdohOexU8xb8=
@@ -257,13 +259,15 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
257259
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
258260
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
259261
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
262+
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
260263
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
261264
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
262265
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
263266
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
264267
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
265268
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
266269
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
270+
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
267271
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
268272
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
269273
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -285,11 +289,13 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
285289
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
286290
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
287291
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
292+
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
288293
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
289294
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
290295
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
291296
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
292297
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
298+
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
293299
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
294300
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
295301
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -299,6 +305,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
299305
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
300306
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
301307
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
308+
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
302309
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
303310
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
304311
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

openfeature/provider.go

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import (
1010
"errors"
1111
"fmt"
1212
"sync"
13+
"time"
1314

1415
"github.com/DataDog/dd-trace-go/v2/internal"
1516
"github.com/DataDog/dd-trace-go/v2/internal/log"
1617
"github.com/open-feature/go-sdk/openfeature"
1718
)
1819

1920
var _ openfeature.FeatureProvider = (*DatadogProvider)(nil)
21+
var _ openfeature.ContextAwareStateHandler = (*DatadogProvider)(nil)
2022

2123
// Sentinel errors for error classification
2224
var (
@@ -93,30 +95,85 @@ func (p *DatadogProvider) Metadata() openfeature.Metadata {
9395

9496
// Init initializes the provider. For the Datadog provider,
9597
// this is waiting for the first configuration to be loaded.
96-
func (p *DatadogProvider) Init(openfeature.EvaluationContext) error {
98+
func (p *DatadogProvider) Init(evaluationContext openfeature.EvaluationContext) error {
99+
// Use a background context with a reasonable timeout for backward compatibility
100+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
101+
defer cancel()
102+
return p.InitWithContext(ctx, evaluationContext)
103+
}
104+
105+
// InitWithContext initializes the provider with context support.
106+
// This method respects context cancellation and timeouts, allowing users
107+
// to cancel the initialization process if needed.
108+
func (p *DatadogProvider) InitWithContext(ctx context.Context, _ openfeature.EvaluationContext) error {
97109
p.mu.Lock()
98110
defer p.mu.Unlock()
111+
99112
for p.configuration == nil {
100-
p.configChange.Wait()
113+
// Check if context was cancelled
114+
select {
115+
case <-ctx.Done():
116+
return ctx.Err()
117+
default:
118+
}
119+
120+
// Use a condition variable with timeout to wait for configuration
121+
// This allows us to periodically check for context cancellation
122+
done := make(chan struct{})
123+
go func() {
124+
p.configChange.Wait()
125+
close(done)
126+
}()
127+
128+
select {
129+
case <-ctx.Done():
130+
return ctx.Err()
131+
case <-done:
132+
// Configuration might have been updated, loop to check
133+
}
101134
}
102135

103136
return nil
104137
}
105138

106139
// Shutdown shuts down the provider and stops Remote Config updates.
107140
func (p *DatadogProvider) Shutdown() {
108-
// Best effort to stop Remote Config - ignore error as we're shutting down anyway
109-
_ = stopRemoteConfig()
141+
// Use a background context with a reasonable timeout for backward compatibility
142+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
143+
defer cancel()
144+
_ = p.ShutdownWithContext(ctx)
145+
}
146+
147+
// ShutdownWithContext shuts down the provider with context support.
148+
// This method respects context cancellation and timeouts, allowing users
149+
// to control how long the shutdown process should take.
150+
func (p *DatadogProvider) ShutdownWithContext(ctx context.Context) error {
151+
// Create a channel to signal completion
152+
done := make(chan error, 1)
153+
154+
go func() {
155+
// Perform the shutdown operations
156+
err := stopRemoteConfig()
157+
done <- err
158+
}()
159+
160+
// Wait for completion or context cancellation
161+
select {
162+
case <-ctx.Done():
163+
return ctx.Err()
164+
case err := <-done:
165+
return err
166+
}
110167
}
111168

112169
// BooleanEvaluation evaluates a boolean feature flag.
113170
func (p *DatadogProvider) BooleanEvaluation(
114-
_ context.Context,
171+
ctx context.Context,
115172
flagKey string,
116173
defaultValue bool,
117174
flatCtx openfeature.FlattenedContext,
118175
) openfeature.BoolResolutionDetail {
119-
result := p.evaluate(flagKey, defaultValue, flatCtx)
176+
result := p.evaluate(ctx, flagKey, defaultValue, flatCtx)
120177

121178
// Convert result to boolean
122179
boolValue, ok := result.Value.(bool)
@@ -138,12 +195,12 @@ func (p *DatadogProvider) BooleanEvaluation(
138195

139196
// StringEvaluation evaluates a string feature flag.
140197
func (p *DatadogProvider) StringEvaluation(
141-
_ context.Context,
198+
ctx context.Context,
142199
flagKey string,
143200
defaultValue string,
144201
flatCtx openfeature.FlattenedContext,
145202
) openfeature.StringResolutionDetail {
146-
result := p.evaluate(flagKey, defaultValue, flatCtx)
203+
result := p.evaluate(ctx, flagKey, defaultValue, flatCtx)
147204

148205
// Convert result to string
149206
strValue, ok := result.Value.(string)
@@ -165,12 +222,12 @@ func (p *DatadogProvider) StringEvaluation(
165222

166223
// FloatEvaluation evaluates a numeric (float) feature flag.
167224
func (p *DatadogProvider) FloatEvaluation(
168-
_ context.Context,
225+
ctx context.Context,
169226
flagKey string,
170227
defaultValue float64,
171228
flatCtx openfeature.FlattenedContext,
172229
) openfeature.FloatResolutionDetail {
173-
result := p.evaluate(flagKey, defaultValue, flatCtx)
230+
result := p.evaluate(ctx, flagKey, defaultValue, flatCtx)
174231

175232
// Convert result to float64
176233
var floatValue float64
@@ -211,12 +268,12 @@ func (p *DatadogProvider) FloatEvaluation(
211268

212269
// IntEvaluation evaluates an integer feature flag.
213270
func (p *DatadogProvider) IntEvaluation(
214-
_ context.Context,
271+
ctx context.Context,
215272
flagKey string,
216273
defaultValue int64,
217274
flatCtx openfeature.FlattenedContext,
218275
) openfeature.IntResolutionDetail {
219-
result := p.evaluate(flagKey, defaultValue, flatCtx)
276+
result := p.evaluate(ctx, flagKey, defaultValue, flatCtx)
220277

221278
// Convert result to int64
222279
var intValue int64
@@ -264,12 +321,12 @@ func (p *DatadogProvider) IntEvaluation(
264321

265322
// ObjectEvaluation evaluates a structured (JSON) feature flag.
266323
func (p *DatadogProvider) ObjectEvaluation(
267-
_ context.Context,
324+
ctx context.Context,
268325
flagKey string,
269326
defaultValue any,
270327
flatCtx openfeature.FlattenedContext,
271328
) openfeature.InterfaceResolutionDetail {
272-
result := p.evaluate(flagKey, defaultValue, flatCtx)
329+
result := p.evaluate(ctx, flagKey, defaultValue, flatCtx)
273330

274331
return openfeature.InterfaceResolutionDetail{
275332
Value: result.Value,
@@ -289,6 +346,7 @@ func (p *DatadogProvider) Hooks() []openfeature.Hook {
289346

290347
// evaluate is the core evaluation method that all type-specific methods use.
291348
func (p *DatadogProvider) evaluate(
349+
ctx context.Context,
292350
flagKey string,
293351
defaultValue any,
294352
flatCtx openfeature.FlattenedContext,
@@ -297,6 +355,18 @@ func (p *DatadogProvider) evaluate(
297355
defer func() {
298356
log.Debug("openfeature: evaluated flag %q: value=%v, reason=%s, error=%v", flagKey, res.Value, res.Reason, res.Error)
299357
}()
358+
359+
// Check if context was cancelled before starting evaluation
360+
select {
361+
case <-ctx.Done():
362+
return evaluationResult{
363+
Value: defaultValue,
364+
Reason: openfeature.ErrorReason,
365+
Error: ctx.Err(),
366+
}
367+
default:
368+
}
369+
300370
config := p.getConfiguration()
301371

302372
// Check if configuration is loaded
@@ -318,7 +388,7 @@ func (p *DatadogProvider) evaluate(
318388
}
319389
}
320390

321-
// Evaluate the flag
391+
// Evaluate the flag (pass context for potential future use in evaluateFlag)
322392
return evaluateFlag(flag, defaultValue, flatCtx)
323393
}
324394

0 commit comments

Comments
 (0)