-
Notifications
You must be signed in to change notification settings - Fork 147
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
Remove reliance on key order in "do" #875
Comments
Note that |
Thanks, good to know 👍 I will updated the initial comment. On another note: could there be value in renaming |
I understand your point but I have 2 remarks:
|
I completely understand. I am also unsure about that. Exiting a branch/loop is also how I've come to remember it now. But yeah lot of overloaded terms in general. |
I think one of the coolest features of the new DSL is the possibiiltiy to write sequential workflows with very few words. See also this somehow related dicussion |
A possibility would be to add a parameter in the document object, like |
I agree that for purely sequential workflows this is a very nice feature. However I personally do not think it is worth the footgun. I have two scenarios in mind here:
Assume I have a sequential workflow that do:
doStuff1: {}
doStuff2: {} when I run into issues where a even number is required I might add a check do:
checkParity:
isOdd:
when: ${ .x % 2 == 1 }
then: double
default:
then: continue
double:
set:
x: ${ x * 2}
doStuff1: {}
doStuff2: {} I would immediately introduce a bug because I believe it would be easier to update a single reference to point to the newly added task than to go around and suddenly have to add then-references everywhere. Having a required
Both the JSON spec as well as the YAML spec define maps/objects as "unordered". So as long as YAML and/or JSON is chosen as the definition language it seems wrong to me to go against these standards.
I think this would be even worse. Because then there would be no way for me to merge multiple branches of a switch task. Then I'd have to either copy and paste all tasks which makes it very verbose and unmaintainable or I'd have to create a completely separate workflow that all branches invoke. |
No, this is not the intended behavior. The |
but what about a default with |
That is true in theory, but in practice, I never used a serializer implementation which did not preserve the definition order. Plus, that would then be theoretically be true for lists, which should then be indexed thanks to some sort of additional property. |
Then it proceeds to "double", which is the next task in line. |
Arrays are defined with order in both specs and parsers respect that. examples of not preserving order: JSON.parse > JSON.parse('{"3+": 1, "2": 2}')
{ '2': 2, '3+': 1 } https://www.npmjs.com/package/yaml ... also not |
I overlooked that.
Damn. |
JavaScript/TypeScriptany parser in Javascript/Typescript that uses a Object representation for mappings (most do, some have support for a map) while the traversal order of objects is defined no it is not the same as the insertion oder: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#:~:text=The%20traversal%20order,of%20property%20creation. JSON.parse('{"3+": 1, "2": 2}')
// { '2': 2, '3+': 1 } Pythonpyyaml uses import yaml
from collections import OrderedDict
def represent_ordereddict(dumper, data):
value = []
for item_key, item_value in data.items():
node_key = dumper.represent_data(item_key)
node_value = dumper.represent_data(item_value)
value.append((node_key, node_value))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
yaml.add_representer(OrderedDict, represent_ordereddict) source: https://stackoverflow.com/questions/16782112/can-pyyaml-dump-dict-items-in-non-alphabetical-order newer version of python (3.7) preserve insertion order in |
Well, if there's no way around it, I believe you have made your point. Unhappily. The It will make authoring a flow a tad more tedious though 😞 Anyways, thanks a lot for the extremely useful insights! |
An alternative would be to do what Google Workflows do, thus solving everyone's problems: do:
- task1:
call: http
- task2:
call: http We ensure proper ordering while keeping brievity. |
That would work for me 👍 My goal really is to not have to bend over backwards to pass the definition as a string everywhere having to parse it every time and be able to send it via our API as JSON and store it in out DB as a JSON Blob instead of string |
Great!
I know. And you saved us from a painful realisation in a near future. Thanks again! Wanna take care of the PR to address it? |
If we are going to use an array, I guess we might introduce names for task after all. isnt it? |
that makes sense 👍 |
@fjtirado I don't know how others feel about it, but I'd prefer keeping the name in the path, therefore using the solution I proposed, like google does (otherwise, would be the same than going back to where we were). |
@cdavernas I think we need to compare the resulting schemas. |
Btw, when using jackson the map sorting is not an issue. It is using a linkedhashmap that preserve the order. Im saying that because we can still keep the map if we want. The concept of keeping order in map is not an alien thing (might not the default for python, but it does exist in Java and is the default for one of the most popular jackson deserializers) |
But to be honest I prefer an array with an optional property name. And that the do schema, as proposed here, is the same both for main flow, loops and retries. If user want to use names for tasks, he can use it. If they do not want, they can also do it. One way or the other, when there is an error, the error task can be identified (by name or index) |
Sure, whatever you guys want is fine. I'm just sad we have to go down that path again 😭
No, that's not acceptable to say that, because the libs we use work fine that way, we should discard the issue with others, specially when we are speaking of some of the most popular languages on earth,
No. It would be identified only by index, regardless of whether the name has been set or not, otherwise JSON pointers using name would fail to evaluate. |
I would say that neither js neither python are serious programming languages, but thats another discussion ;) |
Once we renounce to the map thing because constraints on "popular" programming languages that are not really programming languages ;), I think we should take the the json schema that is easier to parse (in other words, the json schema with the minimun number of anyOf/oneOf constructs) |
Yeah, why not! That's also a pretty good idea. Even though it might be a tad more cumbersome when wanting only a single task. |
true but do I really need a work flow for a single task? 😅 |
Workflow no. However try/for/... fosho! |
touché ... what if we introduced a shorthand to write document:
dsl: "1.0.0-alpha1"
namespace: test
name: execute-all-the-things
version: "0.1.0"
use:
extensions:
- externalLogging:
extend: all
before:
call: http
with:
method: post
uri: https://fake.log.collector.com
body:
message: msg
after:
call: http
with:
method: post
uri: https://fake.log.collector.com
body:
message: msg
execute:
- processOrder:
switch:
- case1:
when: .orderType == "electronic"
then: processElectronicOrder
- case2:
when: .orderType == "physical"
then: processPhysicalOrder
- default:
then: handleUnknownOrderType
- processElectronicOrder:
execute:
sequentially:
- validatePayment:
set:
validate: true
- fulfillOrder:
set:
status: fulfilled
then: exit
- processPhysicalOrder:
execute:
concurrently:
- checkInventory:
set:
inventory: clear
- packItems:
set:
items: 1
- scheduleShipping:
set:
address: Elmer St
then: exit
- handleUnknownOrderType:
for:
each: order
in: .orders
execute:
sequentially: # <- this is also optional
- logWarning:
set:
log: warn
- notifyAdmin:
try:
execute:
sequentially:
- setMessage:
set:
message: something's wrong
- logMessage:
set:
message2: ${ .message } |
@matthias-pichler-warrify @JBBianchi Just do that here #884 (comment) |
I personally do not like it because of OneOfs. It's something that is a huge PITA for us OO devs (isn't it @fjtirado ?). I'd rather go with @JBBianchi's proposal in #884 |
so basically we do not have execute, we have do: for single task and concurrently or sequentially for multi task. That I think works for everyone. Lets see it reflected on the schema ;) |
perfect I'll get to it 👍 |
I think we overlooked the case of do:
execute:
sequentially:
- processOrder:
switch:
# ... https://github.com/serverlessworkflow/specification/blob/main/dsl-reference.md#switch Shouldn't we go back to |
@JBBianchi Yes, indeed. Do as a mapping array is what we should opt for:
|
That is true. And Which given the discussion around
|
That's a good idea. However, even though I like |
Hmm The idea for |
It is a verb, though certainly not the best term. But there is only a little amount of alternatives, such as the (horrible)
Interesting. Might be worth digging. |
Do you mean: do:
execute:
branch:
- task1:
- task2:
- task3: or do:
branch:
- task1:
- task2:
- task3: (an alternative to In the first case, we keep the "controversial" |
@JBBianchi
we should be able to write
isnt it?
Am I missing anything? (probably I am, my apologies in advance) |
That's my concern indeed.
The later example doesn't seem to match any late suggestions made in this issue. I didn't mention removing e.g.: do:
- processOrder:
switch:
- case1:
when: .orderType == "electronic"
then: processElectronicOrder
- case2:
when: .orderType == "physical"
then: processPhysicalOrder
- default:
then: handleUnknownOrderType
- processElectronicOrder:
do:
- validatePayment: {}
- fulfillOrder: {}
then: exit
- processPhysicalOrder:
execute:
compete: true
branch: # `multitask`, `parallelize` or `fork`
- checkInventory: {}
- packItems: {}
- scheduleShipping: {}
then: exit
- handleUnknownOrderType: #... or do:
- processOrder:
switch:
- case1:
when: .orderType == "electronic"
then: processElectronicOrder
- case2:
when: .orderType == "physical"
then: processPhysicalOrder
- default:
then: handleUnknownOrderType
- processElectronicOrder:
do:
- validatePayment: {}
- fulfillOrder: {}
then: exit
- processPhysicalOrder:
branch:
compete: true
on: # `multitask`, `parallelize` or `fork`
- checkInventory: {}
- packItems: {}
- scheduleShipping: {}
then: exit
- handleUnknownOrderType: #... @cdavernas @matthias-pichler-warrify am I getting it right ? |
Guys, what about just focus on solving the problem that was oversight and making the tasks always an array? Then we open another issue to tackle the execute/concurrently debate. It's already hard to follow up on the discussion on this issue. What about a PR to solve the |
@JBBianchi There was a typo in my last example, I edited it. My point was that this is kind of related with the solution for do: execute:, if we simplify how multitask is written, its not really an issue that switch is a task which only makes sense within a multitask (which is why we are proposing to make do: multitask again) |
I strongly disagree: it is a problem, and a big one.
That is not what we are proposing. We are proposing:
|
@cdavernas |
Hehe, sorry, seems I misunderstood you indeed 🤣
Indeed. |
Please close this issue and continue the discussion in #894 |
What would you like to be added:
I propose to:
continue
FlowDirective to continue a loopthen
required in all tasksstart
field to the workflow that denotes the first task to executeWhy is this needed:
Currently the task execution order is determined by the order in which the tasks appear in the "do" field.
This is very convenient for writing small workflows.
I however see two mayor drawbacks with this approach:
Another interesting case is that the
then
field is allowed top level inswitch
tasks. So The way I understand it, this would execute theprocessElectronicOrder
task when the default case is reached because it sets acontinue
clause.Some problems this causes:
and this only works because
Map
keys preserve order of insertion, assuming thatyaml
never changes the implementation that would change the insertion order.The text was updated successfully, but these errors were encountered: