1+ import { MaxRetriesPerRequestError } from 'ioredis/built/errors' ;
2+
13import { interfaces , LDLogger } from '@launchdarkly/node-server-sdk' ;
24
35import RedisClientState from './RedisClientState' ;
@@ -29,6 +31,7 @@ export default class RedisCore implements interfaces.PersistentDataStore {
2931 constructor (
3032 private readonly state : RedisClientState ,
3133 private readonly logger ?: LDLogger ,
34+ private readonly localFeatureStore ?: any ,
3235 ) {
3336 this . initedKey = this . state . prefixedKey ( '$inited' ) ;
3437 }
@@ -99,6 +102,50 @@ export default class RedisCore implements interfaces.PersistentDataStore {
99102 } ) ;
100103 }
101104
105+ #serializeItems( kind : any , itemsObj : Record < string , any > ) {
106+ const serializedItemsObj : Record < string , any > = { } ;
107+ Object . keys ( itemsObj ) . forEach ( ( key ) => {
108+ const value = itemsObj [ key ] ;
109+ serializedItemsObj [ key ] = kind . serialize ( value ) . serializedItem ;
110+ } ) ;
111+ return serializedItemsObj ;
112+ }
113+
114+ #prepareArray( values : Record < string , string > ) {
115+ const results : interfaces . KeyedItem < string , interfaces . SerializedItemDescriptor > [ ] = [ ] ;
116+ Object . keys ( values ) . forEach ( ( key ) => {
117+ const value = values [ key ] ;
118+ // When getting we do not populate version and deleted.
119+ // The SDK will have to deserialize to access these values.
120+ results . push ( { key, item : { version : 0 , deleted : false , serializedItem : value } } ) ;
121+ } ) ;
122+ return results ;
123+ }
124+
125+ #useItemsFromCodefresh(
126+ kind : interfaces . PersistentStoreDataKind ,
127+ callback : (
128+ descriptors : interfaces . KeyedItem < string , interfaces . SerializedItemDescriptor > [ ] | undefined ,
129+ ) => void ,
130+ ) {
131+ this . localFeatureStore ( ) . then (
132+ ( items : any ) => {
133+ let localResults ;
134+ if ( kind . namespace === 'features' ) {
135+ localResults = items . features ;
136+ } else {
137+ localResults = items . segments ;
138+ }
139+ const serializedItems = this . #serializeItems( kind , localResults ) ;
140+ callback ( this . #prepareArray( serializedItems ) ) ;
141+ } ,
142+ ( error : any ) => {
143+ console . log ( error ) ;
144+ callback ( undefined ) ;
145+ } ,
146+ ) ;
147+ }
148+
102149 getAll (
103150 kind : interfaces . PersistentStoreDataKind ,
104151 callback : (
@@ -107,22 +154,17 @@ export default class RedisCore implements interfaces.PersistentDataStore {
107154 ) : void {
108155 if ( ! this . state . isConnected ( ) && ! this . state . isInitialConnection ( ) ) {
109156 this . logger ?. warn ( 'Attempted to fetch all keys while Redis connection is down' ) ;
110- callback ( undefined ) ;
111- return ;
157+ this . #useItemsFromCodefresh( kind , callback ) ;
112158 }
113159
114160 this . state . getClient ( ) . hgetall ( this . state . prefixedKey ( kind . namespace ) , ( err , values ) => {
115161 if ( err ) {
116162 this . logger ?. error ( `Error fetching '${ kind . namespace } ' from Redis ${ err } ` ) ;
163+ if ( err instanceof MaxRetriesPerRequestError ) {
164+ this . #useItemsFromCodefresh( kind , callback ) ;
165+ }
117166 } else if ( values ) {
118- const results : interfaces . KeyedItem < string , interfaces . SerializedItemDescriptor > [ ] = [ ] ;
119- Object . keys ( values ) . forEach ( ( key ) => {
120- const value = values [ key ] ;
121- // When getting we do not populate version and deleted.
122- // The SDK will have to deserialize to access these values.
123- results . push ( { key, item : { version : 0 , deleted : false , serializedItem : value } } ) ;
124- } ) ;
125- callback ( results ) ;
167+ callback ( this . #prepareArray( values ) ) ;
126168 } else {
127169 callback ( undefined ) ;
128170 }
0 commit comments