public async Task Given_journal_When_starting_projection_stream_Then_sink_receives_all_events() { await Init(); _output.WriteLine(Sys.Settings.ToString()); //generate some data var calculationActor = Sys.ActorOf(Props.Create <CalculatorActor>(), "CalculatorOne"); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1+2-3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1-2*3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1/2+3")); var eventName = nameof(CalculatorActor.CalculationPerformed); var readJournal = PersistenceQuery.Get(Sys).ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); var sharedKillSwitch = KillSwitches.Shared("test"); var source = readJournal.EventsByTag(eventName, Offset.NoOffset()) .Via(sharedKillSwitch.Flow <EventEnvelope>()); var flow = FunctionTotalUsageFlow.Instance; var sink = Sink.Seq <SequencedFunctionTotalUsage>(); var materializer = Sys.Materializer(); var runTask = source.RunWith(flow.ToMaterialized(sink, Keep.Right), materializer); await Task.Delay(5000); sharedKillSwitch.Shutdown(); var res = await runTask; res.Should().HaveCount(6); }
public async Task Given_calculator_executed_commands_When_reading_events_from_journal_Then_it_is_available() { var dep = await Init(); _output.WriteLine(Sys.Settings.ToString()); //generate some data var calculationActor = Sys.ActorOf(Props.Create <CalculatorActor>(), "CalculatorOne"); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1+2-3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1-2*3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1/2+3")); var eventName = nameof(CalculatorActor.CalculationPerformed); var readJournal = PersistenceQuery.Get(Sys).ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); await Task.Delay(1000); var source = readJournal.CurrentEventsByTag(eventName, Offset.NoOffset()); var sink = Sink.Seq <EventEnvelope>(); var runTask = source.RunWith(sink, Sys.Materializer()); var res = await runTask; res.Should().NotBeEmpty(); }
/// <summary> /// Registry for entities to be processed using the given actor system /// </summary> /// <param name="actorSystem"></param> public ShardedEntityRegistry(ActorSystem actorSystem) { ActorSystem = actorSystem; var persistenceConfig = ActorSystem.Settings.Config .WithFallback(@"wyvern.persistence {}") .GetConfig("wyvern.persistence"); AskTimeout = persistenceConfig.GetTimeSpan("ask-timeout", 5.0d.seconds(), false); MaxNumberOfShards = Math.Max(persistenceConfig.GetInt("max-number-of-shards", 1), 1); var role = persistenceConfig.GetString("run-entities-on-role", ""); Role = string.IsNullOrWhiteSpace(role) ? Option <string> .None : new Option <string>(role); SnapshotAfter = persistenceConfig.GetInt("snapshot-after", 100); Sharding = ClusterSharding.Get(ActorSystem); ShardingSettings = ClusterShardingSettings .Create(ActorSystem) .WithRole(Role.Value); QueryPluginId = new Option <string>(SqlReadJournal.Identifier); var persistence = SqlServerPersistence.Get(ActorSystem); EventsByTagQuery = PersistenceQuery.Get(ActorSystem) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); EventsByPersistenceIdQuery = PersistenceQuery.Get(ActorSystem) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); //var journalConfig = persistence.DefaultJournalConfig; //var sqlJournal = new SqlServerJournal(journalConfig); ExtractShardId = obj => { switch (obj) { case IShardId i: return($"{i.ShardId}"); case CommandEnvelope commandEnvelope: return(commandEnvelope.EntityId.ToShardId(MaxNumberOfShards)); default: throw new InvalidOperationException("Cannot derive shard identifier from unknown type"); } }; ExtractEntityId = obj => { switch (obj) { case CommandEnvelope commandEnvelope: return(($"{commandEnvelope.EntityId}", commandEnvelope.Payload).ToTuple()); default: throw new InvalidOperationException("Cannot derive entity identifier from unknown type"); } }; }
public ReportingActor() { Behavior = new BehaviorQueue(Become); // obtain read journal by plugin id _readJournal = PersistenceQuery.Get(Context.System).ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); Behavior.Become(Initializing, nameof(Initializing)); _dependencies = Context.System.GetReportingExtension().GetDependencies(); _log = Context.GetLogger(); }
public Consumer <TJournal> Using <TJournal>( string?readJournalPluginId = null) where TJournal : IEventsByTagQuery, ICurrentEventsByTagQuery { var readJournal = PersistenceQuery .Get(ActorSystem) .ReadJournalFor <TJournal>(readJournalPluginId); return(new Consumer <TJournal>(Name, ActorSystem, readJournal)); }
protected override Source <EventEnvelope, NotUsed> CreateSource(Offset offset) { return(PersistenceQuery.Get(_system).ReadJournalFor <TJournal>(_journalId) .EventsByTag(_tag, offset) .Select(x => { var domainEvent = Mapper.FromJournal(x.Event, string.Empty).Events.Single(); return new EventEnvelope(x.Offset, x.PersistenceId, x.SequenceNr, domainEvent); })); }
public ConnectionHandler(Stream pStream, CancellationToken pCancellationToken) { _cancellationToken = pCancellationToken; _readJournal = PersistenceQuery .Get(Context.System).ReadJournalFor <SqlReadJournal>("akka.persistence.query.journal.sql"); _materializer = ActorMaterializer.Create(Context.System); _writer = new StreamWriter(pStream) { NewLine = "\n" }; }
protected void ListenToEvents <TEvent>(string persistenceId, Action <TEvent> applyEvent) => PersistenceQuery .Get(_actorSystem) .ReadJournalFor <EventStoreReadJournal>("akka.persistence.query.journal.eventstore") .EventsByPersistenceId(persistenceId, 0, long.MaxValue) .RunForeach(envelope => { _logger.LogInformation($"Received {envelope.PersistenceId} ({envelope.SequenceNr}) :: {envelope.Event}"); applyEvent((TEvent)envelope.Event); }, ActorMaterializer.Create(_actorSystem) );
public bool Handle(BeginStreaming command) { var mat = ActorMaterializer.Create(Context); var readJournal = PersistenceQuery.Get(Context.System) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); readJournal .EventsByTag("GiftCard", NoOffset.Instance) .RunForeach(e => Handle(Context, e), mat); return(true); }
public async Task Given_journal_When_starting_projection_stream_Then_projection_launched_producing_data() { var dep = await Init(); _output.WriteLine(Sys.Settings.ToString()); //generate some data var calculationActor = Sys.ActorOf(Props.Create <CalculatorActor>(), "CalculatorOne"); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1+2-3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1-2*3")); calculationActor.Tell(new CalculatorActorProtocol.CalculateExpression("1/2+3")); var eventName = nameof(CalculatorActor.CalculationPerformed); var readJournal = PersistenceQuery.Get(Sys).ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); var sharedKillSwitch = KillSwitches.Shared("test"); var source = readJournal.EventsByTag(eventName, Offset.NoOffset()) .Via(sharedKillSwitch.Flow <EventEnvelope>()); var flow = FunctionTotalUsageFlow.Instance; var sink = FunctionTotalUsageSink.Create(Sys, eventName); source.Via(flow).To(sink).Run(Sys.Materializer()); await Task.Delay(5000); var projected = new FindProjectionQuery(dep.CreateFunctionUsageContext()).ExecuteForFunctionsTotalUsage(); Assert.Equal(3, projected.Sequence); var usage = await new FunctionsTotalUsageQuery(dep.CreateFunctionUsageContext()).Execute(); usage.Should().BeEquivalentTo( new FunctionTotalUsage { FunctionName = "AddChecked", InvocationsCount = 2 }, new FunctionTotalUsage { FunctionName = "SubtractChecked", InvocationsCount = 2 }, new FunctionTotalUsage { FunctionName = "MultiplyChecked", InvocationsCount = 1 }, new FunctionTotalUsage { FunctionName = "Divide", InvocationsCount = 1 }); }
static void InitializePersisntenceQuery(ActorSystem actorSystem) { var readJournal = PersistenceQuery.Get(actorSystem) .ReadJournalFor <SqlReadJournal>("akka.persistence.query.journal.sql"); // issue query to journal var source = readJournal.PersistenceIds() .Buffer(3, OverflowStrategy.Backpressure) // Backpressure .MergeMany(25, x => // analogue of join in sql readJournal.CurrentEventsByPersistenceId(x, 0, int.MaxValue)); // materialize stream, consuming events var mat = ActorMaterializer.Create(actorSystem); source.RunForeach(envelope => { Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(envelope.Event)); UpdateStatistics(envelope); }, mat); }
private void Test() { var bidProjection = new MyResumableProjection("bid"); var mat = ActorMaterializer.Create(_actorSystem); var readJournal = PersistenceQuery.Get(_actorSystem) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); //note - readmodels will use tags to locate aggregates readJournal .EventsByTag("CarAggregate", Sequence.Sequence(0L)) .Select(e => e.Event) .Select(bidProjection.Save) .RunForeach(o => bidProjection.Save(o), mat) .Wait(); }
static void Main(string[] args) { var actorSystem = ActorSystem.Create("query"); // obtain read journal by plugin id var readJournal = PersistenceQuery.Get(actorSystem) .ReadJournalFor <SqlReadJournal>("akka.persistence.query.my-read-journal"); // issue query to journal Source <EventEnvelope, NotUsed> source = readJournal .EventsByPersistenceId("user-1337", 0, long.MaxValue); // materialize stream, consuming events var mat = ActorMaterializer.Create(actorSystem); source.RunForeach(envelope => { Console.WriteLine($"event {envelope}"); }, mat); }
private ActorMaterializer ConfigureEventHandling(ActorSystem actorSystem, IActorRef primaryEventHandler, ActorSystemOptions options) { var actorMaterializer = actorSystem.Materializer(); var readJournal = PersistenceQuery.Get(actorSystem).ReadJournalFor <SqlReadJournal>("akka.persistence.query.journal.sql"); readJournal //TODO: [THIS IS MOST PROBABLY A BIG ISSUE] if we always query from sequence number 0, then all //event handlers will produce side effects again and again, each time domain events are replayed. //(e.g. this is obsiously a problem if your event handlers send emails) .EventsByPersistenceId(options.PersistenceId, fromSequenceNr: 0, toSequenceNr: long.MaxValue) .Collect(envelope => envelope.Event) .RunWith(Sink.ActorRef <object>(primaryEventHandler, new object() //TODO: you might want to provide these options //.RunWith(Sink.ActorRefWithAck<ItemAdded>(writer, //onInitMessage: CreateViewsActor.Init.Instance, //ackMessage: CreateViewsActor.Ack.Instance, //onCompleteMessage: CreateViewsActor.Done.Instance), materializer); ), actorMaterializer); return(actorMaterializer); }
public BookNameGuardActor() { var queries = PersistenceQuery.Get(Context.System).ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); var materializer = ActorMaterializer.Create(Context.System); var eventsByTag = queries.EventsByTag(nameof(BookCreated)); var self = Self; eventsByTag.RunForeach(envelope => envelope.Event.Match() .With <BookCreated>(self.Tell), materializer); Receive <BookCreated>(bookCreated => { _bookNames.Add(bookCreated.Title); _nameReservations.Remove(bookCreated.Title); }); Receive <CheckBookTitleAvailability>(query => { var reserved = _nameReservations.Contains(query.Title); if (reserved) { Context.System.Scheduler.ScheduleTellOnce(TimeSpan.FromSeconds(1), Self, query, Sender); return; } var isAvailable = !_bookNames.Contains(query.Title); if (isAvailable) { _nameReservations.Add(query.Title); Context.System.Scheduler.ScheduleTellOnce(TimeSpan.FromSeconds(1), Self, new ClearReservation(query.Title), Self); } Sender.Tell(new BookTitleAvailability(query.Title, isAvailable)); }); Receive <ClearReservation>(clearReservation => _nameReservations.Remove(clearReservation.Title)); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); var config = File.ReadAllText("akka.hokon"); var actorSystem = ActorSystem.Create("demo", ConfigurationFactory.ParseString(config)); var sharding = ClusterSharding.Get(actorSystem); var actorRef = sharding.Start( "book", Props.Create <BookAggregate>(), ClusterShardingSettings.Create(actorSystem), new MessageExtractor()); var readJournal = PersistenceQuery.Get(actorSystem) .ReadJournalFor <SqlReadJournal>("akka.persistence.query.sql"); services.AddSingleton(readJournal); services.AddSingleton(actorSystem); services.AddSingleton <BookQueryService>(); var actorService = new ActorService(actorRef); services.AddSingleton(actorService); }
public void ReadJournal_should_throw_if_unable_to_find_query_journal_by_config_key() { Assert.Throws <ArgumentException>(() => PersistenceQuery.Get(Sys).ReadJournalFor <DummyReadJournal>(DummyReadJournal.Identifier + "-fail")); }
public void ReadJournal_should_be_found_by_full_config_key() { PersistenceQuery.Get(Sys).ReadJournalFor <DummyReadJournal>(DummyReadJournal.Identifier); }
public static async Task Rubbish(Akka.Configuration.Config config) { var actorSystem = ActorSystem.Create("tessttt", config); var repositoryActor = actorSystem.ActorOf(Props.Create(() => new RepositoryActor <GiftCardProjection, GiftCardProjectionId>()), "repository-actor"); var projectorMap = new ProjectorMap <GiftCardProjection, GiftCardProjectionId, GiftCardProjectionContext> { Create = async(key, context, projector, shouldOverride) => { var projection = new GiftCardProjection() { Id = key }; await projector(projection); repositoryActor.Tell(new Create <GiftCardProjection, GiftCardProjectionId> { Projection = projection }); }, Update = async(key, context, projector, createIfMissing) => { var query = new Read <GiftCardProjectionId> { Key = key }; var projection = await repositoryActor.Ask <GiftCardProjection>(query); await projector(projection); repositoryActor.Tell(new Update <GiftCardProjection, GiftCardProjectionId> { Projection = projection }); }, Delete = (key, context) => { var command = new Delete <GiftCardProjectionId> { Key = key }; repositoryActor.Tell(command); return(Task.FromResult(true)); }, Custom = (context, projector) => projector() }; var eventMap = new EventMapBuilder <GiftCardProjection, GiftCardProjectionId, GiftCardProjectionContext>(); eventMap.Map <DomainEvent <GiftCard, GiftCardId, IssuedEvent> >() .AsCreateOf(x => IProjectionIdExtensions.From(x.AggregateIdentity)) .Using((projection, evt) => { projection.Credits = evt.AggregateEvent.Credits; projection.IsCancelled = false; projection.Issued = evt.Timestamp.UtcDateTime; }); eventMap.Map <DomainEvent <GiftCard, GiftCardId, RedeemedEvent> >() .AsUpdateOf(x => IProjectionIdExtensions.From(x.AggregateIdentity)) .Using((projection, evt) => { projection.Credits -= evt.AggregateEvent.Credits; }); eventMap.Map <DomainEvent <GiftCard, GiftCardId, CancelledEvent> >() .AsUpdateOf(x => IProjectionIdExtensions.From(x.AggregateIdentity)) .Using((projection, evt) => { projection.IsCancelled = false; }); var handler = eventMap.Build(projectorMap); var context2 = new GiftCardProjectionContext(); var mat = ActorMaterializer.Create(actorSystem); await PersistenceQuery .Get(actorSystem) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier) .CurrentEventsByPersistenceId("giftcard-a8fd515e-d4fb-4bf6-9d7f-67abdd0fdeef", 0, 10) .RunForeach(async x => await handler.Handle(x.Event, context2), mat); }
public BookViewBuilder(IResumableProjection resumableProjection) { _resumableProjection = resumableProjection; _storageContext = new StorageContext(); var self = Self; SqlReadJournal queries = PersistenceQuery.Get(Context.System) .ReadJournalFor <SqlReadJournal>(SqlReadJournal.Identifier); Receive <Option <long> >(option => { var materializer = ActorMaterializer.Create(Context.System); var eventsByTag = queries.EventsByTag("book", new Sequence(option.Value)); eventsByTag .SelectAsync(1, x => self.Ask(x)) .Recover(exception => { Console.WriteLine(exception); return(Option <object> .None); }) .Select(x => (Sequence)x) .SelectAsync(1, x => _resumableProjection.StoreLatestOffset("book-view", x.Value)) .RunWith(Sink.Ignore <bool>(), materializer); }); Receive <EventEnvelope>(envelope => envelope.Event.Match() .With <BookCreated>(bookCreated => { var updateDefinition = new UpdateDefinitionBuilder <BookReadModel>() .SetOnInsert(model => model.Title, bookCreated.Title) .SetOnInsert(model => model.Author, bookCreated.Author) .SetOnInsert(model => model.Tags, bookCreated.Tags) .SetOnInsert(model => model.Cost, bookCreated.Cost) .SetOnInsert(model => model.InventoryAmount, bookCreated.InventoryAmount) .SetOnInsert(model => model.SequenceNr, envelope.SequenceNr); var updateOptions = new UpdateOptions { IsUpsert = true }; _storageContext.Books .UpdateOneAsync(x => x.Id == bookCreated.Id, updateDefinition, updateOptions) .PipeTo(Sender, Self, () => envelope.Offset, exception => new Status.Failure(exception)); }) .With <TagAdded>(tagAdded => { var updateDefinition = new UpdateDefinitionBuilder <BookReadModel>() .Push(model => model.Tags, tagAdded.Tag) .Set(model => model.SequenceNr, envelope.SequenceNr); _storageContext.Books .FindOneAndUpdateAsync(ShouldBeApplied(tagAdded.Id, envelope.SequenceNr), updateDefinition) .PipeTo(Sender, Self, () => envelope.Offset, exception => new Status.Failure(exception)); }) .With <TagRemoved>(tagRemoved => { var updateDefinition = new UpdateDefinitionBuilder <BookReadModel>() .Pull(model => model.Tags, tagRemoved.Tag) .Set(model => model.SequenceNr, envelope.SequenceNr); _storageContext.Books .FindOneAndUpdateAsync(ShouldBeApplied(tagRemoved.Id, envelope.SequenceNr), updateDefinition) .PipeTo(Sender, Self, () => envelope.Offset, exception => new Status.Failure(exception)); }) ); }