Azure / durabletask

Durable Task Framework allows users to write long running persistent workflows in C# using the async/await capabilities.
Apache License 2.0
1.51k stars 290 forks source link

Project: Durable Task Extensions #486

Closed lucaslorentz closed 3 years ago

lucaslorentz commented 3 years ago

I'm creating a project that concisely extends what Durable Task project already delivers: https://github.com/lucaslorentz/durabletask-extensions

This would be the scope of the project:

I still don't know how far I will go with that project.

So I would like to get some feedback if it would be useful to anyone, or if it conflicts with other projects or plans.

Screenshot of history visualization in UI :-) image

cgillum commented 3 years ago

Very cool! A couple other projects to be aware of:

Generally speaking, all of your planned items are things that I think would be very helpful for the project. There may be potential opportunities for alignment regarding some of the projects mentioned above as well. Let us know how we can help.

lucaslorentz commented 3 years ago

Thanks @cgillum

I would need other storage implementations, like DurableTask.SqlServer, to implement some additional contracts I defined:

Any thoughts about those contracts? I wonder if we can incorporate them in official durable task packages. I can create a PR and we discuss it further there.

cgillum commented 3 years ago

@lucaslorentz this is a very ambitious project, I like it! It's great to see you have some experience with Cadence as well since I think it would be good to adopt some of their learnings back into DTFx.

Regarding IExtendedOrchestrationService and INameVersionInfo[]. I like this idea because of how it would allow you to isolate particular activities and orchestrations to specific workers. However, existing providers like DT.ServiceBus and DT.AzureStorage have a shared queue model where all orchestration events and tasks go through a shared set of queues. There's also the problem of being able to filter messages without any embedded name/version information. Should I assume that this interface is only intended for newer SQL-based storage providers?

How far along are your prototype providers? I think I'd feel okay taking some of these extensions into DT.Core if you think they are stable.

Also, have you considered abstract base classes over interfaces as a way to continue extending the functionality without needing to introduce even more interfaces (i.e. to avoid breaking changes)?

lucaslorentz commented 3 years ago

Actually, I have no practical Cadence experience, just read docs and pieces of source code.

Should I assume that this interface is only intended for newer SQL-based storage providers?

That's a fair assumption. I think it would be implemented first by new SQL-based providers. But I think other providers could follow in the future as well.

When implementing that on SQL I found out that using IN and OR to filter queues affects the index scan and consequentially locks scanned rows from "other queues".

The approach ended up doing is to try to dequeue from each one at random order. I guess it would be possible to implement this approach on any "pull to dequeue" provider?

How far along are your prototype providers?

The providers are functional.

The samples folder has a fully working example of a microservices architecture like this diagram. Which involves EFCore + GRPC providers.

But I haven't test anything on a real application or production environment. A lot of testing and fine tunning is still required.

lucaslorentz commented 3 years ago

Also, have you considered abstract base classes over interfaces as a way to continue extending the functionality without needing to introduce even more interfaces (i.e. to avoid breaking changes)?

I think it might be a better option indeed for an evolving contract.

I can use C# 8 default interface implementation as well in my repo. Not sure if Durable Task can use it because it requires netstandard2.1.

lucaslorentz commented 3 years ago

@cgillum

There's also the problem of being able to filter messages without any embedded name/version information

That's only the case for raising events and termination of orchestrations, right?
Providers that use real queues could query the name/version of the instance before putting the message in the queue.
Not optimal, but I don't see many use cases as well with intensive message sending to orchestrations, I would assume it's acceptable to add delays to those operations.

Edit: I guess activity completions also falls under this scenario, which would affect overall performance a bit. But that could be solved by including the Orchestration queue name in the activity message.

lucaslorentz commented 3 years ago

@cgillum

In the end, I implemented the way I described above: In SendTaskOrchestrationMessage I query the current queue (name+version) of the target instanceId. During orchestration task completion it's possible to know the queue for all messages to be sent. And I store reply queue information within every task activity message created.

This approach could be implemented in Azure Storage and Service Bus.

I'm stabilizing the project with tests and I will continue exploring the distributed workers concept.