public void CheckSubscribeThenPublishFromAnotherThreadShouldReceiveEvent()
        {
            // Arrange
            var eventAggregator = new EventAggregator();
            var source          = new EventSource(eventAggregator);
            var receiver        = new EventReceiver(eventAggregator);

            var data = EventData.Random();

            // Act
            var unsubscriber = receiver.Subscribe();

            Task.Run(() =>
            {
                source.Emit(data);
            });

            receiver.Wait();

            // Assert
            Assert.True(receiver.EventReceived);
            Assert.Equal(data, receiver.Data);
            Assert.Equal(data.TestGuid, receiver.Data.TestGuid);
            Assert.Equal(data.TestInteger, receiver.Data.TestInteger);
            Assert.Equal(data.TestStr, receiver.Data.TestStr);

            // Cleanup
            unsubscriber.Dispose();
            eventAggregator.Dispose();
            receiver.Dispose();
        }
        public void CheckSubscribeThenUnsubscribeShouldNotReceivedEvent()
        {
            // Arrange
            var eventAggregator = new EventAggregator();
            var source          = new EventSource(eventAggregator);
            var receiver        = new EventReceiver(eventAggregator);

            var data = EventData.Random();

            // Act
            var unsubscriber = receiver.Subscribe();

            unsubscriber.Dispose();

            source.Emit(data);

            receiver.Wait();

            // Assert
            Assert.False(receiver.EventReceived);
            Assert.Null(receiver.Data);

            // Cleanup
            eventAggregator.Dispose();
            receiver.Dispose();
        }
예제 #3
0
        public void RunInitialize()
        {
            mongoDatabase = mongoClient.GetDatabase(Guid.NewGuid().ToString());

            var log = new SemanticLog(new ILogChannel[0], new ILogAppender[0], () => new JsonLogWriter(Formatting.Indented, true));

            eventConsumerInfos = new MongoEventConsumerInfoRepository(mongoDatabase);
            eventNotifier      = new DefaultEventNotifier(new InMemoryPubSub());
            eventStore         = new MongoEventStore(mongoDatabase, eventNotifier);
            eventConsumer      = new MyEventConsumer(NumEvents);

            eventReceiver = new EventReceiver(formatter, eventStore, eventNotifier, eventConsumerInfos, log);
            eventReceiver.Subscribe(eventConsumer);
        }
        public void CheckSubscriberWithoutPublisherShouldNotReceivedEvent()
        {
            // Arrange
            var eventAggregator = new EventAggregator();
            var receiver        = new EventReceiver(eventAggregator);

            // Act
            var unsubscriber = receiver.Subscribe();

            receiver.Wait();

            // Assert
            Assert.False(receiver.EventReceived);
            Assert.Null(receiver.Data);

            // Cleanup
            unsubscriber.Dispose();
            eventAggregator.Dispose();
            receiver.Dispose();
        }
        public void CheckTwoSubscribersShouldReceiveSameEvent()
        {
            // Arrange
            var eventAggregator = new EventAggregator();
            var source          = new EventSource(eventAggregator);
            var firstReceiver   = new EventReceiver(eventAggregator);
            var secondReceiver  = new EventReceiver(eventAggregator);

            var data = EventData.Random();

            // Act
            var firstUnsubscriber  = firstReceiver.Subscribe();
            var secondUnsubscriber = secondReceiver.Subscribe();

            source.Emit(data);

            firstReceiver.Wait();
            secondReceiver.Wait();

            // Assert
            Assert.True(firstReceiver.EventReceived);
            Assert.Equal(data, firstReceiver.Data);
            Assert.Equal(data.TestGuid, firstReceiver.Data.TestGuid);
            Assert.Equal(data.TestInteger, firstReceiver.Data.TestInteger);
            Assert.Equal(data.TestStr, firstReceiver.Data.TestStr);

            Assert.True(secondReceiver.EventReceived);
            Assert.Equal(data, secondReceiver.Data);
            Assert.Equal(data.TestGuid, secondReceiver.Data.TestGuid);
            Assert.Equal(data.TestInteger, secondReceiver.Data.TestInteger);
            Assert.Equal(data.TestStr, secondReceiver.Data.TestStr);

            // Cleanup
            firstUnsubscriber.Dispose();
            secondUnsubscriber.Dispose();
            eventAggregator.Dispose();
            firstReceiver.Dispose();
            secondReceiver.Dispose();
        }
