Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input and state lost when one entity call another with SignalEntity on .NET 8 isolated #2690

Closed
thomaseyde opened this issue Dec 8, 2023 · 6 comments
Labels
bug P1 Priority 1

Comments

@thomaseyde
Copy link

Description

When you call an entity from another entity with operation.Context.SignalEntity(), data for input and state are both lost. State is lost if the calling entity try to set the state on the called entity before calling operation.Context.SignalEntity().

Input passed with client.Entities.SignalEntityAsync() from a function trigger works as expected.

Expected behavior

State should be kept. Input should be passed.

Actual behavior

Input as retrieved with operation.GetInput<T>() is null.

Relevant source code snippets

Create a new function app on .NET 8 with isolated worker.

Add a trigger:

public class QueueTrigger
{
    [Function(nameof(QueueTrigger))]
    public async Task Run([QueueTrigger("bug")] string message, [DurableClient] DurableTaskClient client)
    {
        if (message == "one")
        {
            await client.Entities.SignalEntityAsync(
                new EntityInstanceId(nameof(OneFunction), "key"), "queued", message);
        }

        if (message == "another")
        {
            await client.Entities.SignalEntityAsync(
                new EntityInstanceId(nameof(AnotherFunction), "key"), "queued", message);
        }
    }
}

Add two functions:

public class OneFunction(ILogger<OneFunction> logger)
{
    [Function(nameof(OneFunction))]
    public Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
    {
        return dispatcher.DispatchAsync(operation =>
        {
            var input = operation.GetInput<string>();
            logger.LogWarning("OneFunction input: '{Input}'", input);

            var another = new EntityInstanceId(nameof(AnotherFunction), "key");
            operation.Context.SignalEntity(another, "another", input);

            if (operation.Name != "self")
            {
                operation.Context.SignalEntity(operation.Context.Id, "self", input);
            }
        
            return default;
        });
    }
}

public class AnotherFunction(ILogger<AnotherFunction> logger)
{
    [Function(nameof(AnotherFunction))]
    public Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
    {
        return dispatcher.DispatchAsync(
            operation =>
            {
                var input = operation.GetInput<string>();
                logger.LogWarning("AnotherFunction input: '{Input}'", input);
                return default;
            });
    }
}

Create a queue bug and add a message containing one to it.
Set breakpoints on calls to GetInput().
Debug the function app.

Functions receiving a call from the trigger will have input. Functions with calls from an entity does not.

Known workarounds

No workarounds found.

App Details

  • Durable Functions extension version: See package references below
  • Azure Functions runtime version: 2 (from host.json)
  • Programming language used: C#
        <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.20.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.1.0"/>
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues" Version="5.2.0"/>
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.4" />
@thomaseyde
Copy link
Author

I found that SendSignalMessage accepts a SendSignalOperationAction which is created in SignalEntity. However, the input value is not used anywhere. RequestMessage has a property Input which is not assigned anything.

        // TaskEntityDispatcher

        void SendSignalMessage(WorkItemEffects effects, SchedulerState schedulerState, SendSignalOperationAction action)
        {
            OrchestrationInstance destination = new OrchestrationInstance()
            {
                InstanceId = action.InstanceId
            };
            RequestMessage message = new RequestMessage()
            {
                ParentInstanceId = effects.InstanceId,
                ParentExecutionId = null, // for entities, message sorter persists across executions
                Id = Guid.NewGuid(),
                IsSignal = true,
                Operation = action.Name,
                ScheduledTime = action.ScheduledTime,
            };
            string eventName;
            if (action.ScheduledTime.HasValue)
            {
                DateTime original = action.ScheduledTime.Value;
                DateTime capped = this.entityBackendProperties.GetCappedScheduledTime(DateTime.UtcNow, original);
                eventName = EntityMessageEventNames.ScheduledRequestMessageEventName(capped);
            }
            else
            {
                eventName = EntityMessageEventNames.RequestMessageEventName;
                schedulerState.MessageSorter.LabelOutgoingMessage(message, action.InstanceId, DateTime.UtcNow, this.entityBackendProperties.EntityMessageReorderWindow);
            }
            this.ProcessSendEventMessage(effects, destination, eventName, message);
        }

@plamber
Copy link

plamber commented Dec 13, 2023

Hi @thomaseyde,
I had a similar issue that I was able to resolve by downgrading to DurableTask 1.0.4.

I logged the issue #2693 and I am experiencing with the latest package 1.1.0 and .NET isolated.

Cheers,
Patrick

@sebastianburckhardt
Copy link
Collaborator

Thanks for reporting this. Your analysis looks right to me - obviously the input should be passed along. I will create a PR with a fix.

@sebastianburckhardt
Copy link
Collaborator

BTW, I did not understand the part about the lost state:

State is lost if the calling entity try to set the state on the called entity before calling operation.Context.SignalEntity()

How would the calling entity set the state on the called entity before calling signal?

@thomaseyde
Copy link
Author

I am sure I tried to set the state on the target entity from the calling entity as a workaround, but I forgot to take notes. Now I can't figure out how I did it.

@lilyjma lilyjma added the P1 Priority 1 label Dec 15, 2023
@lilyjma
Copy link
Contributor

lilyjma commented Jan 30, 2024

Closing because fixed in Azure/durabletask#1016

@lilyjma lilyjma closed this as completed Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug P1 Priority 1
Projects
None yet
Development

No branches or pull requests

4 participants