Skip to content

Commit 5c1bc00

Browse files
committed
Async initialize SDKs
1 parent e4a8c25 commit 5c1bc00

3 files changed

Lines changed: 39 additions & 5 deletions

File tree

sdk/sdk.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sdk
33
import (
44
"context"
55
"net/http"
6+
"sync"
67
"sync/atomic"
78
"time"
89

@@ -38,6 +39,7 @@ type Client interface {
3839
WebhookSigningKey() string
3940
WebhookSignatureValidFor() int
4041
IsInValidState() bool
42+
Ready() <-chan struct{}
4143
}
4244

4345
type Context struct {
@@ -59,8 +61,11 @@ type client struct {
5961
cache store.EntryStore
6062
sdkCtx *Context
6163
initialized atomic.Bool
64+
ready chan struct{}
65+
readyOnce sync.Once
6266
ctx context.Context
6367
ctxCancel func()
68+
mu sync.Mutex
6469
pubsub.Publisher[struct{}]
6570
}
6671

@@ -87,6 +92,7 @@ func NewClient(sdkCtx *Context, log log.Logger) Client {
8792
log: sdkLog,
8893
cache: storage.(store.EntryStore),
8994
sdkCtx: sdkCtx,
95+
ready: make(chan struct{}),
9096
defaultAttrs: model.MergeUserAttrs(sdkCtx.GlobalDefaultAttrs, sdkCtx.SDKConf.DefaultAttrs),
9197
}
9298
client.ctx, client.ctxCancel = context.WithCancel(context.Background())
@@ -131,7 +137,12 @@ func NewClient(sdkCtx *Context, log log.Logger) Client {
131137
clientConfig.DataGovernance = configcat.EUOnly
132138
}
133139
client.configCatClient = configcat.NewCustomClient(clientConfig)
134-
_ = client.Refresh(client.ctx)
140+
go func() {
141+
client.mu.Lock()
142+
defer client.mu.Unlock()
143+
_ = client.Refresh(client.ctx)
144+
close(client.ready)
145+
}()
135146

136147
if notifier, ok := storage.(store.NotifyingStore); ok {
137148
go client.listen(notifier)
@@ -185,14 +196,25 @@ func (c *client) signal() {
185196
c.Publish(struct{}{})
186197
}
187198

199+
func (c *client) ensureReady() {
200+
c.readyOnce.Do(func() {
201+
select {
202+
case <-c.ready:
203+
case <-c.ctx.Done():
204+
}
205+
})
206+
}
207+
188208
func (c *client) Eval(key string, user model.UserAttrs) model.EvalData {
209+
c.ensureReady()
189210
mergedUser := model.MergeUserAttrs(c.defaultAttrs, user)
190211
details := c.configCatClient.Snapshot(mergedUser).GetValueDetails(key)
191212
return model.EvalData{Value: details.Value, VariationId: details.Data.VariationID, User: details.Data.User, Error: details.Data.Error,
192213
IsTargeting: details.Data.MatchedPercentageOption != nil || details.Data.MatchedTargetingRule != nil}
193214
}
194215

195216
func (c *client) EvalAll(user model.UserAttrs) map[string]model.EvalData {
217+
c.ensureReady()
196218
mergedUser := model.MergeUserAttrs(c.defaultAttrs, user)
197219
allDetails := c.configCatClient.Snapshot(mergedUser).GetAllValueDetails()
198220
result := make(map[string]model.EvalData, len(allDetails))
@@ -204,10 +226,12 @@ func (c *client) EvalAll(user model.UserAttrs) map[string]model.EvalData {
204226
}
205227

206228
func (c *client) Keys() []string {
229+
c.ensureReady()
207230
return c.configCatClient.GetAllKeys()
208231
}
209232

210233
func (c *client) HasKey(key string) bool {
234+
c.ensureReady()
211235
keys := c.configCatClient.GetAllKeys()
212236
for _, k := range keys {
213237
if k == key {
@@ -218,6 +242,7 @@ func (c *client) HasKey(key string) bool {
218242
}
219243

220244
func (c *client) GetCachedJson() *store.EntryWithEtag {
245+
c.ensureReady()
221246
return c.cache.LoadEntry()
222247
}
223248

@@ -250,7 +275,13 @@ func (c *client) IsInValidState() bool {
250275
return !c.GetCachedJson().Empty
251276
}
252277

278+
func (c *client) Ready() <-chan struct{} {
279+
return c.ready
280+
}
281+
253282
func (c *client) Close() {
283+
c.mu.Lock()
284+
defer c.mu.Unlock()
254285
if notifier, ok := c.cache.(store.Notifier); ok {
255286
notifier.Close()
256287
}

sdk/sdk_registrar.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ type manualRegistrar struct {
2828
func NewRegistrar(conf *config.Config, telemetryReporter telemetry.Reporter, statusReporter status.Reporter, externalCache cache.ReaderWriter, log log.Logger) (Registrar, error) {
2929
if conf.Profile.IsSet() {
3030
return newAutoRegistrar(conf, telemetryReporter, statusReporter, externalCache, log)
31-
} else {
32-
return newManualRegistrar(conf, telemetryReporter, statusReporter, externalCache, log)
3331
}
32+
return newManualRegistrar(conf, telemetryReporter, statusReporter, externalCache, log)
3433
}
3534

3635
func newManualRegistrar(conf *config.Config, telemetryReporter telemetry.Reporter, statusReporter status.Reporter, externalCache cache.ReaderWriter, log log.Logger) (*manualRegistrar, error) {

web/webhook/webhook_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ func TestWebhook_Signature_Ok(t *testing.T) {
7171
req.Header.Set("X-ConfigCat-Webhook-Timestamp", timestamp)
7272
testutils.AddSdkIdContextParam(req)
7373
sub := make(chan struct{})
74-
reg.GetSdkOrNil("test").Subscribe(sub)
74+
cl := reg.GetSdkOrNil("test")
75+
<-cl.Ready()
76+
cl.Subscribe(sub)
7577
_ = h.SetFlags(key, map[string]*configcattest.Flag{"flag": {Default: false}})
7678
srv.ServeWebhookSdkId(res, req)
7779
testutils.WithTimeout(2*time.Second, func() {
@@ -97,7 +99,9 @@ func TestWebhook_Signature_Ok(t *testing.T) {
9799
req.Header.Set("X-ConfigCat-Webhook-Timestamp", timestamp)
98100
testutils.AddSdkIdContextParam(req)
99101
sub := make(chan struct{})
100-
reg.GetSdkOrNil("test").Subscribe(sub)
102+
cl := reg.GetSdkOrNil("test")
103+
<-cl.Ready()
104+
cl.Subscribe(sub)
101105
_ = h.SetFlags(key, map[string]*configcattest.Flag{"flag": {Default: false}})
102106
srv.ServeWebhookSdkId(res, req)
103107
testutils.WithTimeout(2*time.Second, func() {

0 commit comments

Comments
 (0)