Skip to content
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

KAFKA-18052: Decouple the dependency of feature stable version to the metadata version #17886

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from

Conversation

dongnuo123
Copy link
Collaborator

@dongnuo123 dongnuo123 commented Nov 20, 2024

Currently the validation of feature upgrade relies on the supported version range generated during registration. For a given feature, its max supported feature version in production is set to be the default version value (the latest feature version with bootstrap metadata value smaller or equal to the latest production metadata value).

This patch introduces a LATEST_PRODUCTION value independent from the metadata version to each feature so that the highest supported feature version can be customized by the feature owner.

The change only applies to dynamic feature upgrade. During formatting, we still use the default value associated the metadata version.

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@github-actions github-actions bot added core Kafka Broker small Small PRs labels Nov 20, 2024
@dongnuo123 dongnuo123 changed the title [Draft] add latest production to features KAFKA-18052: Decouple the dependency of feature stable version to the metadata version Nov 20, 2024
@dongnuo123 dongnuo123 changed the title KAFKA-18052: Decouple the dependency of feature stable version to the metadata version [Draft] KAFKA-18052: Decouple the dependency of feature stable version to the metadata version Nov 20, 2024
@dongnuo123 dongnuo123 changed the title [Draft] KAFKA-18052: Decouple the dependency of feature stable version to the metadata version KAFKA-18052: Decouple the dependency of feature stable version to the metadata version Nov 21, 2024
Copy link
Contributor

@junrao junrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dongnuo123 : Thanks for the PR. Left a comment.

@github-actions github-actions bot removed the small Small PRs label Nov 21, 2024
Copy link
Contributor

@junrao junrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dongnuo123 : Thanks for the updated PR. A few minor comments.

@@ -313,7 +313,7 @@ Map<String, Short> calculateEffectiveFeatureLevels() {
Optional.ofNullable(newFeatureLevels.get(KRaftVersion.FEATURE_NAME))));
} else if (!newFeatureLevels.containsKey(supportedFeature.featureName())) {
newFeatureLevels.put(supportedFeature.featureName(),
supportedFeature.defaultValue(releaseVersion));
supportedFeature.defaultLevel(releaseVersion));
}
});
// Verify that the specified features support the given levels. This requires the full
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FAILED ❌ FormatterTest > "testFeatureFlag(short).version=0"
FAILED ❌ FormatterTest > "testFeatureFlag(short).version=1"
FAILED ❌ FormatterTest > testInvalidFeatureFlag()

Got 3 failed tests. Will fix it today

Copy link
Contributor

@junrao junrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dongnuo123 : Thanks for the updated PR. A few more comments.

* - The feature X has default version = XV_10 (dependency = {}), latest production = XV_5 (dependency = {})
* (Violating rule 2. The latest production value XV_5 is smaller than the default value)
* - The feature X has default version = XV_10 (dependency = {Y: YV_3}), latest production = XV_11 (dependency = {Y: YV_4})
* The feature Y has default version = YV_3 (dependency = {}), latest production = YV_3 (dependency = {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default version part seems unrelated to this example.

* The feature Y has default version = YV_3 (dependency = {}), latest production = YV_3 (dependency = {})
* (Violating rule 3. For latest production XV_11, Y's latest production YV_3 is smaller than the dependency value YV_4)
* - The feature X has default version = XV_10 (dependency = {Y: YV_4}), latest production = XV_11 (dependency = {Y: YV_4})
* The feature Y has default version = YV_3 (dependency = {}), latest production = YV_4 (dependency = {})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest production part seems unrelated to this example.

* - The feature X has default version = XV_10 (dependency = {Y: YV_3}), latest production = XV_11 (dependency = {Y: YV_4})
* The feature Y has default version = YV_3 (dependency = {}), latest production = YV_4 (dependency = {})
* - The feature X has default version = latest production = XV_10 (dependency = {MetadataVersion: IBP_4_0_IV0})
* (Some features can depend on MetadataVersion, which is not checked in this method)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how do we enforce the dependency on MV? For example, if feature level X_1 depends on MV_10 and is production ready, but MV_10 is not production ready, how do we detect that in tests?

KRAFT_VERSION("kraft.version", KRaftVersion.values()),
TRANSACTION_VERSION("transaction.version", TransactionVersion.values()),
GROUP_VERSION("group.version", GroupVersion.values());
TEST_VERSION("test.feature.version", TestFeatureVersion.values(), TestFeatureVersion.LATEST_PRODUCTION),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an existing issue. It seems that Features should be Feature since it represents a single feature?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains many different features so I'm not sure I follow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Features.FEATURES contains many features, but the enum Features represents a single feature. For example, Features.name() provides the name of a single feature.

// value calculated with {@link #defaultValue(MetadataVersion)}.
public final FeatureVersion latestProduction;

private static final Logger log = LoggerFactory.getLogger(Features.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unused?

*/
public enum UnitTestFeatureVersion implements FeatureVersion {
UT_FV0_0(0, MetadataVersion.MINIMUM_KRAFT_VERSION, Collections.emptyMap()),
UT_FV0_1(1, MetadataVersion.IBP_3_7_IV0, Collections.emptyMap()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a comment on the purpose of each feature?

/**
* The enum is only used for testing invalid features in FeaturesTest.java.
*/
public enum UnitTestFeatureVersion implements FeatureVersion {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consolidate UnitTestFeatureVersion with TestFeatureVersion?

* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.server.common;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in the test directory?

UNIT_TEST_VERSION_2(UnitTestFeatureVersion.FEATURE_NAME + ".2", new FeatureVersion[]{UT_FV2_0, UT_FV2_1}, UT_FV2_0),
UNIT_TEST_VERSION_3(UnitTestFeatureVersion.FEATURE_NAME + ".3", new FeatureVersion[]{UT_FV3_0, UT_FV3_1}, UT_FV3_1),
UNIT_TEST_VERSION_4(UnitTestFeatureVersion.FEATURE_NAME + ".4", new FeatureVersion[]{UT_FV4_0, UT_FV4_1}, UT_FV4_1),
UNIT_TEST_VERSION_5(UnitTestFeatureVersion.FEATURE_NAME + ".5", new FeatureVersion[]{UT_FV5_0, UT_FV5_1}, UT_FV5_1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kind of weird to include unit test code in production code.

"UNIT_TEST_VERSION_2",
"UNIT_TEST_VERSION_3",
"UNIT_TEST_VERSION_4",
"UNIT_TEST_VERSION_5"}, mode = EnumSource.Mode.EXCLUDE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is it correct for EXCLUDE to be empty?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants