akkadotnet / akka.net

Canonical actor model implementation for .NET with local + distributed actors in C# and F#.
http://getakka.net
Other
4.69k stars 1.04k forks source link

Inbox.Receive() loses the next message on timeout #2521

Open Jivko1212 opened 7 years ago

Jivko1212 commented 7 years ago

If there is no message in the inbox and receive is called and timeouts the next message is lost. The following code demonstrates the issue:

        var mySys = ActorSystem.Create("mySys");
        var inb = Inbox.Create(mySys);
        mySys.ActorSelection("/user").Tell(new Identify(22), inb.Receiver);

        var ai = (ActorIdentity)inb.Receive();
        try {
            ai = (ActorIdentity)inb.Receive();
        } catch { }
        Thread.Sleep(10);
        mySys.ActorSelection("/user").Tell(new Identify(23), inb.Receiver);
        ai = (ActorIdentity)inb.Receive();

The last receive timeouts. If we comment the line in the try statement it receives an ActorIdentity from the Guardian.

SchiessMax commented 9 months ago

We encountered the same Problem in our code. We polled an inbox, because the receive method does not take a cancellation token as a parameter. If the receive encountered the timeout, then the message was not received with the next call. We now implemented our own inbox that also supports cancellation. This was the unit test that reproduced the Problem for us (pretty much the same like in the original post):

    [Fact]
    public async Task NotReceiveMessageAfterFirstTimeoutReachedWhilePolling()
    {
        // Arrange
        var inbox = Inbox.Create(Sys);

        // Act
        try
        {
            await inbox.ReceiveAsync(TimeSpan.FromMilliseconds(500));
        }
        catch (Exception)
        {
            // Timeout
        }

        var secondReceive = inbox.ReceiveAsync(TimeSpan.FromMilliseconds(500));
        inbox.Receiver.Tell(123);

        // Assert
        var result = await secondReceive;
        result.Should().Be(123);
    }