|
9 | 9 | using NetTopologySuite.IO;
|
10 | 10 | using NetTopologySuite.Geometries;
|
11 | 11 |
|
12 |
| - |
13 | 12 | namespace NRedisStack.Tests.Search;
|
14 | 13 |
|
15 | 14 | public class SearchTests : AbstractNRedisStackTest, IDisposable
|
@@ -1384,7 +1383,7 @@ public void TestDropIndex(string endpointId)
|
1384 | 1383 | }
|
1385 | 1384 | catch (RedisServerException ex)
|
1386 | 1385 | {
|
1387 |
| - Assert.Contains("no such index", ex.Message); |
| 1386 | + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); |
1388 | 1387 | }
|
1389 | 1388 | Assert.Equal("100", db.Execute("DBSIZE").ToString());
|
1390 | 1389 | }
|
@@ -1417,7 +1416,7 @@ public async Task TestDropIndexAsync(string endpointId)
|
1417 | 1416 | }
|
1418 | 1417 | catch (RedisServerException ex)
|
1419 | 1418 | {
|
1420 |
| - Assert.Contains("no such index", ex.Message); |
| 1419 | + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); |
1421 | 1420 | }
|
1422 | 1421 | Assert.Equal("100", db.Execute("DBSIZE").ToString());
|
1423 | 1422 | }
|
@@ -3416,52 +3415,89 @@ public void TestNumericLogicalOperatorsInDialect4(string endpointId)
|
3416 | 3415 | Assert.Equal(1, ft.Search(index, new Query("@version==123 @id==456").Dialect(4)).TotalResults);
|
3417 | 3416 | }
|
3418 | 3417 |
|
| 3418 | + /// <summary> |
| 3419 | + /// this test is to check if the issue 352 is fixed |
| 3420 | + /// Load operation was failing because the document was not being dropped in search result due to this behaviour; |
| 3421 | + /// "If a relevant key expires while a query is running, an attempt to load the key's value will return a null array. |
| 3422 | + /// However, the key is still counted in the total number of results." |
| 3423 | + /// https://redis.io/docs/latest/commands/ft.search/#:~:text=If%20a%20relevant%20key%20expires,the%20total%20number%20of%20results. |
| 3424 | + /// </summary> |
3419 | 3425 | [Fact]
|
3420 | 3426 | public void TestDocumentLoad_Issue352()
|
3421 | 3427 | {
|
3422 | 3428 | Document d = Document.Load("1", 0.5, null, new RedisValue[] { RedisValue.Null });
|
3423 | 3429 | Assert.Empty(d.GetProperties().ToList());
|
3424 | 3430 | }
|
3425 | 3431 |
|
| 3432 | + /// <summary> |
| 3433 | + /// this test is to check if the issue 352 is fixed |
| 3434 | + /// Load operation was failing because the document was not being dropped in search result due to this behaviour; |
| 3435 | + /// "If a relevant key expires while a query is running, an attempt to load the key's value will return a null array. |
| 3436 | + /// However, the key is still counted in the total number of results." |
| 3437 | + /// https://redis.io/docs/latest/commands/ft.search/#:~:text=If%20a%20relevant%20key%20expires,the%20total%20number%20of%20results. |
| 3438 | + /// </summary> |
3426 | 3439 | [SkippableTheory]
|
3427 | 3440 | [MemberData(nameof(EndpointsFixture.Env.StandaloneOnly), MemberType = typeof(EndpointsFixture.Env))]
|
3428 |
| - public void TestDocumentLoadWithDB_Issue352(string endpointId) |
| 3441 | + public async void TestDocumentLoadWithDB_Issue352(string endpointId) |
3429 | 3442 | {
|
3430 | 3443 | IDatabase db = GetCleanDatabase(endpointId);
|
3431 | 3444 | var ft = db.FT();
|
3432 | 3445 |
|
3433 |
| - Schema sc = new Schema().AddTextField("first", 1.0).AddTextField("last", 1.0).AddNumericField("age"); |
| 3446 | + Schema sc = new Schema().AddTextField("firstText", 1.0).AddTextField("lastText", 1.0).AddNumericField("ageNumeric"); |
3434 | 3447 | Assert.True(ft.Create(index, FTCreateParams.CreateParams(), sc));
|
3435 | 3448 |
|
3436 | 3449 | Document droppedDocument = null;
|
3437 | 3450 | int numberOfAttempts = 0;
|
3438 | 3451 | do
|
3439 | 3452 | {
|
3440 |
| - db.HashSet("student:1111", new HashEntry[] { new("first", "Joe"), new("last", "Dod"), new("age", 18) }); |
| 3453 | + // try until succesfully create the key and set the TTL |
| 3454 | + bool ttlRefreshed = false; |
| 3455 | + do |
| 3456 | + { |
| 3457 | + db.HashSet("student:22222", new HashEntry[] { new("firstText", "Joe"), new("lastText", "Dod"), new("ageNumeric", 18) }); |
| 3458 | + ttlRefreshed = db.KeyExpire("student:22222", TimeSpan.FromMilliseconds(500)); |
| 3459 | + } while (!ttlRefreshed); |
3441 | 3460 |
|
3442 |
| - Assert.True(db.KeyExpire("student:1111", TimeSpan.FromMilliseconds(500))); |
| 3461 | + Int32 completed = 0; |
3443 | 3462 |
|
3444 |
| - Boolean cancelled = false; |
3445 |
| - Task searchTask = Task.Run(() => |
| 3463 | + Action checker = () => |
3446 | 3464 | {
|
3447 |
| - for (int i = 0; i < 100000; i++) |
| 3465 | + for (int i = 0; i < 1000000; i++) |
3448 | 3466 | {
|
3449 | 3467 | SearchResult result = ft.Search(index, new Query());
|
3450 | 3468 | List<Document> docs = result.Documents;
|
3451 |
| - if (docs.Count == 0 || cancelled) |
| 3469 | + |
| 3470 | + // check if doc is already dropped before search and load; |
| 3471 | + // if yes then its already late and we missed the window that |
| 3472 | + // doc would show up in search result with no fields |
| 3473 | + if (docs.Count == 0) |
3452 | 3474 | {
|
| 3475 | + Interlocked.Increment(ref completed); |
3453 | 3476 | break;
|
3454 | 3477 | }
|
3455 |
| - else if (docs[0].GetProperties().ToList().Count == 0) |
| 3478 | + // if we get a document with no fields then we know that the key |
| 3479 | + // is going to be expired while the query is running, and we are able to catch the state |
| 3480 | + // but key itself might not be expired yet |
| 3481 | + else if (docs[0].GetProperties().Count() == 0) |
3456 | 3482 | {
|
3457 | 3483 | droppedDocument = docs[0];
|
3458 | 3484 | }
|
3459 | 3485 | }
|
3460 |
| - }); |
3461 |
| - Task.WhenAny(searchTask, Task.Delay(1000)).GetAwaiter().GetResult(); |
3462 |
| - Assert.True(searchTask.IsCompleted); |
3463 |
| - Assert.Null(searchTask.Exception); |
3464 |
| - cancelled = true; |
3465 |
| - } while (droppedDocument == null && numberOfAttempts++ < 3); |
| 3486 | + }; |
| 3487 | + |
| 3488 | + List<Task> tasks = new List<Task>(); |
| 3489 | + // try with 3 different tasks simultaneously to increase the chance of hitting it |
| 3490 | + for (int i = 0; i < 3; i++) { tasks.Add(Task.Run(checker)); } |
| 3491 | + Task checkTask = Task.WhenAll(tasks); |
| 3492 | + await Task.WhenAny(checkTask, Task.Delay(1000)); |
| 3493 | + var keyTtl = db.KeyTimeToLive("student:22222"); |
| 3494 | + Assert.Equal(0, keyTtl.HasValue ? keyTtl.Value.Milliseconds : 0); |
| 3495 | + Assert.Equal(3, completed); |
| 3496 | + } while (droppedDocument == null && numberOfAttempts++ < 5); |
| 3497 | + // we won't do an actual assert here since |
| 3498 | + // it is not guaranteed that window stays open wide enough to catch it. |
| 3499 | + // instead we attempt 5 times. |
| 3500 | + // Without fix for Issue352, document load in this case fails %100 with my local test runs,, and %100 success with fixed version. |
| 3501 | + // The results in pipeline should be the same. |
3466 | 3502 | }
|
3467 | 3503 | }
|
0 commit comments