private void HandleWriteMessages(WriteMessages message)
{
var counter = _resequencerCounter;
/*
* Self MUST BE CLOSED OVER here, or the code below will be subject to race conditions which may result
* in failure, as the `IActorContext` needed for resolving Context.Self will be done outside the current
* execution context.
*/
var self = Self;
_resequencerCounter += message.Messages.Aggregate(1, (acc, m) => acc + m.Size);
var atomicWriteCount = message.Messages.OfType<AtomicWrite>().Count();
AtomicWrite[] prepared;
Task<IImmutableList<Exception>> writeResult;
Exception writeMessagesAsyncException = null;
try
{
prepared = PreparePersistentBatch(message.Messages).ToArray();
// try in case AsyncWriteMessages throws
try
{
writeResult = _breaker.WithCircuitBreaker(() => WriteMessagesAsync(prepared));
}
catch (Exception e)
{
writeResult = Task.FromResult((IImmutableList<Exception>) null);
writeMessagesAsyncException = e;
}
}
catch (Exception e)
{
// exception from PreparePersistentBatch => rejected
writeResult = Task.FromResult((IImmutableList<Exception>)Enumerable.Repeat(e, atomicWriteCount).ToImmutableList());
}
Action<Func<IPersistentRepresentation, Exception, object>, IImmutableList<Exception>> resequence = (mapper, results) =>
{
var i = 0;
var enumerator = results != null ? results.GetEnumerator() : null;
foreach (var resequencable in message.Messages)
{
if (resequencable is AtomicWrite)
{
var aw = resequencable as AtomicWrite;
Exception exception = null;
if (enumerator != null)
{
enumerator.MoveNext();
exception = enumerator.Current;
}
foreach (var p in (IEnumerable<IPersistentRepresentation>)aw.Payload)
{
_resequencer.Tell(new Desequenced(mapper(p, exception), counter + i + 1, message.PersistentActor, p.Sender));
i++;
}
}
else
{
var loopMsg = new LoopMessageSuccess(resequencable.Payload, message.ActorInstanceId);
_resequencer.Tell(new Desequenced(loopMsg, counter + i + 1, message.PersistentActor,
resequencable.Sender));
i++;
}
}
};
writeResult
.ContinueWith(t =>
{
if (!t.IsFaulted && !t.IsCanceled && writeMessagesAsyncException == null)
{
if (t.Result != null && t.Result.Count != atomicWriteCount)
throw new IllegalStateException(string.Format("AsyncWriteMessages return invalid number or results. " +
"Expected [{0}], but got [{1}].", atomicWriteCount, t.Result.Count));
_resequencer.Tell(new Desequenced(WriteMessagesSuccessful.Instance, counter, message.PersistentActor, self));
resequence((x, exception) => exception == null
? (object)new WriteMessageSuccess(x, message.ActorInstanceId)
: new WriteMessageRejected(x, exception, message.ActorInstanceId), t.Result);
}
else
{
var exception = writeMessagesAsyncException != null
? writeMessagesAsyncException
: (t.IsFaulted
? TryUnwrapException(t.Exception)
: new OperationCanceledException(
"WriteMessagesAsync canceled, possibly due to timing out."));
_resequencer.Tell(new Desequenced(new WriteMessagesFailed(exception), counter, message.PersistentActor, self));
resequence((x, _) => new WriteMessageFailure(x, exception, message.ActorInstanceId), null);
}
}, _continuationOptions);
}