void CleanupDispatchedEnvelope(ImmutableEnvelope envelope)
        {
            try
            {
                _manager.Memorize(envelope.EnvelopeId);
            }
            catch (ThreadAbortException)
            {
                // continue;
                throw;
            }
            catch (Exception ex)
            {
                SystemObserver.Notify(new EnvelopeCleanupFailed(ex, _dispatcherName, envelope));
            }

            try
            {
                _quarantine.TryRelease(envelope);
            }
            catch (ThreadAbortException)
            {
                // continue
                throw;
            }
            catch (Exception ex)
            {
                SystemObserver.Notify(new EnvelopeCleanupFailed(ex, _dispatcherName, envelope));
            }

            SystemObserver.Notify(new EnvelopeDispatched(envelope, _dispatcherName));
        }
        void ProcessMessage(EnvelopeTransportContext context)
        {
            var processed = false;

            try
            {
                if (_memory.DoWeRemember(context.Unpacked.EnvelopeId))
                {
                    _observer.Notify(new EnvelopeDuplicateDiscarded(context.QueueName, context.Unpacked.EnvelopeId));
                }
                else
                {
                    _dispatcher.DispatchMessage(context.Unpacked);
                    _memory.Memorize(context.Unpacked.EnvelopeId);
                }

                processed = true;
            }
            catch (Exception dispatchEx)
            {
                // if the code below fails, it will just cause everything to be reprocessed later,
                // which is OK (duplication manager will handle this)

                _observer.Notify(new EnvelopeDispatchFailed(context.Unpacked, context.QueueName, dispatchEx));
                // quarantine is atomic with the processing
                if (_quarantine.TryToQuarantine(context, dispatchEx))
                {
                    _observer.Notify(new EnvelopeQuarantined(dispatchEx, context.Unpacked, context.QueueName));
                    // acking message is the last step!
                    _inbox.AckMessage(context);
                }
                else
                {
                    _inbox.TryNotifyNack(context);
                }
            }
            try
            {
                if (processed)
                {
                    // 1st step - dequarantine, if present
                    _quarantine.TryRelease(context);
                    // 2nd step - ack.
                    _inbox.AckMessage(context);
                    // 3rd - notify.
                    _observer.Notify(new EnvelopeAcked(context.QueueName, context.Unpacked.EnvelopeId, context.Unpacked.GetAllAttributes()));
                }
            }
            catch (Exception ex)
            {
                // not a big deal. Message will be processed again.
                _observer.Notify(new EnvelopeAckFailed(ex, context.Unpacked.EnvelopeId, context.QueueName));
            }
        }