public async Task ShouldCheckMinimumPerformance() { var eventIdProvider = new InMemoryEventIdProvider(); var serializer = new JsonNetSerializer(); var eventSerializer = new EventSerializer(serializer); var eventCache = new InMemoryEventCache(eventIdProvider, eventSerializer); var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOftheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = new BrokerageService(brokerConfiguration, eventCache, serializer); var marketConfiguration = new ProducerConfiguration() { RouterEndpoint = ToPublishersEndpoint, HearbeatEndpoint = HeartbeatEndpoint }; var market = new Market("FxConnect", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(10)); await router.Run(); await market.Run(); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(10), HeartbeatTimeout = TimeSpan.FromSeconds(2) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, eventSerializer); await cache.Run(); //fire a little less than 300 events await Task.Delay(3000); await Task.WhenAll(new[] { router.Destroy(), market.Destroy(), cache.Destroy() }); var cacheItems = cache.GetItems().ToList(); var cacheItemsEvents = cacheItems.SelectMany(items => items.AppliedEvents).ToList(); //At least 3/4 events should be processed Assert.Greater((double)cacheItemsEvents.Count / (double)market.Prices.Count, 0.75); }
public async Task ShouldApplyMultipleEvents() { using (var publisherSocket = new PublisherSocket()) { publisherSocket.Bind(ToSubscribersEndpoint); var streamId = "EUR/USD"; var createEvent = new Func <IEvent <String, CurrencyPair>, Task>(async(@event) => { var message = _eventSerializer.ToProducerMessage(@event); var eventId = new EventId(@event.EventStreamId, 0, @event.Subject, DateTime.Now.Ticks); publisherSocket.SendMoreFrame(message.Subject) .SendMoreFrame(_serializer.Serialize(eventId)) .SendFrame(_serializer.Serialize(message)); await Task.Delay(200); }); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); await Task.Delay(2000); var priceEvent = new ChangeCcyPairPrice(streamId, "FxConnect", 0.0, 0.0, 0.0, 0.0); await createEvent(priceEvent); var changeStateClose = new ChangeCcyPairState(streamId, "FxConnect", CcyPairState.Passive); await createEvent(changeStateClose); Assert.AreEqual(1, cache.Items.Count()); Assert.AreEqual(CcyPairState.Passive, cache.Items.First().State); Assert.AreEqual(2, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await cache.Destroy(); } }
public async Task ShouldSwitchToStaledState() { using (var publisherSocket = new PublisherSocket()) { publisherSocket.Bind(ToSubscribersEndpoint); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(5), HeartbeatTimeout = TimeSpan.FromSeconds(1), IsStaleTimeout = TimeSpan.FromSeconds(2) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); await WaitForCachesToCaughtUp(cache); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(true, cache.IsStaled); var @event = new ChangeCcyPairPrice("TEST", "TEST-Market", 0.0, 0.0, 0.0, 0.0); var message = _eventSerializer.ToProducerMessage(@event); var eventId = new EventId("TEST", 0, "TEST", DateTime.Now.Ticks); publisherSocket.SendMoreFrame(message.Subject) .SendMoreFrame(_serializer.Serialize(eventId)) .SendFrame(_serializer.Serialize(message)); await Task.Delay(200); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(false, cache.IsStaled); await Task.Delay(3500); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(true, cache.IsStaled); await Task.Delay(2000); Assert.AreEqual(true, cache.IsStaled); await cache.Destroy(); } }
public async Task ShouldRetrieveStateOfTheWorld() { var topic = "EUR/USD"; for (var i = 0; i < 49; i++) { var next = Next(topic); var message = _eventSerializer.ToProducerMessage(next); await _eventCache.AppendToStream(Encoding.UTF8.GetBytes(next.Subject), _serializer.Serialize(message)); } var lastPriceEvent = Next(topic); var lastMessage = _eventSerializer.ToProducerMessage(lastPriceEvent); await _eventCache.AppendToStream(Encoding.UTF8.GetBytes(lastPriceEvent.Subject), _serializer.Serialize(lastMessage)); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); await WaitForCachesToCaughtUp(cache); var ccyPairs = cache.Items .ToList(); Assert.AreEqual(1, ccyPairs.Count); var euroDol = ccyPairs.First(); Assert.AreEqual(50, euroDol.AppliedEvents.Count()); Assert.AreEqual(lastPriceEvent.Ask, euroDol.Ask); Assert.AreEqual(lastPriceEvent.Bid, euroDol.Bid); Assert.AreEqual(lastPriceEvent.Mid, euroDol.Mid); Assert.AreEqual(lastPriceEvent.Spread, euroDol.Spread); await cache.Destroy(); }
public async Task ShouldPerformHeartbeat() { var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); await WaitForCachesToCaughtUp(cache); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); await cache.Destroy(); }
public async Task ShouldSubscribeToAllSubjects() { using (var publisherSocket = new PublisherSocket()) { publisherSocket.Bind(ToSubscribersEndpoint); await Task.Delay(200); var createEvent = new Func <string, string, Task>(async(streamId, market) => { var @event = new ChangeCcyPairPrice(streamId, market, 0.0, 0.0, 0.0, 0.0); var message = _eventSerializer.ToProducerMessage(@event); var eventId = new EventId(streamId, 0, string.IsNullOrEmpty(market) ? streamId : $"{streamId}.{market}", DateTime.Now.Ticks); publisherSocket.SendMoreFrame(message.Subject) .SendMoreFrame(_serializer.Serialize(eventId)) .SendFrame(_serializer.Serialize(message)); await Task.Delay(200); }); var euroDol = "EUR/USD"; var harmony = "Harmony"; var euroGbp = "EUR/GBP"; var fxconnect = "FxConnect"; var cacheConfigurationEuroDol = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = $"{euroDol}", HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), }; var cacheConfigurationAll = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), }; var cacheEuroDol = new DynamicCache <string, CurrencyPair>(cacheConfigurationEuroDol, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); var cacheAll = new DynamicCache <string, CurrencyPair>(cacheConfigurationAll, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cacheEuroDol.Run(); await cacheAll.Run(); await WaitForCachesToCaughtUp(cacheEuroDol, cacheAll); Assert.AreEqual(DynamicCacheState.Connected, cacheEuroDol.CacheState); Assert.AreEqual(DynamicCacheState.Connected, cacheAll.CacheState); await createEvent(euroDol, harmony); Assert.AreEqual(1, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(1, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(euroDol, fxconnect); await Task.Delay(500); Assert.AreEqual(2, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(2, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(euroDol, string.Empty); Assert.AreEqual(3, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(3, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(euroGbp, string.Empty); Assert.AreEqual(3, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(4, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(euroGbp, harmony); Assert.AreEqual(3, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(5, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(euroGbp, fxconnect); Assert.AreEqual(3, cacheEuroDol.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); Assert.AreEqual(6, cacheAll.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await cacheEuroDol.Destroy(); await cacheAll.Destroy(); } }
public async Task ShouldHandleNewEventsWhileRebuildingTheCache() { using (var stateUpdatePublish = new PublisherSocket()) { stateUpdatePublish.Bind(ToSubscribersEndpoint); var topic = "EUR/USD"; var stateOfTheWorldEventCount = 10000; var initialEventCache = 50; var raisedEventDuringCacheBuilding = new List <EventId>(); for (var i = 0; i < stateOfTheWorldEventCount; i++) { var next = Next(topic); var message = _eventSerializer.ToProducerMessage(next); await _eventCache.AppendToStream(Encoding.UTF8.GetBytes(next.Subject), _serializer.Serialize(message)); } var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); for (var i = 0; i < initialEventCache; i++) { var next = Next(topic); var message = _eventSerializer.ToProducerMessage(next); var payload = _serializer.Serialize(message); var eventId = _eventCache.AppendToStream(Encoding.UTF8.GetBytes(next.Subject), payload).Result; raisedEventDuringCacheBuilding.Add(eventId); stateUpdatePublish.SendMoreFrame(next.Subject) .SendMoreFrame(_serializer.Serialize(eventId)) .SendFrame(payload); await Task.Delay(100); } await WaitForCachesToCaughtUp(cache); Assert.IsFalse(cache.IsCaughtingUp); var ccyPairs = cache.Items .ToList(); Assert.AreEqual(1, ccyPairs.Count); var euroDol = ccyPairs.First(); Assert.AreEqual(stateOfTheWorldEventCount + initialEventCache, euroDol.AppliedEvents.Count()); await cache.Destroy(); } }
public async Task ShouldHandleDisconnectAndCacheRebuild() { var eventIdProvider = new InMemoryEventIdProvider(); var serializer = new JsonNetSerializer(); var eventSerializer = new EventSerializer(serializer); var eventCache = new InMemoryEventCache(eventIdProvider, eventSerializer); var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOftheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = new BrokerageService(brokerConfiguration, eventCache, serializer); var marketConfiguration = new ProducerConfiguration() { RouterEndpoint = ToPublishersEndpoint, HearbeatEndpoint = HeartbeatEndpoint }; var market1 = new Market("FxConnect", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(100)); var market2 = new Market("Harmony", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(100)); await router.Run(); await Task.Delay(1000); await market1.Run(); await market2.Run(); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromMilliseconds(500), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, eventSerializer); var cacheProof = new DynamicCache <string, CurrencyPair>(cacheConfiguration, eventSerializer); await cacheProof.Run(); await cache.Run(); Assert.AreEqual(DynamicCacheState.NotConnected, cache.CacheState); Assert.AreEqual(DynamicCacheState.NotConnected, cacheProof.CacheState); await Task.Delay(2000); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(DynamicCacheState.Connected, cacheProof.CacheState); await Task.Delay(3000); Assert.AreEqual(cache.GetItems() .SelectMany(item => item.AppliedEvents) .Count(), cacheProof.GetItems() .SelectMany(item => item.AppliedEvents) .Count()); await router.Destroy(); await Task.Delay(cacheConfiguration.HeartbeatDelay); Assert.AreEqual(DynamicCacheState.Disconnected, cache.CacheState); Assert.AreEqual(DynamicCacheState.Disconnected, cacheProof.CacheState); router = new BrokerageService(brokerConfiguration, eventCache, serializer); await router.Run(); await Task.Delay(TimeSpan.FromSeconds(3)); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(DynamicCacheState.Connected, cacheProof.CacheState); await router.Destroy(); var cacheCCyPair = cache.GetItems().ToList(); var cacheProofCcyPair = cacheProof.GetItems().ToList(); Assert.AreEqual(cacheCCyPair.Count(), cacheProofCcyPair.Count()); Assert.AreEqual(cacheCCyPair.Count(), cacheProofCcyPair.Count()); foreach (var ccyPair in cacheCCyPair) { var proof = cacheProofCcyPair.First(ccy => ccy.Id == ccyPair.Id); Assert.AreEqual(ccyPair.Ask, proof.Ask); Assert.AreEqual(ccyPair.Bid, proof.Bid); Assert.AreEqual(ccyPair.Mid, proof.Mid); Assert.AreEqual(ccyPair.Spread, proof.Spread); } var brokerCacheEvents = (await eventCache.GetStreamBySubject(cacheConfiguration.Subject)).ToList(); var cacheEvents = cacheCCyPair.SelectMany(item => item.AppliedEvents).ToList(); var cacheProofEvents = cacheProofCcyPair.SelectMany(item => item.AppliedEvents).ToList(); Assert.AreEqual(cacheEvents.Count(), cacheProofEvents.Count()); Assert.AreEqual(cacheEvents.Count(), cacheProofEvents.Count()); Assert.AreEqual(cacheEvents.Count(), cacheProofEvents.Count()); await Task.WhenAll(new[] { market1.Destroy(), market2.Destroy(), cache.Destroy() }); }
public async Task ShouldSubscribeToEventFeed() { var eventIdProvider = new InMemoryEventIdProvider(); var serializer = new JsonNetSerializer(); var eventSerializer = new EventSerializer(serializer); var eventCache = new InMemoryEventCache(eventIdProvider, eventSerializer); var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOftheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = new BrokerageService(brokerConfiguration, eventCache, serializer); var marketConfiguration = new ProducerConfiguration() { RouterEndpoint = ToPublishersEndpoint, HearbeatEndpoint = HeartbeatEndpoint }; var market1 = new Market("FxConnect", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(750)); var market2 = new Market("Harmony", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(750)); await router.Run(); await market1.Run(); await market2.Run(); //create an event cache await Task.Delay(2000); //Assert.Greater(router.Cache.Count(), 0); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint); var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, eventSerializer); var counter = 0; var cleanup = cache.OnItemChanged() .Connect() .Subscribe(_ => { counter++; }); await cache.Run(); await Task.Delay(2000); // Assert.AreEqual(router.Cache.Count(), counter); Assert.AreEqual(cache.GetItems().SelectMany(item => item.AppliedEvents).Count(), counter); //fxconnext & harmony Assert.AreEqual(2, cache.GetItems() .SelectMany(item => item.AppliedEvents) .Cast <ChangeCcyPairPrice>() .Select(ev => ev.Market) .Distinct() .Count()); cleanup.Dispose(); await Task.WhenAll(new[] { router.Destroy(), market1.Destroy(), market2.Destroy(), cache.Destroy() }); }
public async Task ShouldSubscribeToSubject() { //todo .NET COre MVC implem var eventIdProvider = new InMemoryEventIdProvider(); var serializer = new JsonNetSerializer(); var eventSerializer = new EventSerializer(serializer); var eventCache = new InMemoryEventCache(eventIdProvider, eventSerializer); var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOftheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = new BrokerageService(brokerConfiguration, eventCache, serializer); var marketConfiguration = new ProducerConfiguration() { RouterEndpoint = ToPublishersEndpoint, HearbeatEndpoint = HeartbeatEndpoint }; var market1 = new Market("FxConnect", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(750)); var market2 = new Market("Harmony", marketConfiguration, eventSerializer, TimeSpan.FromMilliseconds(750)); await router.Run(); await market1.Run(); await market2.Run(); var cacheConfigurationEuroDol = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = "EUR/USD" }; var cacheConfigurationEuroDolFxConnect = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = "EUR/USD.FxConnect" }; var cacheEuroDol = new DynamicCache <string, CurrencyPair>(cacheConfigurationEuroDol, eventSerializer); var cacheEuroDolFxConnect = new DynamicCache <string, CurrencyPair>(cacheConfigurationEuroDolFxConnect, eventSerializer); await cacheEuroDol.Run(); await cacheEuroDolFxConnect.Run(); //wait for a substential event stream await Task.Delay(5000); // Assert.Greater(router.Cache.Count(), 1); var ccyPairsCacheEuroDol = cacheEuroDol.GetItems() .SelectMany(item => item.AppliedEvents) .Select(item => item.Subject) .Distinct(); // EUR/USD.FxConnect & EUR/USD.Harmony Assert.AreEqual(2, ccyPairsCacheEuroDol.Count()); Assert.IsTrue(ccyPairsCacheEuroDol.All(subject => subject.EndsWith("FxConnect") || subject.EndsWith("Harmony"))); Assert.IsTrue(ccyPairsCacheEuroDol.All(subject => subject.StartsWith(cacheConfigurationEuroDol.Subject))); var ccyPairsCacheEuroDolFxConnect = cacheEuroDolFxConnect.GetItems() .SelectMany(item => item.AppliedEvents) .Select(item => item.Subject) .Distinct(); // EUR/USD.FxConnect Assert.AreEqual(1, ccyPairsCacheEuroDolFxConnect.Count()); Assert.AreEqual(cacheConfigurationEuroDolFxConnect.Subject, ccyPairsCacheEuroDolFxConnect.First()); await Task.WhenAll(new[] { router.Destroy(), market1.Destroy(), market2.Destroy(), cacheEuroDol.Destroy(), cacheEuroDolFxConnect.Destroy() }); }