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

roachpb: stop mutating RangeDescriptor when Replicas is called #39489

Merged
merged 1 commit into from
Aug 16, 2019

Conversation

danhhz
Copy link
Contributor

@danhhz danhhz commented Aug 9, 2019

It was previously sorting the InternalReplicas field so we could serve
calls to Voters and Learners without allocating, but non-obviousness of
this is not worth it.

Release note: None

@danhhz danhhz requested review from tbg and nvanbenschoten August 9, 2019 15:13
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@danhhz
Copy link
Contributor Author

danhhz commented Aug 9, 2019

Thoughts? We could go even further with the fastpath and return a slice of the wrapped replicas whenever the voters or learners are all consecutive (and then make this more likely by sorting at the end of both AddReplica and RemoveReplica).

@jeffrey-xiao
Copy link
Contributor

If we're removing the sort, we can also remove this hacky fix:

// TODO(jeffreyxiao): This hacky fix ensures that we don't fail the
// conditional get because of the ordering of InternalReplicas. Calling
// Replicas() will sort the list of InternalReplicas as a side-effect. The
// invariant of having InternalReplicas sorted is not maintained in 19.1.
// Additionally, in 19.2, it's possible for the in-memory copy of
// RangeDescriptor to become sorted from a call to Replicas() without
// updating the copy in kv. These two factors makes it possible for the
// in-memory copy to be out of sync from the copy in kv. The sorted invariant
// of InternalReplicas is used by ReplicaDescriptors.Voters() and
// ReplicaDescriptors.Learners().
if existingDesc != nil {
existingDesc.Replicas() // for sorting side-effect
}
if expectation != nil {
expectation.Replicas() // for sorting side-effect
}
.

@@ -1587,6 +1587,6 @@ func TestChangeReplicasTrigger_String(t *testing.T) {
},
}
act := crt.String()
exp := `ADD_REPLICA((n1,s2):3LEARNER): updated=(n4,s5):6,(n1,s2):3LEARNER,(n7,s8):9LEARNER next=10`
exp := `ADD_REPLICA((n1,s2):3LEARNER): updated=(n1,s2):3LEARNER,(n4,s5):6,(n7,s8):9LEARNER next=10`
Copy link
Member

Choose a reason for hiding this comment

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

Why is this changing? Note that in #39484 I ran into problems because the sorting isn't stable but we're passing ReplicaID zero everywhere, just absorb that PR perhaps?

Copy link
Member

Choose a reason for hiding this comment

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

Oh wait the sorting is going away, nvm

sort.Sort((*byTypeThenReplicaID)(replicas))
return ReplicaDescriptors{wrapped: *replicas}
func MakeReplicaDescriptors(replicas []ReplicaDescriptor) ReplicaDescriptors {
return ReplicaDescriptors{wrapped: replicas}
Copy link
Member

Choose a reason for hiding this comment

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

Can't this just be the same underlying type now?

type ReplicaDescriptors []ReplicaDescriptor

@tbg
Copy link
Member

tbg commented Aug 9, 2019

I like this, good call. I thought about doing the same but didn't have the fast path on the radar.

In a world in which we have "permanent" learners we're going to have to fiddle with this once more, but let's buy into complexity when we truly need this

LGTM

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 12 of 12 files at r1.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @danhhz)


pkg/roachpb/metadata_replicas.go, line 170 at r1 (raw file):

// For some related mega-comments, see Replica.sendSnapshot.
func (d ReplicaDescriptors) Learners() []ReplicaDescriptor {
	learners := make([]ReplicaDescriptor, 0, len(d.wrapped))

Should we add a fast-path here as well? Most of the time we expect no LEARNERS, right? I guess that would be equivalent to not pre-allocating this slice:

var learners []ReplicaDesciptor

You might be worried about the potential for repeat allocations if we do that, in which case I'd still make the allocation lazy:

var learners []ReplicaDesciptor
for i := range d.wrapped {
    if d.wrapped[i].GetType() == ReplicaType_LEARNER {
        if learners == nil {
            learners = make([]ReplicaDescriptor, 0, len(d.wrapped)-i)
...

It was previously sorting the InternalReplicas field so we could serve
calls to Voters and Learners without allocating, but non-obviousness of
this is not worth it.

Release note: None
Copy link
Contributor Author

@danhhz danhhz 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 (and 1 stale) (waiting on @danhhz, @nvanbenschoten, and @tbg)


pkg/roachpb/metadata_replicas.go, line 52 at r1 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Can't this just be the same underlying type now?

type ReplicaDescriptors []ReplicaDescriptor

My understanding is there's no overhead to the struct as long as the only field is the wrapped slice and I like making users go through the abstraction. If it was just a type alias for the slice, even users external to the package could simply cast it


pkg/roachpb/metadata_replicas.go, line 170 at r1 (raw file):

Previously, nvanbenschoten (Nathan VanBenschoten) wrote…

Should we add a fast-path here as well? Most of the time we expect no LEARNERS, right? I guess that would be equivalent to not pre-allocating this slice:

var learners []ReplicaDesciptor

You might be worried about the potential for repeat allocations if we do that, in which case I'd still make the allocation lazy:

var learners []ReplicaDesciptor
for i := range d.wrapped {
    if d.wrapped[i].GetType() == ReplicaType_LEARNER {
        if learners == nil {
            learners = make([]ReplicaDescriptor, 0, len(d.wrapped)-i)
...

Done

@danhhz
Copy link
Contributor Author

danhhz commented Aug 16, 2019

TFTRs!

bors r=tbg,nvanbenschoten

craig bot pushed a commit that referenced this pull request Aug 16, 2019
39489: roachpb: stop mutating RangeDescriptor when Replicas is called r=tbg,nvanbenschoten a=danhhz

It was previously sorting the InternalReplicas field so we could serve
calls to Voters and Learners without allocating, but non-obviousness of
this is not worth it.

Release note: None

Co-authored-by: Daniel Harrison <[email protected]>
@craig
Copy link
Contributor

craig bot commented Aug 16, 2019

Build succeeded

@craig craig bot merged commit 2059495 into cockroachdb:master Aug 16, 2019
@danhhz danhhz deleted the replicas_mutate branch September 3, 2019 19:01
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