Skip to content

.NET SDK - Phase 2 - Workflows - Initial Comment Period#76

Merged
cretz merged 2 commits intotemporalio:masterfrom
cretz:dotnet-workflows
Feb 27, 2023
Merged

.NET SDK - Phase 2 - Workflows - Initial Comment Period#76
cretz merged 2 commits intotemporalio:masterfrom
cretz:dotnet-workflows

Conversation

@cretz
Copy link
Copy Markdown
Contributor

@cretz cretz commented Feb 21, 2023

.NET SDK - Phase 2 - Workflows - Initial Comment Period

This is the proposal for the second phase of the .NET SDK.

  • See it rendered (or can use the "rich diff" link in upper right; rendered link will disappear on PR merge)
  • Use this PR for comments/discussion. Don't be afraid of making too many comments; if it gets too noisy, I'll just create another PR.
  • Any points of contention will be discussed in-person by the SDK team and a decision will be made

@cretz cretz requested review from a team, alexshtin and sergeybykov February 21, 2023 22:21
Comment thread dotnet/sdk-phase-2.md
present.
* Workflow queries are public methods with the non-inheritable `[WorkflowQuery]` attribute
* Can be in base class, but if overridden, must also have the attribute
* Method cannot return a `Task`, but can accept arguments
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How hard would it be to add support for Task returning queries? I'm asking since we're talking about allowing starting local activities from update validators and queries.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't be that hard, but it adds confusion for users. Some languages actually can do better things.

In .NET for example where there is likely no sandbox and users can run async code quite easily from sync contexts on the global thread pool, we would be much better off just implicitly calling these as local activities already (or doing workflow task heartbeating) and leaving them as non-Task returning. Users can then choose to invoke async code if they really want, but we don't have the concept of local activities in the way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Chad. I don't think we should even call such things "local activities" if we allow it. We should just say "you can disable the sandbox and do stuff from queries if you really want, just don't mutate state, pinky swear". I don't think making it a "real" local activity really buys people anything, but does wildly complicate internals.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason why we do want to leverage activities here is for dependency injection.
I don't know if we'll want to call the API local activity, I agree that is confusing.
I do see a reason to eventually support returning tasks and also using a mechanism supported by the worker so we can avoid triggering the deadlock detector or in generally blocking the workflow thread.
We don't need to design this now though.
I was just pointing out that this might need to change and wanted to understand how hard it would be to change when needed.

Comment thread dotnet/sdk-phase-2.md Outdated
Comment thread dotnet/sdk-phase-2.md Outdated
Comment thread dotnet/sdk-phase-2.md
Comment on lines +264 to +268
public static Task ExecuteActivityAsync(
string activity, object[] args, ActivityStartOptions options) { /*...*/ }

public static Task<TResult> ExecuteActivityAsync<TResult>(
string activity, object[] args, ActivityStartOptions options) { /*...*/ }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you avoided the ambiguity and only do multiple args with stringly typed activities.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 This was also done client side for workflows, signals, and queries. This was also done in Python (kinda, in a slightly different way).

Comment thread dotnet/sdk-phase-2.md
Comment thread dotnet/sdk-phase-2.md Outdated
Comment on lines +447 to +449
* In client I call options `WorkflowStartOptions`, not only for `StartWorkflowAsync` but also for
`ExecuteWorkflowAsync`. I have a similar pattern here with activities and children. Should I continue this or should I
remove the `Start` word?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Java and TS call this WorkflowOptions, I don't see why we need to mention Start, there's nothing to disambiguate (ATM).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I may make this change everywhere then.

Comment thread dotnet/sdk-phase-2.md Outdated
Comment thread dotnet/sdk-phase-2.md
Comment on lines +481 to +484
* `ReplayWorkflowAsync` is thread safe and we'll document this instead of making an overload of this that accepts an
iterator
* Unlike Python we choose not to provide the history on the `WorkflowReplayResult` because we don't aggregate for the
user (they have LINQ ways to run many in parallel and they can correlate as desired) No newline at end of file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the experience without accepting an iterator is nice, I'm all of less APIs.

Copy link
Copy Markdown
Member

@bergundy bergundy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, didn't have any major comments.

Comment thread dotnet/sdk-phase-2.md Outdated
Comment thread dotnet/sdk-phase-2.md
* Every workflow must have the non-inheritable `[Workflow]` attribute
* Optional `Name` positional property can be given. Default is the unqualified type name with the "I" prefix removed
if it's an interface and the next letter is also capital
* So what does `IDWorkflow` become? Yes, `DWorkflow` :-( Weighed as an acceptably rare cost for obvious benefit
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still maintain just having to type the name is better than the inevitable set of users that will run into this and be surprised.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty normal to use the derived name by default (in .NET and in our SDKs). I don't think this is ever gonna matter in practice. This one gotcha is only for those that are intentionally violating .NET standards (something I've found is very rare for iface names).

Comment thread dotnet/sdk-phase-2.md
present.
* Workflow queries are public methods with the non-inheritable `[WorkflowQuery]` attribute
* Can be in base class, but if overridden, must also have the attribute
* Method cannot return a `Task`, but can accept arguments
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Chad. I don't think we should even call such things "local activities" if we allow it. We should just say "you can disable the sandbox and do stuff from queries if you really want, just don't mutate state, pinky swear". I don't think making it a "real" local activity really buys people anything, but does wildly complicate internals.

Comment thread dotnet/sdk-phase-2.md Outdated
Comment thread dotnet/sdk-phase-2.md

public void SetSignalHandler<T>(string name, Func<T, Task> handler) { /*...*/ }

public void RemoveSignalHandler(string name) { /*...*/ }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with this and tryget (as well as for queries) if the handler was defined by [WorkflowSignal] ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handler map is built from the attributes but technically is mutable through these methods. So yes, you can technically remove a handler added via an attribute. And of course getting a handler by name works no matter how that handler was added. In that sense attribute-based handlers (our preference) are just sugar on this. It doesn't make much sense for a user to remove an attribute-added handler IMO, but at least it's a simple to understand concept. This is the same way it works in Python atm too. I hope nobody ever calls dynamic handler things FWIW.

@cretz
Copy link
Copy Markdown
Contributor Author

cretz commented Feb 24, 2023

Added updates based on design session. Specifically:

  • Fixed typos
  • For clarity use an instance-method for an activity
  • Change Refs to ActivityRefs and WorkflowRefs
  • Confirm NonStickyToStickyPollRatio and MaxConcurrentWorkflowTaskPolls will not be user-visible for now
  • Add typed memo getters
  • Conform to new typed search attribute proposal
  • Changed Workflow.Now to Workflow.UtcNow
  • Updated ContinueAsNewException to show it has to be created with a static function for type safety reasons

Will merge early next week if no more feedback comes

@cretz cretz merged commit a4bd120 into temporalio:master Feb 27, 2023
@cretz cretz deleted the dotnet-workflows branch May 22, 2024 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants