libp2p / specs

Technical specifications for the libp2p networking stack
https://libp2p.io
1.58k stars 275 forks source link

Discuss: Move away from semver for protocol versioning #203

Open raulk opened 5 years ago

raulk commented 5 years ago

We have discussed this in various contexts. I believe the discussion deserves larger visibility.

I documented my rationale in the Ethereum 2.0 networking spec. Copying (with editorial fixes):

Why do we version protocol strings with ordinals instead of semver?

Using semver for network protocols is confusing. It is never clear what a change in a field, even if backwards compatible on deserialisation, actually implies. Network protocol agreement should be explicit. Imagine two peers:

  • Peer A supporting v1.1.1 of protocol X.
  • Peer B supporting v1.1.2 of protocol X.

These two peers should never speak to each other because the results can be unpredictable. This is an oversimplification: imagine the same problem with a set of 10 possible > versions. We now have 10^2 (100) possible outcomes that peers need to model for. The resulting complexity is unwieldy.

For this reason, we rely on negotiation of explicit, verbatim protocols. In the above case, peer B could provide backwards compatibility by supporting and advertising both v1.1.1 and v1.1.2 of the protocol.

Under this scenario, semver would be relegated to convey expectations at the human level, and it wouldn't do a good job there either, because it's unclear if "backwards-compatibility" and "breaking change" apply only to wire schema level, to behaviour, state machine, etc.

Therefore, we remove semver out of the picture and replace it with ordinals that require explicit agreement, and do not mandate a specific policy for changes.

I'm personally for this change. Curious to hear from others.

marten-seemann commented 5 years ago

I agree, semver seems to be a pretty bad fit for wire protocols.

One thing we might want to consider is dealing with extensions. Imagine you have a base protocol that gives peers the option to implement different extensions on top of this protocol. If you want to announce support for extensions upfront (as opposed to negotiating extensions within the protocol itself), you'd have to announce the supported extensions in some way. Even with a modest number of extensions, the number of combinations becomes huge, so giving each combination a unique name (version number) is not a solution.

aschmahmann commented 5 years ago

@raulk your argument applies against the use of semver in general in distributed systems. Not necessarily a good or bad thing, but just pointing out that there's very little uniqueness to protocol versioning.

Under this scenario, semver would be relegated to convey expectations at the human level, and it wouldn't do a good job there either, because it's unclear if "backwards-compatibility" and "breaking change" apply only to wire schema level, to behaviour, state machine, etc.

Wouldn't a spec define what semver means in our context? We can say what constitutes a "breaking change".

These two peers should never speak to each other because the results can be unpredictable.

If these nodes communicate with each other and their software has been tested to work in version 1.1.1, changing to 1.1.2 even with no explicit protocol changes could break all sorts of things. It's even possible that 1.1.2 ends up exposing a bug that crashes 1.1.1 (maybe 1.1.1 is only able to handle one connection per second, and 1.1.2 has better performance and so can send two). Does this make 1.1.2 a "breaking change"? Yes, it was a change that broke the system! However, without running an extensive testnet with all versions 1.1.X it's impossible for the developer to know that in advance. Therefore it might seem reasonable to just ditch semver entirely and release go-libp2p v204. However, then users of go-libp2p would have no quick way of knowing whether an upgrade is a small, unlikely to cause problems, change or a big one.

Therefore, we remove semver out of the picture and replace it with ordinals that require explicit agreement, and do not mandate a specific policy for changes.

All of the above is moot if we never bother to upgrade protocols without bumping the major version number anyway. Do we/have we ever had protocols that are A.X.Y where X,Y > 0? If not then this discussion is basically about whether the four extra characters in our protocol names (.0.0) are worth being able to easily change course at a later date. 🤷‍♂

@marten-seemann

Even with a modest number of extensions, the number of combinations becomes huge, so giving each combination a unique name (version number) is not a solution.

Seems good, although it's worth noting that Noise does exactly this for their configurable parameters. While for many protocols the types of parameters are fixed (a word, number, bool) some, like multiaddr, have unbounded parameters (like http paths) that make it very difficult to use path-like syntax alone (instead of also throwing in something like {} to denote the boundaries of an unbounded parameter).

yusefnapora commented 5 years ago

Do we/have we ever had protocols that are A.X.Y where X,Y > 0?

I'm not sure what the history is, but mplex is at /mplex/6.7.0

I also think that an ordinal is a better fit. semver feels like using too many significant figures; it's implying a precision we probably don't actually have in practice :)

Stebalien commented 5 years ago

I'm not sure what the history is, but mplex is at

Useless factoid of the week: because it was based on https://www.npmjs.com/package/multiplex (which is at version 6.7.0).