private void RecoverAggregateData(MatchAggregatorSnapshot s) { _matchAggregate = new MatchAggregate(TickerSymbol, s.AvgPrice, s.AvgVolume); _priceUpdates.Enqueue(s.RecentPriceUpdates.ToArray()); _volumeUpdates.Enqueue(s.RecentVolumeUpdates.ToArray()); QueryOffset = s.QueryOffset; }
private void RecoverAggregateData(MatchAggregatorSnapshot s) { _matchAggregate = new MatchAggregate(TickerSymbol, s.RecentAvgPrice, s.RecentAvgVolume); if (s.RecentPriceUpdates != null && s.RecentPriceUpdates.Count > 0) { _priceUpdates.Enqueue(s.RecentPriceUpdates.ToArray()); } if (s.RecentVolumeUpdates != null && s.RecentVolumeUpdates.Count > 0) { _volumeUpdates.Enqueue(s.RecentVolumeUpdates.ToArray()); } }
private void Commands() { AwaitingSubscription(); Command <Match>(m => TickerSymbol.Equals(m.StockId), m => { _log.Info("Received MATCH for {0} - price: {1} quantity: {2}", TickerSymbol, m.SettlementPrice, m.Quantity); if (_matchAggregate == null) { _matchAggregate = new MatchAggregate(TickerSymbol, m.SettlementPrice, m.Quantity); return; } if (!_matchAggregate.WithMatch(m)) { // should never get to here _log.Warning("Received Match for ticker symbol [{0}] - but we only accept symbols for [{1}]", m.StockId, TickerSymbol); } }); // Matches for a different ticker symbol Command <Match>(m => { _log.Warning("Received Match for ticker symbol [{0}] - but we only accept symbols for [{1}]", m.StockId, TickerSymbol); }); // Command sent to pull down a complete snapshot of active pricing data for this ticker symbol Command <FetchPriceAndVolume>(f => { // no price data yet if (_priceUpdates.Count == 0 || _volumeUpdates.Count == 0) { Sender.Tell(PriceAndVolumeSnapshot.Empty(TickerSymbol)); } else { Sender.Tell(new PriceAndVolumeSnapshot(TickerSymbol, _priceUpdates.ToArray(), _volumeUpdates.ToArray())); } }); Command <PublishEvents>(p => { if (_matchAggregate == null) { return; } var(latestPrice, latestVolume) = _matchAggregate.FetchMetrics(_timestamper); // Need to update pricing records prior to persisting our state, since this data is included in // output of SaveAggregateData() _priceUpdates.Add(latestPrice); _volumeUpdates.Add(latestVolume); PersistAsync(SaveAggregateData(), snapshot => { _log.Info("Saved latest price {0} and volume {1}", snapshot.RecentAvgPrice, snapshot.RecentAvgVolume); if (LastSequenceNr % SnapshotEveryN == 0) { SaveSnapshot(snapshot); } }); // publish updates to price and volume subscribers _marketEventPublisher.Publish(TickerSymbol, latestPrice); _marketEventPublisher.Publish(TickerSymbol, latestVolume); }); Command <Ping>(p => { if (_log.IsDebugEnabled) { _log.Debug("pinged via {0}", Sender); } }); Command <SaveSnapshotSuccess>(s => { // clean-up prior snapshots and journal events DeleteSnapshots(new SnapshotSelectionCriteria(s.Metadata.SequenceNr - 1)); DeleteMessages(s.Metadata.SequenceNr); }); /* * Handle subscriptions directly in case we're using in-memory, local pub-sub. */ CommandAsync <MarketSubscribe>(async sub => { try { var ack = await _marketEventSubscriptionManager.Subscribe(TickerSymbol, sub.Events, sub.Subscriber); Context.Watch(sub.Subscriber); sub.Subscriber.Tell(ack); Sender.Tell(ack); // need this for ASK operations. } catch (Exception ex) { _log.Error(ex, "Error while processing subscription {0}", sub); sub.Subscriber.Tell(new MarketSubscribeNack(TickerSymbol, sub.Events, ex.Message)); } }); CommandAsync <MarketUnsubscribe>(async unsub => { try { var ack = await _marketEventSubscriptionManager.Unsubscribe(PersistenceId, unsub.Events, unsub.Subscriber); // leave DeathWatch intact, in case actor is still subscribed to additional topics unsub.Subscriber.Tell(ack); Sender.Tell(ack); // need this for ASK operations. } catch (Exception ex) { _log.Error(ex, "Error while processing unsubscribe {0}", unsub); unsub.Subscriber.Tell(new MarketUnsubscribeNack(TickerSymbol, unsub.Events, ex.Message)); } }); CommandAsync <Terminated>(async t => { try { var ack = await _marketEventSubscriptionManager.Unsubscribe(TickerSymbol, t.ActorRef); } catch (Exception ex) { _log.Error(ex, "Error while processing unsubscribe for terminated subscriber {0} for symbol {1}", t.ActorRef, TickerSymbol); } }); }
private void Commands() { Command <EventEnvelope>(e => { if (e.Event is Match m) { // update the offset if (e.Offset is Sequence s) { QueryOffset = s.Value; } if (_matchAggregate == null) { _matchAggregate = new MatchAggregate(TickerSymbol, m.SettlementPrice, m.Quantity); return; } if (!_matchAggregate.WithMatch(m)) { _log.Warning("Received Match for ticker symbol [{0}] - but we only accept symbols for [{1}]", m.StockId, TickerSymbol); } } }); // Command sent by a PriceViewActor to pull down a complete snapshot of active pricing data Command <FetchPriceAndVolume>(f => { // no price data yet if (_priceUpdates.Count == 0 || _volumeUpdates.Count == 0) { Sender.Tell(PriceAndVolumeSnapshot.Empty(TickerSymbol)); } else { Sender.Tell(new PriceAndVolumeSnapshot(TickerSymbol, _priceUpdates.ToArray(), _volumeUpdates.ToArray())); } }); Command <PublishEvents>(p => { if (_matchAggregate == null) { return; } var(latestPrice, latestVolume) = _matchAggregate.FetchMetrics(_timestamper); // Need to update pricing records prior to persisting our state, since this data is included in // output of SaveAggregateData() _priceUpdates.Add(latestPrice); _volumeUpdates.Add(latestVolume); PersistAsync(SaveAggregateData(), snapshot => { _log.Info("Saved latest price {0} and volume {1}", snapshot.AvgPrice, snapshot.AvgVolume); if (LastSequenceNr % SnapshotEveryN == 0) { SaveSnapshot(snapshot); } }); // publish updates to in-memory replicas _mediator.Tell(new Publish(_priceTopic, latestPrice)); _mediator.Tell(new Publish(_volumeTopic, latestVolume)); }); Command <Ping>(p => { if (_log.IsDebugEnabled) { _log.Debug("pinged via {0}", Sender); } }); Command <SaveSnapshotSuccess>(s => { // clean-up prior snapshots and journal events DeleteSnapshots(new SnapshotSelectionCriteria(s.Metadata.SequenceNr - 1)); DeleteMessages(s.Metadata.SequenceNr); }); }