44 "buf.build/gen/go/open-feature/flagd/grpc/go/flagd/sync/v1/syncv1grpc"
55 v1 "buf.build/gen/go/open-feature/flagd/protocolbuffers/go/flagd/sync/v1"
66 "context"
7+ "encoding/json"
78 "fmt"
8- "github.com/goccy/go-json"
99 "github.com/open-feature/flagd/core/pkg/logger"
1010 "github.com/open-feature/flagd/core/pkg/sync"
1111 grpccredential "github.com/open-feature/flagd/core/pkg/sync/grpc/credentials"
@@ -14,6 +14,7 @@ import (
1414 "google.golang.org/grpc/connectivity"
1515 "google.golang.org/grpc/keepalive"
1616 "google.golang.org/grpc/status"
17+ "strings"
1718 msync "sync"
1819 "time"
1920)
@@ -37,21 +38,8 @@ const (
3738 "MaxBackoff": "5s",
3839 "BackoffMultiplier": 2.0,
3940 "RetryableStatusCodes": [
40- "CANCELLED",
4141 "UNKNOWN",
42- "INVALID_ARGUMENT",
43- "NOT_FOUND",
44- "ALREADY_EXISTS",
45- "PERMISSION_DENIED",
46- "RESOURCE_EXHAUSTED",
47- "FAILED_PRECONDITION",
48- "ABORTED",
49- "OUT_OF_RANGE",
50- "UNIMPLEMENTED",
51- "INTERNAL",
52- "UNAVAILABLE",
53- "DATA_LOSS",
54- "UNAUTHENTICATED"
42+ "UNAVAILABLE"
5543 ]
5644 }
5745 }
@@ -61,7 +49,7 @@ const (
6149 nonRetryableStatusCodes = `
6250 [
6351 "PermissionDenied",
64- "Unauthenticated",
52+ "Unauthenticated"
6553 ]
6654 `
6755)
@@ -90,6 +78,7 @@ type Sync struct {
9078 Selector string
9179 URI string
9280 MaxMsgSize int
81+ RetryGracePeriod int
9382
9483 // Runtime state
9584 client FlagSyncServiceClient
@@ -104,7 +93,7 @@ type Sync struct {
10493// Init initializes the gRPC connection and starts background monitoring
10594func (g * Sync ) Init (ctx context.Context ) error {
10695 g .Logger .Info (fmt .Sprintf ("initializing gRPC client for %s" , g .URI ))
107- initNonRetryableStatusCodesSet ()
96+ g . initNonRetryableStatusCodesSet ()
10897
10998 // Initialize channels
11099 g .shutdownComplete = make (chan struct {})
@@ -174,13 +163,16 @@ func (g *Sync) buildDialOptions() ([]grpc.DialOption, error) {
174163}
175164
176165// initNonRetryableStatusCodesSet initializes the set of non-retryable gRPC status codes for quick lookup
177- func initNonRetryableStatusCodesSet () {
166+ func ( g * Sync ) initNonRetryableStatusCodesSet () {
178167 var codes []string
179168 nonRetryableCodes = make (map [string ]struct {})
180- if err := json .Unmarshal ([]byte (nonRetryableStatusCodes ), & codes ); err == nil {
169+ trimmed := strings .TrimSpace (nonRetryableStatusCodes )
170+ if err := json .Unmarshal ([]byte (trimmed ), & codes ); err == nil {
181171 for _ , code := range codes {
182172 nonRetryableCodes [code ] = struct {}{}
183173 }
174+ } else {
175+ g .Logger .Debug ("parsing non-retryable status codes failed, retrying on all errors" )
184176 }
185177}
186178
@@ -243,11 +235,19 @@ func (g *Sync) Sync(ctx context.Context, dataSync chan<- sync.DataSync) error {
243235 if ok {
244236 codeStr := st .Code ().String ()
245237 if _ , found := nonRetryableCodes [codeStr ]; found {
246- g .Logger .Error (fmt .Sprintf ("sync cycle failed with non-retryable code: %v" , codeStr ))
247- return err
238+ errStr := fmt .Sprintf ("sync cycle failed with non-retryable status: %v, " +
239+ "returning provider fatal." , codeStr )
240+ g .Logger .Error (errStr )
241+ return & of.ProviderInitError {
242+ ErrorCode : of .ProviderFatalCode ,
243+ Message : errStr ,
244+ }
248245 }
249246 }
250247
248+ // Backoff before retrying
249+ time .Sleep (time .Duration (g .RetryGracePeriod ))
250+
251251 g .Logger .Warn (fmt .Sprintf ("sync cycle failed: %v, retrying..." , err ))
252252 g .sendEvent (ctx , SyncEvent {event : of .ProviderError })
253253
0 commit comments