public void Add2Remove1UntilFull() { IObjectPool <CachedMessageBlock <TestCachedMessage> > pool = new MyTestPooled(); ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(); CachedMessageBlock <TestCachedMessage> block = pool.Allocate(); int first = 0; int last = -1; while (block.HasCapacity) { // add message to end of block AddAndCheck(block, dataAdapter, first, last); last++; if (!block.HasCapacity) { continue; } // add message to end of block AddAndCheck(block, dataAdapter, first, last); last++; // removed message from start of block RemoveAndCheck(block, first, last); first++; } Assert.Equal(TestBlockSize / 2, block.OldestMessageIndex); Assert.Equal(TestBlockSize - 1, block.NewestMessageIndex); Assert.False(block.IsEmpty); Assert.False(block.HasCapacity); }
public void SimpleCacheMiss() { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(); var cache = new PooledQueueCache(dataAdapter, NullLogger.Instance, null, null, TimeSpan.FromSeconds(10)); var evictionStrategy = new ChronologicalEvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)), null, null); evictionStrategy.PurgeObservable = cache; var converter = new CachedMessageConverter(bufferPool, evictionStrategy); var seqNumber = 123; var streamKey = Guid.NewGuid(); var stream = StreamId.Create(TestStreamNamespace, streamKey); var cursor = cache.GetCursor(stream, new EventSequenceTokenV2(seqNumber)); // Start by enqueuing a message for stream, followed bu another one destined for another one EnqueueMessage(streamKey); EnqueueMessage(Guid.NewGuid()); // Consume the stream, should be fine Assert.True(cache.TryGetNextMessage(cursor, out _)); Assert.False(cache.TryGetNextMessage(cursor, out _)); // Enqueue a new batch // First and last messages destined for stream, following messages // destined for other streams EnqueueMessage(streamKey); for (var idx = 0; idx < 20; idx++) { EnqueueMessage(Guid.NewGuid()); } // Remove first three messages from the cache cache.RemoveOldestMessage(); // Destined for stream, consumed cache.RemoveOldestMessage(); // Not destined for stream cache.RemoveOldestMessage(); // Destined for stream, not consumed // Enqueue a new message for stream EnqueueMessage(streamKey); // Should throw since we missed the second message destined for stream Assert.Throws <QueueCacheMissException>(() => cache.TryGetNextMessage(cursor, out _)); long EnqueueMessage(Guid streamId) { var now = DateTime.UtcNow; var msg = new TestQueueMessage { StreamId = StreamId.Create(TestStreamNamespace, streamId), SequenceNumber = seqNumber, }; cache.Add(new List <CachedMessage>() { converter.ToCachedMessage(msg, now) }, now); seqNumber++; return(msg.SequenceNumber); } }
public void NextInStreamTest() { IObjectPool <CachedMessageBlock <TestCachedMessage> > pool = new MyTestPooled(); ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(); CachedMessageBlock <TestCachedMessage> block = pool.Allocate(); int last = 0; int sequenceNumber = 0; // define 2 streams var streams = new[] { new StreamIdentity(Guid.NewGuid(), null), new StreamIdentity(Guid.NewGuid(), null) }; // add both streams interleaved, until lock is full while (block.HasCapacity) { var stream = streams[last % 2]; var message = new TestQueueMessage { StreamGuid = stream.Guid, SequenceToken = new EventSequenceToken(sequenceNumber) }; // add message to end of block AddAndCheck(block, dataAdapter, message, 0, last - 1); last++; sequenceNumber += 2; } // get index of first stream int streamIndex; Assert.True(block.TryFindFirstMessage(streams[0], TestCacheDataComparer.Instance, out streamIndex)); Assert.Equal(0, streamIndex); Assert.Equal(0, (block.GetSequenceToken(streamIndex, dataAdapter) as EventSequenceToken).SequenceNumber); // find stream1 messages int iteration = 1; while (block.TryFindNextMessage(streamIndex + 1, streams[0], TestCacheDataComparer.Instance, out streamIndex)) { Assert.Equal(iteration * 2, streamIndex); Assert.Equal(iteration * 4, (block.GetSequenceToken(streamIndex, dataAdapter) as EventSequenceToken).SequenceNumber); iteration++; } Assert.Equal(iteration, TestBlockSize / 2); // get index of first stream Assert.True(block.TryFindFirstMessage(streams[1], TestCacheDataComparer.Instance, out streamIndex)); Assert.Equal(1, streamIndex); Assert.Equal(2, (block.GetSequenceToken(streamIndex, dataAdapter) as EventSequenceToken).SequenceNumber); // find stream1 messages iteration = 1; while (block.TryFindNextMessage(streamIndex + 1, streams[1], TestCacheDataComparer.Instance, out streamIndex)) { Assert.Equal(iteration * 2 + 1, streamIndex); Assert.Equal(iteration * 4 + 2, (block.GetSequenceToken(streamIndex, dataAdapter) as EventSequenceToken).SequenceNumber); iteration++; } Assert.Equal(iteration, TestBlockSize / 2); }
public void Add1Remove1Test() { IObjectPool <CachedMessageBlock <TestCachedMessage> > pool = new MyTestPooled(); ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(); CachedMessageBlock <TestCachedMessage> block = pool.Allocate(); AddAndCheck(block, dataAdapter, 0, -1); RemoveAndCheck(block, 0, 0); }
public void GoldenPathTest() { var bufferPool = new TestBlockPool(); var dataAdapter = new TestCacheDataAdapter(bufferPool); var cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter, TestCacheDataComparer.Instance); dataAdapter.PurgeAction = cache.Purge; RunGoldenPath(cache, 111); }
public void AvoidCacheMissMultipleStreamsActive() { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(); var cache = new PooledQueueCache(dataAdapter, NullLogger.Instance, null, null, TimeSpan.FromSeconds(30)); var evictionStrategy = new ChronologicalEvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)), null, null); evictionStrategy.PurgeObservable = cache; var converter = new CachedMessageConverter(bufferPool, evictionStrategy); var seqNumber = 123; var streamKey = Guid.NewGuid(); var stream = StreamId.Create(TestStreamNamespace, streamKey); // Enqueue a message for our stream var firstSequenceNumber = EnqueueMessage(streamKey); // Enqueue a few other messages for other streams EnqueueMessage(Guid.NewGuid()); EnqueueMessage(Guid.NewGuid()); // Consume the first event and see that the cursor has moved to last seen event (not matching our streamIdentity) var cursor = cache.GetCursor(stream, new EventSequenceTokenV2(firstSequenceNumber)); Assert.True(cache.TryGetNextMessage(cursor, out var firstContainer)); Assert.False(cache.TryGetNextMessage(cursor, out _)); // Remove multiple events, including the one that the cursor is currently pointing to cache.RemoveOldestMessage(); cache.RemoveOldestMessage(); cache.RemoveOldestMessage(); // Enqueue another message for stream var lastSequenceNumber = EnqueueMessage(streamKey); // Should be able to consume the event just pushed Assert.True(cache.TryGetNextMessage(cursor, out var lastContainer)); Assert.Equal(stream, lastContainer.StreamId); Assert.Equal(lastSequenceNumber, lastContainer.SequenceToken.SequenceNumber); long EnqueueMessage(Guid streamId) { var now = DateTime.UtcNow; var msg = new TestQueueMessage { StreamId = StreamId.Create(TestStreamNamespace, streamId), SequenceNumber = seqNumber, }; cache.Add(new List <CachedMessage>() { converter.ToCachedMessage(msg, now) }, now); seqNumber++; return(msg.SequenceNumber); } }
public void GoldenPathTest() { var bufferPool = new TestBlockPool(); PooledQueueCache <TestQueueMessage, TestCachedMessage> cache = null; ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(bufferPool, disposable => cache.Purge(disposable)); cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter); RunGoldenPath(cache, 111); }
public void GoldenPathTest() { var bufferPool = new TestBlockPool(); var dataAdapter = new TestCacheDataAdapter(bufferPool); var cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter, TestCacheDataComparer.Instance, NoOpTestLogger.Instance); var evictionStrategy = new ExplicitEvictionStrategy(); evictionStrategy.PurgeObservable = cache; dataAdapter.OnBlockAllocated = evictionStrategy.OnBlockAllocated; RunGoldenPath(cache, 111); }
public void GoldenPathTest() { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(bufferPool); var cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter, TestCacheDataComparer.Instance, NullLogger.Instance, null, null); var evictionStrategy = new EvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)), null, null); evictionStrategy.PurgeObservable = cache; dataAdapter.OnBlockAllocated = evictionStrategy.OnBlockAllocated; RunGoldenPath(cache, 111); }
public void GoldenPathTest() { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(); var cache = new PooledQueueCache(dataAdapter, NullLogger.Instance, null, null); var evictionStrategy = new ChronologicalEvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)), null, null); evictionStrategy.PurgeObservable = cache; var converter = new CachedMessageConverter(bufferPool, evictionStrategy); RunGoldenPath(cache, converter, 111); }
public void SimpleCacheMiss() { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(); var cache = new PooledQueueCache(dataAdapter, NullLogger.Instance, null, null, TimeSpan.FromSeconds(10)); var evictionStrategy = new ChronologicalEvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)), null, null); evictionStrategy.PurgeObservable = cache; var converter = new CachedMessageConverter(bufferPool, evictionStrategy); int idx; var seqNumber = 123; var stream = StreamId.Create(TestStreamNamespace, Guid.NewGuid()); // First and last messages destined for stream, following messages // destined for other streams for (idx = 0; idx < 20; idx++) { var now = DateTime.UtcNow; var msg = new TestQueueMessage { StreamId = (idx == 0) ? stream : StreamId.Create(TestStreamNamespace, Guid.NewGuid()), SequenceNumber = seqNumber + idx, }; cache.Add(new List <CachedMessage>() { converter.ToCachedMessage(msg, now) }, now); } var cursor = cache.GetCursor(stream, new EventSequenceTokenV2(seqNumber)); // Remove first message cache.RemoveOldestMessage(); // Enqueue a new message for stream { idx++; var now = DateTime.UtcNow; var msg = new TestQueueMessage { StreamId = stream, SequenceNumber = seqNumber + idx, }; cache.Add(new List <CachedMessage>() { converter.ToCachedMessage(msg, now) }, now); } // Should throw since we missed the first message Assert.Throws <QueueCacheMissException>(() => cache.TryGetNextMessage(cursor, out _)); }
public void CacheDrainTest() { var bufferPool = new TestBlockPool(); var dataAdapter = new TestCacheDataAdapter(bufferPool); var cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter, TestCacheDataComparer.Instance); dataAdapter.PurgeAction = cache.Purge; int startSequenceNuber = 222; startSequenceNuber = RunGoldenPath(cache, startSequenceNuber); bufferPool.PurgeAll(); RunGoldenPath(cache, startSequenceNuber); }
public void CacheDrainTest() { var bufferPool = new TestBlockPool(); PooledQueueCache <TestQueueMessage, TestCachedMessage> cache = null; ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(bufferPool, disposable => cache.Purge(disposable)); cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter); int startSequenceNuber = 222; startSequenceNuber = RunGoldenPath(cache, startSequenceNuber); bufferPool.PurgeAll(); RunGoldenPath(cache, startSequenceNuber); }
public void FirstMessageWithSequenceNumberTest() { IObjectPool <CachedMessageBlock <TestCachedMessage> > pool = new MyTestPooled(); ICacheDataAdapter <TestQueueMessage, TestCachedMessage> dataAdapter = new TestCacheDataAdapter(); CachedMessageBlock <TestCachedMessage> block = pool.Allocate(); int last = -1; int sequenceNumber = 0; while (block.HasCapacity) { // add message to end of block AddAndCheck(block, dataAdapter, 0, last, sequenceNumber); last++; sequenceNumber += 2; } Assert.Equal(block.OldestMessageIndex, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(0), TestCacheDataComparer.Instance)); Assert.Equal(block.OldestMessageIndex, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(1), TestCacheDataComparer.Instance)); Assert.Equal(block.NewestMessageIndex, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(sequenceNumber - 2), TestCacheDataComparer.Instance)); Assert.Equal(block.NewestMessageIndex - 1, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(sequenceNumber - 3), TestCacheDataComparer.Instance)); Assert.Equal(50, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(sequenceNumber / 2), TestCacheDataComparer.Instance)); Assert.Equal(50, block.GetIndexOfFirstMessageLessThanOrEqualTo(new EventSequenceToken(sequenceNumber / 2 + 1), TestCacheDataComparer.Instance)); }
public void QueueCacheMissTest() { var bufferPool = new TestBlockPool(); var dataAdapter = new TestCacheDataAdapter(bufferPool); var cache = new PooledQueueCache <TestQueueMessage, TestCachedMessage>(dataAdapter, TestCacheDataComparer.Instance); dataAdapter.PurgeAction = cache.Purge; int sequenceNumber = 10; IBatchContainer batch; IStreamIdentity streamId = new TestStreamIdentity { Guid = Guid.NewGuid(), Namespace = StreamNamespace }; // No data in cache, cursors should not throw. object cursor = cache.GetCursor(streamId, new EventSequenceToken(sequenceNumber++)); Assert.IsNotNull(cursor); // try to iterate, should throw bool gotNext = cache.TryGetNextMessage(cursor, out batch); Assert.IsNotNull(cursor); Assert.IsFalse(gotNext); // now add messages into cache newer than cursor // Adding enough to fill the pool for (int i = 0; i < MessagesPerBuffer * PooledBufferCount; i++) { cache.Add(new TestQueueMessage { StreamGuid = streamId.Guid, StreamNamespace = StreamNamespace, SequenceNumber = sequenceNumber++, }); } // now that there is data, and the cursor should point to data older than in the cache, using cursor should throw Exception ex = null; try { cache.TryGetNextMessage(cursor, out batch); } catch (QueueCacheMissException cacheMissException) { ex = cacheMissException; } Assert.IsNotNull(ex); // Try getting new cursor into cache from data before the cache. Should throw ex = null; try { cursor = cache.GetCursor(streamId, new EventSequenceToken(10)); } catch (QueueCacheMissException cacheMissException) { ex = cacheMissException; } Assert.IsNotNull(ex); // Get valid cursor into cache cursor = cache.GetCursor(streamId, new EventSequenceToken(13)); // query once, to make sure cursor is good gotNext = cache.TryGetNextMessage(cursor, out batch); Assert.IsNotNull(cursor); Assert.IsTrue(gotNext); // Since pool should be full, adding one more message should trigger the cache to purge. cache.Add(new TestQueueMessage { StreamGuid = streamId.Guid, StreamNamespace = StreamNamespace, SequenceNumber = sequenceNumber++, }); // After purge, use of cursor should throw. ex = null; try { cache.TryGetNextMessage(cursor, out batch); } catch (QueueCacheMissException cacheMissException) { ex = cacheMissException; } Assert.IsNotNull(ex); }
private void AvoidCacheMiss(bool emptyCache) { var bufferPool = new ObjectPool <FixedSizeBuffer>(() => new FixedSizeBuffer(PooledBufferSize)); var dataAdapter = new TestCacheDataAdapter(); var cache = new PooledQueueCache(dataAdapter, NullLogger.Instance, null, null, TimeSpan.FromSeconds(30)); var evictionStrategy = new ChronologicalEvictionStrategy(NullLogger.Instance, new TimePurgePredicate(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)), null, null); evictionStrategy.PurgeObservable = cache; var converter = new CachedMessageConverter(bufferPool, evictionStrategy); var seqNumber = 123; var stream = StreamId.Create(TestStreamNamespace, Guid.NewGuid()); // Enqueue a message for stream var firstSequenceNumber = EnqueueMessage(stream); // Consume first event var cursor = cache.GetCursor(stream, new EventSequenceTokenV2(firstSequenceNumber)); Assert.True(cache.TryGetNextMessage(cursor, out var firstContainer)); Assert.Equal(stream, firstContainer.StreamId); Assert.Equal(firstSequenceNumber, firstContainer.SequenceToken.SequenceNumber); // Remove first message, that was consumed cache.RemoveOldestMessage(); if (!emptyCache) { // Enqueue something not related to the stream // so the cache isn't empty EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); EnqueueMessage(StreamId.Create(TestStreamNamespace, Guid.NewGuid())); } // Enqueue another message for stream var lastSequenceNumber = EnqueueMessage(stream); // Should be able to consume the event just pushed Assert.True(cache.TryGetNextMessage(cursor, out var lastContainer)); Assert.Equal(stream, lastContainer.StreamId); Assert.Equal(lastSequenceNumber, lastContainer.SequenceToken.SequenceNumber); long EnqueueMessage(StreamId streamId) { var now = DateTime.UtcNow; var msg = new TestQueueMessage { StreamId = streamId, SequenceNumber = seqNumber, }; cache.Add(new List <CachedMessage>() { converter.ToCachedMessage(msg, now) }, now); seqNumber++; return(msg.SequenceNumber); } }