microsoft / DurableFunctionsMonitor

A monitoring/debugging UI tool for Azure Durable Functions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Signaling a Durable Entity doesn't allow entry of a plain string.

marked23 opened this issue · comments

Microsoft.Azure.Functions.Worker.Extensions.DurableTask          1.1.0-preview.2
Microsoft.Data.SqlClient                                         5.1.2
Azure.Identity                                                   1.10.4
Azure.Security.KeyVault.Certificates                             4.5.1
Azure.Storage.Blobs                                              12.17.0
Microsoft.ApplicationInsights.AspNetCore                         2.21.0
Microsoft.ApplicationInsights.WorkerService                      2.21.0
Microsoft.Azure.AppConfiguration.Functions.Worker                6.1.1
Microsoft.Azure.Cosmos                                           3.35.4
Microsoft.Azure.Functions.Worker.Extensions.ApplicationInsights  1.0.0-preview4
Microsoft.Azure.Functions.Worker.Extensions.CosmosDB             4.4.2
Microsoft.Azure.Functions.Worker.Extensions.Http                 3.1.0
Microsoft.Azure.Functions.Worker.Extensions.Timer                4.3.0
Microsoft.Azure.Functions.Worker.Sdk                             1.16.2
Microsoft.Azure.Functions.Worker                                 1.20.0
Microsoft.Extensions.Configuration                               7.0.0
Microsoft.Extensions.Configuration.AzureAppConfiguration         6.1.1
Microsoft.Extensions.Configuration.UserSecrets                   7.0.0
Microsoft.Extensions.DependencyInjection                         7.0.0
Microsoft.Extensions.DependencyInjection.Abstractions            7.0.0

Durable Functions Monitor in VSCode                              6.3.0

Dotnet                                                           6

I have this signal method in my isolated Durable Entity class.

public Task ResetLastRunStartDate(string dateTimeOffset) 
{
    LastRunStartDate = DateTimeOffset.Parse(dateTimeOffset);
    IsRunning = false;
    ThisRunStartDate = null;
    ThisRunLookbackDate = null;
    ContinuationCount = 0;
    return Task.CompletedTask;
}

I'm running the project locally, in the Visual Studio debugger, using Azurite.

In the DFM Send Signal dialog, I enter:
Signal Name:
ResetLastRunStartDate
Signal Data (JSON):
{"dateTimeOffset" : "2023-07-01T00:00:00Z"}

In the logs, I see that it errored:

@jobstate@AnalyticsRetrieve_Organization: Processing [EventRaised] (total delay = 14770ms)
...
@jobstate@AnalyticsRetrieve_Organization: Preparing to process a [EventRaised] message
@jobstate@AnalyticsRetrieve_Organization: executing batch of 1 operations on entity state of length 147.
@jobstate@AnalyticsRetrieve_Organization: Function 'jobstate (Entity)' started. IsReplay: False. Input: (588 bytes). State: Started. RuntimeStatus: Running. HubName: PmPdtHub. AppName: . SlotName: . ExtensionVersion: 2.13.0. SequenceNumber: 13. TaskEventId: -1
Executing 'Functions.JobState' (Reason='(null)', Id=4528202a-a8af-4c07-9f7d-a9d27b816e95)
...
Executed 'Functions.JobState' (Succeeded, Id=4528202a-a8af-4c07-9f7d-a9d27b816e95, Duration=5734ms)
@jobstate@AnalyticsRetrieve_Organization: Function 'jobstate (Entity)' completed. ContinuedAsNew: True. IsReplay: False. Output: (588 bytes). State: Completed. RuntimeStatus: Completed. HubName: PmPdtHub. AppName: . SlotName: . ExtensionVersion: 2.13.0. SequenceNumber: 14. TaskEventId: -1
@jobstate@AnalyticsRetrieve_Organization: completed 1 of 1 entity operations, resulting in 1 errors, 0 actions, and entity state of length 147.

Is there a special convention for specifying parameter values when signaling an entity?
I'm able to signal entities that don't take parameters by specifying an empty json doc: { }.

I found more exception details. I'll also show the inputs I've tried:

All of these attempts are signaling to ResetLastRunStartDate:

"2023-07-01"
Failed to raise an event. Can not convert from System.String to Newtonsoft.Json.Linq.JObject.

{ "2023-07-01" }
Failed to parse event data. Expected ':' after property name in JSON at position 15

{ "string" : "2023-07-01" }

�����
��
�System.Text.Json.JsonException�hThe JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.��
�   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerOptions options)
   at Microsoft.DurableTask.Converters.JsonDataConverter.Deserialize(String data, Type targetType)
   at Microsoft.DurableTask.Worker.Shims.TaskEntityShim.OperationShim.GetInput(Type inputType)
   at Microsoft.DurableTask.Entities.TaskEntityOperationExtensions.TryGetInput(TaskEntityOperation operation, ParameterInfo parameter, Object& input)
   at Microsoft.DurableTask.Entities.TaskEntityOperationExtensions.TryDispatch(TaskEntityOperation operation, Object target, Object& result, Type& returnType)
   at Microsoft.DurableTask.Entities.TaskEntity`1.TryDispatchState(TaskEntityOperation operation, Object& result, Type& returnType)
   at Microsoft.DurableTask.Entities.TaskEntity`1.RunAsync(TaskEntityOperation operation)
   at Microsoft.DurableTask.Worker.Shims.TaskEntityShim.ExecuteOperationBatchAsync(EntityBatchRequest operations)"��
 System.InvalidOperationException�?Cannot get the value of a token type 'StartObject' as a string.���
