/// <summary>
    /// Persist the <see cref="IStreamProcessorState" /> for <see cref="StreamProcessorId"/> and <see cref="SubscriptionId"/>.
    /// Handles <see cref="Partitioned.PartitionedStreamProcessorState"/> separately also.
    /// IsUpsert option creates the document if one isn't found.
    /// </summary>
    /// <param name="id">The <see cref="StreamProcessorId" />.</param>
    /// <param name="baseStreamProcessorState">The <see cref="IStreamProcessorState" />.</param>
    /// <param name="cancellationToken">The <see cref="CancellationToken" />.</param>
    /// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
    public async Task Persist(IStreamProcessorId id, IStreamProcessorState baseStreamProcessorState, CancellationToken cancellationToken)
    {
        _logger.PersistingStreamProcessorState(id);
        try
        {
            if (id is SubscriptionId subscriptionId)
            {
                if (baseStreamProcessorState is Runtime.Events.Processing.Streams.StreamProcessorState streamProcessorState)
                {
                    var replacementState = new MongoSubscriptionState(
                        subscriptionId.ProducerMicroserviceId,
                        subscriptionId.ProducerTenantId,
                        subscriptionId.StreamId,
                        subscriptionId.PartitionId,
                        streamProcessorState.Position,
                        streamProcessorState.RetryTime.UtcDateTime,
                        streamProcessorState.FailureReason,
                        streamProcessorState.ProcessingAttempts,
                        streamProcessorState.LastSuccessfullyProcessed.UtcDateTime,
                        streamProcessorState.IsFailing);
                    var states = await _subscriptionStates.Get(subscriptionId.ScopeId, cancellationToken).ConfigureAwait(false);

                    var persistedState = await states.ReplaceOneAsync(
                        CreateFilter(subscriptionId),
                        replacementState,
                        new ReplaceOptions { IsUpsert = true })
                                         .ConfigureAwait(false);
                }
                else
                {
                    throw new UnsupportedStreamProcessorStatewithSubscriptionId(subscriptionId, baseStreamProcessorState);
                }
            }
            else if (baseStreamProcessorState is Runtime.Events.Processing.Streams.Partitioned.StreamProcessorState partitionedStreamProcessorState)
            {
                var streamProcessorId = id as StreamProcessorId;
                var states            = await _streamProcessorStates.Get(streamProcessorId.ScopeId, cancellationToken).ConfigureAwait(false);

                var state = await states.ReplaceOneAsync(
                    CreateFilter(streamProcessorId),
                    new Partitioned.PartitionedStreamProcessorState(
                        streamProcessorId.EventProcessorId,
                        streamProcessorId.SourceStreamId,
                        partitionedStreamProcessorState.Position,
                        partitionedStreamProcessorState.FailingPartitions.ToDictionary(
                            kvp => kvp.Key.Value.ToString(),
                            kvp => new FailingPartitionState(
                                kvp.Value.Position,
                                kvp.Value.RetryTime.UtcDateTime,
                                kvp.Value.Reason,
                                kvp.Value.ProcessingAttempts,
                                kvp.Value.LastFailed.UtcDateTime)),
                        partitionedStreamProcessorState.LastSuccessfullyProcessed.UtcDateTime),
                    new ReplaceOptions { IsUpsert = true })
                            .ConfigureAwait(false);
            }
            else if (baseStreamProcessorState is Runtime.Events.Processing.Streams.StreamProcessorState streamProcessorState)
            {
                var streamProcessorId = id as StreamProcessorId;
                var states            = await _streamProcessorStates.Get(streamProcessorId.ScopeId, cancellationToken).ConfigureAwait(false);

                var state = await states.ReplaceOneAsync(
                    CreateFilter(streamProcessorId),
                    new StreamProcessorState(
                        streamProcessorId.EventProcessorId,
                        streamProcessorId.SourceStreamId,
                        streamProcessorState.Position,
                        streamProcessorState.RetryTime.UtcDateTime,
                        streamProcessorState.FailureReason,
                        streamProcessorState.ProcessingAttempts,
                        streamProcessorState.LastSuccessfullyProcessed.UtcDateTime,
                        streamProcessorState.IsFailing),
                    new ReplaceOptions { IsUpsert = true })
                            .ConfigureAwait(false);
            }
            else
            {
                throw new StreamProcessorStateOfUnsupportedType(id, baseStreamProcessorState);
            }
        }
        catch (MongoWaitQueueFullException ex)
        {
            throw new EventStoreUnavailable("Mongo wait queue is full", ex);
        }
    }