Skip to content

Commit 10e72b9

Browse files
fix(flagd): do not retry for certain status codes (#756)
Signed-off-by: Alexandra Oberaigner <[email protected]>
1 parent d822d43 commit 10e72b9

File tree

1 file changed

+36
-1
lines changed

1 file changed

+36
-1
lines changed

providers/flagd/pkg/service/in_process/grpc_sync.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
6173
type FlagSyncServiceClient interface {
6274
syncv1grpc.FlagSyncServiceClient
@@ -92,6 +104,7 @@ type Sync struct {
92104
// Init initializes the gRPC connection and starts background monitoring
93105
func (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
164188
func (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

Comments
 (0)