-
Notifications
You must be signed in to change notification settings - Fork 34
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
Add versioning support in durabletask-dotnet(phase1) #295
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
This PR is depended on the PR of Azure/durabletask#1071 to build. |
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
… breaking change. Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
…et into versioning-phase1
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit concerned about the usability of this API. Both behavior we had before this PR, but also behavior afterwards.
Right now my biggest concern is we have a semantic of using :
as the separator for name & version when going to string. We never restricted characters in the task name before, but we have a need for it now (restricting :
) which is a breaking change. Wonder how we can work through this.
src/Abstractions/TaskName.cs
Outdated
{ | ||
return this.Name + ":" + this.Version; | ||
} | ||
return this.Name + ":" + this.Version; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider adding FromString semantics. But I wonder if that will be a breaking change? We have not restricted what characters can be used in a task name before this.
This change is going to introduce a confusing behavior:
TaskName name1 = new("MyTask", "2"); // Name = "MyTask", Version = "2'
TaskName name2 = name1.ToString(); // implicit operator convers. Name = "MyTask:2", Version = ""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the implicit operator be updated to parse out the version?
I'm not super concerned about the breaking change since 99+% of .NET users should be relying on the function name, which already can't have special characters. However, we can take a look at Kusto data to confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes the implicit operator would be changed to call FromString
.
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
In this PR I just want to pass version field correctly so that it can used to support versioning feature. But I don't know if I should update the related method like string() / Equals() / GetHashCode() because before this PR these methods ignored version field. I'm afraid that after I updated these methods (string() / Equals() / GetHashCode()) to include version field but not only name field, there are some broken changes to exist code. @jviau @cgillum please help to make a decision that should I update these methods to include version field. |
src/Abstractions/TaskName.cs
Outdated
/// <param name="version">The version of the task.</param> | ||
public TaskName(string name, string version) | ||
{ | ||
if (name is null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good opportunity for pattern matching. Can replace this if/else statements with:
(this.Name, this.Version) = (name, version) switch
{
(null, null) => (null!, null!), // Force the default struct when null is passed in.
(null, string _) => throw new ArgumentException("name must not be null when version is non-null"),
(string _, null) => (name, string.Empty),
(string _, string _) => (name, version),
};
Also while we are at it, can replace the other ctor with:
(this.Name, this.Version) = name switch
{
null => (null!, null!), // Force the default struct when null is passed in.
string _ => (name, string.Empty),
}
The from string conversion is the only breaking change I see. We need to parse the version out of the string, otherwise round-tripping between string and back will give different results. But if we choose a delimiter a customer already uses, it can break their orchestrations. But I am not sure what other choice we have if we want to introduce @cgillum what do you think? Do we introduce a behavior breaking change? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comments. One thing that's odd to me is that we don't roundtrip the version information when converting between TaskName
and string
. I left a comment about this.
The breaking change part might be something we can confirm with telemetry. I can help with this.
src/Abstractions/TaskName.cs
Outdated
{ | ||
return this.Name + ":" + this.Version; | ||
} | ||
return this.Name + ":" + this.Version; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the implicit operator be updated to parse out the version?
I'm not super concerned about the breaking change since 99+% of .NET users should be relying on the function name, which already can't have special characters. However, we can take a look at Kusto data to confirm.
Yes, we definitely need to add this. @skyao can you also add |
src/Abstractions/TaskName.cs
Outdated
{ | ||
return this.Name + ":" + this.Version; | ||
} | ||
return this.Name + ":" + this.Version; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have a discussion on what separator to use. I like @
, as that is what we have used for distributed tracing to indicate the version - and ":" was used to separate the task type: {task_type}:{name}@{version}
However, we already use @
to separate entity name and key - will that be an issue? Or is that a good thing to use @
as a separator in both for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using @
could break entity ID parsing. Looking at this code, I can see that entity IDs take the form of {name}@{key}
and the code that parses this looks for the first instance of @
to separate the name from the key. If we inject a version into the name, we make it {name}@{version}@{key}
, and the key for entities will be parsed as {version}@{key}
instead of {key}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cgillum yeah I am trying to think through how that behaves. One thing is that it is a separate type for entities. We have a few options:
- use
@
for separator. Entity instance IDs can then look like@{entity}@{version}@{key}
(or we can do key first, then version). RISK: keys could already contain@
. This could break those instances.- @sebastianburckhardt suggested we could prefix with double
@@
to indicate this entity has a version field. So@myEntity@some@key
-> no version field.@@myEntity@version@some@key
-> version field present.
- @sebastianburckhardt suggested we could prefix with double
- use a different character.
@{entity}?{version}@{key}
(or key first). RISK: this character could already be used by customers in name or key - Alternatively, we could opt to not support versioning of entities just yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to clarify that I am not opposed to a different separator char for version. We haven't officially GA'd distributed tracing v2, so we could update that spec with the new separator. But what one exactly?
Some that I have ruled out myself in the past:
/
- avoided this so it wasn't confused with HTTP routes in distributed tracing%
- often used for encoding special chars, so wanted to avoid that*
- wild card, wanted to avoid that.!
- used in netherite for partition targeting. We are looking to also support that in other backends.:
- used in distributed tracing to separate the task-type. ie:orchestration:name@version
oractivity:name@version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we merge this PR first and then open a new issue to trace and update the separator after we have a final decision?
Because today is my last day, I'm afraid that I can't continue to update the code in this PR because of permissions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
…tor to call FromString() method Signed-off-by: Sky Ao <aoxiaojian@gmail.com>
Done, code updated. Please have a look. |
Code updates:
TaskName
Add a new constructor that sets the version expected by the client.
The existing code has a version field and associated implementation, but no constructor to set the version is provided. Adding this constructor makes it possible to do so in client-side code:
TaskOrchestrationContext
Add new abstract method to get instance version.
So that in the orchestration code, we get the instance version by this context:
TaskOrchestrationContextWrapper
Implemented new added abstract method to get instance version.
This method will get the value of instance version from OrchestrationContext.Version.
This PR is depended on another PR in durable repo.
Azure/durabletask#1071