nats.rs icon indicating copy to clipboard operation
nats.rs copied to clipboard

async-nats 1.0.0 release planning

Open Jarema opened this issue 1 year ago • 3 comments

This issue gathers all work needed for 1.0.0 release and encourages discussions.

Current list of pending work

  • [x] Concrete errors for Core NATS (#632)
  • [x] Switch back to tokio-rustls after 0.21 is released (https://github.com/nats-io/nats.rs/pull/915)
  • [x] Add concrete error types to JetStream (#874)
  • [x] Concrete error types for higher-level abstractions (KV, Services, ObjectStore)
  • [x] Allow multiple auth methods for 2.10 Auth Callouts (https://github.com/nats-io/nats.rs/pull/937)
  • [x] Add Subject type and/or trait and avoid subject allocations (https://github.com/nats-io/nats.rs/issues/950)
  • [x] Rework headers to avoid allocations (#949, #945)
  • [x] Consistent builders (partially in #782)
  • [x] Improved creds reloading (#997)
  • [ ] Higher level abstractions as features enabled by default
  • [x] Flush rework #905
  • [ ] TLS double buffer removed #905
  • [x] Prepare (check) API for adding WebSocket support without breaking changes
  • [x] Add muxing requests (#643, #1069)
  • [x] Docs readability improvements
  • [ ] Final parity check before release

Release 0.30.0 should bring most of the work and hopefully all breaking changes.

Jarema avatar Mar 23 '23 22:03 Jarema

We decided to not touch current builders, and instead add new ones whenver there is a need for them.

Jarema avatar Nov 08 '23 09:11 Jarema

Any updates on the 1.0 release plan? Looks like all the work is done, from that checklist?

ekalosak avatar Mar 02 '24 14:03 ekalosak

@ekalosak After some consideration, we settled at:

  1. Do not release actual 1.0.0
  2. Provide guarantees and standards that library will be held to, which means:
  • being very explicit whenever any breaking change is introduced
  • avoid doing breaking changes when not well justified or forced by upstream dependencies.

Despite library being in stable, production ready state, making it 1.0.0 brings a hefty set of problems Let's list some of them:

  1. Any change to a public struct is technically a breaking change and CAN break people. And we are expanding many public structs with each release of nats-server when new features are added to streams, consumers etc.
  2. Any change to error enums is a breaking change. While we don't do that often, sometimes its forced, when server adds new errors. We could mitigate it by making all the error enums non exhaustive, but that impacts ergonomics quite a bit.
  3. Some of our critical dependencies are < 1.0.0, like rustls. This library is mature, well written, well maintained, but it also decided to stay < 1.0.0. We do expose some of its structs to not block users from niche, yet important use cases and setups.
  4. While async ecosystem is maturing, a lot of new important features are still not there. We just got async traits, while we still wait for async closures and more, that we would love to incorporate instead of hacky closures returning async blocks etc.

All of above means that we would either really often iterate on major version of the library, or, trying to avoid any breaking changes, keep the library "in the past" and not benefit from the good changes in maturing ecosystem.

Jarema avatar Mar 04 '24 12:03 Jarema

  1. Any change to a public struct is technically a breaking change and CAN break people. And we are expanding many public structs with each release of nats-server when new features are added to streams, consumers etc.
  2. Any change to error enums is a breaking change. While we don't do that often, sometimes its forced, when server adds new errors. We could mitigate it by making all the error enums non exhaustive, but that impacts ergonomics quite a bit.

If the server can add new errors in the future, shouldn't the client's enum be non-exhaustive anyway?

Are there breaking changes that happen to structs that aren't also covered by the ability to mark structs as non-exhaustive?

Kinrany avatar Apr 16 '24 19:04 Kinrany

@Kinrany we did consider non-exhaustive errors, but the ergonomics of such are really bad. We decided on leaving them exhaustive. Especially that rarely we expose those new errors directly. Usualy they are abstracted away into user-actionable items. It can happen though. Every single time its clearly telegraphed in release notes.

The actual server errors are represented in client as

pub struct ErrorCode(u64);

impl ErrorCode {

and are reachable via checking source of errors.

Regarding the non-exhaustive structs: those could not be used with ..Default::default() so not sure how that could help here?

Jarema avatar Apr 16 '24 19:04 Jarema

Regarding the non-exhaustive structs: those could not be used with ..Default::default() so not sure how that could help here?

..Default::default() is used purely for ergonomics, right? Wouldn't it be better for ergonomics to provide a builder for every type anyway? 🤔

Foo::new().option(value).build() fits on a single line when Foo { option: value, ..Foo::default() } always takes four 😄

Kinrany avatar Apr 17 '24 07:04 Kinrany

Default::default() is used when you want to specify some fields for Configs, but you are happy with others being default values.

Many users prefer to use config structs rather than builders. Especially in NATS context, where its sometimes easier to look at whole config rather than a code (as this is how you often see those in cli when caling nats stream info nats consumer info etc.

That's why we went with struct variant. However, it's of course fine to add builders as an extension in the future. However I would like to avoid high maintenance cost of those, so probably not manual, but generated.

Jarema avatar Apr 17 '24 09:04 Jarema