Compound index is not working properly. In a test where I had one million indexed (ClassA & ClassB) entities and one million non-indexed (ClassC & ClassD), a search with index hint using the compound index had similar performance of a non-indexed search, being slightly above 50 seconds for both searches.
When using an indexed search with no query hint, the search took less then one millisecond (>1ms).
This matter has been discussed with @bigwad and the solutions is to not use index hints since the QP optimizes it better.
Posting it here for documentation.
Reproduction code snippet
using System;
using System.Diagnostics;
using System.Linq;
using Starcounter;
using static CompoundIndexTest.Test;
namespace CompoundIndexTest
{
class Program
{
static Action StarcounterCompoundIndexSearch;
static Action StarcounterFullTableScanSearch;
static Action StarcounterRegularIndexSearch;
static readonly ClassA TheAReference =
Db.SQL<ClassA>("SELECT t FROM CompoundIndexTest.ClassA t").FirstOrDefault() ??
Db.Transact(() => new ClassA());
static readonly ClassC TheCReference =
Db.SQL<ClassC>("SELECT t FROM CompoundIndexTest.ClassC t").FirstOrDefault() ??
Db.Transact(() => new ClassC());
static void Main()
{
Indexes();
GenerateData();
Init();
Warmup();
var t1 = TestCase(StarcounterRegularIndexSearch);
var t2 = TestCase(StarcounterRegularIndexSearch);
var t3 = TestCase(StarcounterCompoundIndexSearch);
var t4 = TestCase(StarcounterFullTableScanSearch);
var t5 = TestCase(StarcounterFullTableScanSearch);
var t6 = TestCase(StarcounterCompoundIndexSearch);
}
private static void Indexes()
{
if (Db.SQL("SELECT i FROM Starcounter.Metadata.\"Index\" i WHERE Name = ?", "ClassB_Reference_Created")
.FirstOrDefault() == null)
Db.SQL("CREATE INDEX ClassB_Reference_Created ON CompoundIndexTest.ClassB (Reference, Created DESC)");
}
private static void GenerateData()
{
if (Db.SQL<ClassB>("SELECT t FROM CompoundIndexTest.ClassB t").Any()) return;
Db.Transact(() =>
{
for (var i = 0; i < 500000; i++)
{
new ClassB {Reference = TheAReference};
}
for ( var i = 0; i < 500000; i++ )
{
new ClassD { Reference = TheCReference };
}
});
// Set the interesting value in the middle
Db.Transact(() => new ClassB(DateTime.MaxValue) {Reference = TheAReference});
Db.Transact(() => new ClassD(DateTime.MaxValue) {Reference = TheCReference});
Db.Transact(() =>
{
for (var i = 0; i < 500000; i++)
{
new ClassB {Reference = TheAReference};
}
for ( var i = 0; i < 500000; i++ )
{
new ClassD { Reference = TheCReference };
}
});
}
private static void Init()
{
StarcounterRegularIndexSearch = () =>
Db.SQL<ClassA>(
"SELECT a FROM CompoundIndexTest.ClassA a JOIN CompoundIndexTest.ClassB b ON b.Reference=a WHERE b.Reference =? ORDER BY b.Reference, b.Created DESC",
TheAReference).FirstOrDefault();
StarcounterCompoundIndexSearch = () =>
Db.SQL<ClassA>(
"SELECT a FROM CompoundIndexTest.ClassA a JOIN CompoundIndexTest.ClassB b ON b.Reference=a WHERE b.Reference =? ORDER BY b.Reference, b.Created DESC OPTION INDEX (b ClassB_Reference_Created)",
TheAReference).FirstOrDefault();
StarcounterFullTableScanSearch = () =>
Db.SQL<ClassC>(
"SELECT c FROM CompoundIndexTest.ClassC c JOIN CompoundIndexTest.ClassD d ON d.Reference=c WHERE d.Reference =? ORDER BY d.Reference, d.Created DESC",
TheCReference).FirstOrDefault();
}
private static void Warmup()
{
for (var i = 0; i < 1; i++)
{
var t1 = TestCase(StarcounterCompoundIndexSearch);
var t2 = TestCase(StarcounterFullTableScanSearch);
var t3 = TestCase(StarcounterRegularIndexSearch);
}
}
}
[Database]
public class ClassA
{
}
[Database]
public class ClassB
{
public ClassB() : this(DateTime.Now.AddMinutes(new Random().Next()))
{
}
public ClassB(DateTime created)
{
Created = created;
}
public DateTime Created { get; set; }
public ClassA Reference { get; set; }
}
[Database]
public class ClassC
{
}
[Database]
public class ClassD
{
public ClassD() : this(DateTime.Now.AddMinutes(new Random().Next()))
{
}
public ClassD(DateTime created)
{
Created = created;
}
public DateTime Created { get; set; }
public ClassC Reference { get; set; }
}
public static class Test
{
public static readonly Stopwatch watch = new Stopwatch();
public static long TestCase(Action a)
{
watch.Restart();
a.Invoke();
return watch.ElapsedMilliseconds;
}
}
}
Starcounter version:
<2.4.990.121>
.Issue type
Issue description
Compound index is not working properly. In a test where I had one million indexed (ClassA & ClassB) entities and one million non-indexed (ClassC & ClassD), a search with index hint using the compound index had similar performance of a non-indexed search, being slightly above 50 seconds for both searches.
When using an indexed search with no query hint, the search took less then one millisecond (>1ms).
This matter has been discussed with @bigwad and the solutions is to not use index hints since the QP optimizes it better.
Posting it here for documentation.
Reproduction code snippet
Query Plans
Compound Index
Full Table Scan
Regular Search