@@ -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
1920var _ openfeature.FeatureProvider = (* DatadogProvider )(nil )
21+ var _ openfeature.ContextAwareStateHandler = (* DatadogProvider )(nil )
2022
2123// Sentinel errors for error classification
2224var (
@@ -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.
107140func (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.
113170func (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.
140197func (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.
167224func (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.
213270func (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.
266323func (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.
291348func (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