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

kv: decompose roles of Gossip dependency in DistSender #50116

Merged

Conversation

nvanbenschoten
Copy link
Member

This change decomposes the roles of Gossip as a dependency in DistSender into that of a NodeDescStore, a source of node descriptors, and that of a FirstRangeProvider, a provider of information on the first range in a cluster.

This decomposition will be used to address #47909 by:

  1. replacing Gossip with a TenantService as a NodeDescStore
  2. providing a custom RangeDescriptorDB (also backed by a TenantService) instead of a FirstRangeProvider.

Together, these changes will allow us to remove DistSender's dependency on Gossip for SQL-only tenant processes.

The next step after this will be to introduce a TenantService that can satisfy these two dependencies (NodeDescStore and RangeDescriptorDB) and also use the new NodeDescStore-to-AddressResolver binding to replace the use of Gossip with the TenantService in nodedialer instances.

@nvanbenschoten nvanbenschoten requested a review from tbg June 11, 2020 20:15
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/tenantDistSender branch from 62a8fe3 to ec3d0e5 Compare June 11, 2020 21:41
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.

:lgtm: mod open discussions.

Can you give the outlook here for future releases? Do we expect to use the TenantService for all deployments once all nodes have the endpoint (i.e. 21.1)? For symmetry, I think this is preferable - we'd want DistSender to work essentially the same on tenants and KV nodes, except that the implementation of the RangeDescriptorDB on KV nodes should not incur an RPC hop, which the local server optimization will already give us.
I suspect you weren't planning to do that (or TenantServicewouldn't have been your choice of name). My expectation/hope was that the tenant-accessible RPCs are really just a subset of the KV RPCs with extra auth, validation, and more restrictions (for example, we'll have Node.Batch on both), so that we maximize coverage and converge handling of operations as much as possible, regardless of the tenancy type. Doesn't block this PR - just something to align on before your next round of PRs. If we end up doing this as I suggest, in effect you would just add these endpoints right next to (*Node).Batch and they would get mirrored into a service TenantKV in my follow-up to #50082.

PS: I would even apply the same argument to NodeDescStorage and any future endpoint really - serve them from the same endpoint the tenants would use (but on the KV service) so that it all works the same for tenants and integrated nodes alike).

Reviewed 1 of 1 files at r1, 1 of 1 files at r2, 1 of 1 files at r3, 3 of 3 files at r4, 15 of 15 files at r5.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @nvanbenschoten)


pkg/gossip/gossip.go, line 985 at r4 (raw file):

// getNodeIDAddressLocked looks up the SQL address of the node by ID. The mutex
// is assumed held by the caller. This method is called externally via
// GetNodeIDSQLAddress.

This sentence is pretty much removable, right? A method XLocked ~always gets called by X.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 266 at r1 (raw file):

		gossip:     g,
		metrics:    makeDistSenderMetrics(),
		nodeDialer: cfg.NodeDialer,

The way I like to rewrite these methods is by initializing everything needed to make a &DistSender before making the &DistSender (so that we never deal in a half-inited struct). Might be better done separately though.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 260 at r4 (raw file):

