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-14124: improve quorum controller fault handling #12447

Merged
merged 4 commits into from
Aug 5, 2022

Conversation

cmccabe
Copy link
Contributor

@cmccabe cmccabe commented Jul 28, 2022

Before trying to commit a batch of records to the __cluster_metadata log, the active controller should try to apply them to its current in-memory state. If this application process fails, the active controller process should exit, allowing another node to take leadership. This will prevent most bad metadata records from ending up in the log and help to surface errors during testing.

Similarly, if the active controller attempts to renounce leadership, and the renunciation process itself fails, the process should exit. This will help avoid bugs where the active controller continues in an undefined state.

In contrast, standby controllers that experience metadata application errors should continue on, in order to avoid a scenario where a bad record brings down the whole controller cluster. The intended effect of these changes is to make it harder to commit a bad record to the metadata log, but to continue to ride out the bad record as well as possible if such a record does get committed.

This PR introduces the FaultHandler interface to implement these concepts. In junit tests, we use a FaultHandler implementation which does not exit the process. This allows us to avoid terminating the gradle test runner, which would be very disruptive. It also allows us to ensure that the test surfaces these exceptions, which we previously were not doing (the mock fault handler stores the exception).

In addition to the above, this PR fixes a bug where RaftClient#resign was not being called from the renounce() function. This bug could have resulted in the raft layer not being informed of an active controller resigning.

@cmccabe cmccabe changed the title MINOR: improve quorum controller fault handling KAFKA-14124: improve quorum controller fault handling Jul 28, 2022
Copy link
Member

@mumrah mumrah left a comment

Choose a reason for hiding this comment

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

Thanks for the patch, @cmccabe. I'm happy to see us tightening up the error handling :)

I like the new interface for fault handling. I left some comments inline

Just to make sure I understand the behavioral changes in this patch, it looks like we are:

  • Applying records to the leader before sending to raft (i.e., verified input)
  • Killing the leader if we cannot apply a record (again, prior to sending to raft)
  • Not killing a follower if there is an error applying a record
  • Not killing a follower if there is an error applying a snapshot

It would be nice to amend the javadoc for QuorumController to include some notes on the error handling semantics.

}
i++;
}

// If the operation returned a batch of records, those records need to be
Copy link
Member

Choose a reason for hiding this comment

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

We should update this comment to something like "if the records could be applied ... "

);
fatalFaultHandler.handleFault(String.format("Asked to load snapshot " +
"(%s) when it is the active controller (%d)", reader.snapshotId(),
curClaimEpoch), null);
Copy link
Member

Choose a reason for hiding this comment

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

Can call the default method on the fault handler here instead of passing null

Comment on lines +931 to +932
int i = 1;
for (ApiMessageAndVersion message : messages) {
Copy link
Member

Choose a reason for hiding this comment

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

We have this pattern in three places now in QuorumController. Any benefit of refactoring into a private method? Maybe we add some helper to iterate the messages along with the index?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, but the problem is it's subtly different in each of these loops. The message, for example, is different, and whether the snapshot ID is passed in, etc. I think it would just be confusing to try to unify them (it's a single "for" loop after all)



