@@ -44,15 +44,14 @@ const genKeyStrings = (n, identifier) => {
4444 return ( new Array ( n ) . fill ( 0 ) . map ( ( _ , idx ) => {
4545 const char = String . fromCharCode ( 97 + idx % 26 )
4646 // list-[a-z]-[0-(N-1)]
47- return `${ uniquePrefix } __ ${ identifier } _${ char } _${ idx } `
47+ return `${ identifier } _${ char } _${ idx } `
4848 } ) )
4949}
50- const putKeys = async ( state , keys , ttl ) => {
50+ const putKeys = async ( state , keys , { ttl, batchSize = 50 } ) => {
5151 const _putKeys = async ( keys , ttl ) => {
5252 await Promise . all ( keys . map ( async ( k , idx ) => await state . put ( k , `value-${ idx } ` , { ttl } ) ) )
5353 }
5454
55- const batchSize = 20
5655 let i = 0
5756 while ( i < keys . length - batchSize ) {
5857 await _putKeys ( keys . slice ( i , i + batchSize ) , ttl )
@@ -62,6 +61,13 @@ const putKeys = async (state, keys, ttl) => {
6261 await _putKeys ( keys . slice ( i ) , ttl )
6362}
6463const waitFor = ( ms ) => new Promise ( resolve => setTimeout ( resolve , ms ) )
64+ const listAll = async ( state , options = { } ) => {
65+ const acc = [ ]
66+ for await ( const { keys } of state . list ( options ) ) {
67+ acc . push ( ...keys )
68+ }
69+ return acc
70+ }
6571
6672test ( 'env vars' , ( ) => {
6773 expect ( process . env . TEST_AUTH_1 ) . toBeDefined ( )
@@ -166,113 +172,91 @@ describe('e2e tests using OpenWhisk credentials (as env vars)', () => {
166172 await expect ( state . put ( testKey , testValue , { ttl : - 1 } ) ) . rejects . toThrow ( )
167173 } )
168174
169- test ( 'listKeys test: few, many, and expired entries ' , async ( ) => {
175+ test ( 'list: countHint & match ' , async ( ) => {
170176 const state = await initStateEnv ( )
171177
172- // 1. test with not many elements, one iteration should return all
173- const keys90 = genKeyStrings ( 90 , 'list' ) . sort ( )
174- await putKeys ( state , keys90 , 60 )
175-
176- let it , ret
177-
178- // note: when listing all we are not in isolation
179- it = state . list ( )
180- ret = await it . next ( )
181- expect ( ret . value . keys . length ) . toBeGreaterThanOrEqual ( 90 )
182-
183- it = state . list ( { match : `${ uniquePrefix } __list_*` } )
184- ret = await it . next ( )
185- expect ( ret . value . keys . sort ( ) ) . toEqual ( keys90 )
186- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
187-
188- it = state . list ( { match : `${ uniquePrefix } __list_a*` } )
189- ret = await it . next ( )
190- expect ( ret . value . keys . sort ( ) ) . toEqual ( [
191- `${ uniquePrefix } __list_a_0` ,
192- `${ uniquePrefix } __list_a_26` ,
193- `${ uniquePrefix } __list_a_52` ,
194- `${ uniquePrefix } __list_a_78`
195- ] )
196- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
197-
198- it = state . list ( { match : `${ uniquePrefix } __list_*_1` } )
199- ret = await it . next ( )
200- expect ( ret . value . keys . sort ( ) ) . toEqual ( [ `${ uniquePrefix } __list_b_1` ] )
201- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
202-
203- // 2. test with many elements and large countHint
204- const keys900 = genKeyStrings ( 900 , 'list' )
205- await putKeys ( state , keys900 , 60 )
206-
207- // note: we can't list in isolation without prefix
208- it = state . list ( { countHint : 1000 } )
209- ret = await it . next ( )
210- expect ( ret . value . keys . length ) . toBeGreaterThanOrEqual ( 900 )
211-
212- it = state . list ( { countHint : 1000 , match : `${ uniquePrefix } __li*t_*` } )
213- ret = await it . next ( )
214- expect ( ret . value . keys . length ) . toEqual ( 900 )
215- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
216-
217- it = state . list ( { countHint : 1000 , match : `${ uniquePrefix } __list_z*` } )
218- ret = await it . next ( )
219- expect ( ret . value . keys . length ) . toEqual ( 34 )
220- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
221-
222- it = state . list ( { match : `${ uniquePrefix } __list_*_1` } )
223- ret = await it . next ( )
224- expect ( ret . value . keys . sort ( ) ) . toEqual ( [ `${ uniquePrefix } __list_b_1` ] )
225- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
226-
227- // 3. test with many elements while iterating
228- let iterations = 0
229- let retArray = [ ]
230- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __l*st_*` } ) ) {
231- iterations ++
232- retArray . push ( ...keys )
233- }
234- expect ( iterations ) . toBeGreaterThan ( 5 ) // should be around 9-10
235- expect ( retArray . length ) . toEqual ( 900 )
236-
237- iterations = 0
238- retArray = [ ]
239- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __list_z*` } ) ) {
240- iterations ++
241- retArray . push ( ...keys )
242- }
243- expect ( iterations ) . toEqual ( 1 )
244- expect ( retArray . length ) . toEqual ( 34 )
178+ const prefix = `${ uniquePrefix } __list`
179+ const keys900 = genKeyStrings ( 900 , prefix ) . sort ( )
180+ await putKeys ( state , keys900 , { ttl : 60 } )
181+
182+ // listAll without match, note that other keys may be stored in namespace.
183+ const retAll = await listAll ( state )
184+ expect ( retAll . length ) . toBeGreaterThanOrEqual ( 900 )
185+
186+ // default countHint = 100
187+ const retHint100 = await listAll ( state , { match : `${ uniquePrefix } __list*` } )
188+ expect ( retHint100 . length ) . toEqual ( 900 )
189+ expect ( retHint100 . sort ( ) ) . toEqual ( keys900 )
190+
191+ // set countHint = 1000
192+ // in most cases, list should return in 1 iteration,
193+ // but we can't guarantee this as the server may return with less keys and
194+ // require additional iterations, especially if there are many keys in the namespace.
195+ // This is why we call listAll with countHint 1000 too.
196+ const retHint1000 = await listAll ( state , { match : `${ uniquePrefix } __list*` , countHint : 1000 } )
197+ expect ( retHint1000 . length ) . toEqual ( 900 )
198+ expect ( retHint1000 . sort ( ) ) . toEqual ( keys900 )
199+
200+ // sub patterns
201+ const retA = await listAll ( state , { match : `${ uniquePrefix } __list_a*` } )
202+ expect ( retA . length ) . toEqual ( 35 )
203+ expect ( retA ) . toContain (
204+ `${ uniquePrefix } __list_a_26`
205+ )
206+
207+ const ret1 = await listAll ( state , { match : `${ uniquePrefix } __list_*_1` } )
208+ expect ( ret1 . length ) . toEqual ( 1 )
209+
210+ const retstar = await listAll ( state , { match : `${ uniquePrefix } __l*st_*` } )
211+ expect ( retstar . length ) . toEqual ( 900 )
212+ } )
245213
246- iterations = 0
247- retArray = [ ]
248- for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __list_*_1` } ) ) {
249- iterations ++
250- retArray . push ( ...keys )
251- }
252- expect ( iterations ) . toEqual ( 1 )
253- expect ( retArray . length ) . toEqual ( 1 )
214+ test ( 'list expired keys' , async ( ) => {
215+ const state = await initStateEnv ( )
254216
255- // 4. make sure expired keys aren't listed
256- await putKeys ( state , keys90 , 1 )
217+ // make sure expired keys aren't listed
218+ const keysExpired = genKeyStrings ( 90 , `${ uniquePrefix } __exp_yes` )
219+ const keysNotExpired = genKeyStrings ( 90 , `${ uniquePrefix } __exp_no` ) . sort ( )
220+ await putKeys ( state , keysExpired , { ttl : 1 } )
221+ await putKeys ( state , keysNotExpired , { ttl : 120 } )
257222 await waitFor ( 2000 )
258223
259- it = state . list ( { countHint : 1000 , match : ` ${ uniquePrefix } __list_*` } )
260- ret = await it . next ( )
261- expect ( ret . value . keys . length ) . toEqual ( 810 ) // 900 - 90
262- expect ( await it . next ( ) ) . toEqual ( { done : true , value : undefined } )
224+ // Note, we don't guarantee not returning expired keys, and in some rare cases it may happen.
225+ // if the test fails we should disable it.
226+ const ret = await listAll ( state , { match : ` ${ uniquePrefix } __exp*` } )
227+ expect ( ret . sort ( ) ) . toEqual ( keysNotExpired )
263228 } )
264229
230+ test ( 'list while having a large dataset stored' , async ( ) => {
231+ // reason: https://github.com/adobe/aio-lib-state/issues/194
232+ const state = await initStateEnv ( )
233+
234+ const keysBig = genKeyStrings ( 15000 , `${ uniquePrefix } __big_list` ) . sort ( )
235+ await putKeys ( state , keysBig , { ttl : 300 } )
236+
237+ const keysSmall = genKeyStrings ( 82 , `${ uniquePrefix } __small_list` ) . sort ( )
238+ await putKeys ( state , keysSmall , { ttl : 300 } ) // ttl=300s
239+
240+ // ensure we can list adhoc data
241+ const retArray = [ ]
242+ for await ( const { keys } of state . list ( { match : `${ uniquePrefix } __small_list*` , countHint : 100 } ) ) {
243+ retArray . push ( ...keys )
244+ }
245+ // in this test we want to make sure that list works even when many keys are included
246+ expect ( retArray . length ) . toEqual ( 82 )
247+ } , 300 * 1000 )
248+
265249 test ( 'deleteAll test' , async ( ) => {
266250 const state = await initStateEnv ( )
267251
268252 // < 100 keys
269- const keys90 = genKeyStrings ( 90 , 'deleteAll' ) . sort ( )
253+ const keys90 = genKeyStrings ( 90 , ` ${ uniquePrefix } __deleteAll` ) . sort ( )
270254 await putKeys ( state , keys90 , 60 )
271255 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_a*` } ) ) . toEqual ( { keys : 4 } )
272256 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*` } ) ) . toEqual ( { keys : 86 } )
273257
274258 // > 1000 keys
275- const keys1100 = genKeyStrings ( 1100 , 'deleteAll' ) . sort ( )
259+ const keys1100 = genKeyStrings ( 1100 , ` ${ uniquePrefix } __deleteAll` ) . sort ( )
276260 await putKeys ( state , keys1100 , 60 )
277261 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*_1` } ) ) . toEqual ( { keys : 1 } )
278262 expect ( await state . deleteAll ( { match : `${ uniquePrefix } __deleteAll_*_1*0` } ) ) . toEqual ( { keys : 21 } ) // 10, 100 - 190, 1000-1090
0 commit comments