Skip to content

Commit 52b0e78

Browse files
authored
Merge pull request #79 from harness/FFM-1943
Streaming connections are now closed when you call Close on a client
2 parents 6bb5f78 + 41c4822 commit 52b0e78

File tree

2 files changed

+59
-13
lines changed

2 files changed

+59
-13
lines changed

client/client.go

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package client
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"net/http"
89
"strings"
910
"sync"
11+
"sync/atomic"
1012
"time"
1113

1214
"github.com/harness/ff-golang-server-sdk/evaluation"
@@ -46,7 +48,6 @@ type CfClient struct {
4648
config *config
4749
environmentID string
4850
token string
49-
cancelFunc context.CancelFunc
5051
streamConnected bool
5152
streamConnectedLock sync.RWMutex
5253
authenticated chan struct{}
@@ -55,17 +56,14 @@ type CfClient struct {
5556
initializedLock sync.RWMutex
5657
analyticsService *analyticsservice.AnalyticsService
5758
clusterIdentifier string
59+
stop chan struct{}
60+
stopped *atomicBool
5861
}
5962

6063
// NewCfClient creates a new client instance that connects to CF with the default configuration.
6164
// For advanced configuration options use ConfigOptions functions
6265
func NewCfClient(sdkKey string, options ...ConfigOption) (*CfClient, error) {
6366

64-
var (
65-
ctx context.Context
66-
err error
67-
)
68-
6967
// functional options for config
7068
config := newDefaultConfig()
7169
for _, opt := range options {
@@ -81,8 +79,9 @@ func NewCfClient(sdkKey string, options ...ConfigOption) (*CfClient, error) {
8179
analyticsService: analyticsService,
8280
clusterIdentifier: "1",
8381
postEvalChan: make(chan evaluation.PostEvalData),
82+
stop: make(chan struct{}),
83+
stopped: newAtomicBool(false),
8484
}
85-
ctx, client.cancelFunc = context.WithCancel(context.Background())
8685

8786
if sdkKey == "" {
8887
return client, types.ErrSdkCantBeEmpty
@@ -97,13 +96,20 @@ func NewCfClient(sdkKey string, options ...ConfigOption) (*CfClient, error) {
9796
return nil, err
9897
}
9998

100-
go client.initAuthentication(ctx)
101-
102-
go client.setAnalyticsServiceClient(ctx)
99+
client.start()
100+
return client, nil
101+
}
103102

104-
go client.pullCronJob(ctx)
103+
func (c *CfClient) start() {
104+
ctx, cancel := context.WithCancel(context.Background())
105+
go func() {
106+
<-c.stop
107+
cancel()
108+
}()
105109

106-
return client, nil
110+
go c.initAuthentication(ctx)
111+
go c.setAnalyticsServiceClient(ctx)
112+
go c.pullCronJob(ctx)
107113
}
108114

109115
// PostEvaluateProcessor push the data to the analytics service
@@ -432,7 +438,12 @@ func (c *CfClient) JSONVariation(key string, target *evaluation.Target, defaultV
432438
// Close shuts down the Feature Flag client. After calling this, the client
433439
// should no longer be used
434440
func (c *CfClient) Close() error {
435-
c.cancelFunc()
441+
if c.stopped.get() {
442+
return errors.New("client already closed")
443+
}
444+
close(c.stop)
445+
446+
c.stopped.set(true)
436447
return nil
437448
}
438449

@@ -448,3 +459,25 @@ func (c *CfClient) InterceptAddCluster(ctx context.Context, req *http.Request) e
448459
req.URL.RawQuery = q.Encode()
449460
return nil
450461
}
462+
463+
type atomicBool struct {
464+
flag int32
465+
}
466+
467+
func newAtomicBool(value bool) *atomicBool {
468+
b := new(atomicBool)
469+
b.set(value)
470+
return b
471+
}
472+
473+
func (a *atomicBool) set(value bool) {
474+
var i int32 = 0
475+
if value {
476+
i = 1
477+
}
478+
atomic.StoreInt32(&(a.flag), i)
479+
}
480+
481+
func (a *atomicBool) get() bool {
482+
return atomic.LoadInt32(&(a.flag)) != int32(0)
483+
}

client/client_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,16 @@ var FeatureConfigsResponse = func(req *http.Request) (*http.Response, error) {
199199

200200
return httpmock.NewJsonResponse(200, FeatureConfigResponse)
201201
}
202+
203+
func TestCfClient_Close(t *testing.T) {
204+
client, err := newClient(&http.Client{})
205+
if err != nil {
206+
t.Error(err)
207+
}
208+
209+
t.Log("When I close the client for the first time I should not get an error")
210+
assert.Nil(t, client.Close())
211+
212+
t.Log("When I close the client for the second time I should an error")
213+
assert.NotNil(t, client.Close())
214+
}

0 commit comments

Comments
 (0)