1 package ldcomponents 2 3 import ( 4 "time" 5 6 "github.com/launchdarkly/go-sdk-common/v3/ldvalue" 7 "github.com/launchdarkly/go-server-sdk/v6/internal/datastore" 8 "github.com/launchdarkly/go-server-sdk/v6/subsystems" 9 ) 10 11 // PersistentDataStoreDefaultCacheTime is the default amount of time that recently read or updated items 12 // will be cached in memory, if you use [PersistentDataStore]. You can specify otherwise with 13 // [PersistentDataStoreBuilder.CacheTime]. 14 const PersistentDataStoreDefaultCacheTime = 15 * time.Second 15 16 // PersistentDataStore returns a configuration builder for some implementation of a persistent data store. 17 // 18 // The return value of this function should be stored in the DataStore field of 19 // [github.com/launchdarkly/go-server-sdk/v6.Config]. 20 // 21 // This method is used in conjunction with another configuration builder object provided by specific 22 // packages such as the Redis integration. Each LaunchDarkly Go SDK database integration has a 23 // DataStore() method that returns a configuration builder, with builder methods for options that 24 // are specific to that database. The SDK also provides some universal behaviors for all persistent 25 // data stores, such as caching; [PersistentDataStoreBuilder] provides methods to configure those 26 // behaviors. For instance, in this example, URL() is an option that is specific to the Redis 27 // integration, whereas CacheSeconds is not specific to Redis: 28 // 29 // config := ld.Config{ 30 // DataStore: ldcomponents.PersistentDataStore( 31 // ldredis.DataStore().URL("redis://my-redis-host"), 32 // ).CacheSeconds(15), 33 // } 34 // 35 // See PersistentDataStoreBuilder for more on how this method is used. 36 // 37 // For more information on the available persistent data store implementations, see the reference 38 // guide on "Persistent data stores": https://docs.launchdarkly.com/sdk/concepts/data-stores 39 func PersistentDataStore( 40 persistentDataStoreFactory subsystems.ComponentConfigurer[subsystems.PersistentDataStore], 41 ) *PersistentDataStoreBuilder { 42 return &PersistentDataStoreBuilder{ 43 persistentDataStoreFactory: persistentDataStoreFactory, 44 cacheTTL: PersistentDataStoreDefaultCacheTime, 45 } 46 } 47 48 // PersistentDataStoreBuilder is a configurable factory for a persistent data store. 49 // 50 // Each LaunchDarkly Go SDK database integration has its own configuration builder, with builder methods 51 // for options that are specific to that database. The SDK also provides some universal behaviors for all 52 // persistent data stores, such as caching; PersistentDataStoreBuilder provides methods to configure those 53 // behaviors. For instance, in this example, URL() is an option that is specific to the Redis 54 // integration, whereas CacheSeconds is not specific to Redis: 55 // 56 // config := ld.Config{ 57 // DataStore: ldcomponents.PersistentDataStore( 58 // ldredis.DataStore().URL("redis://my-redis-host"), 59 // ).CacheSeconds(15), 60 // } 61 type PersistentDataStoreBuilder struct { 62 persistentDataStoreFactory subsystems.ComponentConfigurer[subsystems.PersistentDataStore] 63 cacheTTL time.Duration 64 } 65 66 // CacheTime specifies the cache TTL. Items will be evicted from the cache after this amount of time 67 // from the time when they were originally cached. 68 // 69 // If the value is zero, caching is disabled (equivalent to [PersistentDataStoreBuilder.NoCaching]). 70 // 71 // If the value is negative, data is cached forever (equivalent to [PersistentDataStoreBuilder.CacheForever]). 72 func (b *PersistentDataStoreBuilder) CacheTime(cacheTime time.Duration) *PersistentDataStoreBuilder { 73 b.cacheTTL = cacheTime 74 return b 75 } 76 77 // CacheSeconds is a shortcut for calling [PersistentDataStoreBuilder.CacheTime] with a duration in seconds. 78 func (b *PersistentDataStoreBuilder) CacheSeconds(cacheSeconds int) *PersistentDataStoreBuilder { 79 return b.CacheTime(time.Duration(cacheSeconds) * time.Second) 80 } 81 82 // CacheForever specifies that the in-memory cache should never expire. In this mode, data will be 83 // written to both the underlying persistent store and the cache, but will only ever be read from the 84 // persistent store if the SDK is restarted. 85 // 86 // Use this mode with caution: it means that in a scenario where multiple processes are sharing 87 // the database, and the current process loses connectivity to LaunchDarkly while other processes 88 // are still receiving updates and writing them to the database, the current process will have 89 // stale data. 90 func (b *PersistentDataStoreBuilder) CacheForever() *PersistentDataStoreBuilder { 91 return b.CacheTime(-1 * time.Millisecond) 92 } 93 94 // NoCaching specifies that the SDK should not use an in-memory cache for the persistent data store. 95 // This means that every feature flag evaluation will trigger a data store query. 96 func (b *PersistentDataStoreBuilder) NoCaching() *PersistentDataStoreBuilder { 97 return b.CacheTime(0) 98 } 99 100 // Build is called internally by the SDK. 101 func (b *PersistentDataStoreBuilder) Build(clientContext subsystems.ClientContext) (subsystems.DataStore, error) { 102 core, err := b.persistentDataStoreFactory.Build(clientContext) 103 if err != nil { 104 return nil, err 105 } 106 return datastore.NewPersistentDataStoreWrapper(core, clientContext.GetDataStoreUpdateSink(), b.cacheTTL, 107 clientContext.GetLogging().Loggers), nil 108 } 109 110 // DescribeConfiguration is used internally by the SDK to inspect the configuration. 111 func (b *PersistentDataStoreBuilder) DescribeConfiguration(context subsystems.ClientContext) ldvalue.Value { 112 if dd, ok := b.persistentDataStoreFactory.(subsystems.DiagnosticDescription); ok { 113 return dd.DescribeConfiguration(context) 114 } 115 return ldvalue.String("custom") 116 } 117