@@ -3,6 +3,7 @@ package sdk
33import (
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
4345type 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+
188208func (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
195216func (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
206228func (c * client ) Keys () []string {
229+ c .ensureReady ()
207230 return c .configCatClient .GetAllKeys ()
208231}
209232
210233func (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
220244func (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+
253282func (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 }
0 commit comments