-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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-18844: Stale features information in QuorumController#registerBroker #18997
base: trunk
Are you sure you want to change the base?
Conversation
This is related to #18845, thanks for the quick fix! |
We tried to remove these defaults as part of #18845 - looks like I missed this one. Can you try removing the default? Is that an easy change or does it cause a lot of issues? |
I think we can't remove the default one. Each variable get default value if it's null. Or do you mean that trying to set kafka/metadata/src/main/java/org/apache/kafka/controller/FeatureControlManager.java Lines 96 to 111 in 8f13e7c
|
Yes, if it's mandatory, then we should set it in the constructor. Any issues doing that? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrankYang0529 : Thanks for the PR.
Regarding FeatureControlManager.metadataVersion
, it's bootstrapped from the bootstrap file or the log. Any test that depends on it should wait for the bootstrap to complete. So, it does seem reasonable to initialize it to null. @cmccabe : What do you think?
@@ -753,6 +753,10 @@ public void testBalancePartitionLeaders() throws Throwable { | |||
QuorumController active = controlEnv.activeController(); | |||
Map<Integer, Long> brokerEpochs = new HashMap<>(); | |||
|
|||
TestUtils.waitForCondition(() -> | |||
active.featureControl().finalizedFeatures(Long.MAX_VALUE).featureMap().get(MetadataVersion.FEATURE_NAME) == SIMPLE_BOOTSTRAP.metadataVersion().featureLevel(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we need to wait for QuorumController to fully bootstrap before doing the remaining test, it seems that we should just call controlEnv.activeController(true)
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0f8190a
to
2544030
Compare
It looks like this would basically always be
It seems to me that we should actually remove the metadata version from |
@ijuma, it looks like kafka/server-common/src/main/java/org/apache/kafka/timeline/TimelineObject.java Lines 55 to 56 in 3fc103b
|
@FrankYang0529 Yes, I know it cannot be |
How about initializing it to the bootstrapped metadata? If the role is quorum leader, it will eventually be initialized to the bootstrapped metadata. It is still valid as the "current" metadata version if the role is a standby controller. |
@chia7712 Is the bootstrapped metadata available when |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrankYang0529 : Thanks for the updated PR. A couple of more comments.
How about initializing it to the bootstrapped metadata? If the role is quorum leader, it will eventually be initialized to the bootstrapped metadata. It is still valid as the "current" metadata version if the role is a standby controller.
Chatted with Colin offline a bit. The bootstrap MV (from the bootstrap file) in QC can be higher or lower than the MV in the log. So, it should only be used if the log is empty. We could use MINIMUM_VERSION for initialization and the correct MV will be set after replaying the log. If the log is empty, in production, we prepend the bootstrap MV in QC. In testing, any test depending on the correct MV needs to explicitly ensure that the correct MV is populated.
return activeController(false); | ||
} | ||
|
||
QuorumController activeController(boolean waitForActivation) throws InterruptedException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
controlEnv.activeController()
is called multiple times in some of the tests. It's a bit wasteful to have to append/wait for a new event in every call. Perhaps we could cache the returned QuorumController
and reuse it in those tests.
This is error-prone though. While working on #18845, we found an existing race condition that was hard to detect because we silently used the wrong metadata version in a number of situations. I fear this approach may lead to similar problems. I think it's cleanest to leave the metadata version unset until we actually read it from the log. |
That's safer. The only question is what to do in |
Yes, that's the approach we took with |
I think it includes following changes.
If above list is correct, maybe we can address it in follow-up? |
|
We could change We could do 1-3 in a followup jira. |
@@ -373,7 +373,7 @@ public void testElrEnabledByDefault() throws Throwable { | |||
)). | |||
build() | |||
) { | |||
controlEnv.activeController(true); | |||
controlEnv.activeController(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking a bit more. It seems that this test exposes a real bug introduced in https://github.com/apache/kafka/pull/16848/files#diff-77dc2adb187fd078084644613cff2b53021c8a5fbcdcfa116515734609d1332a.
In the following code, we pick up the finalized feature version (including MV) first and pass them to the registerBroker event. It's possible that the passed in MV is outdated, but when the registerBroker event is processed, the MV becomes update to date. This will fail a registerBroker request that shouldn't be failed. It seems that we should pick up the finalized feature version when the registerBroker event is processed.
public CompletableFuture<BrokerRegistrationReply> registerBroker(
ControllerRequestContext context,
BrokerRegistrationRequestData request
) {
// populate finalized features map with latest known kraft version for validation
Map<String, Short> controllerFeatures = new HashMap<>(featureControl.finalizedFeatures(Long.MAX_VALUE).featureMap());
controllerFeatures.put(KRaftVersion.FEATURE_NAME, raftClient.kraftVersion().featureLevel());
return appendWriteEvent("registerBroker", context.deadlineNs(),
() -> clusterControl.
registerBroker(request, offsetControl.nextWriteOffset(),
new FinalizedControllerFeatures(controllerFeatures, Long.MAX_VALUE),
context.requestHeader().requestApiVersion() >= 3),
EnumSet.noneOf(ControllerOperationFlag.class));
}
Colin confirmed that this is an issue. In general, we should not be accessing featureControl.finalizedFeatures outside of the event queue thread since it introduces potential concurrency issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I try to change the function as following and the test can pass without active controller check.
@Override
public CompletableFuture<BrokerRegistrationReply> registerBroker(
ControllerRequestContext context,
BrokerRegistrationRequestData request
) {
return appendWriteEvent("registerBroker", context.deadlineNs(),
() -> {
// populate finalized features map with latest known kraft version for validation
Map<String, Short> controllerFeatures = new HashMap<>(featureControl.finalizedFeatures(Long.MAX_VALUE).featureMap());
controllerFeatures.put(KRaftVersion.FEATURE_NAME, raftClient.kraftVersion().featureLevel());
return clusterControl.
registerBroker(request, offsetControl.nextWriteOffset(),
new FinalizedControllerFeatures(controllerFeatures, Long.MAX_VALUE),
context.requestHeader().requestApiVersion() >= 3);
},
EnumSet.noneOf(ControllerOperationFlag.class));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly. If we fix the registerBroker code, just waiting for the QC to be the leader should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch @junrao!
…rs() Signed-off-by: PoAn Yang <[email protected]>
2544030
to
0543db9
Compare
Once we merge this, we should also backport it to 4.0 so the build is stable there (and the bug is fixed). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrankYang0529 : Thanks for the updated PR. Just a minor comment. Also, could we change the description of the jira and the PR since this is not just a flaky test fix?
@ahuang98 and @jsancio : Should we cherry pick this to 3.9 since the issue was introduced in https://github.com/apache/kafka/pull/16848/files#diff-77dc2adb187fd078084644613cff2b53021c8a5fbcdcfa116515734609d1332a?
context.requestHeader().requestApiVersion() >= 3), | ||
() -> { | ||
// Populate finalized features map with latest known kraft version for validation. | ||
// Get the finalized features map in controller operation to avoid outdated features. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in controller operation => in the controller event handling thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the important point that both reads and writes have to happen within the controller event handling thread (versus the more specific finalized features map part)? Overly specific comments tend to go stale more easily.
Signed-off-by: PoAn Yang <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrankYang0529 : Thanks for the updated PR. LGTM too assuming the tests pass. Also, could you change the title/description of the jira and the PR?
Hi @junrao, thanks for the review. Updated title and description on Jira and the PR. |
In #16848, we added
kraft.version
to finalized features and got finalized features outside controller event handling thread. This makes finalized features may be stale when processingregisterBroker
event. Also, some cases likeQuorumControllerTest.testBalancePartitionLeaders
become flaky cause of outdated MV. This PR moves finalized features back to controller event handling thread to avoid the error.Previous PR title: Fix flaky QuorumControllerTest.testBalancePartitionLeaders()
Previous PR description:
When we initialize
FeatureControlManager
inQuorumController
[0], we don't setbootstrapMetadata
for it, so it useMetadata.latestProduction()
[1]. In the test case, we useIBP_3_7_IV0
asbootstrapMetadata
[2]. When initializingQuorumController
, it takes time to replay record to overwriteFeatureControlManager#metadataVersion
. If we addThread.sleep(1000L)
beforemetadataVersion.set(mv)
[3], we can reproduce the flaky test steadily.I'm not sure whether setting
bootstrapMetadata
toFeatureControlManager
is correct, so I fix the case by waitingmetadataVersion
is overwrote.[0]
kafka/metadata/src/main/java/org/apache/kafka/controller/QuorumController.java
Lines 1536 to 1541 in 8f13e7c
[1]
kafka/metadata/src/main/java/org/apache/kafka/controller/FeatureControlManager.java
Line 57 in 8f13e7c
[2]
kafka/metadata/src/test/java/org/apache/kafka/controller/QuorumControllerTest.java
Lines 177 to 178 in 8f13e7c
[3]
kafka/metadata/src/main/java/org/apache/kafka/controller/FeatureControlManager.java
Lines 371 to 379 in 8f13e7c
Committer Checklist (excluded from commit message)