예제 #6
0
        private IDisposable BinaryReferenceEventsProducing(CancellationToken cancellationToken)
        {
            async Task ProcessAsync(KafkaEvent <ObjectVersionCreatingEvent> @event)
            {
                var objectId  = @event.Source.ObjectId;
                var versionId = @event.Source.CurrentVersionId;

                IReadOnlyCollection <ObjectVersionRecord> versionRecords;

                if (string.IsNullOrEmpty(versionId))
                {
                    var policy = CreateGetObjectVersionsResiliencePolicy();
                    versionRecords = await policy.ExecuteAsync(() => _objectsStorageReader.GetObjectVersions(objectId, versionId));

                    if (versionRecords == null)
                    {
                        _logger.LogWarning(
                            "{taskName}: Got an event for the object with id = '{objectId}' that was not eventually created. The event will be skipped.",
                            nameof(ObjectVersionCreatedEventsProducing),
                            objectId);
                        return;
                    }
                }
                else
                {
                    versionRecords = await _objectsStorageReader.GetObjectVersions(objectId, versionId);
                }

                _logger.LogInformation(
                    "{taskName}: There are '{versionsCount}' new versions were created after the versionId = {versionId} " +
                    "for the object id = '{objectId}'.",
                    nameof(BinaryReferenceEventsProducing),
                    versionRecords.Count,
                    versionId,
                    objectId);
                foreach (var record in versionRecords)
                {
                    var fileInfos = record.Elements
                                    .Where(x => x.Value is IBinaryElementValue binaryValue && !string.IsNullOrEmpty(binaryValue.Raw))
                                    .Select(x => (TemplateCode: x.TemplateCode, FileKey: ((IBinaryElementValue)x.Value).Raw))
                                    .ToList();
                    foreach (var fileInfo in fileInfos)
                    {
                        await _eventSender.SendAsync(
                            _binariesUsingsTopic,
                            new BinaryReferencedEvent(objectId, record.VersionId, fileInfo.TemplateCode, fileInfo.FileKey, record.LastModified));

                        _logger.LogInformation(
                            "{taskName}: Event for binary reference {fileKey} for element with templateCode = '{templateCode}' " +
                            "for object id = '{objectId}' and versionId = {versionId} sent to {topic}.",
                            nameof(BinaryReferenceEventsProducing),
                            fileInfo.FileKey,
                            fileInfo.TemplateCode,
                            record.Id,
                            record.VersionId,
                            _binariesUsingsTopic);
                    }
                }

                await _binariesEventReceiver.CommitAsync(@event);
            }

            var observable = _binariesEventReceiver.Subscribe <ObjectVersionCreatingEvent>(cancellationToken);

            return(observable
                   .Do(@event =>
            {
                var retry = CreateRetryPolicy(_logger, nameof(BinaryReferenceEventsProducing));
                retry.Execute(() => ProcessAsync(@event).GetAwaiter().GetResult());
            })
                   .Subscribe());
        }
