public async Task ShouldSubscribeToSpecificSubject() { using (var publisherSocket = new PublisherSocket()) { publisherSocket.Bind(ToSubscribersEndpoint); 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(500); }); var subscribedToStreamId = "EUR/USD"; var subscribedToMarket = "Harmony"; var NOTsubscribedToStreamId = "EUR/GBP"; var NOTsubscribedToMarket = "FxConnect"; var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = $"{subscribedToStreamId}.{subscribedToMarket}", 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(1000); await createEvent(NOTsubscribedToStreamId, NOTsubscribedToMarket); Assert.AreEqual(0, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(NOTsubscribedToStreamId, subscribedToMarket); Assert.AreEqual(0, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(subscribedToStreamId, NOTsubscribedToMarket); Assert.AreEqual(0, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(subscribedToStreamId, subscribedToMarket); Assert.AreEqual(1, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); await createEvent(subscribedToStreamId, string.Empty); Assert.AreEqual(1, cache.Items.SelectMany(ccy => ccy.AppliedEvents).Count()); } }
public static DynamicCacheServiceBuilder <TKey, TAggregate> AddDynamicCache <TKey, TAggregate>(this DynamicCacheServiceBuilder <TKey, TAggregate> builder, Action <IDynamicCacheConfiguration> cacheConfigurationBuilder) where TAggregate : class, IAggregate <TKey>, new() { var configuration = new DynamicCacheConfiguration(); cacheConfigurationBuilder(configuration); builder.Options.ServiceCollection.AddSingleton <IDynamicCacheConfiguration>(configuration); builder.Options.ServiceCollection.AddSingleton <IDynamicCache <TKey, TAggregate>, DynamicCache <TKey, TAggregate> >(); return(builder); }
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 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 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 ShouldCheckMinimumPerformance() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfiguration = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, PriceGenerationDelay = TimeSpan.FromMilliseconds(20) }; var market = GetMarket(marketConfiguration); await router.Run(); await market.Run(); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromMilliseconds(500), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = GetCache(cacheConfiguration); await cache.Run(); await WaitForCachesToCaughtUp(cache); await Task.Delay(2000); var cacheItemsEvents = cache.Items .SelectMany(items => items.AppliedEvents) .ToList(); //when run as standalone test, we should expect 100% //when run in a test batch, the result is less deterministic, thus we lower to 70% Assert.Greater((double)cacheItemsEvents.Count / (double)market.Prices.Count, 0.70); }
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 ShouldHandleConnectivityErrors() { var cacheConfiguration = new DynamicCacheConfiguration("tcp://localhost:9090", "tcp://localhost:9090", HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromMilliseconds(500), HeartbeatTimeout = TimeSpan.FromMilliseconds(500), StateCatchupTimeout = TimeSpan.FromMilliseconds(500) }; var cache = new DynamicCache <string, CurrencyPair>(cacheConfiguration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); await cache.Run(); await Task.Delay(2000); Assert.Greater(cache.Errors.Count, 0); var error = cache.Errors.First(); Assert.AreEqual(ActorErrorType.DynamicCacheGetStateOfTheWorldFailure, error.CacheErrorStatus); Assert.AreEqual(typeof(UnreachableBrokerException), error.Exception.GetType()); }
public async Task ShouldCheckMaxPerformance() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfigurationFxConnect = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), PriceGenerationDelay = TimeSpan.FromMilliseconds(50) }; var marketConfigurationHarmony = new MarketConfiguration("Harmony") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), PriceGenerationDelay = TimeSpan.FromMilliseconds(50) }; var market1 = GetMarket(marketConfigurationFxConnect); var market2 = GetMarket(marketConfigurationHarmony); 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.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = GetCache(cacheConfiguration); await cache.Run(); await WaitForCachesToCaughtUp(cache); var cacheEvents = cache.Items .SelectMany(item => item.AppliedEvents) .Cast <IEvent <string, CurrencyPair> >() .GroupBy(ev => ev.EventStreamId) .ToList(); Assert.Greater(cacheEvents.Count, 0); Assert.IsTrue(cacheEvents.All(c => c.Count() > 0)); foreach (var grp in cacheEvents) { var index = 0; foreach (var ev in grp) { Assert.AreEqual(index++, ev.Version); } } }
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() }); }
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 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 void SetUp() { _eventIdProvider = new InMemoryEventIdProvider(); _serializer = new JsonNetSerializer(); _eventSerializer = new EventSerializer(_serializer); _eventCache = new InMemoryEventCache(_eventIdProvider, _eventSerializer); JsonConvert.DefaultSettings = () => { var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Objects, ContractResolver = new CamelCasePropertyNamesContractResolver() }; return(settings); }; var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfigurationFxConnect = new MarketConfiguration("FxConnect") { IsAutoGen = false, BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(5), HeartbeatTimeout = TimeSpan.FromSeconds(5) }; _market1 = GetMarket(marketConfigurationFxConnect); router.Run().Wait(); Task.Delay(1000).Wait(); _market1.Run().Wait(); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(5), HeartbeatTimeout = TimeSpan.FromSeconds(5), ZmqHighWatermark = 1000, UseEventBatching = UseEventBatch, DoStoreEvents = false }; _cache = GetCache(cacheConfiguration); _cache.Run().Wait(); WaitForCachesToCaughtUp(_cache).Wait(); }
public async Task ShouldConnectAndDisconnectFromBroker() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var marketConfiguration = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromMilliseconds(500), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromMilliseconds(500), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var router = GetBrokerageService(brokerConfiguration); var market = GetMarket(marketConfiguration); var cache = GetCache(cacheConfiguration); var cacheStates = new List <DynamicCacheState>(); var stateObservable = cache.OnStateChanged .Subscribe(state => { cacheStates.Add(state); }); await market.Run(); await cache.Run(); Assert.AreEqual(ProducerState.NotConnected, market.ProducerState); Assert.AreEqual(DynamicCacheState.NotConnected, cache.CacheState); await router.Run(); await Task.Delay(1000); await WaitForCachesToCaughtUp(cache); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(ProducerState.Connected, market.ProducerState); await router.Destroy(); await Task.Delay(2000); Assert.AreEqual(DynamicCacheState.Disconnected, cache.CacheState); Assert.AreEqual(ProducerState.Disconnected, market.ProducerState); router = GetBrokerageService(brokerConfiguration); await router.Run(); await WaitForCachesToCaughtUp(cache); await Task.Delay(2000); Assert.AreEqual(5, cacheStates.Count); Assert.AreEqual(DynamicCacheState.NotConnected, cacheStates.ElementAt(0)); Assert.AreEqual(DynamicCacheState.Connected, cacheStates.ElementAt(1)); Assert.AreEqual(DynamicCacheState.Disconnected, cacheStates.ElementAt(2)); Assert.AreEqual(DynamicCacheState.Reconnected, cacheStates.ElementAt(3)); Assert.AreEqual(DynamicCacheState.Connected, cacheStates.ElementAt(4)); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(ProducerState.Connected, market.ProducerState); stateObservable.Dispose(); }
public async Task ShouldSubscribeToEventFeed() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfigurationFxConnect = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var marketConfigurationHarmony = new MarketConfiguration("Harmony") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var market1 = GetMarket(marketConfigurationFxConnect); var market2 = GetMarket(marketConfigurationHarmony); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = GetCache(cacheConfiguration); await router.Run(); await market1.Run(); await market2.Run(); //create an event cache await Task.Delay(2000); var routerEventCacheItems = await _eventCache.GetStreamBySubject(string.Empty); Assert.Greater(routerEventCacheItems.Count(), 0); var counter = 0; var cleanup = cache.OnItemChanged .Connect() .Subscribe(changes => { counter += changes.Count; }); await cache.Run(); await WaitForCachesToCaughtUp(cache); var eventCacheItems = cache.Items.SelectMany(item => item.AppliedEvents).ToList(); Assert.AreEqual(eventCacheItems.Count(), counter); var markets = cache.Items .SelectMany(item => item.AppliedEvents) .Cast <ChangeCcyPairPrice>() .Select(ev => ev.Market) .Distinct(); //fxconnext & harmony Assert.AreEqual(2, markets.Count()); cleanup.Dispose(); }
public async Task ShouldHandleDisconnectAndCacheRebuild() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfiguration = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1), IsAutoGen = false }; var market1 = GetMarket(marketConfiguration); var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var cache = GetCache(cacheConfiguration); var cacheProof = GetCache(cacheConfiguration); var cacheStates = new List <DynamicCacheState>(); var stateObservable = cache.OnStateChanged .Subscribe(state => { cacheStates.Add(state); }); await router.Run(); await market1.Run(); await cache.Run(); await cacheProof.Run(); Assert.AreEqual(DynamicCacheState.NotConnected, cache.CacheState); Assert.AreEqual(DynamicCacheState.NotConnected, cacheProof.CacheState); await Task.Delay(1000); await market1.WaitUntilConnected(); Assert.AreEqual(ProducerState.Connected, market1.ProducerState); market1.PublishNext(); market1.PublishNext(); market1.PublishNext(); await WaitForCachesToCaughtUp(cache, cacheProof); Assert.AreEqual(DynamicCacheState.Connected, cache.CacheState); Assert.AreEqual(DynamicCacheState.Connected, cacheProof.CacheState); var cacheEvents = cache.Items.SelectMany(item => item.AppliedEvents).ToList(); var cacheProofEvents = cacheProof.Items.SelectMany(item => item.AppliedEvents).ToList(); Assert.AreEqual(cacheEvents.Count(), cacheProofEvents.Count()); await router.Destroy(); await Task.Delay(2000); Assert.AreEqual(DynamicCacheState.Disconnected, cache.CacheState); Assert.AreEqual(DynamicCacheState.Disconnected, cacheProof.CacheState); router = GetBrokerageService(brokerConfiguration); await router.Run(); await Task.Delay(1000); market1.PublishNext(); market1.PublishNext(); market1.PublishNext(); await WaitForCachesToCaughtUp(cache, cacheProof); var cacheCCyPair = cache.Items.ToList(); var cacheProofCcyPair = cacheProof.Items.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); } cacheEvents = cacheCCyPair.SelectMany(item => item.AppliedEvents).ToList(); 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.Delay(1000); Assert.AreEqual(5, cacheStates.Count); Assert.AreEqual(DynamicCacheState.NotConnected, cacheStates.ElementAt(0)); Assert.AreEqual(DynamicCacheState.Connected, cacheStates.ElementAt(1)); Assert.AreEqual(DynamicCacheState.Disconnected, cacheStates.ElementAt(2)); Assert.AreEqual(DynamicCacheState.Reconnected, cacheStates.ElementAt(3)); Assert.AreEqual(DynamicCacheState.Connected, cacheStates.ElementAt(4)); stateObservable.Dispose(); }
public async Task ShouldRetrievedEventsSequentially() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var marketConfigurationFxConnect = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint }; var marketConfigurationHarmony = new MarketConfiguration("Harmony") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint }; var cacheConfiguration = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = string.Empty, HeartbeatDelay = TimeSpan.FromSeconds(10), HeartbeatTimeout = TimeSpan.FromSeconds(2) }; var router = GetBrokerageService(brokerConfiguration); var market1 = GetMarket(marketConfigurationFxConnect); var market2 = GetMarket(marketConfigurationHarmony); var cache = GetCache(cacheConfiguration); var cacheProof = GetCache(cacheConfiguration); await router.Run(); await market1.Run(); await market2.Run(); await cache.Run(); await cacheProof.Run(); await WaitForCachesToCaughtUp(cache, cacheProof); var cacheEvents = cache.Items .SelectMany(item => item.AppliedEvents) .Cast <IEvent <string, CurrencyPair> >() .GroupBy(ev => ev.EventStreamId) .ToList(); foreach (var grp in cacheEvents) { var index = 0; foreach (var ev in grp) { Assert.AreEqual(index++, ev.Version); } } var cacheProofEvents = cache.Items .SelectMany(item => item.AppliedEvents) .Cast <IEvent <string, CurrencyPair> >() .GroupBy(ev => ev.EventStreamId) .ToList(); foreach (var grp in cacheProofEvents) { var index = 0; foreach (var ev in grp) { Assert.AreEqual(index++, ev.Version); } } }
public async Task ShouldSubscribeToSubject() { var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; var router = GetBrokerageService(brokerConfiguration); var marketConfigurationFxConnect = new MarketConfiguration("FxConnect") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, PriceGenerationDelay = TimeSpan.FromMilliseconds(500) }; var marketConfigurationHarmony = new MarketConfiguration("Harmony") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, PriceGenerationDelay = TimeSpan.FromMilliseconds(500) }; var market1 = GetMarket(marketConfigurationFxConnect); var market2 = GetMarket(marketConfigurationHarmony); var cacheConfigurationEuroDol = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = "EUR/USD" }; var cacheConfigurationEuroDolFxConnect = new DynamicCacheConfiguration(ToSubscribersEndpoint, StateOfTheWorldEndpoint, HeartbeatEndpoint) { Subject = "EUR/USD.FxConnect" }; var cacheEuroDol = GetCache(cacheConfigurationEuroDol); var cacheEuroDolFxConnect = GetCache(cacheConfigurationEuroDolFxConnect); await router.Run(); await Task.Delay(1000); await market1.Run(); await market2.Run(); await Task.Delay(2000); await cacheEuroDol.Run(); await cacheEuroDolFxConnect.Run(); await WaitForCachesToCaughtUp(cacheEuroDol, cacheEuroDolFxConnect); var routerEventCacheItems = (await _eventCache.GetStreamBySubject(string.Empty)).ToList(); Assert.Greater(routerEventCacheItems.Count(), 0); var ccyPairsCacheEuroDol = cacheEuroDol.Items .SelectMany(item => item.AppliedEvents) .Select(item => item.Subject) .Distinct() .ToList(); var ccyPairsCacheEuroDolFxConnect = cacheEuroDolFxConnect.Items .SelectMany(item => item.AppliedEvents) .Select(item => item.Subject) .Distinct() .ToList(); while (market1.Prices.GroupBy(p => p.EventStreamId).Count() != 2) { await Task.Delay(500); } // 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 cacheEvents = cacheEuroDol.Items .SelectMany(item => item.AppliedEvents) .Cast <IEvent <string, CurrencyPair> >() .GroupBy(ev => ev.EventStreamId) .ToList(); foreach (var grp in cacheEvents) { var index = 0; foreach (var ev in grp) { Assert.AreEqual(index++, ev.Version); } } // EUR/USD.FxConnect Assert.AreEqual(1, ccyPairsCacheEuroDolFxConnect.Count()); Assert.AreEqual(cacheConfigurationEuroDolFxConnect.Subject, ccyPairsCacheEuroDolFxConnect.First()); }
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(); } }