-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Improve TopoServer Performance and Efficiency For Keyspace Shards #15047
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
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
1264391
Make GetServingShards concurrent
mattlord b26f7cf
Get all shards in a keyspace via List when we can
mattlord ca2ea86
Return error when keyspace doesn't exist
mattlord cc1aec4
Use default concurrency based on vCPUs
mattlord d4b0609
Improve the logic in processing list results
mattlord b6286ff
Utilize in callsites and improve test coverage
mattlord c26678a
Add dedicated unit test
mattlord fbefa4e
This change was not needed (may be more expensive)
mattlord 6d7a440
Nit from self review
mattlord 4871d81
Small improvements to the unit test
mattlord 1e36795
Minor improvements after self review
mattlord 8a9f8d5
Add keyrange validity check
mattlord 1041e59
Support legacy sequences as shard names
mattlord cebde7a
Use existing ValidateShardName function
mattlord 4cd8a23
Can't stop nitting... (send help)
mattlord c7a27d0
Address review comments
mattlord 8a0638e
Address review comment
mattlord 3fb3e04
Address review comment
mattlord 76e49a0
Remove unnecessary opt usage spot (and kick CI)
mattlord 99a7f86
Move topo_read_concurrency to int
mattlord 484c7dc
Restore the old hardcoded default value for binaries w/o the flag
mattlord 3cdba40
healthcheck: remove unused constant
deepthi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,11 +19,15 @@ package topo | |
| import ( | ||
| "context" | ||
| "path" | ||
| "sort" | ||
| "sync" | ||
|
|
||
| "github.com/spf13/pflag" | ||
| "golang.org/x/sync/errgroup" | ||
|
|
||
| "vitess.io/vitess/go/constants/sidecar" | ||
| "vitess.io/vitess/go/vt/key" | ||
| "vitess.io/vitess/go/vt/servenv" | ||
| "vitess.io/vitess/go/vt/vterrors" | ||
|
|
||
| "vitess.io/vitess/go/event" | ||
|
|
@@ -34,7 +38,20 @@ import ( | |
| vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" | ||
| ) | ||
|
|
||
| // This file contains keyspace utility functions | ||
| // This file contains keyspace utility functions. | ||
|
|
||
| // Default concurrency to use in order to avoid overhwelming the topo server. | ||
| var DefaultConcurrency = 32 | ||
|
|
||
| func registerFlags(fs *pflag.FlagSet) { | ||
| fs.IntVar(&DefaultConcurrency, "topo_read_concurrency", DefaultConcurrency, "Concurrency of topo reads.") | ||
| } | ||
|
|
||
| func init() { | ||
| servenv.OnParseFor("vtcombo", registerFlags) | ||
| servenv.OnParseFor("vtctld", registerFlags) | ||
| servenv.OnParseFor("vtgate", registerFlags) | ||
| } | ||
|
|
||
| // KeyspaceInfo is a meta struct that contains metadata to give the | ||
| // data more context and convenience. This is the main way we interact | ||
|
|
@@ -188,12 +205,60 @@ func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, | |
| opt = &FindAllShardsInKeyspaceOptions{} | ||
| } | ||
| if opt.Concurrency <= 0 { | ||
| opt.Concurrency = 1 | ||
| opt.Concurrency = DefaultConcurrency | ||
| } | ||
|
|
||
| // First try to get all shards using List if we can. | ||
| buildResultFromList := func(kvpairs []KVInfo) (map[string]*ShardInfo, error) { | ||
| result := make(map[string]*ShardInfo, len(kvpairs)) | ||
| for _, entry := range kvpairs { | ||
| // The shard key looks like this: /vitess/global/keyspaces/commerce/shards/-80/Shard | ||
| shardKey := string(entry.Key) | ||
| shardName := path.Base(path.Dir(shardKey)) // The base part of the dir is "-80" | ||
| // Validate the extracted shard name. | ||
| if _, _, err := ValidateShardName(shardName); err != nil { | ||
| return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): unexpected shard key/path %q contains invalid shard name/range %q", | ||
| keyspace, shardKey, shardName) | ||
| } | ||
| shard := &topodatapb.Shard{} | ||
| if err := shard.UnmarshalVT(entry.Value); err != nil { | ||
| return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): invalid data found for shard %q in %q", | ||
| keyspace, shardName, shardKey) | ||
| } | ||
| result[shardName] = &ShardInfo{ | ||
| keyspace: keyspace, | ||
| shardName: shardName, | ||
| version: entry.Version, | ||
| Shard: shard, | ||
| } | ||
| } | ||
| return result, nil | ||
| } | ||
| shardsPath := path.Join(KeyspacesPath, keyspace, ShardsPath) | ||
| listRes, err := ts.globalCell.List(ctx, shardsPath) | ||
| if err == nil { // We have everything we need to build the result | ||
| return buildResultFromList(listRes) | ||
| } | ||
| if IsErrType(err, NoNode) { | ||
| // The path doesn't exist, let's see if the keyspace exists. | ||
| if _, kerr := ts.GetKeyspace(ctx, keyspace); kerr != nil { | ||
| return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): List", keyspace) | ||
| } | ||
| // We simply have no shards. | ||
| return make(map[string]*ShardInfo, 0), nil | ||
| } | ||
| // Currently the ZooKeeper implementation does not support index prefix | ||
| // scans so we fall back to concurrently fetching the shards one by one. | ||
| // It is also possible that the response containing all shards is too | ||
| // large in which case we also fall back to the one by one fetch. | ||
| if !IsErrType(err, NoImplementation) && !IsErrType(err, ResourceExhausted) { | ||
| return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%s): List", keyspace) | ||
| } | ||
|
|
||
| // Fall back to the shard by shard method. | ||
| shards, err := ts.GetShardNames(ctx, keyspace) | ||
| if err != nil { | ||
| return nil, vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace) | ||
| return nil, vterrors.Wrapf(err, "failed to get list of shard names for keyspace '%s'", keyspace) | ||
| } | ||
|
|
||
| // Keyspaces with a large number of shards and geographically distributed | ||
|
|
@@ -213,7 +278,7 @@ func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, | |
| ) | ||
|
|
||
| eg, ctx := errgroup.WithContext(ctx) | ||
| eg.SetLimit(opt.Concurrency) | ||
| eg.SetLimit(int(opt.Concurrency)) | ||
|
|
||
| for _, shard := range shards { | ||
| shard := shard | ||
|
|
@@ -222,7 +287,7 @@ func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, | |
| si, err := ts.GetShard(ctx, keyspace, shard) | ||
| switch { | ||
| case IsErrType(err, NoNode): | ||
| log.Warningf("GetShard(%v, %v) returned ErrNoNode, consider checking the topology.", keyspace, shard) | ||
| log.Warningf("GetShard(%s, %s) returned ErrNoNode, consider checking the topology.", keyspace, shard) | ||
| return nil | ||
| case err == nil: | ||
| mu.Lock() | ||
|
|
@@ -231,7 +296,7 @@ func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, | |
|
|
||
| return nil | ||
| default: | ||
| return vterrors.Wrapf(err, "GetShard(%v, %v) failed", keyspace, shard) | ||
| return vterrors.Wrapf(err, "GetShard(%s, %s) failed", keyspace, shard) | ||
| } | ||
| }) | ||
| } | ||
|
|
@@ -245,25 +310,26 @@ func (ts *Server) FindAllShardsInKeyspace(ctx context.Context, keyspace string, | |
|
|
||
| // GetServingShards returns all shards where the primary is serving. | ||
| func (ts *Server) GetServingShards(ctx context.Context, keyspace string) ([]*ShardInfo, error) { | ||
| shards, err := ts.GetShardNames(ctx, keyspace) | ||
| shards, err := ts.FindAllShardsInKeyspace(ctx, keyspace, nil) | ||
| if err != nil { | ||
| return nil, vterrors.Wrapf(err, "failed to get list of shards for keyspace '%v'", keyspace) | ||
| } | ||
|
|
||
| result := make([]*ShardInfo, 0, len(shards)) | ||
| for _, shard := range shards { | ||
| si, err := ts.GetShard(ctx, keyspace, shard) | ||
| if err != nil { | ||
| return nil, vterrors.Wrapf(err, "GetShard(%v, %v) failed", keyspace, shard) | ||
| } | ||
| if !si.IsPrimaryServing { | ||
| if !shard.IsPrimaryServing { | ||
| continue | ||
| } | ||
| result = append(result, si) | ||
| result = append(result, shard) | ||
| } | ||
| if len(result) == 0 { | ||
| return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%v has no serving shards", keyspace) | ||
| } | ||
| // Sort the shards by KeyRange for deterministic results. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| sort.Slice(result, func(i, j int) bool { | ||
| return key.KeyRangeLess(result[i].KeyRange, result[j].KeyRange) | ||
| }) | ||
|
|
||
| return result, nil | ||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Good catch. This was unused 🤦