예제 #7
0
        private IDisposable ObjectVersionCreatedEventsProducing(CancellationToken cancellationToken)
        {
            async Task ProcessAsync(KafkaEvent <ObjectVersionCreatingEvent> @event)
            {
                var objectId  = @event.Source.ObjectId;
                var versionId = @event.Source.CurrentVersionId;

                IReadOnlyCollection <ObjectVersionRecord> versionRecords;

                if (string.IsNullOrEmpty(versionId))
                {
                    var policy = CreateGetObjectVersionsResiliencePolicy();
                    versionRecords = await policy.ExecuteAsync(() => _objectsStorageReader.GetObjectVersions(objectId, versionId));

                    if (versionRecords == null)
                    {
                        _logger.LogWarning(
                            "{taskName}: Got an event for the object with id = '{objectId}' that was not eventually created. The event will be skipped.",
                            nameof(ObjectVersionCreatedEventsProducing),
                            objectId);
                        return;
                    }
                }
                else
                {
                    versionRecords = await _objectsStorageReader.GetObjectVersions(objectId, versionId);
                }

                _logger.LogInformation(
                    "{taskName}: There are '{versionsCount}' new versions were created after the versionId = {versionId} for the object id = '{objectId}'.",
                    nameof(ObjectVersionCreatedEventsProducing),
                    versionRecords.Count,
                    versionId,
                    objectId);
                foreach (var record in versionRecords)
                {
                    var versionCreatedEvent = new ObjectVersionCreatedEvent(
                        record.Id,
                        record.VersionId,
                        record.VersionIndex,
                        record.Author,
                        record.Properties,
                        record.LastModified);
                    await _eventSender.SendAsync(_objectVersionsTopic, versionCreatedEvent);

                    _logger.LogInformation(
                        "{taskName}: Event for object id = '{objectId}' and versionId = {versionId} sent to {topic}.",
                        nameof(ObjectVersionCreatedEventsProducing),
                        record.Id,
                        record.VersionId,
                        _objectVersionsTopic);
                }

                await _versionEventReceiver.CommitAsync(@event);
            }

            var observable = _versionEventReceiver.Subscribe <ObjectVersionCreatingEvent>(cancellationToken);

            return(observable
                   .Do(@event =>
            {
                var retry = CreateRetryPolicy(_logger, nameof(ObjectVersionCreatedEventsProducing));
                retry.Execute(() => ProcessAsync(@event).GetAwaiter().GetResult());
            })
                   .Subscribe());
        }
