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

kvserver: don't refuse to fwd lease proposals in some edge cases #58722

Merged
merged 3 commits into from
Jan 25, 2021

Conversation

andreimatei
Copy link
Contributor

This patch backpedals a little bit on the logic introduced in #55148.
That patch said that, if a leader is known, every other replica refuses
to propose a lease acquisition. Instead, the replica in question
redirects whomever was triggering the lease acquisition to the leader,
thinking that the leader should take the lease.
That patch introduced a deadlock: some replicas refuse to take the lease
because they are not VOTER_FULL (see CheckCanReceiveLease()). To fix the
deadlock, this patch incorporates that check in the proposal buffer's
decision about whether or not to reject a proposal: if the leader is
believed to refuse to take the lease, then we again forward our own
lease request.

An edge case to the edge case is when the leader is not even part of the
proposer's range descriptor. This can happen if the proposer is far
behind. In this case, we assume that the leader is eligible. If it
isn't, the deadlock will resolve once the proposer catches up.

A future patch will relax the conditions under which a replica agrees to
take the lease. VOTER_INCOMING replicas should take the lease.
VOTER_DEMOTING are more controversial.

Fixes #57798

Release note: None

@cockroach-teamcity
Copy link
Member

This change is Reviewable

Copy link
Member

@nvanbenschoten nvanbenschoten left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 1 files at r1, 3 of 3 files at r2, 5 of 5 files at r3.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @andreimatei, @bdarnell, and @tbg)


pkg/kv/kvserver/replica_proposal_buf.go, line 163 at r3 (raw file):

	leaderKnown bool

	leader roachpb.ReplicaID

Might as well give this a comment, mentioning the value of this field when !leaderKnown.


pkg/kv/kvserver/replica_proposal_buf.go, line 168 at r3 (raw file):

	// leaderIneligibleForLease is set if the leader is known, but its type of
	// replica prevents it from acquiring a lease.
	leaderIneligibleForLease bool

nit: Invert this condition to avoid the double-negative down below.


pkg/kv/kvserver/replica_proposal_buf.go, line 178 at r3 (raw file):

	replicaID() roachpb.ReplicaID
	leaderStatusRLocked(raftGroup proposerRaft) leaderInfo

stray line?


pkg/kv/kvserver/replica_proposal_buf.go, line 446 at r3 (raw file):

	buf := b.arr.asSlice()[:used]
	ents := make([]raftpb.Entry, 0, used)
	var leaderInfo leaderInfo

nit: move this down to where the other variable declarations use to live. No need to mix it up with these slices of entries.


