@@ -5,13 +5,15 @@ import (
55 v1 "buf.build/gen/go/open-feature/flagd/protocolbuffers/go/flagd/sync/v1"
66 "context"
77 "fmt"
8+ "github.com/goccy/go-json"
89 "github.com/open-feature/flagd/core/pkg/logger"
910 "github.com/open-feature/flagd/core/pkg/sync"
1011 grpccredential "github.com/open-feature/flagd/core/pkg/sync/grpc/credentials"
1112 of "github.com/open-feature/go-sdk/openfeature"
1213 "google.golang.org/grpc"
1314 "google.golang.org/grpc/connectivity"
1415 "google.golang.org/grpc/keepalive"
16+ "google.golang.org/grpc/status"
1517 msync "sync"
1618 "time"
1719)
@@ -55,8 +57,18 @@ const (
5557 }
5658 ]
5759 }`
60+
61+ nonRetryableStatusCodes = `
62+ [
63+ "PermissionDenied",
64+ "Unauthenticated",
65+ ]
66+ `
5867)
5968
69+ // Set of non-retryable gRPC status codes for faster lookup
70+ var nonRetryableCodes map [string ]struct {}
71+
6072// Type aliases for interfaces required by this component - needed for mock generation with gomock
6173type FlagSyncServiceClient interface {
6274 syncv1grpc.FlagSyncServiceClient
@@ -92,6 +104,7 @@ type Sync struct {
92104// Init initializes the gRPC connection and starts background monitoring
93105func (g * Sync ) Init (ctx context.Context ) error {
94106 g .Logger .Info (fmt .Sprintf ("initializing gRPC client for %s" , g .URI ))
107+ initNonRetryableStatusCodesSet ()
95108
96109 // Initialize channels
97110 g .shutdownComplete = make (chan struct {})
@@ -160,6 +173,17 @@ func (g *Sync) buildDialOptions() ([]grpc.DialOption, error) {
160173 return dialOptions , nil
161174}
162175
176+ // initNonRetryableStatusCodesSet initializes the set of non-retryable gRPC status codes for quick lookup
177+ func initNonRetryableStatusCodesSet () {
178+ var codes []string
179+ nonRetryableCodes = make (map [string ]struct {})
180+ if err := json .Unmarshal ([]byte (nonRetryableStatusCodes ), & codes ); err == nil {
181+ for _ , code := range codes {
182+ nonRetryableCodes [code ] = struct {}{}
183+ }
184+ }
185+ }
186+
163187// ReSync performs a one-time fetch of all flags
164188func (g * Sync ) ReSync (ctx context.Context , dataSync chan <- sync.DataSync ) error {
165189 g .Logger .Debug ("performing ReSync - fetching all flags" )
@@ -207,12 +231,23 @@ func (g *Sync) Sync(ctx context.Context, dataSync chan<- sync.DataSync) error {
207231 }
208232
209233 // Attempt to create sync stream
210- if err := g .performSyncCycle (ctx , dataSync ); err != nil {
234+ err := g .performSyncCycle (ctx , dataSync )
235+ if err != nil {
211236 if ctx .Err () != nil {
212237 g .Logger .Info ("sync cycle failed due to context cancellation" )
213238 return ctx .Err ()
214239 }
215240
241+ // Check if error is a gRPC status error and if code is retryable
242+ st , ok := status .FromError (err )
243+ if ok {
244+ codeStr := st .Code ().String ()
245+ if _ , found := nonRetryableCodes [codeStr ]; found {
246+ g .Logger .Error (fmt .Sprintf ("sync cycle failed with non-retryable code: %v" , codeStr ))
247+ return err
248+ }
249+ }
250+
216251 g .Logger .Warn (fmt .Sprintf ("sync cycle failed: %v, retrying..." , err ))
217252 g .sendEvent (ctx , SyncEvent {event : of .ProviderError })
218253
0 commit comments