예제 #8
0
        private IDisposable CleanupBinaries(int batchSize, TimeSpan delay, CancellationToken cancellationToken)
        {
            (IReadOnlyCollection <KafkaEvent <SessionCreatingEvent> >, DateTime) EvaluateExpiredSessions(
                IEnumerable <KafkaEvent <SessionCreatingEvent> > sessionCreatingEvents,
                IEnumerable <KafkaEvent <BinaryReferencedEvent> > referenceEvents)
            {
                var lastReferenceEvent = referenceEvents.Last();
                var periodEnd          = lastReferenceEvent.Source.ReferencedAt ?? lastReferenceEvent.Timestamp.UtcDateTime;

                _logger.LogInformation("Evaluating the number of expired sessions by '{periodEnd:u}'.", periodEnd);
                return(sessionCreatingEvents.Where(x => x.Source.ExpiresAt <= periodEnd).ToList(), periodEnd);
            }

            async Task ProcessAsync(IList <KafkaEvent <SessionCreatingEvent> > sessionCreatingEvents)
            {
                var oldestSessionDate = sessionCreatingEvents[0].Timestamp.UtcDateTime;
                var dateToStart       = oldestSessionDate.Subtract(SafetyPeriod);

                _logger.LogInformation(
                    "Starting to process '{totalSessionsCount}' sessions. Oldest session date: {oldestSessionDate:u}. " +
                    "Binary reference events stream will be read starting from {dateToStart:u}.",
                    sessionCreatingEvents.Count,
                    oldestSessionDate,
                    dateToStart);

                var referenceEventsPolicy = CreateReferenceEventsReadingPolicy(_logger, dateToStart, delay);
                var referenceEvents       = await referenceEventsPolicy.ExecuteAsync(
                    async() =>
                {
                    var result = await _referencesEventReader.ReadAsync <BinaryReferencedEvent>(
                        _binariesReferencesTopicName,
                        dateToStart);
                    return(new List <KafkaEvent <BinaryReferencedEvent> >(result));
                });

                _logger.LogInformation(
                    "'{referenceEventsCount}' events of type {eventType} read in time interval from {dateToStart:u} till now.",
                    referenceEvents.Count,
                    typeof(BinaryReferencedEvent).Name,
                    dateToStart);

                var(expiredSessionEvents, periodEnd) = EvaluateExpiredSessions(sessionCreatingEvents, referenceEvents);
                while (expiredSessionEvents.Count != sessionCreatingEvents.Count)
                {
                    _logger.LogWarning(
                        "There are only '{expiredSessionsCount}' of '{totalSessionsCount}' sessions expired by {periodEnd:u} in current batch. " +
                        "The {workerJobType} will now wait for {totalWaitTime:g}.",
                        expiredSessionEvents.Count,
                        sessionCreatingEvents.Count,
                        periodEnd,
                        typeof(BinariesCleanupJob).Name,
                        delay);
                    await Task.Delay(delay, cancellationToken);

                    var additional = await _referencesEventReader.ReadAsync <BinaryReferencedEvent>(_binariesReferencesTopicName, periodEnd.Subtract(SafetyPeriod));

                    referenceEvents.AddRange(additional);
                    var(extendedExpired, extendedPeriodEnd) = EvaluateExpiredSessions(sessionCreatingEvents, referenceEvents);

                    expiredSessionEvents = extendedExpired;
                    periodEnd            = extendedPeriodEnd;
                }

                var sessionsWithReferences = new HashSet <Guid>(referenceEvents.Select(EvaluateSessionId));

                _logger.LogInformation(
                    "Starting to archive unreferenced sessions in batch of '{expiredSessionsCount}' sessions " +
                    "considering that '{referencedSessionsCount}' sessions were referenced in time interval from {dateToStart:u} till now.",
                    expiredSessionEvents.Count,
                    sessionsWithReferences.Count,
                    dateToStart);

                var archievedSessionsCount = await ArchieveUnreferencedBinaries(expiredSessionEvents, sessionsWithReferences, cancellationToken);

                _logger.LogInformation(
                    "Total '{totalSessionsCount}' sessions has been processed, '{archievedSessionsCount}' were archived.",
                    sessionCreatingEvents.Count,
                    archievedSessionsCount);
            }

            var observable = _sessionsEventReceiver.Subscribe <SessionCreatingEvent>(cancellationToken);

            return(observable
                   .Buffer(batchSize)
                   .Do(batch =>
            {
                var retry = CreateRetryPolicy(_logger);
                retry.Execute(() => ProcessAsync(batch).GetAwaiter().GetResult());
            })
                   .Subscribe());
        }
        public void CheckLongRunningHandlerShouldNotBlockOtherHandler()
        {
            // Arrange
            var timerElapsed = new ManualResetEventSlim(false);
            var timer        = new Timer(
                _ =>
            {
                timerElapsed.Set();
            },
                null,
                1000,
                0);

            var eventAggregator = new EventAggregator();
            var source          = new EventSource(eventAggregator);
            var firstReceiver   = new EventReceiver(eventAggregator, () =>
            {
                timerElapsed.Wait();
            });
            var secondReceiver = new EventReceiver(eventAggregator);

            var data = EventData.Random();

            // Act
            var firstUnsubscriber  = firstReceiver.Subscribe();
            var secondUnsubscriber = secondReceiver.Subscribe();

            source.Emit(data);

            firstReceiver.Wait();
            secondReceiver.Wait();

            // Assert
            Assert.False(firstReceiver.EventReceived);
            Assert.True(secondReceiver.EventReceived);

            Assert.Null(firstReceiver.Data);
            Assert.Equal(data, secondReceiver.Data);
            Assert.Equal(data.TestGuid, secondReceiver.Data.TestGuid);
            Assert.Equal(data.TestInteger, secondReceiver.Data.TestInteger);
            Assert.Equal(data.TestStr, secondReceiver.Data.TestStr);

            firstReceiver.Wait(1000);

            Assert.True(firstReceiver.EventReceived);

            Assert.Equal(data, firstReceiver.Data);
            Assert.Equal(data.TestGuid, firstReceiver.Data.TestGuid);
            Assert.Equal(data.TestInteger, firstReceiver.Data.TestInteger);
            Assert.Equal(data.TestStr, firstReceiver.Data.TestStr);

            // Cleanup
            firstUnsubscriber.Dispose();
            secondUnsubscriber.Dispose();
            eventAggregator.Dispose();
            firstReceiver.Dispose();
            secondReceiver.Dispose();

            timer.Dispose();
            timerElapsed.Dispose();
        }