Skip to content

Commit

Permalink
RavenDB-22953 Fixing sorting edge case in Corax where all matches hav…
Browse files Browse the repository at this point in the history
…e been found already so there is no need to fallback to normal sorting (which expected to get non empty batch of results)
  • Loading branch information
arekpalinski committed Oct 2, 2024
1 parent 316ab31 commit a9e1cbb
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 4 deletions.
12 changes: 8 additions & 4 deletions src/Corax/Querying/Matches/SortingMatches/SortingMatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,14 @@ private static void SortUsingIndex<TEntryComparer, TDirection>(ref SortingMatch<
// direct SortResult, but first we need to remove all the items that we already matched
int notMatchedYet = FilterAlreadyFoundMatches(allMatches);

// if we scanned through the index more than twice the amount of records of the query, but still
// didn't find enough to fill the page size, we'll fall back to normal sorting, instead of using the
// index method. That would prevent degenerate cases.
SortResults<TEntryComparer>(ref match, allMatches[..notMatchedYet]);
if (notMatchedYet > 0)
{
// if we scanned through the index more than twice the amount of records of the query, but still
// didn't find enough to fill the page size, we'll fall back to normal sorting, instead of using the
// index method. That would prevent degenerate cases.
SortResults<TEntryComparer>(ref match, allMatches[..notMatchedYet]);
}

return;
}

Expand Down
121 changes: 121 additions & 0 deletions test/SlowTests/Corax/RavenDB_22953.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Globalization;
using System.Linq;
using FastTests;
using Raven.Client.Documents.Indexes;
using Tests.Infrastructure;
using Xunit;
using Xunit.Abstractions;

namespace SlowTests.Corax;

public class RavenDB_22953 : RavenTestBase
{
public RavenDB_22953(ITestOutputHelper output) : base(output)
{
}

[RavenTheory(RavenTestCategory.Corax)]
[RavenData(SearchEngineMode = RavenSearchEngineMode.Corax)]
public void StreamingQueryMustNotThrowIndexOutOfRangeException(Options options)
{
using (var store = GetDocumentStore(options))
{
var timestamps = new[]
{
new DateTimeOffset(new DateTime(2024, 9, 2), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 3), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 5), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 12), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 13), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 15), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 17), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 20), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 21), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 22), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 22), TimeSpan.Zero),
new DateTimeOffset(new DateTime(2024, 9, 27), TimeSpan.Zero),
};

using (var bulk = store.BulkInsert())
{
for (int i = 0; i < 10000; i++)
{
bulk.Store(new Entry
{
Timestamp = timestamps[i % timestamps.Length],
Schema = "foo",
Name = "bar"
});
}
}

using (var bulk = store.BulkInsert())
{
for (int i = 0; i < 11000; i++)
{
bulk.Store(new Entry
{
Timestamp = timestamps[i % timestamps.Length],
Schema = "zzz",
Name = "bar"
});
}
}

new Entries_ByTimestampAndQualifiedName().Execute(store);

Indexes.WaitForIndexing(store);

using (var session = store.OpenSession())
{
var count = 0;

var q = session.Advanced.DocumentQuery<Entries_ByTimestampAndQualifiedName.IndexEntry, Entries_ByTimestampAndQualifiedName>()
.WhereGreaterThanOrEqual(x => x.Timestamp, new DateTimeOffset(new DateTime(2024, 9, 2), TimeSpan.Zero))
.WhereLessThanOrEqual(x => x.Timestamp, new DateTimeOffset(new DateTime(2024, 9, 27), TimeSpan.Zero))
.WhereIn(x => x.QualifiedName, new[] { "foo:bar" })
.OrderBy(x => x.Timestamp);

var s = session.Advanced.Stream(q);

while (s.MoveNext())
{
count++;
}

Assert.Equal(10000, count);
}
}
}

private class Entry
{
public DateTimeOffset? Timestamp { get; set; }

public string Name { get; set; }

public string Schema { get; set; }
}

private class Entries_ByTimestampAndQualifiedName : AbstractIndexCreationTask<Entry>
{
public class IndexEntry
{
public DateTimeOffset Timestamp { get; set; }

public string QualifiedName { get; set; }
}

public Entries_ByTimestampAndQualifiedName()
{
Map = entries => from entry in entries
select new IndexEntry
{
Timestamp = entry.Timestamp ??
default(DateTimeOffset),
QualifiedName = string.Format("{0}:{1}", entry.Schema, entry.Name),
};
}
}
}

0 comments on commit a9e1cbb

Please sign in to comment.