-
-
Notifications
You must be signed in to change notification settings - Fork 280
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
Proposal to add a 'version' value into the message object to support a more granular versioning #1068
Comments
Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our contributors guide and the instructions about a basic recommended setup useful for opening a pull request. |
I'm all in with this proposal. Just want to leave a note on the following:
I think it's more like the latter. The version of the application has nothing to do with the version of the messages. This surely can be clarified. |
Sure, in my case the implementation of this in EventCatalog has assumed the events has a version, what has created the problem for me. I agree the spec don't imply events have a version. Do you want to edit my message/proposal ? Not sure how this contributions work on that aspect, and if the proposal gets edited or not. Thanks a lot! Also, if this gets approved, I am happy to make the changes in the parser, spec files, etc. It shouldn't be a big job (famous last words)... |
I +1 this. I believe it is a good addition @IsmaelMartinez 💯 |
No, I think that's fine. Just wanted to clarify it 👍 |
Sounds like a good proposal yea 👍 Maybe make global version optional 🤔 |
Can that be for another RFC ? Otherwise changes can grow arms, legs and tentacles ;) There doesn't seem to be any opposition to the matter. Reading the CONTRIBUTING.md I understand there are a few promotion steps that needs to happen and then a few changes to the parser and other projects. I would be happy to start making those PRs just to have them ready for when that promotion happens. Just let me know if I am jumping the gun. Ta |
Good to see some movement in the spec proposals, thanks @IsmaelMartinez I saw your PR and have some questions/requests - I put these here in the issue as for now the PR is very small without much detail. There is an old and similar issue, not sure if you had a chance to see it: #697 It is schema versioning related - but seems to be the same topic, just a different level of defining version.
Application level version is application-related and does not imply the version change on the message level. With the above, we already end up with the option to have a version maybe on the message level, maybe on the schema level, but maybe also on the channel level. Have a look for example at below case study (channel names in the example AsyncAPI document) and notice how Adeo also specifies the version in the name (address in v3) of the channel. https://www.asyncapi.com/casestudies/adeogroup To complex things even more, you can see that it may also happen that a version of the schema, like also in the case of CloudEvents, can be defined at the schema level because integration systems require it in the payload -> https://www.asyncapi.com/casestudies/adeogroup#versioning-of-schemas With all this EDA complexity that we have to deal with, and different patterns around - does it make sense to somehow arbitrarily say that versioning should be defined on the message level? Of course, I might be missing something and have wrong assumptions - sorry for that. Sorry but just wanted to drop all these questions to make sure we clarify some things first. Also for me a must-have for this proposal is to have some real examples of AsyncAPI documents with this new version flag. If you could take some time and show some of your real AsyncAPI documents and how they would benefit from this new flag - anonymize them to the maximum if needed. What is the benefit? for docs readers that they can see a kind of drop-down and switch between message definitions in different versions? but messages are usually shown in context of an operation, would you have a case where you have one operation that might receive two different message versions? like This is what travelling is for, to sit at the airport for a few hours and find time to interact in GitHub issues 😄 Thanks again a lot for the proposal 🙏🏼 🙏🏼 🙏🏼 and please do not feel discouraged about all these questions 🙏🏼 |
Hi @derberg , Thanks for the long reply and apologies I didn't find the related issue. I think we got different use cases in there, but would suggest for the focus of this change to be only related to the message part. Currently there isn't a standard way to specify a version for a message in asyncapi, what means there is a divergence on how people are implementing the versioning.
My description applies to EventCatalog, but as you have highlighted, seems to be a broader issue that other people are tackling differently. In EventCatalog they have accepted the application version as the version of all events. This is incorrect and have driven me into this quest to try to put some sense to the madness. From my understanding, adding an optional version in the messageObject level can help standardise this area. I don't think there is a standard way to do this neither in OpenAPI, JSONSchema, avro, CloudEvents, etc. OpenAPI has a similar version for the document/file, but not for the objects. Same applies to CloudEvents that only version the spec (I believe is called Reading those adeo links, they seem to have gone for specifying the version in within their avro messages (so outside of the asyncAPI specification). While this is ok, and is a way, it is outside of asyncapi remit/power, what would mean having many different ways of doing this. Regarding the schema spec version (like openapi 3.0.0), I think is currently ok as it is. We could discuss this as a side quest, but similar with channels and others, that would end up been a never ending discussion. I think the name
I don't think we are saying that versioning should be defined on the message level, but might be defined on the message level. The current approach, shared by all the specs is "it is not my problem", so I think there is an opportunity to do something about it. I will get some popcorn and read the linked issue later when I have some time, and see if I can put some examples (I suspect I will probably finish drinking whisky by the end of it). I do appreciate the feedback and understand is a complex and controversial topic, but I also think is extremely important and, again, a plus to have. Happy to setup a call and we can discuss a bit further this. Sometimes in writing is not the best way to move things forward. Again, thanks a lot for the comments and links and have a save journey! |
Just read the other issue and it seems the consensus was going towards a similar approach (but using a metadata object, so using the data/metadata pattern). Happy going that direction, but that would probably mean moving other parts into it. |
In most cases it is good practice to use the tolerant reader pattern. And assuming you are referencing to senatic version styled versions. The minor and patch level version or only interesting for debug uses cases, because extra field can safely be ignore by the tolerant reader / receiver. But major changes have to be rollet out in steps to not bild the life cycle of receiver and sender. Meaning eigther sender habe to send v1 and v2 or receiver have to listen on both and process what is send. There for, for typed language based software need to get find the major version before parsing the message. This is why the major versions habe to be part of the channel / topic name or a header field (depending on used messaging solution / transport system). |
I think that works then well for putting the version on the message level. It is outside of the payload and a property of the message envelope. That should allow for easier locating the versions, for example if you need to parse $ref objects. Version should not be tied to semver IMO, as I personally only find useful tracking breaking changes in EDA. It should support any version, reason why I think a string should do the job. Let me put some some examples to see the problems and options this provides.
I can see a 'small' caveat/issue with this solution, as the message identifier needs to be different for each version (to avoid clashes). I think that is ok to leave to the user to decide. It is then possible to have 2 messages with different version by using the combination for name and version, but both aren't required, so this is more of a tool/implementation decision. To resume, you can have what you got now that don't include a version:
you can have versions on those messages:
You can even add version and name, so you can know when a message representation is a versioned version of another message
But true that only adding the version only provides you with the version of the message, but doesn't tell you much about the message itself nor the relationship with other messages. What do you think lovely people? I am probably missing a lot of caveats and happy to learn about them! Happy to be completely wrong also, so do shout! |
yeah, you have Also, regarding the related issue of schema versioning - this is why in this other discussion it was proposed to have versioning on schema level. This way you see that one message has multiple schema versions, and because they are part of one message, it is clear what is the relation. The problem with versioning in the schema level only is that then you lose access to all the other message-level flags that are useful, like for example |
I feel it goes better in the message object rather than the schema. Mainly because it would be more difficult to apply to external schemas. Like when importing JSONSchema, OpenAPI, Avro or similar. Maybe we can use the version and name?!?. We can require both if a version is provided?!? Use the name as in the blog? I need to think about it. I understand this is a complex issue. We all do it differently, as we are pragmatic, and have different problems. Happy to explain how we do it, if that helps. The questions I have currently are: What do we need to support a message that has multiple versions of a schema? Is this the right question? What do you think? |
but is it the right direction? As I wrote, if we go schema-versioning-way then we lose access to all the other properties of message object, like for example message level versioning is better, but again, what is the value if you still cannot set a relation between messages |
Why not set that relationship them with this change then? Even without the relationship I see it been in a better position, but thanks to this talk I am now thinking more an more making name required IF a version is provided. That way we got that relationship. The options I see on this:
My preference goes on that order for the following reasons.
What do you all think? I think we are getting somewhere with the conversation, so I do appreciate the time and effort and understand this might take some time. |
Hi there, This is very a very interesting proposal and discussion. I'm really keen to see where it goes, but it's not completely clear to me exactly what issue we're trying to solve with this, I will try to elaborate my confusion below (I hope I don't make it worse!). First to articulate my understanding and context, to make sure I'm not missing anything as much as anything else: As I understand AsyncAPI documents they're there to describe what an application that implements the document should do. i.e. it should publish x message to y channel, or receive foo message from bar channel etc. When an application is implemented it will use a version (application version) of the AsyncAPI document for this, either using some generated code or manually. At the point this is done the application is committing to working with a fixed set of schemas from that document. e.g. the application publishes If there were more than one version of the schema the publish operations would surely (I am assuming here) only use one version (until the application itself is updated, then it might switch to the next version. One application would not publish two messages using different schemas to the same channel). Whereas a receive operation might have to deal with multiple versions of a schema as there could be multiple applications out there of different versions, and those versions may be compatible or not (i.e. if semver 1.1.1, 1.1.2 and 2.0.0) Also if we look at OpenAPI which has parallels here. It's interesting that it also does not attempt to solve this problem r.e. requests / responses. In a single version of an OpenAPI document each path has only one definition for its request and response schemas, and within the lifecycle of the document you are expected not to break those schemas so that older clients don't break. If a breaking change is required typically either a Finally in my long preamble (sorry) there is the question of documentation via the AsyncAPI, this is information that does not impact the implementation of the application but is useful meta that can be used to give information to a developer or architect etc. who wants to understand what this application does ( Given I have bored you to death with talking to myself, onto my confusion: it seems to me from the issue description, that this is really a problem of documentation via the async API (Please help me understand if I am wrong). As it seems from the use case being described, that you are looking to show people what different versions of messages there are flowing around the architecture and how they vary. And that this cannot sensibly be done just using the application version, which is true. Additionally I don't see how (from the application developers perspective) knowing the version of the message helps me implement the application, if I am doing a publish I will just use whatever singular schema I have been given in the document for that message for that operation. If I am doing a receive I will ensure I handle potentially multiple versions on that channel, where each message could really be two schema versions of the same thing which I can do like:
So it's not completely clear to me what we are discussing is really changing / adding / fixing. And I would really appreciate it if you could help me understand. Looking forward to it! Also, again, this is a really great discussion |
Hi @chrispatmore, Just to make sure we are all in the same page, the current proposal has evolved to: Allow for an optional version field that will enforce providing a name when present (that would be my preferred option currently) Thanks for the long reply and taking the time to read through the messages. Looking 1st on the articulate section With regards the application version, I see it more of a document spec/contract version. Nothing to do with my application version as otherwise I will need to modify it multiple times a day making it more noisy than useful. Moving on into the publishing multiple versions of a message, I don't think we can mandate published to only publish one version. We do incremental roll outs meaning we do move from version A to version B slowly, so a published might be publishing version A and B to facilitate that transition. Sometimes is easier to do it in the consumers (allow to consume both) but sometimes it make sense for the producers to do some of the job. For the OpenAPI I believe you can use oneOf to provide multiple responses (or accept multiple requests), so you can handle versioning that way if you wish. I think for them it would have been more difficult to standardise this, as there where already various options for versioning. What means consumer/processing applications need to work for x type or y type of versioning, making it pretty difficult to work for all (if any). I do see this useful for automation of documentation but also to help on helping on the implementation. As having a version can avoid or help understand problems that a version change can introduce. Now trying to help you understand where I am thinking From my point of view, I think it would be invaluable to include a version in the messages to give that context to the consumers/processors of the specification. Using your example, a consumer of that asyncapi specification needs to know that the version format is For a human this is fine as we know that we have 2 versions of lightMeasured (v1 and v2). For libraries consuming/processing the spec, they will see lightMeasuredv1 and lightMeasuredv2 as different messages that have nothing to do with one another. Updating your example with the proposal, it should look like this:
This will allow applications to understand that both lightMeasuredv1 and lightMeasuredv2 are the same message (with name This will allow things like:
I am sure there will be others benefits, the list is just a few things that come to my head. I think it would be incredibly useful, and should help standardise that area, without it is a free for all and there isn't really a one solution that solves the problem for all. Consumer applications could use But that then means each could decide is best to do it a different way and use different formats or x- keys what I think would lead us to something like this: https://www.explainxkcd.com/wiki/images/6/60/standards.png Again, extremely useful discussion and happy to take it into a call to see if I am missing something from AsyncAPI and/or how others use asyncAPI that might make this more of a problem than a helpful solution. |
Thanks for the reply @IsmaelMartinez, it's certainly helping me to get a handle on this. It definitely seems as though this is looking to define a new standard for how people should document the versions of their messages using AsyncAPI, not because it cannot be done, but because there are too many competing options and it makes it hard to produce useful tooling. However we definitely want to avoid the problem you have indicated of producing just another unhelpful option. Therefore if we want to do this we need to make sure that what we choose is sensible, suitable an useful in the majority of use cases, accepting that it might not fit everyone's needs or all use cases! Which means I would like to understand the use cases we are trying to tackle a little better. First can you elaborate on your publishing of multiple versions, I still don't follow the scenario there. As I see it, these are the options you could be indicating, and what I see the problem being (which is why I don't understand the case):
w.r.t
Therefore I think we should assume that most people use it as stated, as the version of their application API, and that if you are changing the API a lot on a daily basis, and publishing those updates every time, then that version will change a lot. I don't think that's a bad thing, just the mark of an active API. It would be interesting to understand the scenarios where the message version changes would / would not impact the application version in this proposal. Additionally I still don't see this as impacting the actual implementation of the application (this is NOT a bad thing). I can see how it would have a big impact on tooling, all the other things you listed, and be useful for potentially highlighting problems from a version change. But these (IMO) are all documentation / meta, after the document has been coded there would be no difference in the implementation brought about by the introduction of these changes, this is pretty normal, but I think it's important to be clear on it. e.g. using the above short doc example you updated, the behaviour of the application is defined via the different operations, and the links through to the various channels / messages. The inclusion of the new version field (+ required name) does not change this. Sorry for being so long winded about everything! just really helps me to get clarity, especially when using message threads. I think a call could be good, but I like to make sure I'm joining the call with sufficient context and understanding, and not using people's valuable time getting that in the call. EDIT - added NOT as I missed it and it completely changed the meaning! If you read this in the email update (which won't have it) I am sorry |
Hi @chrispatmore, I am still thinking about this, just been busy lately. Thanks for clarifying the application API and it makes sense. We tend to only make the version matter when we are stable enough, what tends to remove the use of versioning for most services. We currently use channels as just a generic object. We don't define much in it as we just use EventBridge, and with AWS-CDK IaC you are already doing the definition part in code. We use versioning and we do phase deployments, parallel runs, shadow runs and the likes. As such, we send messages of different versions in production. Sometimes this can run for a long period as we can also use them to allow to run multiple test cases in parallel, most times this is just for a few minutes while we are deploying a change. As such, having metadata from the event is almost vital for us. I agree is metadata like the correlationId, and maybe that approach might be a better one. Another option is to include an optional metadata object like correlationId that is a regex on the message identified and identifies the version. Like that people can optionally use it to identify that from the |
That's alright I know the feeling! I think after this discussion and clarity my vote would be for a new I think that including it in the message key like additionally I think not putting it inside a "metadata" section would imply a level of importance / relevance to the application implementation that I think would cause more confusion or bad practise than the problems it would solve. e.g. I can see someone writing / generating an app that's trying to use some weird envvar or logic to conditionally switch the message version being sent, rather than ensuring their consumers are also updated to handle the new changes / make sure the change are non breaking etc. Basically, the standard should net improve the situation not create more space for confusion finally w.r.t to schema version, you could extend the argument to include the same new section at that point too. Add in a metadata section that someone can optionally use to provide more info in a standardised way if people want it. But there is no requirement to do so. Any implementation based on the version should continue to use fixed name keys like |
Thanks for taking the time to reply and for your thoughts. I do like the meta/data separation of concerns on events. I try to put an example of what I understand your proposal would be, assuming you only want to move the version into the metadata field: (only putting the components section as the other part doesn't change)
From my understanding on your proposal we will still need to have a way to identify the message mapping of name/version, but would move the version into a metadata object. The use of the optional regex would add extra context to the field keys, that people can use as they wish, but will remove the need of having a version and name, as that would be implicit in the keys. Putting an example of what I mean: (only putting the components section as the other part doesn't change much - also I haven't tested the regex)
That solves 3 problems:
While I see the point of having a metadata object and a payload (or data) object, I think this is a bigger conversation to be had that would imply moving some of the other fields into it (like name, correlationId, title, contentType, etc). I would be happy to contribute to that conversation, but I think it would be wise to not make this version conversation a meta(data) conversation as the scope of the later is much bigger, and adding a way to determine a version now, could help drive that conversation depending on how people use it. For the record, we do use the metadata object in our events, and have the version in it (and name). For reference, we include the following fields:
What do people think? Do you want to move this conversation to be a more metadata/payload separation or keep it into the how do allow for a mapping between event/version? Also, I don't like regex but I see this as the simplest way to solve that mapping, but I am all ears for other options. |
Introduction
This proposal is to add a
version
field into the message object. This is to allow a more granular versioning.Problem Statement
Currently, versioning in AsyncAPI is done at the application level. This inherently implies that all messages are of the same version, or that events do not hold a version at all. This approach works when an application fully controls its specifications/messages, with no cross references from other applications. An example are openAPI specs.
However, in environments with continuous development and/or fast iterations, application level versioning looses its benefit. Versioning becomes more important at the message level. Without granular versioning, any change in the application version applies to all messages it sends and receives, which can be problematic as shown in the example.
Example
Payment-service (v1.0.0) receives [billing-agreement-created...], sends [payment-success].
Comms-service (v1.0.0) receives [payment-success, payment-failed, billing-agreement-created, address-changed..] sends [communication-send...]
All the events received and sent are then version
1.0.0
.If the
communication-send
message needs a new field (e.g., adding SMS as a type), thecomms-service
would becomev1.1.0
, and all its messages would then be version1.1.0
.This change would require updating all other AsyncAPI files for
payment-service
and others, as thecomms-service
would be otherwise consuming message versions no-one is producing.Proposed Solution
The proposal is to add an optional
version
on the message object level to allow for granular versioning.This in turn will provide more visibility into what has changed, as an application version can change without modifying any of the underlying messages it sends and/or receives.
Example outcome
With the proposed solution, we can fix the message version in the
comms-service
for messages that haven't changed:Alternatives
NOTE: Not sure if this relates in part to #432 . I made it a different proposal as it seems granular enough.
The text was updated successfully, but these errors were encountered: