void SendBatchOnceEventIsPersisted(PartitionUpdateEvent evt, EffectTracker effects, Batch batch)
        {
            // put the messages in the outbox where they are kept until actually sent
            var commitPosition = evt.NextCommitLogPosition;

            // Update the ready to send timestamp to check the delay caused
            // by non-speculation
            evt.ReadyToSendTimestamp = this.Partition.CurrentTimeMs;

            this.Outbox[commitPosition] = batch;
            batch.Position  = commitPosition;
            batch.Partition = this.Partition;

            if (!effects.IsReplaying)
            {
                if (!this.Partition.Settings.PersistStepsFirst)
                {
                    // we must not send messages until this step has been persisted
                    evt.OutboxBatch = batch;
                    DurabilityListeners.Register(evt, this);
                }
                else
                {
                    // we can send the messages now
                    this.Send(batch);
                }
            }
        }
 void Send(Batch batch)
 {
     // now that we know the sending event is persisted, we can send the messages
     foreach (var outmessage in batch.OutgoingMessages)
     {
         DurabilityListeners.Register(outmessage, batch);
         outmessage.OriginPartition = this.Partition.PartitionId;
         outmessage.OriginPosition  = batch.Position;
         //outmessage.SentTimestampUnixMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
         this.Partition.Send(outmessage);
     }
 }
        protected override async Task Process(IList <PartitionEvent> batch)
        {
            try
            {
                var effects = new EffectTracker(
                    this.partition,
                    this.ApplyToStore,
                    () => (this.commitPosition, this.inputQueuePosition)
                    );

                if (batch.Count != 0)
                {
                    foreach (var partitionEvent in batch)
                    {
                        // record the current time, for measuring latency in the event processing pipeline
                        partitionEvent.IssuedTimestamp = this.partition.CurrentTimeMs;

                        try
                        {
                            switch (partitionEvent)
                            {
                            case PartitionUpdateEvent updateEvent:
                                updateEvent.NextCommitLogPosition = this.commitPosition + 1;
                                await effects.ProcessUpdate(updateEvent).ConfigureAwait(false);

                                DurabilityListeners.ConfirmDurable(updateEvent);
                                if (updateEvent.NextCommitLogPosition > 0)
                                {
                                    this.partition.Assert(updateEvent.NextCommitLogPosition > this.commitPosition);
                                    this.commitPosition = updateEvent.NextCommitLogPosition;
                                }
                                break;

                            case PartitionReadEvent readEvent:
                                readEvent.OnReadIssued(this.partition);
                                if (readEvent.Prefetch.HasValue)
                                {
                                    var prefetchTarget = this.GetOrAdd(readEvent.Prefetch.Value);
                                    effects.ProcessReadResult(readEvent, readEvent.Prefetch.Value, prefetchTarget);
                                }
                                var readTarget = this.GetOrAdd(readEvent.ReadTarget);
                                effects.ProcessReadResult(readEvent, readEvent.ReadTarget, readTarget);
                                break;

                            case PartitionQueryEvent queryEvent:
                                var instances      = this.QueryOrchestrationStates(queryEvent.InstanceQuery);
                                var backgroundTask = Task.Run(() => effects.ProcessQueryResultAsync(queryEvent, instances.ToAsyncEnumerable()));
                                break;

                            default:
                                throw new InvalidCastException("could not cast to neither PartitionReadEvent nor PartitionUpdateEvent");
                            }

                            if (partitionEvent.NextInputQueuePosition > 0)
                            {
                                this.partition.Assert(partitionEvent.NextInputQueuePosition > this.inputQueuePosition);
                                this.inputQueuePosition = partitionEvent.NextInputQueuePosition;
                            }
                        }
                        catch (Exception e)
                        {
                            this.partition.ErrorHandler.HandleError(nameof(Process), $"Encountered exception while processing event {partitionEvent}", e, false, false);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception in MemoryQueue BatchWorker: {exception}", e);
            }
        }