8
8
using System . Runtime . InteropServices ;
9
9
using NetTopologySuite . IO ;
10
10
using NetTopologySuite . Geometries ;
11
+ using System . Linq . Expressions ;
12
+ using System . Collections ;
11
13
12
14
13
15
namespace NRedisStack . Tests . Search ;
@@ -3416,13 +3418,27 @@ public void TestNumericLogicalOperatorsInDialect4(string endpointId)
3416
3418
Assert . Equal ( 1 , ft . Search ( index , new Query ( "@version==123 @id==456" ) . Dialect ( 4 ) ) . TotalResults ) ;
3417
3419
}
3418
3420
3421
+ /// <summary>
3422
+ /// this test is to check if the issue 352 is fixed
3423
+ /// Load operation was failing because the document was not being dropped in search result due to this behaviour;
3424
+ /// "If a relevant key expires while a query is running, an attempt to load the key's value will return a null array.
3425
+ /// However, the key is still counted in the total number of results."
3426
+ /// https://redis.io/docs/latest/commands/ft.search/#:~:text=If%20a%20relevant%20key%20expires,the%20total%20number%20of%20results.
3427
+ /// </summary>
3419
3428
[ Fact ]
3420
3429
public void TestDocumentLoad_Issue352 ( )
3421
3430
{
3422
3431
Document d = Document . Load ( "1" , 0.5 , null , new RedisValue [ ] { RedisValue . Null } ) ;
3423
3432
Assert . Empty ( d . GetProperties ( ) . ToList ( ) ) ;
3424
3433
}
3425
3434
3435
+ /// <summary>
3436
+ /// this test is to check if the issue 352 is fixed
3437
+ /// Load operation was failing because the document was not being dropped in search result due to this behaviour;
3438
+ /// "If a relevant key expires while a query is running, an attempt to load the key's value will return a null array.
3439
+ /// However, the key is still counted in the total number of results."
3440
+ /// https://redis.io/docs/latest/commands/ft.search/#:~:text=If%20a%20relevant%20key%20expires,the%20total%20number%20of%20results.
3441
+ /// </summary>
3426
3442
[ SkippableTheory ]
3427
3443
[ MemberData ( nameof ( EndpointsFixture . Env . StandaloneOnly ) , MemberType = typeof ( EndpointsFixture . Env ) ) ]
3428
3444
public void TestDocumentLoadWithDB_Issue352 ( string endpointId )
@@ -3435,33 +3451,56 @@ public void TestDocumentLoadWithDB_Issue352(string endpointId)
3435
3451
3436
3452
Document droppedDocument = null ;
3437
3453
int numberOfAttempts = 0 ;
3454
+
3438
3455
do
3439
3456
{
3440
- db . HashSet ( "student:1111" , new HashEntry [ ] { new ( "first" , "Joe" ) , new ( "last" , "Dod" ) , new ( "age" , 18 ) } ) ;
3441
-
3442
- Assert . True ( db . KeyExpire ( "student:1111" , TimeSpan . FromMilliseconds ( 500 ) ) ) ;
3457
+ // try until succesfully create the key and set the TTL
3458
+ bool ttlRefreshed = false ;
3459
+ do
3460
+ {
3461
+ db . HashSet ( "student:1111" , new HashEntry [ ] { new ( "first" , "Joe" ) , new ( "last" , "Dod" ) , new ( "age" , 18 ) } ) ;
3462
+ ttlRefreshed = db . KeyExpire ( "student:1111" , TimeSpan . FromMilliseconds ( 500 ) ) ;
3463
+ } while ( ! ttlRefreshed ) ;
3443
3464
3444
3465
Boolean cancelled = false ;
3445
- Task searchTask = Task . Run ( ( ) =>
3466
+ Action checker = ( ) =>
3446
3467
{
3447
- for ( int i = 0 ; i < 100000 ; i ++ )
3468
+ for ( int i = 0 ; i < 100000 && ! cancelled ; i ++ )
3448
3469
{
3449
3470
SearchResult result = ft . Search ( index , new Query ( ) ) ;
3450
3471
List < Document > docs = result . Documents ;
3451
- if ( docs . Count == 0 || cancelled )
3472
+ // check if doc is already dropped before search and load;
3473
+ // if yes then its already late and we missed the window that
3474
+ // doc would show up in search result with no fields
3475
+ if ( docs . Count == 0 )
3452
3476
{
3453
3477
break ;
3454
3478
}
3455
- else if ( docs [ 0 ] . GetProperties ( ) . ToList ( ) . Count == 0 )
3479
+ // if we get a document with no fields then we know that the key
3480
+ // expired while the query is running, and we are able to catch the state
3481
+ // so we can break the loop
3482
+ else if ( docs [ 0 ] . GetProperties ( ) . Count ( ) == 0 )
3456
3483
{
3457
3484
droppedDocument = docs [ 0 ] ;
3485
+ break ;
3458
3486
}
3459
3487
}
3460
- } ) ;
3461
- Task . WhenAny ( searchTask , Task . Delay ( 1000 ) ) . GetAwaiter ( ) . GetResult ( ) ;
3462
- Assert . True ( searchTask . IsCompleted ) ;
3463
- Assert . Null ( searchTask . Exception ) ;
3488
+ } ;
3489
+
3490
+ List < Task > tasks = new List < Task > ( ) ;
3491
+ // try with 3 different tasks simultaneously to increase the chance of hitting it
3492
+ for ( int i = 0 ; i < 3 ; i ++ )
3493
+ {
3494
+ tasks . Add ( Task . Run ( checker ) ) ;
3495
+ }
3496
+ Task checkTask = Task . WhenAll ( tasks ) ;
3497
+ Task . WhenAny ( checkTask , Task . Delay ( 1500 ) ) . GetAwaiter ( ) . GetResult ( ) ;
3498
+ Assert . True ( checkTask . IsCompleted ) ;
3499
+ Assert . Null ( checkTask . Exception ) ;
3464
3500
cancelled = true ;
3465
- } while ( droppedDocument == null && numberOfAttempts ++ < 3 ) ;
3501
+ } while ( droppedDocument == null && numberOfAttempts ++ < 5 ) ;
3502
+ // we wont do an actual assert here since
3503
+ // it is not guaranteed that window stays open wide enough to catch it.
3504
+ // instead we attempt 5 times
3466
3505
}
3467
3506
}
0 commit comments