��   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType)
   at System.Text.Json.Utf8JsonReader.GetString()
   at System.Text.Json.Serialization.Converters.StringConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)���
��{"LastRunStartDate":"2023-11-28T21:10:12.7638627+00:00","ThisRunStartDate":null,"ThisRunLookbackDate":null,"ContinuationCount":1,"IsRunning":false}

{ "dateTimeOffset" : "2023-07-01" }

�����
��
�System.Text.Json.JsonException�hThe JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.��
�   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerOptions options)
   at Microsoft.DurableTask.Converters.JsonDataConverter.Deserialize(String data, Type targetType)
   at Microsoft.DurableTask.Worker.Shims.TaskEntityShim.OperationShim.GetInput(Type inputType)
   at Microsoft.DurableTask.Entities.TaskEntityOperationExtensions.TryGetInput(TaskEntityOperation operation, ParameterInfo parameter, Object& input)
   at Microsoft.DurableTask.Entities.TaskEntityOperationExtensions.TryDispatch(TaskEntityOperation operation, Object target, Object& result, Type& returnType)
   at Microsoft.DurableTask.Entities.TaskEntity`1.TryDispatchState(TaskEntityOperation operation, Object& result, Type& returnType)
   at Microsoft.DurableTask.Entities.TaskEntity`1.RunAsync(TaskEntityOperation operation)
   at Microsoft.DurableTask.Worker.Shims.TaskEntityShim.ExecuteOperationBatchAsync(EntityBatchRequest operations)"��
 System.InvalidOperationException�?Cannot get the value of a token type 'StartObject' as a string.���
��   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType)
   at System.Text.Json.Utf8JsonReader.GetString()
   at System.Text.Json.Serialization.Converters.StringConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)���
��{"LastRunStartDate":"2023-11-28T21:10:12.7638627+00:00","ThisRunStartDate":null,"ThisRunLookbackDate":null,"ContinuationCount":1,"IsRunning":false}

Examining the request by observing the entity's dispatcher.

A good request (executed from c# code) looks like this in the dispatcher:

(@jobstate@AnalyticsRetrieve_Organization���
��{"LastRunStartDate":"2023-06-01T00:00:00-07:00","ThisRunStartDate":null,"ThisRunLookbackDate":null,"ContinuationCount":0,"IsRunning":false}�M
�ResetLastRunStartDate�$2ec2d4f9-fc60-4f61-a77d-96f2c06b6f09��
"2023-05-01"

Note that the parameter is on the last line. It's just a string in quotes.

A bad request (sent from DFM's UI) looks like this:

(@jobstate@AnalyticsRetrieve_Organization���
��{"LastRunStartDate":"2023-05-01T00:00:00-07:00","ThisRunStartDate":"2023-11-29T22:50:46.7319416+00:00","ThisRunLookbackDate":"2023-04-30T23:30:00-07:00","ContinuationCount":0,"IsRunning":true}�\
�ResetLastRunStartDate�$a36a0f11-c0d9-4e5d-91a8-06c42c48647a��
�{ "dateTimeOffset" : "2023-07-01" }

Note that the parameter is on the last line. It's a JSON document.

If a user was allowed to type in non-JSON text into the Send Signal dialog, I think this signaling would work.

Sorry for the spammy thread. I hope I'm providing helpful details.

If I modify my signal method to expect a JsonDocument, it works end-to-end, provided that my input is a JSON document.

public Task ResetLastRunStartDate(System.Text.Json.JsonDocument input) 
{
    var element = input.RootElement.GetProperty("dateTimeOffset");
    var dateTimeOffset = element.GetDateTimeOffset();
    LastRunStartDate = dateTimeOffset;
    IsRunning = false;
    ThisRunStartDate = null;
    ThisRunLookbackDate = null;
    ContinuationCount = 0;
    return Task.CompletedTask;
}

However, it seems that a plain string is a legitimate parameter type for an Entity Signal.
It would be helpful if the Durable Functions Monitor UI would allow entry of plain strings.

Thanks for those details, @marked23 !

To make it clear: support for Durable Entities in Isolated mode just recently went into GA, so DfMon didn't have a chance to keep up yet.

Can you confirm that you're using DfMon in form of VsCode extension to send those signals?
I'm asking because I'm surprised it let you go that far - normally it should fail, complaining that 'The function doesn't exist, is disabled, or is not an entity function'.

Can you confirm that you're using DfMon in form of VsCode extension to send those signals?

Yes. Here are some entities running in the emulator.
image

I can view and kinda signal an Entity:
image

Done in v6.4. Pls, validate.