110 - Adopt plugin versioning scheme#110
Conversation
Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Robert Young <robertyoungnz@gmail.com>
1e30107 to
0622ff2
Compare
|
Prototype implementation kroxylicious/kroxylicious#3952 |
| For proxy users, configuration syntax extends from `type: RecordEncryption` to support `type: RecordEncryption/v1alpha1`. | ||
| When a version is present in the type string, the proxy uses it both for validation and for name resolution. | ||
| A plugin named RecordEncryption annotated with `@Version("v1alpha1")` can be referenced as either `RecordEncryption/v1alpha1` (explicit version) or `RecordEncryption` (implicit, triggers warning). | ||
| If multiple RecordEncryption implementations exist with different versions (v1alpha1 and v1beta1), users disambiguate by including the version rather than switching to fully qualified class names. |
There was a problem hiding this comment.
that should say can disambiguate it's optional and they could still disambiguate using fully qualified name, backwards compat is preserved in phase 1.0.0
| Kroxylicious is approaching its 1.0.0 release, which will establish backward compatibility guarantees for public APIs. | ||
| However, different plugins may have different maturity levels and API stability guarantees. | ||
| Coupling all plugin APIs to a single project version creates tension between shipping experimental features and maintaining stability. | ||
| The Record Encryption filter, for example, may need multiple major revisions before its API stabilizes, but tying those revisions to the project version would either delay 1.0.0 or force premature API commitments. |
There was a problem hiding this comment.
claude has picked our most stable plugin for the example 🤷
|
|
||
| Phase 2 begins with the 1.0.0 release. | ||
|
|
||
| - The enforcement policy changes from warning to error when a plugin declares a version but the configuration omits it. |
There was a problem hiding this comment.
needs a bullet to say that the annotation on the plugin becomes mandatory too
tombentley
left a comment
There was a problem hiding this comment.
Thanks @robobario. I really appreciate that someone is trying to see if there's a simpler alternative to #96 which might satisfy some of the things we agreed in #82 are important to have for Kroxylicious 1.0.
I'll admit that 96 doesn't do a great job of calling out all of its goals, but let me try to compare the two:
| Goal | #96 | #100 |
|---|---|---|
| Explicit plugin config version | Yes | Yes |
| Support config schema evolution | Yes | Yes |
| Plugin identification | Yes | No |
| Deeper runtime understanding of nested plugins | Yes | No |
| Fine grained dependency analysys | Yes | No |
| Better documentation of schema configs | Yes | No |
| External validation of schema configs | Yes | No |
| Editor support for writing configs | Yes | No |
| Globally shared interpretation of proxy config | Yes | No |
| Synergy with K8S | Stronger | Weaker |
It's clear that there's a very different level of ambition between the two proposals. My fear is that, out of a desire to see 1.0 happen sooner, we make decisions which undermine our ability to address some of the concerns in the left hand column.
I asked Claude to look had how compatible there two proposals are and this is the result. Reading that I think it's difficult to see 110 as an outright good move that's compatible with the ideas in 96 as a solution of those goals. Instead while there are those structural differences between the two (like the annotation, the YAML syntax and the difference in terms of simple vs FQCNs) 110 looks more like a temporary thing which would cause disruption to plugin developers.
So I think it would be helpful to hear if you think any of those goals are not actually worth striving for. If they are worth solving then I'd appreciate your review on 96: is it a good way of solving them (maybe there are better ways)? In the light of that we can figure out if 110 is the best way of doing something that's "good enough for 1.0", or if we can tweak 110 to be more compatible with 96.
I also think it's worth considering that kroxylicious/kroxylicious#3691, which is a POC of the changes proposed in 96, is "only" a +7k change. Sure that's a lot, and it needs work on the tests and so on, so would likely grow a bit. But in the light of some of the recent big things we've merged, I don't think we should immediately be thinking that 96 is a huge piece of work that would take many months to implement.
| For proxy users, configuration syntax extends from `type: RecordEncryption` to support `type: RecordEncryption/v1alpha1`. | ||
| When a version is present in the type string, the proxy uses it both for validation and for name resolution. | ||
| A plugin named RecordEncryption annotated with `@Version("v1alpha1")` can be referenced as either `RecordEncryption/v1alpha1` (explicit version) or `RecordEncryption` (implicit, triggers warning). | ||
| If multiple RecordEncryption implementations exist with different versions (v1alpha1 and v1beta1), users disambiguate by including the version rather than switching to fully qualified class names. |
There was a problem hiding this comment.
If two unrelated plugins share a simple class name then they can be disambiguated via version number, but logically that's not the right way to fix it: The correct fix is to use an FQ name.
It's nasty and surprising to have two different disambiguation mechanisms.
| When a plugin reaches stability, annotating it with `@Version("v1")` communicates that the API will remain backward compatible within the v1 series. | ||
|
|
||
| Version-based disambiguation enables new packaging strategies. | ||
| A plugin author maintaining both alpha and beta implementations can ship `io.kroxylicious.filter.encryption.alpha.RecordEncryption` annotated with `@Version("v1alpha1")` and `io.kroxylicious.filter.encryption.beta.RecordEncryption` annotated with `@Version("v1beta1")` in the same JAR. |
There was a problem hiding this comment.
This again highlights the weirdness of having two different ways of referring to the same thing. You need the alpha/beta in the FQ class name, but you also have it in the annotation.
This can make for a poor UX: It's easy to imagine a user meaning to update from alpha (specified as io.kroxylicious.filter.encryption.alpha.RecordEncryption/v1alpha1) to beta, but not really paying attention to the alpha in the package name and being confused when they change it to io.kroxylicious.filter.encryption.alpha.RecordEncryption/v1beta1.
The reality of this situation is there there are two different plugins masquerading as being one plugin which accepts two different configuration formats, and it's only when you use simple class naming that that pretence works. To my mind, this is pushing people towards using simple class names. However, I think simple class names are a mistake, because it means the interpretation of a configuration is relative to the plugins available to the runtime. Ultimately a configuration is about communicating intent. A configuration schema which results in configurations that are open to interpretation is a poor configuration schema. This is why #96 adopts FQ names.
| An environment variable `KROXYLICIOUS_REQUIRE_PLUGIN_VERSIONS` allows operators to opt into strict enforcement before 1.0.0 for testing purposes. | ||
|
|
||
| For filter authors and plugin developers, the `@Version` annotation becomes part of the public API contract. | ||
| Placing `@Version("v1alpha1")` on a filter implementation signals to users that the API is experimental and subject to breaking changes. |
There was a problem hiding this comment.
It's very odd that the thing that the version is intended to describe (the schema for the configuration) ends up in an annotation on the plugin itself.
I asked Claude about this and it's the second file in the gist |
This is a smaller idea for adopting plugin versioning without tackling all of the other ideas like contract-first Configuration based on JSON schemas, kubernetes sympathy by decomposing into multiple configuration files which are covered in #96 . It only makes versioning explicit and adopts k8s CRD versioning conventions to try and make our stability guarantees explicit.
Proposal generated with the help of Claude Sonnet 4.5