public async Task GuidQueryPartitioner_partitions_guids_fairly() { var totalNumberOfGuids = 1000; var numberOfPartitions = 50; var partitions = StreamQuery.Partition( Guid.Empty, Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff")) .Among(numberOfPartitions); var guids = Enumerable.Range(1, totalNumberOfGuids).Select(_ => Guid.NewGuid()).ToArray(); var partitioner = Stream.Partition<Guid, int, Guid>( async (q, p) => guids.Where(g => new SqlGuid(g).CompareTo(new SqlGuid(p.LowerBoundExclusive)) > 0 && new SqlGuid(g).CompareTo(new SqlGuid(p.UpperBoundInclusive)) <= 0), advanceCursor: (q, b) => q.Cursor.AdvanceTo(totalNumberOfGuids)); var aggregator = Aggregator.Create<Projection<HashSet<Guid>, int>, Guid>((p, b) => { if (p.Value == null) { p.Value = new HashSet<Guid>(); } foreach (var guid in b) { p.Value.Add(guid); } }); var store = new InMemoryProjectionStore<Projection<HashSet<Guid>, int>>(); await Task.WhenAll(partitions.Select(async partition => { var stream = await partitioner.GetStream(partition); var catchup = StreamCatchup.Create(stream, batchCount: int.MaxValue); catchup.Subscribe(aggregator, store); await catchup.RunSingleBatch(); await Task.Delay(500); var projection = await store.Get(stream.Id); Console.WriteLine(partition + ": " + projection.Value.Count); })); var approximateGuidsPerPartition = totalNumberOfGuids/numberOfPartitions; var tolerance = (int) (totalNumberOfGuids*.12); Console.WriteLine("\nMissing guids: "); foreach (var guid in guids.Where(g => !store.Any(p => p.Value.Contains(g)))) { Console.WriteLine(" " + guid); } store.Sum(p => p.Value.Count).Should().Be(totalNumberOfGuids); store.ToList().ForEach(projection => { projection.Value .Count .Should() .BeInRange(approximateGuidsPerPartition - tolerance, approximateGuidsPerPartition + tolerance); }); }