@@ -281,7 +281,7 @@ public async Task<CacheValue<ICollection<T>>> GetListAsync<T>(string key, int? p
281281 if ( page is < 1 )
282282 throw new ArgumentOutOfRangeException ( nameof ( page ) , "Page cannot be less than 1" ) ;
283283
284- await ExpireListValuesAsync < T > ( key , typeof ( T ) == typeof ( string ) ) . AnyContext ( ) ;
284+ await RemoveExpiredListValuesAsync < T > ( key , typeof ( T ) == typeof ( string ) ) . AnyContext ( ) ;
285285
286286 if ( ! page . HasValue )
287287 {
@@ -311,37 +311,28 @@ public async Task<long> ListAddAsync<T>(string key, IEnumerable<T> values, TimeS
311311 throw new ArgumentNullException ( nameof ( key ) , "Key cannot be null or empty" ) ;
312312
313313 if ( values == null )
314- throw new ArgumentNullException ( nameof ( values ) ) ;
314+ return 0 ;
315315
316316 var expiresAt = expiresIn . HasValue ? _timeProvider . GetUtcNow ( ) . UtcDateTime . SafeAdd ( expiresIn . Value ) : DateTime . MaxValue ;
317317 if ( expiresAt < _timeProvider . GetUtcNow ( ) . UtcDateTime )
318- {
319- await ListRemoveAsync ( key , values ) . AnyContext ( ) ;
320- return 0 ;
321- }
318+ return await ListRemoveAsync ( key , values ) . AnyContext ( ) ;
322319
323320 var redisValues = new List < SortedSetEntry > ( ) ;
324321 long expiresAtMilliseconds = expiresAt . ToUnixTimeMilliseconds ( ) ;
325322
326323 if ( values is string stringValue )
327- {
328324 redisValues . Add ( new SortedSetEntry ( stringValue . ToRedisValue ( _options . Serializer ) , expiresAtMilliseconds ) ) ;
329- }
330325 else
331- {
332- var valuesArray = values . Where ( v => v is not null ) . ToArray ( ) ;
333- foreach ( var value in valuesArray )
334- redisValues . Add ( new SortedSetEntry ( value . ToRedisValue ( _options . Serializer ) , expiresAtMilliseconds ) ) ;
335- }
326+ redisValues . AddRange ( values . Where ( v => v is not null ) . Select ( value => new SortedSetEntry ( value . ToRedisValue ( _options . Serializer ) , expiresAtMilliseconds ) ) ) ;
336327
337- await ExpireListValuesAsync < T > ( key , values is string ) . AnyContext ( ) ;
328+ await RemoveExpiredListValuesAsync < T > ( key , values is string ) . AnyContext ( ) ;
338329
339330 if ( redisValues . Count == 0 )
340331 return 0 ;
341332
342333 long added = await Database . SortedSetAddAsync ( key , redisValues . ToArray ( ) ) . AnyContext ( ) ;
343334 if ( added > 0 )
344- await ExpireListKeyExpirationAsync ( key ) . AnyContext ( ) ;
335+ await SetListExpirationAsync ( key ) . AnyContext ( ) ;
345336
346337 return added ;
347338 }
@@ -352,46 +343,47 @@ public async Task<long> ListRemoveAsync<T>(string key, IEnumerable<T> values, Ti
352343 throw new ArgumentNullException ( nameof ( key ) , "Key cannot be null or empty" ) ;
353344
354345 if ( values == null )
355- throw new ArgumentNullException ( nameof ( values ) ) ;
346+ return 0 ;
356347
357348 var redisValues = new List < RedisValue > ( ) ;
358349 if ( values is string stringValue )
359350 redisValues . Add ( stringValue . ToRedisValue ( _options . Serializer ) ) ;
360351 else
361- foreach ( var value in values . Where ( v => v is not null ) )
362- redisValues . Add ( value . ToRedisValue ( _options . Serializer ) ) ;
352+ redisValues . AddRange ( values . Where ( v => v is not null ) . Select ( value => value . ToRedisValue ( _options . Serializer ) ) ) ;
363353
364- await ExpireListValuesAsync < T > ( key , values is string ) . AnyContext ( ) ;
354+ await RemoveExpiredListValuesAsync < T > ( key , values is string ) . AnyContext ( ) ;
365355
366356 if ( redisValues . Count == 0 )
367357 return 0 ;
368358
369359 long removed = await Database . SortedSetRemoveAsync ( key , redisValues . ToArray ( ) ) . AnyContext ( ) ;
370360 if ( removed > 0 )
371- await ExpireListKeyExpirationAsync ( key ) . AnyContext ( ) ;
361+ await SetListExpirationAsync ( key ) . AnyContext ( ) ;
372362
373363 return removed ;
374364 }
375365
376- private async Task ExpireListKeyExpirationAsync ( string key )
366+ private const long MaxUnixEpochMilliseconds = 253_402_300_799_999L ; // 9999-12-31T23:59:59.999Z
367+
368+ private async Task SetListExpirationAsync ( string key )
377369 {
378370 var items = await Database . SortedSetRangeByRankWithScoresAsync ( key , 0 , 0 , order : Order . Descending ) . AnyContext ( ) ;
379371 if ( items . Length == 0 )
380372 return ;
381373
382374 long highestExpirationInMs = ( long ) items . Single ( ) . Score ;
383- if ( highestExpirationInMs is < - 62135596800000 or > 253402300799999 )
375+ if ( highestExpirationInMs > MaxUnixEpochMilliseconds )
384376 {
385- _logger . LogWarning ( "List key {Key} has an invalid expiration value: {Expiration}. Setting Expiration to DateTime. MaxValue" , key , highestExpirationInMs ) ;
386- highestExpirationInMs = 253402300799999 ;
377+ await SetExpirationAsync ( key , TimeSpan . MaxValue ) . AnyContext ( ) ;
378+ return ;
387379 }
388380
389381 var furthestExpirationUtc = highestExpirationInMs . FromUnixTimeMilliseconds ( ) ;
390382 var expiresIn = furthestExpirationUtc - _timeProvider . GetUtcNow ( ) ;
391383 await SetExpirationAsync ( key , expiresIn ) . AnyContext ( ) ;
392384 }
393385
394- private async Task ExpireListValuesAsync < T > ( string key , bool isStringValues )
386+ private async Task RemoveExpiredListValuesAsync < T > ( string key , bool isStringValues )
395387 {
396388 try
397389 {
0 commit comments