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

Improve TopoServer Performance and Efficiency For Keyspace Shards #15047

Merged
merged 22 commits into from
Jan 30, 2024
Merged
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions go/vt/topo/keyspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
import (
"context"
"path"
"runtime"
"sort"
"sync"

"golang.org/x/sync/errgroup"

"vitess.io/vitess/go/constants/sidecar"
"vitess.io/vitess/go/vt/key"
"vitess.io/vitess/go/vt/vterrors"

"vitess.io/vitess/go/event"
Expand All @@ -36,6 +39,12 @@

// This file contains keyspace utility functions

// Default concurrency to use in order to avoid overhwelming the topo server.
// This uses a heuristic based on the number of vCPUs available -- where it's
// assumed that as larger machines are used for Vitess deployments they will
// be able to do more concurrently.
var defaultConcurrency = runtime.NumCPU()

// KeyspaceInfo is a meta struct that contains metadata to give the
// data more context and convenience. This is the main way we interact
// with a keyspace.
Expand Down Expand Up @@ -191,9 +200,50 @@
opt.Concurrency = 1
}

// First try to get all shards using List if we can.
shardsPath := path.Join(KeyspacesPath, keyspace, ShardsPath)
listResults, err := ts.globalCell.List(ctx, shardsPath)
if err != nil || len(listResults) == 0 {
mattlord marked this conversation as resolved.
Show resolved Hide resolved
if IsErrType(err, NoNode) {
mattlord marked this conversation as resolved.
Show resolved Hide resolved
// The path doesn't exist, let's see if the keyspace exists.
_, kerr := ts.GetKeyspace(ctx, keyspace)
if kerr == nil {
// We simply have no shards.
return make(map[string]*ShardInfo, 0), nil
}
return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%v): List", keyspace)

Check warning on line 214 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L214

Added line #L214 was not covered by tests
}
// Currently the ZooKeeper implementation does not support scans
// so we fall back to concurrently fetching the shards one by one.
// It is possible that the response is too large in which case we
// also fall back to the one by one fetch in that case.
if !IsErrType(err, NoImplementation) && !IsErrType(err, ResourceExhausted) {
return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%v): List", keyspace)
}

Check warning on line 222 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L221-L222

Added lines #L221 - L222 were not covered by tests
// Continue on with the shard by shard method.
} else {
result := make(map[string]*ShardInfo, len(listResults))
for _, entry := range listResults {
// The key looks like this: /vitess/global/keyspaces/commerce/shards/-80/Shard
shardName := path.Base(path.Dir(string(entry.Key))) // The base part of the dir is "-80"
shard := &topodatapb.Shard{}
if err = shard.UnmarshalVT(entry.Value); err != nil {
return nil, vterrors.Wrapf(err, "FindAllShardsInKeyspace(%v): bad shard data", keyspace)
}

Check warning on line 232 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L231-L232

Added lines #L231 - L232 were not covered by tests
result[shardName] = &ShardInfo{
keyspace: keyspace,
shardName: shardName,
version: entry.Version,
Shard: shard,
}
}
return result, nil
}

// 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 '%v'", keyspace)

Check warning on line 246 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L246

Added line #L246 was not covered by tests
}

// Keyspaces with a large number of shards and geographically distributed
Expand Down Expand Up @@ -245,25 +295,28 @@

// 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, &FindAllShardsInKeyspaceOptions{
Concurrency: defaultConcurrency, // Limit concurrency to avoid overwhelming the topo server.
})

Check warning on line 300 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L298-L300

Added lines #L298 - L300 were not covered by tests
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 {

Check warning on line 307 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L307

Added line #L307 was not covered by tests
continue
}
result = append(result, si)
result = append(result, shard)

Check warning on line 310 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L310

Added line #L310 was not covered by tests
}
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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
})

Check warning on line 318 in go/vt/topo/keyspace.go

View check run for this annotation

Codecov / codecov/patch

go/vt/topo/keyspace.go#L316-L318

Added lines #L316 - L318 were not covered by tests

return result, nil
}

Expand Down
Loading