/**
* A metadata fault.
Copy link
Member

Choose a reason for hiding this comment

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

Can we elaborate on when it's expected to use this exception? Is it just when applying records?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, will do.

// NoOpRecord is an empty record and doesn't need to be replayed
break;
default:
throw new RuntimeException("Unhandled record type " + type);
Copy link
Member

Choose a reason for hiding this comment

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

MetadataFaultException?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this gets wrapped by the caller


@Override
public void handleFault(String failureMessage, Throwable cause) {
FaultHandler.logFailureMessage(log, failureMessage, cause);
Copy link
Member

Choose a reason for hiding this comment

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

Is this where we would increment one of the metrics being discussed in KIP-859?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah. exactly

Copy link
Member

@mumrah mumrah left a comment

Choose a reason for hiding this comment

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

Changes look good, thanks @cmccabe! :shipit:

cmccabe added 2 commits August 4, 2022 10:13
Before trying to commit a batch of records to the __cluster_metadata log, the active controller
should try to apply them to its current in-memory state. If this application process fails, the
active controller process should exit, allowing another node to take leadership. This will prevent
most bad metadata records from ending up in the log and help to surface errors during testing.

Similarly, if the active controller attempts to renounce leadership, and the renunciation process
itself fails, the process should exit. This will help avoid bugs where the active controller
continues in an undefined state.

In contrast, standby controllers that experience metadata application errors should continue on, in
order to avoid a scenario where a bad record brings down the whole controller cluster.  The
intended effect of these changes is to make it harder to commit a bad record to the metadata log,
but to continue to ride out the bad record as well as possible if such a record does get committed.

This PR introduces the FaultHandler interface to implement these concepts. In junit tests, we use a
FaultHandler implementation which does not exit the process. This allows us to avoid terminating
the gradle test runner, which would be very disruptive. It also allows us to ensure that the test
surfaces these exceptions, which we previously were not doing (the mock fault handler stores the
exception).

In addition to the above, this PR fixes a bug where RaftClient#resign was not being called from the
renounce() function. This bug could have resulted in the raft layer not being informed of an active
controller resigning.
@cmccabe cmccabe merged commit 555744d into apache:trunk Aug 5, 2022
@cmccabe cmccabe deleted the faults-II branch August 5, 2022 05:49
@ijuma
Copy link
Contributor

ijuma commented Aug 5, 2022

There are a bunch of test failures in the PR builder, how come this was merged?

@ijuma
Copy link
Contributor

ijuma commented Aug 5, 2022

Synced with Colin offline, he submitted a fix here #12488

cmccabe added a commit that referenced this pull request Aug 9, 2022
Before trying to commit a batch of records to the __cluster_metadata log, the active controller
should try to apply them to its current in-memory state. If this application process fails, the
active controller process should exit, allowing another node to take leadership. This will prevent
most bad metadata records from ending up in the log and help to surface errors during testing.

Similarly, if the active controller attempts to renounce leadership, and the renunciation process
itself fails, the process should exit. This will help avoid bugs where the active controller
continues in an undefined state.

In contrast, standby controllers that experience metadata application errors should continue on, in
order to avoid a scenario where a bad record brings down the whole controller cluster.  The
intended effect of these changes is to make it harder to commit a bad record to the metadata log,
but to continue to ride out the bad record as well as possible if such a record does get committed.

This PR introduces the FaultHandler interface to implement these concepts. In junit tests, we use a
FaultHandler implementation which does not exit the process. This allows us to avoid terminating
the gradle test runner, which would be very disruptive. It also allows us to ensure that the test
surfaces these exceptions, which we previously were not doing (the mock fault handler stores the
exception).

In addition to the above, this PR fixes a bug where RaftClient#resign was not being called from the
renounce() function. This bug could have resulted in the raft layer not being informed of an active
controller resigning.

Reviewers: David Arthur <mumrah@gmail.com>
ijuma added a commit to confluentinc/kafka that referenced this pull request Aug 10, 2022
…(10 August 2022)

Trivial conflict in gradle/dependencies.gradle due to the newer Netty
version in confluentinc/kafka.

* apache-github/trunk:
MINOR: Upgrade gradle to 7.5.1 and bump other build/test dependencies
(apache#12495)
KAFKA-14140: Ensure an offline or in-controlled-shutdown replica is
not eligible to join ISR in ZK mode (apache#12487)
  KAFKA-14114: Add Metadata Error Related Metrics
MINOR: BrokerMetadataSnapshotter must avoid exceeding batch size
(apache#12486)
  MINOR: Upgrade mockito test dependencies (apache#12460)
KAFKA-14144:; Compare AlterPartition LeaderAndIsr before fencing
partition epoch (apache#12489)
KAFKA-14134: Replace EasyMock with Mockito for WorkerConnectorTest
(apache#12472)
  MINOR: Update scala version in bin scripts to 2.13.8 (apache#12477)
KAFKA-14104; Add CRC validation when iterating over Metadata Log
Records (apache#12457)
  MINOR: add :server-common test dependency to :storage (apache#12488)
  KAFKA-14107: Upgrade Jetty version for CVE fixes (apache#12440)
  KAFKA-14124: improve quorum controller fault handling (apache#12447)
ijuma added a commit to franz1981/kafka that referenced this pull request Aug 12, 2022
* apache-github/trunk: (447 commits)
  KAFKA-13959: Controller should unfence Broker with busy metadata log (apache#12274)
  KAFKA-10199: Expose read only task from state updater (apache#12497)
  KAFKA-14154; Return NOT_CONTROLLER from AlterPartition if leader is ahead of controller (apache#12506)
  KAFKA-13986; Brokers should include node.id in fetches to metadata quorum (apache#12498)
  KAFKA-14163; Retry compilation after zinc compile cache error (apache#12507)
  Remove duplicate common.message.* from clients:test jar file (apache#12407)
  KAFKA-13060: Replace EasyMock and PowerMock with Mockito in WorkerGroupMemberTest.java (apache#12484)
  Fix the rate window size calculation for edge cases (apache#12184)
  MINOR: Upgrade gradle to 7.5.1 and bump other build/test dependencies (apache#12495)
  KAFKA-14140: Ensure an offline or in-controlled-shutdown replica is not eligible to join ISR in ZK mode (apache#12487)
  KAFKA-14114: Add Metadata Error Related Metrics
  MINOR: BrokerMetadataSnapshotter must avoid exceeding batch size (apache#12486)
  MINOR: Upgrade mockito test dependencies (apache#12460)
  KAFKA-14144:; Compare AlterPartition LeaderAndIsr before fencing partition epoch (apache#12489)
  KAFKA-14134: Replace EasyMock with Mockito for WorkerConnectorTest (apache#12472)
  MINOR: Update scala version in bin scripts to 2.13.8 (apache#12477)
  KAFKA-14104; Add CRC validation when iterating over Metadata Log Records (apache#12457)
  MINOR: add :server-common test dependency to :storage (apache#12488)
  KAFKA-14107: Upgrade Jetty version for CVE fixes (apache#12440)
  KAFKA-14124: improve quorum controller fault handling (apache#12447)
  ...
@tamama
Copy link

tamama commented May 13, 2023

Hi @cmccabe @mumrah

  • Our production cluster (Apache Kafka 3.3.1) is having occasional problem with KRaft Quorum controller
  • A little search brings me to this PR.
  • A quick question: does this PR resolve this issue?
  • Thank you!

[2023-05-13 00:35:47,509] ERROR Encountered fatal fault: exception while renouncing leadership (org.apache.kafka.server.fault.ProcessExitingFaultHandler) java.lang.NullPointerException: Cannot invoke "org.apache.kafka.timeline.BaseHashTable.baseAddOrReplace(Object)" because "this.deltaTable" is null ...

image

@tamama
Copy link

tamama commented May 14, 2023

I confirm that this issue is not resolved with latest Apache Kafka 3.4.0

@ijuma
Copy link
Contributor

ijuma commented May 14, 2023

@tamama Can you please file a ticket in JIRA?

@showuon
Copy link
Contributor

showuon commented May 15, 2023

@tamama , this issue will be fixed in v3.4.1/v3.5.0 via this patch: #13653 . Thanks.

@tamama
Copy link

tamama commented May 15, 2023

@showuon Thanks for your update!

@ijuma Will raise a JIRA ticket if problem remains in 3.5.0 :)

@tamama
Copy link

tamama commented Jun 5, 2023

Hi @showuon , any chance of Kafka-3.5.0 release any time near?

Thank you very much!

@mumrah
Copy link
Member

mumrah commented Jun 5, 2023

@tamama you can subscribe to the kafka-users mailing list for updates on releases. https://kafka.apache.org/contact.

Edit: looks like 3.5.0 RC1 is out now, so the release is fairly close at this point.

@tamama
Copy link

tamama commented Jun 5, 2023

@mumrah Subscribed with thanks.

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

Successfully merging this pull request may close these issues.

5 participants