88using System . Runtime . InteropServices ;
99using NetTopologySuite . IO ;
1010using NetTopologySuite . Geometries ;
11+ using System . Linq . Expressions ;
12+ using System . Collections ;
1113
1214
1315namespace NRedisStack . Tests . Search ;
@@ -3416,13 +3418,27 @@ public void TestNumericLogicalOperatorsInDialect4(string endpointId)
34163418 Assert . Equal ( 1 , ft . Search ( index , new Query ( "@version==123 @id==456" ) . Dialect ( 4 ) ) . TotalResults ) ;
34173419 }
34183420
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>
34193428 [ Fact ]
34203429 public void TestDocumentLoad_Issue352 ( )
34213430 {
34223431 Document d = Document . Load ( "1" , 0.5 , null , new RedisValue [ ] { RedisValue . Null } ) ;
34233432 Assert . Empty ( d . GetProperties ( ) . ToList ( ) ) ;
34243433 }
34253434
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>
34263442 [ SkippableTheory ]
34273443 [ MemberData ( nameof ( EndpointsFixture . Env . StandaloneOnly ) , MemberType = typeof ( EndpointsFixture . Env ) ) ]
34283444 public void TestDocumentLoadWithDB_Issue352 ( string endpointId )
@@ -3435,33 +3451,56 @@ public void TestDocumentLoadWithDB_Issue352(string endpointId)
34353451
34363452 Document droppedDocument = null ;
34373453 int numberOfAttempts = 0 ;
3454+
34383455 do
34393456 {
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 ) ;
34433464
34443465 Boolean cancelled = false ;
3445- Task searchTask = Task . Run ( ( ) =>
3466+ Action checker = ( ) =>
34463467 {
3447- for ( int i = 0 ; i < 100000 ; i ++ )
3468+ for ( int i = 0 ; i < 100000 && ! cancelled ; i ++ )
34483469 {
34493470 SearchResult result = ft . Search ( index , new Query ( ) ) ;
34503471 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 )
34523476 {
34533477 break ;
34543478 }
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 )
34563483 {
34573484 droppedDocument = docs [ 0 ] ;
3485+ break ;
34583486 }
34593487 }
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 ) ;
34643500 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
34663505 }
34673506}
0 commit comments