public async Task OneTimeSetUp() { _eventIdProvider = new InMemoryEventIdProvider(); _serializer = new JsonNetSerializer(); _eventSerializer = new EventSerializer(_serializer); _eventCache = new InMemoryEventCache(_eventIdProvider, _eventSerializer); var brokerConfiguration = new BrokerageServiceConfiguration() { HeartbeatEndpoint = HeartbeatEndpoint, StateOfTheWorldEndpoint = StateOfTheWorldEndpoint, ToSubscribersEndpoint = ToSubscribersEndpoint, ToPublisherEndpoint = ToPublishersEndpoint }; _broker = new BrokerageService(brokerConfiguration, LoggerForTests <BrokerageService> .Default(), _eventCache, _serializer); await _broker.Run(); JsonConvert.DefaultSettings = () => { var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Objects, ContractResolver = new CamelCasePropertyNamesContractResolver() }; return(settings); }; await Task.Delay(1000); }
public IActor GetBrokerageService(IBrokerageServiceConfiguration configuration) { var router = new BrokerageService(configuration, LoggerForTests <BrokerageService> .Default(), _eventCache, _serializer); _actors.Add(router); return(router); }
public Market GetMarket(MarketConfiguration configuration) { var market = new Market(configuration, LoggerForTests <Market> .Default(), _eventSerializer); _actors.Add(market); return(market); }
public DynamicCache <string, CurrencyPair> GetCache(IDynamicCacheConfiguration configuration) { var cache = new DynamicCache <string, CurrencyPair>(configuration, LoggerForTests <DynamicCache <string, CurrencyPair> > .Default(), _eventSerializer); _actors.Add(cache); return(cache); }
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 ShouldProduceEventsAndConsumeThemViaEventCache() { var marketConfiguration = new MarketConfiguration("TEST") { BrokerEndpoint = ToPublishersEndpoint, HeartbeatEndpoint = HeartbeatEndpoint, HeartbeatDelay = TimeSpan.FromSeconds(1), HeartbeatTimeout = TimeSpan.FromSeconds(1) }; var producer = new Market(marketConfiguration, LoggerForTests <Market> .Default(), _eventSerializer); await producer.Run(); await Task.Delay(2000); var eventCountOnProducer = producer.Prices.Count; var eventsOnEventCache = (await _eventCache.GetAllStreams()) .Select(msg => _serializer.Deserialize <ChangeCcyPairPrice>(msg.ProducerMessage.MessageBytes)) .ToList(); Assert.Greater(eventCountOnProducer, 0); Assert.Greater(eventsOnEventCache.Count, 0); Assert.AreEqual(eventCountOnProducer, eventsOnEventCache.Count); Assert.IsTrue(eventsOnEventCache.All(ev => producer.Prices.Any(p => p.Ask == ev.Ask))); producer.Publish(new ChangeCcyPairPrice("TEST", "TEST-Market", 0.0, 0.0, 0.0, 0.0)); await Task.Delay(200); var testEvent = await _eventCache.GetStream("TEST"); Assert.AreEqual(1, testEvent.Count()); await producer.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 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 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 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 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(); } }