// DistSenderContext or the fields within is optional. For omitted values, sane
// defaults will be used.
func NewDistSender(cfg DistSenderConfig, g *gossip.Gossip) *DistSender {

❤️


pkg/kv/kvclient/kvcoord/dist_sender.go, line 189 at r5 (raw file):

	// the first range has changed. The callback is passed the RangeDescriptor
	// in its marshaled form.
	OnFirstRangeChanged(func(roachpb.Value))

Wouldn't it be more idiomatic to pass the *RangeDescriptor here? I think you're trying to avoid that struct leaking into Gossip, but IMO it would be about as idiomatic (perhaps more) to have an adapter in this package that wraps a *Gossip into a FirstRangeProvider anyway, so that gossip wouldn't have to know about FirstRangeProvider.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 209 at r5 (raw file):

	// which span ranges and don't require read consistency.
	clock *hlc.Clock
	// nodeDescs provides information on other nodes that DistSender may

why "other nodes"? DistSender will also run on tenants, so we could be more explicit and say "KV nodes":

// nodeDescs provides information on the KV nodes that DistSender may consider routing requests to.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 217 at r5 (raw file):

	rangeCache *RangeDescriptorCache
	// firstRangeProvider provides the range descriptor for range one.
	// The dependency is optional.

What does this sentence mean? If you don't give one then... what? I'm seeing the answer further below, but maybe hint at it by saying "This is not required if a range descriptor db is supplied".


pkg/kv/kvclient/kvcoord/dist_sender.go, line 259 at r5 (raw file):

	NodeDialer      *nodedialer.Dialer

	// One of the following dependencies must be provided, but not both.

Could this be explained more easily by saying:


One of the following two must be provided, but not both.

If FirstRangeProvider is supplied, DistSender will use itself as a RangeDescriptorDB and scan the meta ranges directly (using the FirstRangeProvider to bootstrap the location of the meta1 range). Additionally, it will proactively update its range descriptor cache with any meta1 updates from the provider.

If RangeDescriptorDB is provided, all range lookups will be delegated to it.


Is the intention that we drop the Gossip dependency when the migration is through? If so, can we stop listening to meta1 updates in the DistSender itself today already and really just hand the FirstRangeProvider through to the RangeDescriptorDB (which is right now implemented by DistSender itself, but a small layer between wouldn't hurt)?
That way we will have more confidence that we're not relying on the meta1 updates going into the range desc cache proactively (it should not matter that it does other than that this enables DistSender's use as a RangeDescriptorDB)? I think I would prefer that.

For simplicity, I would also then say that you provide exactly one but are prevented from supplying both. (You have conflicting statements about that in the comment right now).

I'm not sure why we were doing this.
A few of the comments were stale, primarily around the structured
value that was associated with a given key.
While doing this, remove a pessimization. Not only was the
localityTierMap not worth it given how few locality tiers we expect in
practice, but it was represented as a `map[string]struct{}` instead of a
`map[roachpb.Tier]struct{}`, meaning that we needed to stringify each
LocalityAddress tier before each map access, which forced memory
allocations and of extra work. An n^2 approach is simpler and almost
certainly faster up to ~100 locality tiers.
@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/tenantDistSender branch from ec3d0e5 to 9bc4e19 Compare June 15, 2020 19:28
Copy link
Member Author

@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.

Yes, you're right that I wasn't planning on merging the tenant service into the existing Internal service, but that was mostly driven by following the path of least resistance. I agree with your stance on all of this and favor maintaining as much symmetry between DistSender on tenant and KV nodes.

The one wrinkle here is that the TenantService client that I've been prototyping maintains some state that only makes sense in the tenant configuration. For instance, it implements NodeDescStorage off of a local cache that is asynchronously updated from a gRPC stream on a new NodeInfo RPC. We don't need any of this caching on KV nodes, so we wouldn't want them to have to pay for it.

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


pkg/gossip/gossip.go, line 985 at r4 (raw file):

Previously, tbg (Tobias Grieger) wrote…

This sentence is pretty much removable, right? A method XLocked ~always gets called by X.

Yeah, it doesn't add a lot, but it maintains symmetry with all of the other unexpected Gossip methods like getNodeDescriptorLocked and getNodeIDAddressLocked. I figured it doesn't hurt.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 266 at r1 (raw file):

Previously, tbg (Tobias Grieger) wrote…

The way I like to rewrite these methods is by initializing everything needed to make a &DistSender before making the &DistSender (so that we never deal in a half-inited struct). Might be better done separately though.

Yeah, that style made server construction must clearer because you could always tell when a struct was missing fields. I'll hold off on that here though.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 189 at r5 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Wouldn't it be more idiomatic to pass the *RangeDescriptor here? I think you're trying to avoid that struct leaking into Gossip, but IMO it would be about as idiomatic (perhaps more) to have an adapter in this package that wraps a *Gossip into a FirstRangeProvider anyway, so that gossip wouldn't have to know about FirstRangeProvider.

I was actually trying to avoid unmarshalling the RangeDescriptor when !log.V(1) because we didn't use to, but given that this is only called on updates to the meta1 descriptor, that seems like an acceptable cost to keep this interface clean. Done.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 209 at r5 (raw file):

Previously, tbg (Tobias Grieger) wrote…

why "other nodes"? DistSender will also run on tenants, so we could be more explicit and say "KV nodes":

// nodeDescs provides information on the KV nodes that DistSender may consider routing requests to.

Done.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 217 at r5 (raw file):

Previously, tbg (Tobias Grieger) wrote…

What does this sentence mean? If you don't give one then... what? I'm seeing the answer further below, but maybe hint at it by saying "This is not required if a range descriptor db is supplied".

Done.


pkg/kv/kvclient/kvcoord/dist_sender.go, line 259 at r5 (raw file):

For simplicity, I would also then say that you provide exactly one but are prevented from supplying both. (You have conflicting statements about that in the comment right now).

That's how this was originally written, but it was convenient to provide both for TestEvictOnFirstRangeGossip. I updated the comment based on your suggestion and removed the ambiguity.

Is the intention that we drop the Gossip dependency when the migration is through? If so, can we stop listening to meta1 updates in the DistSender itself today already and really just hand the FirstRangeProvider through to the RangeDescriptorDB (which is right now implemented by DistSender itself, but a small layer between wouldn't hurt)?
That way we will have more confidence that we're not relying on the meta1 updates going into the range desc cache proactively)

The intention wasn't to drop the Gossip dependency when we have access to it, if that's what you're asking.

Are you suggesting getting rid of the proactive update to the range descriptor cache on gossip updates (i.e. removing OnFirstRangeChanged)?

This commit decomposes the roles of Gossip as a dependency in DistSender
into that of a NodeDescStore, a source of node descriptors, and that of
a FirstRangeProvider, a provider of information on the first range in a
cluster.

This decomposition will be used to address cockroachdb#47909 by:
1. replacing Gossip with a TenantService as a NodeDescStore
2. providing a custom RangeDescriptorDB (also backed by a TenantService)
   instead of a FirstRangeProvider.

Together, these changes will allow us to remove DistSender's dependency
on Gossip for SQL-only tenant processes.

The next step after this will be to introduce a TenantService that can
satisfy these two dependencies (NodeDescStore and RangeDescriptorDB) and
also use the new NodeDescStore-to-AddressResolver binding to replace the
use of Gossip with the TenantService in nodedialer instances.
@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/tenantDistSender branch from 9bc4e19 to 54333ab Compare June 16, 2020 16:15
@nvanbenschoten
Copy link
Member Author

bors r+

@craig
Copy link
Contributor

craig bot commented Jun 18, 2020

Build succeeded

@craig craig bot merged commit f009e89 into cockroachdb:master Jun 18, 2020
@nvanbenschoten nvanbenschoten deleted the nvanbenschoten/tenantDistSender branch June 23, 2020 18:02
@lunevalex lunevalex added the A-multitenancy Related to multi-tenancy label Jul 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-multitenancy Related to multi-tenancy
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants