示例#1
0
        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);
        }
示例#2
0
        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");
                }
            };
        }
示例#4
0
 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"
     };
 }
示例#8
0
 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)
             );
示例#9
0
        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);
        }
示例#10
0
        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
            });
        }
示例#11
0
        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);
        }
示例#12
0
        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);
        }
示例#14
0
        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);
        }
示例#15
0
        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));
        }
示例#16
0
文件: Startup.cs 项目: llehn/UgKaCqrs
        // 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);
 }
示例#19
0
        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);
        }
示例#20
0
        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));
            })
                                    );
        }