pkg/kv/kvserver/replica_proposal_buf.go, line 501 at r3 (raw file):

		// A special case is when the leader is known, but is ineligible to get the
		// lease. In that case, we have no choice but to continue with the proposal.
		if !leaderInfo.iAmTheLeader && p.Request.IsLeaseRequest() {

We may need to extend this check even more. In 91f58af, I noticed that we are rejecting lease extensions when we are the current leaseholder if we haven't yet grabbed raft leadership. That seems wrong.

No need to do anything here.


pkg/kv/kvserver/replica_proposal_buf.go, line 503 at r3 (raw file):

		if !leaderInfo.iAmTheLeader && p.Request.IsLeaseRequest() {
			proposeAnyway := leaderInfo.leaderIneligibleForLease || b.testing.allowLeaseProposalWhenNotLeader
			if leaderInfo.leaderKnown && !proposeAnyway {

This grouping seems odd to me. We're saying leader known && (either leader eligible || not testing allow when not leader). So if leaderKnown && !leaderIneligibleForLease, aren't we rejecting, regardless of the testing knob? I don't think that's the intention. I think we want to group the first two conditions together like:

leaderKnownAndEligible := ...
if leaderKnownAndEligible && !b.testing.allowLeaseProposalWhenNotLeader {
    ...
}

pkg/kv/kvserver/replica_proposal_buf.go, line 764 at r3 (raw file):

}

func (rp *replicaProposer) leaderStatusRLocked(raftGroup proposerRaft) leaderInfo {

Keep this in order with the interface. Either pull this all the way up above destroyed() (but don't do that) or shuffle the interface def. Ideally, this would go right above rejectProposalWithRedirectLocked in the declaration and the definition.


pkg/kv/kvserver/replica_proposal_buf.go, line 775 at r3 (raw file):

	if leaderKnown {
		// Figure out if the leader is eligible for getting a lease. Only VOTER_FULL
		// replicas are eligible.

This seems like the kind of comment that is going to rot. How should we avoid that?


pkg/kv/kvserver/replica_proposal_buf.go, line 785 at r3 (raw file):

			leaderIneligibleForLease = false
		} else {
			err := batcheval.CheckCanReceiveLease(leaderRep, *rangeDesc)

nit: Seems like we should just pass the descriptor by pointer, since that's what every caller wants and that's generally how we interact with RangeDescriptors.


pkg/kv/kvserver/replica_proposal_buf_test.go, line 59 at r3 (raw file):

}

func (t *testProposer) leaderStatusRLocked(raftGroup proposerRaft) leaderInfo {

Why define this method in such a random place? I know these kinds of comments seem like nits, but a few of these changes in a row and the entire structure of a piece of code falls apart and we're left with chaos! And then a month later, we'll decide that we need to rewrite everything because the code is jumbled and hard to read. And then we introduce a bug during the rewrite. And then data gets corrupted in an important deployment. And then a SpaceX satellite falls out of the sky. And then SpaceX goes bankrupt due to a lawsuit. And we never make it to Mars. Don't you want to go to Mars??


pkg/kv/kvserver/batcheval/cmd_lease.go, line 45 at r2 (raw file):

// If we decide to start using long-lived learners at some point, that math may change.

If we decide?


pkg/kv/kvserver/batcheval/cmd_lease.go, line 47 at r2 (raw file):

// point, that math may change.
func checkCanReceiveLease(
	newLeaseholer roachpb.ReplicaDescriptor, rngDesc roachpb.RangeDescriptor,

Should rngDesc be a pointer?


pkg/kv/kvserver/batcheval/cmd_lease.go, line 46 at r3 (raw file):

// out the edge cases. If we decide to start using long-lived learners at some
// point, that math may change.
func CheckCanReceiveLease(

Want to add a unit test around this here in pkg/kv/kvserver/batcheval/cmd_lease.go?

Copy link
Contributor Author

@andreimatei andreimatei left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @andreimatei, @bdarnell, @nvanbenschoten, and @tbg)


pkg/kv/kvserver/replica_proposal_buf.go, line 163 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

Might as well give this a comment, mentioning the value of this field when !leaderKnown.

done


pkg/kv/kvserver/replica_proposal_buf.go, line 178 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

stray line?

done


pkg/kv/kvserver/replica_proposal_buf.go, line 446 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

nit: move this down to where the other variable declarations use to live. No need to mix it up with these slices of entries.

done


pkg/kv/kvserver/replica_proposal_buf.go, line 503 at r3 (raw file):

So if leaderKnown && !leaderIneligibleForLease, aren't we rejecting, regardless of the testing knob? I don't think that's the intention.

This was, in fact, the intention. The testing knob is kinda crap, and is only used by one rando. I wanted to keep the scope of the knob as small as possible - i.e. only apply it in the most regular situation (when the leader is eligible). In particular, I don't want the knob to introduce the possibility of the deadlock.
WDYT


pkg/kv/kvserver/replica_proposal_buf.go, line 764 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

Keep this in order with the interface. Either pull this all the way up above destroyed() (but don't do that) or shuffle the interface def. Ideally, this would go right above rejectProposalWithRedirectLocked in the declaration and the definition.

done


pkg/kv/kvserver/replica_proposal_buf.go, line 775 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

This seems like the kind of comment that is going to rot. How should we avoid that?

I've deleted it :P


pkg/kv/kvserver/replica_proposal_buf.go, line 785 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

nit: Seems like we should just pass the descriptor by pointer, since that's what every caller wants and that's generally how we interact with RangeDescriptors.

yeah... I had made it a value for symmetry with the ReplicaDescriptor arg, where we do use values more. Changed.


pkg/kv/kvserver/replica_proposal_buf_test.go, line 59 at r3 (raw file):

Don't you want to go to Mars??

https://www.youtube.com/watch?v=KDXbl54HpQE


pkg/kv/kvserver/batcheval/cmd_lease.go, line 45 at r2 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…
// If we decide to start using long-lived learners at some point, that math may change.

If we decide?

well I'm just moving a comment around

But yeah, I've deleted it. Cause we have decided, but the math is not changing (as far as learners go)


pkg/kv/kvserver/batcheval/cmd_lease.go, line 47 at r2 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

Should rngDesc be a pointer?

done


pkg/kv/kvserver/batcheval/cmd_lease.go, line 46 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

Want to add a unit test around this here in pkg/kv/kvserver/batcheval/cmd_lease.go?

done

Copy link
Member

@nvanbenschoten nvanbenschoten left a comment

Choose a reason for hiding this comment

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

:lgtm: though there are still a few minor tweaks to make in that inner condition to unravel it a little.

We should get more eyes on this too, since I assume we're planning for another backport attempt.

Reviewed 6 of 6 files at r6.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @andreimatei, @bdarnell, and @tbg)


pkg/kv/kvserver/replica_proposal_buf.go, line 168 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

nit: Invert this condition to avoid the double-negative down below.

I still think this would be an improvement.


pkg/kv/kvserver/replica_proposal_buf.go, line 503 at r3 (raw file):

Previously, andreimatei (Andrei Matei) wrote…

So if leaderKnown && !leaderIneligibleForLease, aren't we rejecting, regardless of the testing knob? I don't think that's the intention.

This was, in fact, the intention. The testing knob is kinda crap, and is only used by one rando. I wanted to keep the scope of the knob as small as possible - i.e. only apply it in the most regular situation (when the leader is eligible). In particular, I don't want the knob to introduce the possibility of the deadlock.
WDYT

I think this makes the knob super hard to reason about. I'd be in favor of broadening it to just disable this entire rejection mechanism. That's the effect it had before.

Copy link
Member

@nvanbenschoten nvanbenschoten left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @andreimatei, @bdarnell, and @tbg)


pkg/kv/kvserver/replica_proposal_buf_test.go, line 59 at r3 (raw file):

Previously, andreimatei (Andrei Matei) wrote…

Don't you want to go to Mars??

https://www.youtube.com/watch?v=KDXbl54HpQE

https://www.youtube.com/watch?v=qoHU57KtUws

This assertion had become nonsensical in #9915f0d0f104aa918c94340e9e47129b90421999.
It's from a time where the surrounding function was dealing with the
local store only.

Release note: None
Copy link
Contributor Author

@andreimatei andreimatei left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @andreimatei, @bdarnell, and @tbg)


pkg/kv/kvserver/replica_proposal_buf.go, line 168 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

I still think this would be an improvement.

done


pkg/kv/kvserver/replica_proposal_buf.go, line 503 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

I think this makes the knob super hard to reason about. I'd be in favor of broadening it to just disable this entire rejection mechanism. That's the effect it had before.

ok, done

Copy link
Member

@nvanbenschoten nvanbenschoten left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewed 6 of 6 files at r9.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @bdarnell and @tbg)


pkg/kv/kvserver/replica_proposal_buf.go, line 501 at r3 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

We may need to extend this check even more. In 91f58af, I noticed that we are rejecting lease extensions when we are the current leaseholder if we haven't yet grabbed raft leadership. That seems wrong.

No need to do anything here.

By the way, this is now tracked in #59179.

Copy link
Member

@tbg tbg left a comment

Choose a reason for hiding this comment

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

Reviewed 6 of 6 files at r7, 3 of 3 files at r8, 6 of 6 files at r9.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @andreimatei and @bdarnell)


pkg/kv/kvserver/batcheval/cmd_lease.go, line 51 at r9 (raw file):

	wouldbeLeaseholer roachpb.ReplicaDescriptor, rngDesc *roachpb.RangeDescriptor,
) error {
	repDesc, ok := rngDesc.GetReplicaDescriptorByID(wouldbeLeaseholer.ReplicaID)

Leaseholder

Make checkCanReceiveLease() more nimble, anticipating broader use.

Release note: None
This patch backpedals a little bit on the logic introduced in cockroachdb#55148.
That patch said that, if a leader is known, every other replica refuses
to propose a lease acquisition. Instead, the replica in question
redirects whomever was triggering the lease acquisition to the leader,
thinking that the leader should take the lease.
That patch introduced a deadlock: some replicas refuse to take the lease
because they are not VOTER_FULL (see CheckCanReceiveLease()). To fix the
deadlock, this patch incorporates that check in the proposal buffer's
decision about whether or not to reject a proposal: if the leader is
believed to refuse to take the lease, then we again forward our own
lease request.

An edge case to the edge case is when the leader is not even part of the
proposer's range descriptor. This can happen if the proposer is far
behind. In this case, we assume that the leader is eligible. If it
isn't, the deadlock will resolve once the proposer catches up.

A future patch will relax the conditions under which a replica agrees to
take the lease. VOTER_INCOMING replicas should take the lease.
VOTER_DEMOTING are more controversial.

Fixes cockroachdb#57798

Release note: None
Copy link
Contributor Author

@andreimatei andreimatei left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @bdarnell and @tbg)


pkg/kv/kvserver/batcheval/cmd_lease.go, line 51 at r9 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Leaseholder

Done.

Copy link
Contributor Author

@andreimatei andreimatei left a comment

Choose a reason for hiding this comment

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

bors r+

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (and 1 stale) (waiting on @bdarnell, @nvanbenschoten, and @tbg)

@craig
Copy link
Contributor

craig bot commented Jan 25, 2021

Build succeeded:

@craig craig bot merged commit 2ec7d92 into cockroachdb:master Jan 25, 2021
irfansharif added a commit to irfansharif/cockroach that referenced this pull request Feb 3, 2021
Fixes cockroachdb#57342. This looks to have been the same thing as cockroachdb#57798, and was
fixed by cockroachdb#58722.

Release note: None
craig bot pushed a commit that referenced this pull request Feb 3, 2021
59762: roachtest: unskip acceptance/bank/cluster-recovery r=irfansharif a=irfansharif

Fixes #57342. This looks to have been the same thing as #57798, and was
fixed by #58722.

Release note: None

Co-authored-by: irfan sharif <irfanmahmoudsharif@gmail.com>
@andreimatei andreimatei deleted the leader-lease branch December 16, 2021 19:34
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.

roachtest: acceptance/bank/node-restart flake
4 participants