зеркало из https://github.com/github/vitess-gh.git
Merge pull request #7751 from tinyspeck/am_vtadmin_aggregate_schema_sizes
[vtadmin] Aggregate schema sizes
This commit is contained in:
Коммит
b6a8acbad8
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -19,6 +19,7 @@ package vtadmin
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -40,6 +41,7 @@ import (
|
|||
vtadminhttp "vitess.io/vitess/go/vt/vtadmin/http"
|
||||
vthandlers "vitess.io/vitess/go/vt/vtadmin/http/handlers"
|
||||
"vitess.io/vitess/go/vt/vtadmin/sort"
|
||||
"vitess.io/vitess/go/vt/vtadmin/vtadminproto"
|
||||
"vitess.io/vitess/go/vt/vterrors"
|
||||
"vitess.io/vitess/go/vt/vtexplain"
|
||||
|
||||
|
@ -180,7 +182,10 @@ func (api *API) FindSchema(ctx context.Context, req *vtadminpb.FindSchemaRequest
|
|||
return
|
||||
}
|
||||
|
||||
schemas, err := api.getSchemas(ctx, c, tablets)
|
||||
schemas, err := api.getSchemas(ctx, c, cluster.GetSchemaOptions{
|
||||
Tablets: tablets,
|
||||
TableSizeOptions: req.TableSizeOptions,
|
||||
})
|
||||
if err != nil {
|
||||
err := fmt.Errorf("%w: while collecting schemas for cluster %s", err, c.ID)
|
||||
rec.RecordError(err)
|
||||
|
@ -346,18 +351,12 @@ func (api *API) GetKeyspaces(ctx context.Context, req *vtadminpb.GetKeyspacesReq
|
|||
go func(c *cluster.Cluster, ks *vtctldatapb.Keyspace) {
|
||||
defer kwg.Done()
|
||||
|
||||
span, ctx := trace.NewSpan(ctx, "Cluster.FindAllShardsInKeyspace")
|
||||
defer span.Finish()
|
||||
|
||||
cluster.AnnotateSpan(c, span)
|
||||
span.Annotate("keyspace", ks.Name)
|
||||
|
||||
sr, err := c.Vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{
|
||||
Keyspace: ks.Name,
|
||||
shards, err := c.FindAllShardsInKeyspace(ctx, ks.Name, cluster.FindAllShardsInKeyspaceOptions{
|
||||
SkipDial: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
er.RecordError(fmt.Errorf("FindAllShardsInKeyspace(%s): %w", ks.Name, err))
|
||||
er.RecordError(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -365,7 +364,7 @@ func (api *API) GetKeyspaces(ctx context.Context, req *vtadminpb.GetKeyspacesReq
|
|||
kss = append(kss, &vtadminpb.Keyspace{
|
||||
Cluster: c.ToProto(),
|
||||
Keyspace: ks,
|
||||
Shards: sr.Shards,
|
||||
Shards: shards,
|
||||
})
|
||||
km.Unlock()
|
||||
}(c, ks)
|
||||
|
@ -398,30 +397,19 @@ func (api *API) GetSchema(ctx context.Context, req *vtadminpb.GetSchemaRequest)
|
|||
span.Annotate("cluster_id", req.ClusterId)
|
||||
span.Annotate("keyspace", req.Keyspace)
|
||||
span.Annotate("table", req.Table)
|
||||
vtadminproto.AnnotateSpanWithGetSchemaTableSizeOptions(req.TableSizeOptions, span)
|
||||
|
||||
clusters, _ := api.getClustersForRequest([]string{req.ClusterId})
|
||||
if len(clusters) == 0 {
|
||||
c, ok := api.clusterMap[req.ClusterId]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: no cluster with id %s", errors.ErrUnsupportedCluster, req.ClusterId)
|
||||
}
|
||||
|
||||
cluster := clusters[0]
|
||||
|
||||
tablet, err := cluster.FindTablet(ctx, func(t *vtadminpb.Tablet) bool {
|
||||
return t.Tablet.Keyspace == req.Keyspace && t.State == vtadminpb.Tablet_SERVING
|
||||
return c.GetSchema(ctx, req.Keyspace, cluster.GetSchemaOptions{
|
||||
BaseRequest: &vtctldatapb.GetSchemaRequest{
|
||||
Tables: []string{req.Table},
|
||||
},
|
||||
TableSizeOptions: req.TableSizeOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: no serving tablet found for keyspace %s", err, req.Keyspace)
|
||||
}
|
||||
|
||||
span.Annotate("tablet_alias", topoproto.TabletAliasString(tablet.Tablet.Alias))
|
||||
|
||||
if err := cluster.Vtctld.Dial(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cluster.GetSchema(ctx, &vtctldatapb.GetSchemaRequest{
|
||||
Tables: []string{req.Table},
|
||||
}, tablet)
|
||||
}
|
||||
|
||||
// GetSchemas is part of the vtadminpb.VTAdminServer interface.
|
||||
|
@ -453,7 +441,10 @@ func (api *API) GetSchemas(ctx context.Context, req *vtadminpb.GetSchemasRequest
|
|||
return
|
||||
}
|
||||
|
||||
ss, err := api.getSchemas(ctx, c, tablets)
|
||||
ss, err := api.getSchemas(ctx, c, cluster.GetSchemaOptions{
|
||||
Tablets: tablets,
|
||||
TableSizeOptions: req.TableSizeOptions,
|
||||
})
|
||||
if err != nil {
|
||||
er.RecordError(err)
|
||||
return
|
||||
|
@ -477,7 +468,7 @@ func (api *API) GetSchemas(ctx context.Context, req *vtadminpb.GetSchemasRequest
|
|||
}
|
||||
|
||||
// getSchemas returns all of the schemas across all keyspaces in the given cluster.
|
||||
func (api *API) getSchemas(ctx context.Context, c *cluster.Cluster, tablets []*vtadminpb.Tablet) ([]*vtadminpb.Schema, error) {
|
||||
func (api *API) getSchemas(ctx context.Context, c *cluster.Cluster, opts cluster.GetSchemaOptions) ([]*vtadminpb.Schema, error) {
|
||||
if err := c.Vtctld.Dial(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -507,14 +498,26 @@ func (api *API) getSchemas(ctx context.Context, c *cluster.Cluster, tablets []*v
|
|||
go func(c *cluster.Cluster, ks *vtctldatapb.Keyspace) {
|
||||
defer wg.Done()
|
||||
|
||||
ss, err := api.getSchemasForKeyspace(ctx, c, ks, tablets)
|
||||
ss, err := c.GetSchema(ctx, ks.Name, opts)
|
||||
if err != nil {
|
||||
// Ignore keyspaces without any serving tablets.
|
||||
if stderrors.Is(err, errors.ErrNoServingTablet) {
|
||||
log.Infof(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
er.RecordError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore keyspaces without schemas
|
||||
if ss == nil {
|
||||
log.Infof("No schemas for %s", ks.Name)
|
||||
return
|
||||
}
|
||||
|
||||
if len(ss.TableDefinitions) == 0 {
|
||||
log.Infof("No tables in schema for %s", ks.Name)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -533,41 +536,6 @@ func (api *API) getSchemas(ctx context.Context, c *cluster.Cluster, tablets []*v
|
|||
return schemas, nil
|
||||
}
|
||||
|
||||
func (api *API) getSchemasForKeyspace(ctx context.Context, c *cluster.Cluster, ks *vtctldatapb.Keyspace, tablets []*vtadminpb.Tablet) (*vtadminpb.Schema, error) {
|
||||
// Choose the first serving tablet.
|
||||
var kt *vtadminpb.Tablet
|
||||
for _, t := range tablets {
|
||||
if t.Tablet.Keyspace != ks.Name || t.State != vtadminpb.Tablet_SERVING {
|
||||
continue
|
||||
}
|
||||
|
||||
kt = t
|
||||
}
|
||||
|
||||
// Skip schema lookup on this keyspace if there are no serving tablets.
|
||||
if kt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := c.Vtctld.Dial(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, err := c.GetSchema(ctx, &vtctldatapb.GetSchemaRequest{}, kt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ignore any schemas without table definitions; otherwise we return
|
||||
// a vtadminpb.Schema object with only Cluster and Keyspace defined,
|
||||
// which is not particularly useful.
|
||||
if s == nil || len(s.TableDefinitions) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// GetTablet is part of the vtadminpb.VTAdminServer interface.
|
||||
func (api *API) GetTablet(ctx context.Context, req *vtadminpb.GetTabletRequest) (*vtadminpb.Tablet, error) {
|
||||
span, ctx := trace.NewSpan(ctx, "API.GetTablet")
|
||||
|
@ -911,7 +879,9 @@ func (api *API) VTExplain(ctx context.Context, req *vtadminpb.VTExplainRequest)
|
|||
go func(c *cluster.Cluster) {
|
||||
defer wg.Done()
|
||||
|
||||
res, err := c.GetSchema(ctx, &vtctldatapb.GetSchemaRequest{}, tablet)
|
||||
res, err := c.GetSchema(ctx, req.Keyspace, cluster.GetSchemaOptions{
|
||||
Tablets: []*vtadminpb.Tablet{tablet},
|
||||
})
|
||||
if err != nil {
|
||||
er.RecordError(fmt.Errorf("GetSchema(%s): %w", topoproto.TabletAliasString(tablet.Tablet.Alias), err))
|
||||
return
|
||||
|
@ -963,23 +933,16 @@ func (api *API) VTExplain(ctx context.Context, req *vtadminpb.VTExplainRequest)
|
|||
go func(c *cluster.Cluster) {
|
||||
defer wg.Done()
|
||||
|
||||
span, ctx := trace.NewSpan(ctx, "Cluster.FindAllShardsInKeyspace")
|
||||
defer span.Finish()
|
||||
|
||||
span.Annotate("keyspace", req.Keyspace)
|
||||
cluster.AnnotateSpan(c, span)
|
||||
|
||||
ksm, err := c.Vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{
|
||||
Keyspace: req.Keyspace,
|
||||
shards, err := c.FindAllShardsInKeyspace(ctx, req.Keyspace, cluster.FindAllShardsInKeyspaceOptions{
|
||||
SkipDial: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
er.RecordError(fmt.Errorf("FindAllShardsInKeyspace(%s): %w", req.Keyspace, err))
|
||||
er.RecordError(err)
|
||||
return
|
||||
}
|
||||
|
||||
vtsm := make(map[string]*topodatapb.Shard)
|
||||
for _, s := range ksm.Shards {
|
||||
for _, s := range shards {
|
||||
vtsm[s.Name] = s.Shard
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ func TestFindSchema(t *testing.T) {
|
|||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
|
@ -495,6 +496,7 @@ func TestFindSchema(t *testing.T) {
|
|||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
|
@ -526,6 +528,234 @@ func TestFindSchema(t *testing.T) {
|
|||
assert.Equal(t, tt.expected, resp)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("size aggregation", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c1pb := &vtadminpb.Cluster{
|
||||
Id: "c1",
|
||||
Name: "cluster1",
|
||||
}
|
||||
c2pb := &vtadminpb.Cluster{
|
||||
Id: "c2",
|
||||
Name: "cluster2",
|
||||
}
|
||||
|
||||
c1 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: c1pb,
|
||||
VtctldClient: &vtadmintestutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ks1": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-": {
|
||||
Keyspace: "ks1",
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetKeyspacesResults: struct {
|
||||
Keyspaces []*vtctldatapb.Keyspace
|
||||
Error error
|
||||
}{
|
||||
Keyspaces: []*vtctldatapb.Keyspace{
|
||||
{Name: "testkeyspace"},
|
||||
{Name: "ks1"},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"c1zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"c1zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
c2 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: c2pb,
|
||||
VtctldClient: &vtadmintestutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"ks2": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-": {
|
||||
Keyspace: "ks2",
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetKeyspacesResults: struct {
|
||||
Keyspaces []*vtctldatapb.Keyspace
|
||||
Error error
|
||||
}{
|
||||
Keyspaces: []*vtctldatapb.Keyspace{
|
||||
{
|
||||
Name: "ks2",
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"c2z1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Cluster: c2pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c2z1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "ks2",
|
||||
Shard: "-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
api := NewAPI([]*cluster.Cluster{c1, c2}, grpcserver.Options{}, http.Options{})
|
||||
schema, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{
|
||||
Table: "testtable",
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
})
|
||||
|
||||
expected := &vtadminpb.Schema{
|
||||
Cluster: c1pb,
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"testtable": {
|
||||
RowCount: 10 + 20,
|
||||
DataLength: 100 + 200,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-80": {
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
"80-": {
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if schema != nil {
|
||||
for _, td := range schema.TableDefinitions {
|
||||
// Zero these out because they're non-deterministic and also not
|
||||
// relevant to the final result.
|
||||
td.RowCount = 0
|
||||
td.DataLength = 0
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, schema)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetClusters(t *testing.T) {
|
||||
|
@ -986,6 +1216,7 @@ func TestGetSchema(t *testing.T) {
|
|||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
|
@ -1148,6 +1379,160 @@ func TestGetSchema(t *testing.T) {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("size aggregation", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c1pb := &vtadminpb.Cluster{
|
||||
Id: "c1",
|
||||
Name: "cluster1",
|
||||
}
|
||||
c1 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: c1pb,
|
||||
VtctldClient: &vtadmintestutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"c1zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"c1zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
c2 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c2",
|
||||
Name: "cluster2",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
api := NewAPI([]*cluster.Cluster{c1, c2}, grpcserver.Options{}, http.Options{})
|
||||
schema, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{
|
||||
ClusterId: c1.ID,
|
||||
Keyspace: "testkeyspace",
|
||||
Table: "testtable",
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
})
|
||||
|
||||
expected := &vtadminpb.Schema{
|
||||
Cluster: c1pb,
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"testtable": {
|
||||
RowCount: 10 + 20,
|
||||
DataLength: 100 + 200,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-80": {
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
"80-": {
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if schema != nil {
|
||||
for _, td := range schema.TableDefinitions {
|
||||
// Zero these out because they're non-deterministic and also not
|
||||
// relevant to the final result.
|
||||
td.RowCount = 0
|
||||
td.DataLength = 0
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, schema)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSchemas(t *testing.T) {
|
||||
|
@ -1237,6 +1622,7 @@ func TestGetSchemas(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1336,6 +1722,7 @@ func TestGetSchemas(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
|
@ -1359,6 +1746,7 @@ func TestGetSchemas(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1460,6 +1848,7 @@ func TestGetSchemas(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1655,6 +2044,284 @@ func TestGetSchemas(t *testing.T) {
|
|||
}, vtctlds...)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("size aggregation", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c1pb := &vtadminpb.Cluster{
|
||||
Id: "c1",
|
||||
Name: "cluster1",
|
||||
}
|
||||
c2pb := &vtadminpb.Cluster{
|
||||
Id: "c2",
|
||||
Name: "cluster2",
|
||||
}
|
||||
|
||||
c1 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: c1pb,
|
||||
VtctldClient: &vtadmintestutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Keyspace: "testkeyspace",
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ks1": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-": {
|
||||
Keyspace: "ks1",
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetKeyspacesResults: struct {
|
||||
Keyspaces []*vtctldatapb.Keyspace
|
||||
Error error
|
||||
}{
|
||||
Keyspaces: []*vtctldatapb.Keyspace{
|
||||
{Name: "testkeyspace"},
|
||||
{Name: "ks1"},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"c1zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"c1zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c1zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
c2 := vtadmintestutil.BuildCluster(
|
||||
vtadmintestutil.TestClusterConfig{
|
||||
Cluster: c2pb,
|
||||
VtctldClient: &vtadmintestutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"ks2": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-": {
|
||||
Keyspace: "ks2",
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetKeyspacesResults: struct {
|
||||
Keyspaces []*vtctldatapb.Keyspace
|
||||
Error error
|
||||
}{
|
||||
Keyspaces: []*vtctldatapb.Keyspace{
|
||||
{
|
||||
Name: "ks2",
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"c2z1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "t2",
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
},
|
||||
{
|
||||
Name: "_t2_ghc",
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Cluster: c2pb,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "c2z1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "ks2",
|
||||
Shard: "-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
api := NewAPI([]*cluster.Cluster{c1, c2}, grpcserver.Options{}, http.Options{})
|
||||
resp, err := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
})
|
||||
|
||||
expected := &vtadminpb.GetSchemasResponse{
|
||||
Schemas: []*vtadminpb.Schema{
|
||||
{
|
||||
Cluster: c1pb,
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "testtable",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"testtable": {
|
||||
RowCount: 10 + 20,
|
||||
DataLength: 100 + 200,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-80": {
|
||||
RowCount: 10,
|
||||
DataLength: 100,
|
||||
},
|
||||
"80-": {
|
||||
RowCount: 20,
|
||||
DataLength: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Cluster: c2pb,
|
||||
Keyspace: "ks2",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{Name: "t2"},
|
||||
{Name: "_t2_ghc"},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"t2": {
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-": {
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
"_t2_ghc": {
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-": {
|
||||
DataLength: 5,
|
||||
RowCount: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
for _, schema := range resp.Schemas {
|
||||
for _, td := range schema.TableDefinitions {
|
||||
// Zero these out because they're non-deterministic and also not
|
||||
// relevant to the final result.
|
||||
td.RowCount = 0
|
||||
td.DataLength = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, expected.Schemas, resp.Schemas)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTablet(t *testing.T) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -38,6 +39,7 @@ import (
|
|||
"vitess.io/vitess/go/vt/vtadmin/vtsql"
|
||||
|
||||
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
|
||||
"vitess.io/vitess/go/vt/proto/vtadmin"
|
||||
vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
|
||||
vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
|
||||
)
|
||||
|
@ -204,6 +206,42 @@ func (c *Cluster) parseTablet(rows *sql.Rows) (*vtadminpb.Tablet, error) {
|
|||
return tablet, nil
|
||||
}
|
||||
|
||||
// FindAllShardsInKeyspaceOptions modify the behavior of a cluster's
|
||||
// FindAllShardsInKeyspace method.
|
||||
type FindAllShardsInKeyspaceOptions struct {
|
||||
// SkipDial indicates that the cluster can assume the vtctldclient has
|
||||
// already dialed up a connection to a vtctld.
|
||||
SkipDial bool
|
||||
}
|
||||
|
||||
// FindAllShardsInKeyspace proxies a FindAllShardsInKeyspace RPC to a cluster's
|
||||
// vtctld, unpacking the response struct.
|
||||
//
|
||||
// It can also optionally ensure the vtctldclient has a valid connection before
|
||||
// making the RPC call.
|
||||
func (c *Cluster) FindAllShardsInKeyspace(ctx context.Context, keyspace string, opts FindAllShardsInKeyspaceOptions) (map[string]*vtctldatapb.Shard, error) {
|
||||
span, ctx := trace.NewSpan(ctx, "Cluster.FindAllShardsInKeyspace")
|
||||
defer span.Finish()
|
||||
|
||||
AnnotateSpan(c, span)
|
||||
span.Annotate("keyspace", keyspace)
|
||||
|
||||
if !opts.SkipDial {
|
||||
if err := c.Vtctld.Dial(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to Dial vtctld for cluster = %s for FindAllShardsInKeyspace: %w", c.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := c.Vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{
|
||||
Keyspace: keyspace,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FindAllShardsInKeyspace(cluster = %s, keyspace = %s) failed: %w", c.ID, keyspace, err)
|
||||
}
|
||||
|
||||
return resp.Shards, nil
|
||||
}
|
||||
|
||||
// FindWorkflowsOptions is the set of options for FindWorkflows requests.
|
||||
type FindWorkflowsOptions struct {
|
||||
ActiveOnly bool
|
||||
|
@ -373,50 +411,260 @@ func (c *Cluster) getTablets(ctx context.Context) ([]*vtadminpb.Tablet, error) {
|
|||
return c.parseTablets(rows)
|
||||
}
|
||||
|
||||
// GetSchema returns the schema for a GetSchemaRequest on the given tablet. The
|
||||
// caller is responsible for making at least one call to c.Vtctld.Dial prior to
|
||||
// calling this function.
|
||||
// GetSchemaOptions contains the options that modify the behavior of the
|
||||
// (*Cluster).GetSchema method.
|
||||
type GetSchemaOptions struct {
|
||||
// Tablets is the starting set of tablets that GetSchema will filter to find
|
||||
// suitable tablet(s) to make GetSchema RPC(s) to.
|
||||
//
|
||||
// If empty, GetSchema will first call (*Cluster).FindTablets() to fetch all
|
||||
// tablets for the keyspace.
|
||||
Tablets []*vtadminpb.Tablet
|
||||
// BaseRequest is used to share some common parameters to use for the
|
||||
// individual tablet GetSchema RPCs made by (*Cluster).GetSchema, which
|
||||
// takes a copy of this request in order to makeb certain overrides as
|
||||
// needed, so these mutations are transparent to the caller.
|
||||
//
|
||||
// The TabletAlias field is ignored completely by (*Cluster).GetSchema, as
|
||||
// it is overwritten for each tablet RPC that method makes.
|
||||
//
|
||||
// The TableSizesOnly field is overwritten only in certain tablet RPCs when
|
||||
// SizeOpts.AggregateSizes is true. In order to move minimal bytes over the
|
||||
// wire, we assume that schema definitions match across all shards, so we
|
||||
// can get the full schema from just one tablet, and then just the table
|
||||
// size information from the other N-1 tablets.
|
||||
//
|
||||
// The TableNamesOnly field is untouched by (*Cluster).GetSchema when not
|
||||
// doing size aggregation. However, when doing size aggregation, if
|
||||
// TableNamesOnly is true, we log a warning and override it. This is because
|
||||
// TableNamesOnly is mutually exclusive with TableSizesOnly, and size
|
||||
// aggregation requires setting TableSizesOnly in the cases described above.
|
||||
BaseRequest *vtctldatapb.GetSchemaRequest
|
||||
// TableSizeOptions control whether the (*Cluster).GetSchema method performs
|
||||
// cross-shard table size aggregation (via the AggregateSizes field).
|
||||
//
|
||||
// If the AggregateSizes field is false, the rest of this struct is ignored,
|
||||
// no size aggregation is done, and (*Cluster).GetSchema will make exactly
|
||||
// one GetSchema RPC to a SERVING tablet in the keyspace.
|
||||
//
|
||||
// If the AggregateSizes field is true, (*Cluster).GetSchema will make a
|
||||
// FindAllShardsInKeyspace vtctld RPC, and then filter the given Tablets
|
||||
// (described above) to find one SERVING tablet for each shard in the
|
||||
// keyspace, skipping any non-serving shards in the keyspace.
|
||||
TableSizeOptions *vtadminpb.GetSchemaTableSizeOptions
|
||||
}
|
||||
|
||||
// GetSchema returns the schema for a given keyspace. GetSchema has a few
|
||||
// different behaviors depending on the GetSchemaOptions provided, as follows:
|
||||
//
|
||||
// Note that the request's TabletAlias field will be ignored, using the provided
|
||||
// tablet's Alias instead. This override is done on a copy of the request, so it
|
||||
// is transparent to the caller.
|
||||
// (1) If opts.Tablets is empty, we will first use FindTablets to fetch all
|
||||
// tablets for the keyspace, regardless of their serving state. Additional
|
||||
// filtering of either this set, or the provided Tablets, will happen later.
|
||||
//
|
||||
// This function takes both the request argument and tablet argument to
|
||||
// (a) set the Keyspace field on the resulting Schema object, which comes from
|
||||
// the provided tablet; and, (b) allow a caller, like vtadmin.API to collect a
|
||||
// bunch of tablets once and make a series of GetSchema calls without Cluster
|
||||
// refetching the tablet list each time.
|
||||
func (c *Cluster) GetSchema(ctx context.Context, req *vtctldatapb.GetSchemaRequest, tablet *vtadminpb.Tablet) (*vtadminpb.Schema, error) {
|
||||
// (2) If opts.SizeOpts.AggregateSizes is true, we will also make a call to
|
||||
// FindAllShardsInKeyspace, in order to fan out GetSchema RPCs to a tablet in
|
||||
// each shard. If this option is false, we make exactly one GetSchema request to
|
||||
// a single, randomly-chosen, tablet in the keyspace.
|
||||
//
|
||||
// (3) We will only make GetSchema RPCs to tablets that are in SERVING state; we
|
||||
// don't want to use a tablet that might be in a bad state as the source of
|
||||
// truth for a schema. Therefore if we can't find a SERVING tablet for the
|
||||
// keyspace (in non-aggregation mode) or for a shard in that keyspace (in
|
||||
// aggregation mode), then we will return an error back to the caller.
|
||||
func (c *Cluster) GetSchema(ctx context.Context, keyspace string, opts GetSchemaOptions) (*vtadminpb.Schema, error) {
|
||||
span, ctx := trace.NewSpan(ctx, "Cluster.GetSchema")
|
||||
defer span.Finish()
|
||||
|
||||
if opts.TableSizeOptions == nil {
|
||||
opts.TableSizeOptions = &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: false,
|
||||
}
|
||||
}
|
||||
|
||||
if opts.BaseRequest == nil {
|
||||
opts.BaseRequest = &vtctldatapb.GetSchemaRequest{}
|
||||
}
|
||||
|
||||
if opts.TableSizeOptions.AggregateSizes && opts.BaseRequest.TableNamesOnly {
|
||||
log.Warningf("GetSchema(cluster = %s) size aggregation is incompatible with TableNamesOnly, ignoring the latter in favor of aggregating sizes", c.ID)
|
||||
opts.BaseRequest.TableNamesOnly = false
|
||||
}
|
||||
|
||||
AnnotateSpan(c, span)
|
||||
span.Annotate("keyspace", keyspace)
|
||||
annotateGetSchemaRequest(opts.BaseRequest, span)
|
||||
vtadminproto.AnnotateSpanWithGetSchemaTableSizeOptions(opts.TableSizeOptions, span)
|
||||
|
||||
// Copy the request to not mutate the caller's request object.
|
||||
r := *req
|
||||
r.TabletAlias = tablet.Tablet.Alias
|
||||
if len(opts.Tablets) == 0 {
|
||||
// Fetch all tablets for the keyspace.
|
||||
var err error
|
||||
|
||||
span.Annotate("tablet_alias", topoproto.TabletAliasString(r.TabletAlias))
|
||||
span.Annotate("exclude_tables", strings.Join(r.ExcludeTables, ","))
|
||||
span.Annotate("tables", strings.Join(r.Tables, ","))
|
||||
span.Annotate("include_views", r.IncludeViews)
|
||||
span.Annotate("table_names_only", r.TableNamesOnly)
|
||||
span.Annotate("table_sizes_only", r.TableSizesOnly)
|
||||
opts.Tablets, err = c.FindTablets(ctx, func(tablet *vtadminpb.Tablet) bool {
|
||||
return tablet.Tablet.Keyspace == keyspace
|
||||
}, -1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w for keyspace %s", errors.ErrNoTablet, keyspace)
|
||||
}
|
||||
}
|
||||
|
||||
schema, err := c.Vtctld.GetSchema(ctx, &r)
|
||||
if err := c.Vtctld.Dial(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to Dial vtctld for cluster = %s for GetSchema: %w", c.ID, err)
|
||||
}
|
||||
|
||||
tabletsToQuery, err := c.getTabletsToQueryForSchemas(ctx, keyspace, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if schema == nil || schema.Schema == nil {
|
||||
return nil, nil
|
||||
return c.getSchemaFromTablets(ctx, keyspace, tabletsToQuery, opts)
|
||||
}
|
||||
|
||||
// Note that for this function we use the tablets parameter, ignoring the
|
||||
// opts.Tablets value completely.
|
||||
func (c *Cluster) getSchemaFromTablets(ctx context.Context, keyspace string, tablets []*vtadminpb.Tablet, opts GetSchemaOptions) (*vtadminpb.Schema, error) {
|
||||
var (
|
||||
m sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
rec concurrency.AllErrorRecorder
|
||||
schema = &vtadminpb.Schema{
|
||||
Cluster: c.ToProto(),
|
||||
Keyspace: keyspace,
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
}
|
||||
// Instead of starting at false, we start with whatever the base request
|
||||
// specified. If we have exactly one tablet to query (i.e. we're not
|
||||
// doing multi-shard aggregation), it's possible the request was to
|
||||
// literally just get the table sizes; we shouldn't assume. If we have
|
||||
// more than one tablet to query, then we are doing size aggregation,
|
||||
// and we'll flip this to true after spawning the first GetSchema rpc.
|
||||
sizesOnly = opts.BaseRequest.TableSizesOnly
|
||||
)
|
||||
|
||||
for _, tablet := range tablets {
|
||||
wg.Add(1)
|
||||
|
||||
go func(tablet *vtadminpb.Tablet, sizesOnly bool) {
|
||||
defer wg.Done()
|
||||
|
||||
span, ctx := trace.NewSpan(ctx, "Vtctld.GetSchema")
|
||||
defer span.Finish()
|
||||
|
||||
req := *opts.BaseRequest
|
||||
req.TableSizesOnly = sizesOnly
|
||||
req.TabletAlias = tablet.Tablet.Alias
|
||||
|
||||
AnnotateSpan(c, span)
|
||||
annotateGetSchemaRequest(&req, span)
|
||||
span.Annotate("keyspace", keyspace)
|
||||
span.Annotate("shard", tablet.Tablet.Shard)
|
||||
|
||||
resp, err := c.Vtctld.GetSchema(ctx, &req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("GetSchema(cluster = %s, keyspace = %s, tablet = %s) failed: %w", c.ID, keyspace, tablet.Tablet.Alias, err)
|
||||
rec.RecordError(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if resp == nil || resp.Schema == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if !sizesOnly {
|
||||
schema.TableDefinitions = resp.Schema.TableDefinitions
|
||||
}
|
||||
|
||||
if !opts.TableSizeOptions.AggregateSizes {
|
||||
return
|
||||
}
|
||||
|
||||
for _, td := range resp.Schema.TableDefinitions {
|
||||
tableSize, ok := schema.TableSizes[td.Name]
|
||||
if !ok {
|
||||
tableSize = &vtadminpb.Schema_TableSize{
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{},
|
||||
}
|
||||
schema.TableSizes[td.Name] = tableSize
|
||||
}
|
||||
|
||||
if _, ok = tableSize.ByShard[tablet.Tablet.Shard]; ok {
|
||||
err := fmt.Errorf("duplicate shard queries for table %s on shard %s/%s", td.Name, keyspace, tablet.Tablet.Shard)
|
||||
log.Warningf("Impossible: %s", err)
|
||||
rec.RecordError(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
tableSize.RowCount += td.RowCount
|
||||
tableSize.DataLength += td.DataLength
|
||||
|
||||
tableSize.ByShard[tablet.Tablet.Shard] = &vtadminpb.Schema_ShardTableSize{
|
||||
RowCount: td.RowCount,
|
||||
DataLength: td.DataLength,
|
||||
}
|
||||
}
|
||||
}(tablet, sizesOnly)
|
||||
|
||||
// If we have more than one tablet to query, we definitely don't want to
|
||||
// get more than the sizes twice, so invariably set this to true for
|
||||
// subsequent iterations
|
||||
sizesOnly = true
|
||||
}
|
||||
|
||||
return &vtadminpb.Schema{
|
||||
Cluster: c.ToProto(),
|
||||
Keyspace: tablet.Tablet.Keyspace,
|
||||
TableDefinitions: schema.Schema.TableDefinitions,
|
||||
}, nil
|
||||
wg.Wait()
|
||||
|
||||
if rec.HasErrors() {
|
||||
return nil, rec.Error()
|
||||
}
|
||||
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
func (c *Cluster) getTabletsToQueryForSchemas(ctx context.Context, keyspace string, opts GetSchemaOptions) ([]*vtadminpb.Tablet, error) {
|
||||
if opts.TableSizeOptions.AggregateSizes {
|
||||
shards, err := c.FindAllShardsInKeyspace(ctx, keyspace, FindAllShardsInKeyspaceOptions{SkipDial: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tabletsToQuery := make([]*vtadminpb.Tablet, 0, len(shards))
|
||||
|
||||
for _, shard := range shards {
|
||||
if !shard.Shard.IsMasterServing {
|
||||
log.Infof("%s/%s is not serving; ignoring because IncludeNonServingShards=false", keyspace, shard.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
shardTablets := vtadminproto.FilterTablets(func(tablet *vtadminpb.Tablet) bool {
|
||||
return tablet.Tablet.Shard == shard.Name && tablet.State == vtadminpb.Tablet_SERVING
|
||||
}, opts.Tablets, len(opts.Tablets))
|
||||
|
||||
if len(shardTablets) == 0 {
|
||||
return nil, fmt.Errorf("%w for shard %s/%s", errors.ErrNoServingTablet, shard.Keyspace, shard.Name)
|
||||
}
|
||||
|
||||
randomServingTablet := shardTablets[rand.Intn(len(shardTablets))]
|
||||
tabletsToQuery = append(tabletsToQuery, randomServingTablet)
|
||||
}
|
||||
|
||||
return tabletsToQuery, nil
|
||||
}
|
||||
|
||||
keyspaceTablets := vtadminproto.FilterTablets(func(tablet *vtadminpb.Tablet) bool {
|
||||
return tablet.Tablet.Keyspace == keyspace && tablet.State == vtadminpb.Tablet_SERVING
|
||||
}, opts.Tablets, len(opts.Tablets))
|
||||
|
||||
if len(keyspaceTablets) == 0 {
|
||||
err := fmt.Errorf("%w for keyspace %s", errors.ErrNoServingTablet, keyspace)
|
||||
log.Warningf("%s. Searched tablets: %v", err, vtadminproto.Tablets(opts.Tablets).AliasStringList())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
randomServingTablet := keyspaceTablets[rand.Intn(len(keyspaceTablets))]
|
||||
return []*vtadmin.Tablet{randomServingTablet}, nil
|
||||
}
|
||||
|
||||
// GetVSchema returns the vschema for a given keyspace in this cluster. The
|
||||
|
@ -568,16 +816,5 @@ func (c *Cluster) findTablets(ctx context.Context, filter func(*vtadminpb.Tablet
|
|||
span.Annotate("max_result_length", n) // this is a bad name; I didn't want just "n", but it's more like, "requested result length".
|
||||
}
|
||||
|
||||
results := make([]*vtadminpb.Tablet, 0, n)
|
||||
for _, t := range tablets {
|
||||
if len(results) >= n {
|
||||
break
|
||||
}
|
||||
|
||||
if filter(t) {
|
||||
results = append(results, t)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
return vtadminproto.FilterTablets(filter, tablets, n), nil
|
||||
}
|
||||
|
|
|
@ -404,6 +404,7 @@ func TestGetSchema(t *testing.T) {
|
|||
},
|
||||
req: &vtctldatapb.GetSchemaRequest{},
|
||||
tablet: &vtadminpb.Tablet{
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
|
@ -423,6 +424,7 @@ func TestGetSchema(t *testing.T) {
|
|||
Name: "some_table",
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
|
@ -440,6 +442,7 @@ func TestGetSchema(t *testing.T) {
|
|||
},
|
||||
req: &vtctldatapb.GetSchemaRequest{},
|
||||
tablet: &vtadminpb.Tablet{
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
|
@ -468,6 +471,7 @@ func TestGetSchema(t *testing.T) {
|
|||
},
|
||||
req: &vtctldatapb.GetSchemaRequest{},
|
||||
tablet: &vtadminpb.Tablet{
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
|
@ -476,7 +480,15 @@ func TestGetSchema(t *testing.T) {
|
|||
Keyspace: "testkeyspace",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expected: &vtadminpb.Schema{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c2",
|
||||
Name: "cluster2",
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: nil,
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
}
|
||||
|
@ -490,7 +502,7 @@ func TestGetSchema(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cluster := testutil.BuildCluster(testutil.TestClusterConfig{
|
||||
c := testutil.BuildCluster(testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: fmt.Sprintf("c%d", i),
|
||||
Name: fmt.Sprintf("cluster%d", i),
|
||||
|
@ -500,10 +512,13 @@ func TestGetSchema(t *testing.T) {
|
|||
DBConfig: testutil.Dbcfg{},
|
||||
})
|
||||
|
||||
err := cluster.Vtctld.Dial(ctx)
|
||||
err := c.Vtctld.Dial(ctx)
|
||||
require.NoError(t, err, "could not dial test vtctld")
|
||||
|
||||
schema, err := cluster.GetSchema(ctx, tt.req, tt.tablet)
|
||||
schema, err := c.GetSchema(ctx, "testkeyspace", cluster.GetSchemaOptions{
|
||||
Tablets: []*vtadminpb.Tablet{tt.tablet},
|
||||
BaseRequest: tt.req,
|
||||
})
|
||||
if tt.shouldErr {
|
||||
assert.Error(t, err)
|
||||
|
||||
|
@ -544,7 +559,7 @@ func TestGetSchema(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
cluster := testutil.BuildCluster(testutil.TestClusterConfig{
|
||||
c := testutil.BuildCluster(testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
|
@ -552,13 +567,630 @@ func TestGetSchema(t *testing.T) {
|
|||
VtctldClient: vtctld,
|
||||
})
|
||||
|
||||
err := cluster.Vtctld.Dial(ctx)
|
||||
err := c.Vtctld.Dial(ctx)
|
||||
require.NoError(t, err, "could not dial test vtctld")
|
||||
|
||||
cluster.GetSchema(ctx, req, tablet)
|
||||
c.GetSchema(ctx, "testkeyspace", cluster.GetSchemaOptions{
|
||||
BaseRequest: req,
|
||||
Tablets: []*vtadminpb.Tablet{tablet},
|
||||
})
|
||||
|
||||
assert.NotEqual(t, req.TabletAlias, tablet.Tablet.Alias, "expected GetSchema to not modify original request object")
|
||||
})
|
||||
|
||||
t.Run("size aggregation", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg testutil.TestClusterConfig
|
||||
keyspace string
|
||||
opts cluster.GetSchemaOptions
|
||||
expected *vtadminpb.Schema
|
||||
shouldErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
cfg: testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
VtctldClient: &testutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"-": {
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keyspace: "testkeyspace",
|
||||
opts: cluster.GetSchemaOptions{
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
},
|
||||
expected: &vtadminpb.Schema{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 0,
|
||||
RowCount: 0,
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"foo": {
|
||||
DataLength: 100 + 200,
|
||||
RowCount: 5 + 420,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-80": {
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
"80-": {
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
{
|
||||
name: "no serving tablets found for shard",
|
||||
cfg: testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_NOT_SERVING,
|
||||
},
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
VtctldClient: &testutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"-": {
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keyspace: "testkeyspace",
|
||||
opts: cluster.GetSchemaOptions{
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
shouldErr: true,
|
||||
},
|
||||
{
|
||||
name: "ignore TableNamesOnly",
|
||||
cfg: testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
VtctldClient: &testutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"-": {
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keyspace: "testkeyspace",
|
||||
opts: cluster.GetSchemaOptions{
|
||||
BaseRequest: &vtctldatapb.GetSchemaRequest{
|
||||
TableNamesOnly: true, // Just checking things to blow up if this gets set.
|
||||
},
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
},
|
||||
expected: &vtadminpb.Schema{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 0,
|
||||
RowCount: 0,
|
||||
},
|
||||
},
|
||||
TableSizes: map[string]*vtadminpb.Schema_TableSize{
|
||||
"foo": {
|
||||
DataLength: 100 + 200,
|
||||
RowCount: 5 + 420,
|
||||
ByShard: map[string]*vtadminpb.Schema_ShardTableSize{
|
||||
"-80": {
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
"80-": {
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldErr: false,
|
||||
},
|
||||
{
|
||||
name: "single GetSchema error fails the request",
|
||||
cfg: testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
VtctldClient: &testutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{
|
||||
Shards: map[string]*vtctldatapb.Shard{
|
||||
"-80": {
|
||||
Name: "-80",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"80-": {
|
||||
Name: "80-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: true,
|
||||
},
|
||||
},
|
||||
"-": {
|
||||
Name: "-",
|
||||
Shard: &topodatapb.Shard{
|
||||
IsMasterServing: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"zone1-0000000200": {
|
||||
Error: assert.AnError,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keyspace: "testkeyspace",
|
||||
opts: cluster.GetSchemaOptions{
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
shouldErr: true,
|
||||
},
|
||||
{
|
||||
name: "FindAllShardsInKeyspace error",
|
||||
cfg: testutil.TestClusterConfig{
|
||||
Cluster: &vtadminpb.Cluster{
|
||||
Id: "c0",
|
||||
Name: "cluster0",
|
||||
},
|
||||
Tablets: []*vtadminpb.Tablet{
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 100,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "-80",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
{
|
||||
Tablet: &topodatapb.Tablet{
|
||||
Alias: &topodatapb.TabletAlias{
|
||||
Cell: "zone1",
|
||||
Uid: 200,
|
||||
},
|
||||
Keyspace: "testkeyspace",
|
||||
Shard: "80-",
|
||||
},
|
||||
State: vtadminpb.Tablet_SERVING,
|
||||
},
|
||||
},
|
||||
VtctldClient: &testutil.VtctldClient{
|
||||
FindAllShardsInKeyspaceResults: map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}{
|
||||
"testkeyspace": {
|
||||
Error: assert.AnError,
|
||||
},
|
||||
},
|
||||
GetSchemaResults: map[string]struct {
|
||||
Response *vtctldatapb.GetSchemaResponse
|
||||
Error error
|
||||
}{
|
||||
"zone1-0000000100": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 100,
|
||||
RowCount: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"zone1-0000000200": {
|
||||
Response: &vtctldatapb.GetSchemaResponse{
|
||||
Schema: &tabletmanagerdatapb.SchemaDefinition{
|
||||
DatabaseSchema: "CREATE DATABASE vt_testkeyspcae",
|
||||
TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
|
||||
{
|
||||
Name: "foo",
|
||||
Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB",
|
||||
DataLength: 200,
|
||||
RowCount: 420,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keyspace: "testkeyspace",
|
||||
opts: cluster.GetSchemaOptions{
|
||||
TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: true,
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
shouldErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if tt.keyspace == "" {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
c := testutil.BuildCluster(tt.cfg)
|
||||
schema, err := c.GetSchema(ctx, tt.keyspace, tt.opts)
|
||||
if tt.shouldErr {
|
||||
assert.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if schema.TableDefinitions != nil {
|
||||
// For simplicity, we're going to assert only on the state
|
||||
// of the aggregated sizes (in schema.TableSizes), since the
|
||||
// TableDefinitions size values depends on tablet iteration
|
||||
// order, and that's not something we're interested in
|
||||
// coupling the implementation to.
|
||||
for _, td := range schema.TableDefinitions {
|
||||
td.DataLength = 0
|
||||
td.RowCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
testutil.AssertSchemaSlicesEqual(t, []*vtadminpb.Schema{tt.expected}, []*vtadminpb.Schema{schema})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestFindWorkflows(t *testing.T) {
|
||||
|
|
|
@ -17,8 +17,13 @@ limitations under the License.
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"vitess.io/vitess/go/trace"
|
||||
"vitess.io/vitess/go/vt/topo/topoproto"
|
||||
"vitess.io/vitess/go/vt/vtadmin/vtadminproto"
|
||||
|
||||
vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
|
||||
)
|
||||
|
||||
// AnnotateSpan adds the cluster_id and cluster_name to a span.
|
||||
|
@ -27,3 +32,16 @@ func AnnotateSpan(c *Cluster, span trace.Span) {
|
|||
// (TODO:@ajm188) add support for discovery impls to add annotations to a
|
||||
// span, like `discovery_impl` and any parameters that might be relevant.
|
||||
}
|
||||
|
||||
// (TODO: @ajm188) perhaps we want a ./go/vt/vtctl/vtctlproto package for this?
|
||||
func annotateGetSchemaRequest(req *vtctldatapb.GetSchemaRequest, span trace.Span) {
|
||||
if req.TabletAlias != nil {
|
||||
span.Annotate("tablet_alias", topoproto.TabletAliasString(req.TabletAlias))
|
||||
}
|
||||
|
||||
span.Annotate("exclude_tables", strings.Join(req.ExcludeTables, ","))
|
||||
span.Annotate("tables", strings.Join(req.Tables, ","))
|
||||
span.Annotate("include_views", req.IncludeViews)
|
||||
span.Annotate("table_names_only", req.TableNamesOnly)
|
||||
span.Annotate("table_sizes_only", req.TableSizesOnly)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ limitations under the License.
|
|||
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAmbiguousSchema occurs when more than one schema is found for a given
|
||||
|
@ -35,6 +38,10 @@ var (
|
|||
// ErrNoSchema occurs when a schema definition cannot be found for a given
|
||||
// set of filter criteria.
|
||||
ErrNoSchema = errors.New("no such schema")
|
||||
// ErrNoServingTablet occurs when a tablet with state SERVING cannot be
|
||||
// found for a given set of filter criteria. It is a more specific form of
|
||||
// ErrNoTablet
|
||||
ErrNoServingTablet = fmt.Errorf("%w with state=SERVING", ErrNoTablet)
|
||||
// ErrNoSrvVSchema occurs when no SrvVSchema is found for a given keyspace.
|
||||
ErrNoSrvVSchema = errors.New("SrvVSchema not found")
|
||||
// ErrNoTablet occurs when a tablet cannot be found for a given set of
|
||||
|
|
|
@ -28,9 +28,15 @@ func FindSchema(ctx context.Context, r Request, api *API) *JSONResponse {
|
|||
vars := r.Vars()
|
||||
query := r.URL.Query()
|
||||
|
||||
sizeOpts, err := getTableSizeOpts(r)
|
||||
if err != nil {
|
||||
return NewJSONResponse(nil, err)
|
||||
}
|
||||
|
||||
schema, err := api.server.FindSchema(ctx, &vtadminpb.FindSchemaRequest{
|
||||
Table: vars["table"],
|
||||
ClusterIds: query["cluster"],
|
||||
Table: vars["table"],
|
||||
ClusterIds: query["cluster"],
|
||||
TableSizeOptions: sizeOpts,
|
||||
})
|
||||
|
||||
return NewJSONResponse(schema, err)
|
||||
|
@ -41,10 +47,16 @@ func FindSchema(ctx context.Context, r Request, api *API) *JSONResponse {
|
|||
func GetSchema(ctx context.Context, r Request, api *API) *JSONResponse {
|
||||
vars := r.Vars()
|
||||
|
||||
sizeOpts, err := getTableSizeOpts(r)
|
||||
if err != nil {
|
||||
return NewJSONResponse(nil, err)
|
||||
}
|
||||
|
||||
schema, err := api.server.GetSchema(ctx, &vtadminpb.GetSchemaRequest{
|
||||
ClusterId: vars["cluster_id"],
|
||||
Keyspace: vars["keyspace"],
|
||||
Table: vars["table"],
|
||||
ClusterId: vars["cluster_id"],
|
||||
Keyspace: vars["keyspace"],
|
||||
Table: vars["table"],
|
||||
TableSizeOptions: sizeOpts,
|
||||
})
|
||||
|
||||
return NewJSONResponse(schema, err)
|
||||
|
@ -53,9 +65,26 @@ func GetSchema(ctx context.Context, r Request, api *API) *JSONResponse {
|
|||
// GetSchemas implements the http wrapper for the /schemas[?cluster=[&cluster=]
|
||||
// route.
|
||||
func GetSchemas(ctx context.Context, r Request, api *API) *JSONResponse {
|
||||
sizeOpts, err := getTableSizeOpts(r)
|
||||
if err != nil {
|
||||
return NewJSONResponse(nil, err)
|
||||
}
|
||||
|
||||
schemas, err := api.server.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{
|
||||
ClusterIds: r.URL.Query()["cluster"],
|
||||
ClusterIds: r.URL.Query()["cluster"],
|
||||
TableSizeOptions: sizeOpts,
|
||||
})
|
||||
|
||||
return NewJSONResponse(schemas, err)
|
||||
}
|
||||
|
||||
func getTableSizeOpts(r Request) (*vtadminpb.GetSchemaTableSizeOptions, error) {
|
||||
aggregateSizes, err := r.ParseQueryParamAsBool("aggregate_sizes", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vtadminpb.GetSchemaTableSizeOptions{
|
||||
AggregateSizes: aggregateSizes,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ import (
|
|||
type VtctldClient struct {
|
||||
vtctldclient.VtctldClient
|
||||
|
||||
FindAllShardsInKeyspaceResults map[string]struct {
|
||||
Response *vtctldatapb.FindAllShardsInKeyspaceResponse
|
||||
Error error
|
||||
}
|
||||
GetKeyspacesResults struct {
|
||||
Keyspaces []*vtctldatapb.Keyspace
|
||||
Error error
|
||||
|
@ -56,6 +60,19 @@ type VtctldClient struct {
|
|||
// incorrectly.
|
||||
var _ vtctldclient.VtctldClient = (*VtctldClient)(nil)
|
||||
|
||||
// FindAllShardsInKeyspace is part of the vtctldclient.VtctldClient interface.
|
||||
func (fake *VtctldClient) FindAllShardsInKeyspace(ctx context.Context, req *vtctldatapb.FindAllShardsInKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.FindAllShardsInKeyspaceResponse, error) {
|
||||
if fake.FindAllShardsInKeyspaceResults == nil {
|
||||
return nil, fmt.Errorf("%w: FindAllShardsInKeyspaceResults not set on fake vtctldclient", assert.AnError)
|
||||
}
|
||||
|
||||
if result, ok := fake.FindAllShardsInKeyspaceResults[req.Keyspace]; ok {
|
||||
return result.Response, result.Error
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: no result set for keyspace %s", assert.AnError, req.Keyspace)
|
||||
}
|
||||
|
||||
// GetKeyspaces is part of the vtctldclient.VtctldClient interface.
|
||||
func (fake *VtctldClient) GetKeyspaces(ctx context.Context, req *vtctldatapb.GetKeyspacesRequest, opts ...grpc.CallOption) (*vtctldatapb.GetKeyspacesResponse, error) {
|
||||
if fake.GetKeyspacesResults.Error != nil {
|
||||
|
|
|
@ -16,7 +16,50 @@ limitations under the License.
|
|||
|
||||
package vtadminproto
|
||||
|
||||
import vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
|
||||
import (
|
||||
"vitess.io/vitess/go/vt/topo/topoproto"
|
||||
|
||||
vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
|
||||
)
|
||||
|
||||
// Tablets is a list of Tablet protobuf objects.
|
||||
type Tablets []*vtadminpb.Tablet
|
||||
|
||||
// AliasStringList returns a list of TabletAlias strings for each tablet in the
|
||||
// list.
|
||||
func (tablets Tablets) AliasStringList() []string {
|
||||
aliases := make([]string, len(tablets))
|
||||
|
||||
for i, tablet := range tablets {
|
||||
aliases[i] = topoproto.TabletAliasString(tablet.Tablet.Alias)
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
// FilterTablets returns a subset of tablets (not exceeding maxResults) that
|
||||
// satisfy the given condition.
|
||||
//
|
||||
// If maxResults is negative, len(tablets) is used instead.
|
||||
func FilterTablets(condition func(tablet *vtadminpb.Tablet) bool, tablets []*vtadminpb.Tablet, maxResults int) []*vtadminpb.Tablet {
|
||||
if maxResults < 0 {
|
||||
maxResults = len(tablets)
|
||||
}
|
||||
|
||||
results := make([]*vtadminpb.Tablet, 0, maxResults)
|
||||
|
||||
for _, tablet := range tablets {
|
||||
if len(results) >= maxResults {
|
||||
break
|
||||
}
|
||||
|
||||
if condition(tablet) {
|
||||
results = append(results, tablet)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// ParseTabletServingState returns a ServingState value from the given string.
|
||||
// If the string does not map to a valid value, this function returns UNKNOWN.
|
||||
|
|
|
@ -27,3 +27,13 @@ func AnnotateClusterSpan(c *vtadminpb.Cluster, span trace.Span) {
|
|||
span.Annotate("cluster_id", c.Id)
|
||||
span.Annotate("cluster_name", c.Name)
|
||||
}
|
||||
|
||||
// AnnotateSpanWithGetSchemaTableSizeOptions adds the aggregate_table_sizes to a
|
||||
// span. It is a noop if the size options object is nil.
|
||||
func AnnotateSpanWithGetSchemaTableSizeOptions(opts *vtadminpb.GetSchemaTableSizeOptions, span trace.Span) {
|
||||
if opts == nil {
|
||||
opts = &vtadminpb.GetSchemaTableSizeOptions{}
|
||||
}
|
||||
|
||||
span.Annotate("aggregate_table_sizes", opts.AggregateSizes)
|
||||
}
|
||||
|
|
|
@ -96,6 +96,21 @@ message Schema {
|
|||
string keyspace = 2;
|
||||
|
||||
repeated tabletmanagerdata.TableDefinition table_definitions = 3;
|
||||
// TableSizes is a mapping of table name to TableSize information.
|
||||
map<string, TableSize> table_sizes = 4;
|
||||
|
||||
message ShardTableSize {
|
||||
uint64 row_count = 1;
|
||||
uint64 data_length = 2;
|
||||
}
|
||||
|
||||
// TableSize aggregates table size information across all shards containing
|
||||
// in the given keyspace and cluster, as well as per-shard size information.
|
||||
message TableSize {
|
||||
uint64 row_count = 1;
|
||||
uint64 data_length = 2;
|
||||
map<string, ShardTableSize> by_shard = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Tablet groups the topo information of a tablet together with the Vitess
|
||||
|
@ -154,6 +169,7 @@ message Workflow {
|
|||
message FindSchemaRequest {
|
||||
string table = 1;
|
||||
repeated string cluster_ids = 2;
|
||||
GetSchemaTableSizeOptions table_size_options = 3;
|
||||
}
|
||||
|
||||
message GetClustersRequest {}
|
||||
|
@ -182,16 +198,22 @@ message GetSchemaRequest {
|
|||
string cluster_id = 1;
|
||||
string keyspace = 2;
|
||||
string table = 3;
|
||||
GetSchemaTableSizeOptions table_size_options = 4;
|
||||
}
|
||||
|
||||
message GetSchemasRequest {
|
||||
repeated string cluster_ids = 1;
|
||||
GetSchemaTableSizeOptions table_size_options = 2;
|
||||
}
|
||||
|
||||
message GetSchemasResponse {
|
||||
repeated Schema schemas = 1;
|
||||
}
|
||||
|
||||
message GetSchemaTableSizeOptions {
|
||||
bool aggregate_sizes = 1;
|
||||
}
|
||||
|
||||
message GetTabletRequest {
|
||||
string hostname = 1;
|
||||
// ClusterIDs is an optional parameter to narrow the scope of the search, if
|
||||
|
|
|
@ -604,6 +604,9 @@ export namespace vtadmin {
|
|||
|
||||
/** Schema table_definitions */
|
||||
table_definitions?: (tabletmanagerdata.ITableDefinition[]|null);
|
||||
|
||||
/** Schema table_sizes */
|
||||
table_sizes?: ({ [k: string]: vtadmin.Schema.ITableSize }|null);
|
||||
}
|
||||
|
||||
/** Represents a Schema. */
|
||||
|
@ -624,6 +627,9 @@ export namespace vtadmin {
|
|||
/** Schema table_definitions. */
|
||||
public table_definitions: tabletmanagerdata.ITableDefinition[];
|
||||
|
||||
/** Schema table_sizes. */
|
||||
public table_sizes: { [k: string]: vtadmin.Schema.ITableSize };
|
||||
|
||||
/**
|
||||
* Creates a new Schema instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
|
@ -695,6 +701,207 @@ export namespace vtadmin {
|
|||
public toJSON(): { [k: string]: any };
|
||||
}
|
||||
|
||||
namespace Schema {
|
||||
|
||||
/** Properties of a ShardTableSize. */
|
||||
interface IShardTableSize {
|
||||
|
||||
/** ShardTableSize row_count */
|
||||
row_count?: (number|Long|null);
|
||||
|
||||
/** ShardTableSize data_length */
|
||||
data_length?: (number|Long|null);
|
||||
}
|
||||
|
||||
/** Represents a ShardTableSize. */
|
||||
class ShardTableSize implements IShardTableSize {
|
||||
|
||||
/**
|
||||
* Constructs a new ShardTableSize.
|
||||
* @param [properties] Properties to set
|
||||
*/
|
||||
constructor(properties?: vtadmin.Schema.IShardTableSize);
|
||||
|
||||
/** ShardTableSize row_count. */
|
||||
public row_count: (number|Long);
|
||||
|
||||
/** ShardTableSize data_length. */
|
||||
public data_length: (number|Long);
|
||||
|
||||
/**
|
||||
* Creates a new ShardTableSize instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
* @returns ShardTableSize instance
|
||||
*/
|
||||
public static create(properties?: vtadmin.Schema.IShardTableSize): vtadmin.Schema.ShardTableSize;
|
||||
|
||||
/**
|
||||
* Encodes the specified ShardTableSize message. Does not implicitly {@link vtadmin.Schema.ShardTableSize.verify|verify} messages.
|
||||
* @param message ShardTableSize message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encode(message: vtadmin.Schema.IShardTableSize, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Encodes the specified ShardTableSize message, length delimited. Does not implicitly {@link vtadmin.Schema.ShardTableSize.verify|verify} messages.
|
||||
* @param message ShardTableSize message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encodeDelimited(message: vtadmin.Schema.IShardTableSize, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Decodes a ShardTableSize message from the specified reader or buffer.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @param [length] Message length if known beforehand
|
||||
* @returns ShardTableSize
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.Schema.ShardTableSize;
|
||||
|
||||
/**
|
||||
* Decodes a ShardTableSize message from the specified reader or buffer, length delimited.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @returns ShardTableSize
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.Schema.ShardTableSize;
|
||||
|
||||
/**
|
||||
* Verifies a ShardTableSize message.
|
||||
* @param message Plain object to verify
|
||||
* @returns `null` if valid, otherwise the reason why it is not
|
||||
*/
|
||||
public static verify(message: { [k: string]: any }): (string|null);
|
||||
|
||||
/**
|
||||
* Creates a ShardTableSize message from a plain object. Also converts values to their respective internal types.
|
||||
* @param object Plain object
|
||||
* @returns ShardTableSize
|
||||
*/
|
||||
public static fromObject(object: { [k: string]: any }): vtadmin.Schema.ShardTableSize;
|
||||
|
||||
/**
|
||||
* Creates a plain object from a ShardTableSize message. Also converts values to other types if specified.
|
||||
* @param message ShardTableSize
|
||||
* @param [options] Conversion options
|
||||
* @returns Plain object
|
||||
*/
|
||||
public static toObject(message: vtadmin.Schema.ShardTableSize, options?: $protobuf.IConversionOptions): { [k: string]: any };
|
||||
|
||||
/**
|
||||
* Converts this ShardTableSize to JSON.
|
||||
* @returns JSON object
|
||||
*/
|
||||
public toJSON(): { [k: string]: any };
|
||||
}
|
||||
|
||||
/** Properties of a TableSize. */
|
||||
interface ITableSize {
|
||||
|
||||
/** TableSize row_count */
|
||||
row_count?: (number|Long|null);
|
||||
|
||||
/** TableSize data_length */
|
||||
data_length?: (number|Long|null);
|
||||
|
||||
/** TableSize by_shard */
|
||||
by_shard?: ({ [k: string]: vtadmin.Schema.IShardTableSize }|null);
|
||||
}
|
||||
|
||||
/** Represents a TableSize. */
|
||||
class TableSize implements ITableSize {
|
||||
|
||||
/**
|
||||
* Constructs a new TableSize.
|
||||
* @param [properties] Properties to set
|
||||
*/
|
||||
constructor(properties?: vtadmin.Schema.ITableSize);
|
||||
|
||||
/** TableSize row_count. */
|
||||
public row_count: (number|Long);
|
||||
|
||||
/** TableSize data_length. */
|
||||
public data_length: (number|Long);
|
||||
|
||||
/** TableSize by_shard. */
|
||||
public by_shard: { [k: string]: vtadmin.Schema.IShardTableSize };
|
||||
|
||||
/**
|
||||
* Creates a new TableSize instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
* @returns TableSize instance
|
||||
*/
|
||||
public static create(properties?: vtadmin.Schema.ITableSize): vtadmin.Schema.TableSize;
|
||||
|
||||
/**
|
||||
* Encodes the specified TableSize message. Does not implicitly {@link vtadmin.Schema.TableSize.verify|verify} messages.
|
||||
* @param message TableSize message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encode(message: vtadmin.Schema.ITableSize, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Encodes the specified TableSize message, length delimited. Does not implicitly {@link vtadmin.Schema.TableSize.verify|verify} messages.
|
||||
* @param message TableSize message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encodeDelimited(message: vtadmin.Schema.ITableSize, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Decodes a TableSize message from the specified reader or buffer.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @param [length] Message length if known beforehand
|
||||
* @returns TableSize
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.Schema.TableSize;
|
||||
|
||||
/**
|
||||
* Decodes a TableSize message from the specified reader or buffer, length delimited.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @returns TableSize
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.Schema.TableSize;
|
||||
|
||||
/**
|
||||
* Verifies a TableSize message.
|
||||
* @param message Plain object to verify
|
||||
* @returns `null` if valid, otherwise the reason why it is not
|
||||
*/
|
||||
public static verify(message: { [k: string]: any }): (string|null);
|
||||
|
||||
/**
|
||||
* Creates a TableSize message from a plain object. Also converts values to their respective internal types.
|
||||
* @param object Plain object
|
||||
* @returns TableSize
|
||||
*/
|
||||
public static fromObject(object: { [k: string]: any }): vtadmin.Schema.TableSize;
|
||||
|
||||
/**
|
||||
* Creates a plain object from a TableSize message. Also converts values to other types if specified.
|
||||
* @param message TableSize
|
||||
* @param [options] Conversion options
|
||||
* @returns Plain object
|
||||
*/
|
||||
public static toObject(message: vtadmin.Schema.TableSize, options?: $protobuf.IConversionOptions): { [k: string]: any };
|
||||
|
||||
/**
|
||||
* Converts this TableSize to JSON.
|
||||
* @returns JSON object
|
||||
*/
|
||||
public toJSON(): { [k: string]: any };
|
||||
}
|
||||
}
|
||||
|
||||
/** Properties of a Tablet. */
|
||||
interface ITablet {
|
||||
|
||||
|
@ -1229,6 +1436,9 @@ export namespace vtadmin {
|
|||
|
||||
/** FindSchemaRequest cluster_ids */
|
||||
cluster_ids?: (string[]|null);
|
||||
|
||||
/** FindSchemaRequest table_size_options */
|
||||
table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
}
|
||||
|
||||
/** Represents a FindSchemaRequest. */
|
||||
|
@ -1246,6 +1456,9 @@ export namespace vtadmin {
|
|||
/** FindSchemaRequest cluster_ids. */
|
||||
public cluster_ids: string[];
|
||||
|
||||
/** FindSchemaRequest table_size_options. */
|
||||
public table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
|
||||
/**
|
||||
* Creates a new FindSchemaRequest instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
|
@ -1862,6 +2075,9 @@ export namespace vtadmin {
|
|||
|
||||
/** GetSchemaRequest table */
|
||||
table?: (string|null);
|
||||
|
||||
/** GetSchemaRequest table_size_options */
|
||||
table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
}
|
||||
|
||||
/** Represents a GetSchemaRequest. */
|
||||
|
@ -1882,6 +2098,9 @@ export namespace vtadmin {
|
|||
/** GetSchemaRequest table. */
|
||||
public table: string;
|
||||
|
||||
/** GetSchemaRequest table_size_options. */
|
||||
public table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
|
||||
/**
|
||||
* Creates a new GetSchemaRequest instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
|
@ -1958,6 +2177,9 @@ export namespace vtadmin {
|
|||
|
||||
/** GetSchemasRequest cluster_ids */
|
||||
cluster_ids?: (string[]|null);
|
||||
|
||||
/** GetSchemasRequest table_size_options */
|
||||
table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
}
|
||||
|
||||
/** Represents a GetSchemasRequest. */
|
||||
|
@ -1972,6 +2194,9 @@ export namespace vtadmin {
|
|||
/** GetSchemasRequest cluster_ids. */
|
||||
public cluster_ids: string[];
|
||||
|
||||
/** GetSchemasRequest table_size_options. */
|
||||
public table_size_options?: (vtadmin.IGetSchemaTableSizeOptions|null);
|
||||
|
||||
/**
|
||||
* Creates a new GetSchemasRequest instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
|
@ -2133,6 +2358,96 @@ export namespace vtadmin {
|
|||
public toJSON(): { [k: string]: any };
|
||||
}
|
||||
|
||||
/** Properties of a GetSchemaTableSizeOptions. */
|
||||
interface IGetSchemaTableSizeOptions {
|
||||
|
||||
/** GetSchemaTableSizeOptions aggregate_sizes */
|
||||
aggregate_sizes?: (boolean|null);
|
||||
}
|
||||
|
||||
/** Represents a GetSchemaTableSizeOptions. */
|
||||
class GetSchemaTableSizeOptions implements IGetSchemaTableSizeOptions {
|
||||
|
||||
/**
|
||||
* Constructs a new GetSchemaTableSizeOptions.
|
||||
* @param [properties] Properties to set
|
||||
*/
|
||||
constructor(properties?: vtadmin.IGetSchemaTableSizeOptions);
|
||||
|
||||
/** GetSchemaTableSizeOptions aggregate_sizes. */
|
||||
public aggregate_sizes: boolean;
|
||||
|
||||
/**
|
||||
* Creates a new GetSchemaTableSizeOptions instance using the specified properties.
|
||||
* @param [properties] Properties to set
|
||||
* @returns GetSchemaTableSizeOptions instance
|
||||
*/
|
||||
public static create(properties?: vtadmin.IGetSchemaTableSizeOptions): vtadmin.GetSchemaTableSizeOptions;
|
||||
|
||||
/**
|
||||
* Encodes the specified GetSchemaTableSizeOptions message. Does not implicitly {@link vtadmin.GetSchemaTableSizeOptions.verify|verify} messages.
|
||||
* @param message GetSchemaTableSizeOptions message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encode(message: vtadmin.IGetSchemaTableSizeOptions, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Encodes the specified GetSchemaTableSizeOptions message, length delimited. Does not implicitly {@link vtadmin.GetSchemaTableSizeOptions.verify|verify} messages.
|
||||
* @param message GetSchemaTableSizeOptions message or plain object to encode
|
||||
* @param [writer] Writer to encode to
|
||||
* @returns Writer
|
||||
*/
|
||||
public static encodeDelimited(message: vtadmin.IGetSchemaTableSizeOptions, writer?: $protobuf.Writer): $protobuf.Writer;
|
||||
|
||||
/**
|
||||
* Decodes a GetSchemaTableSizeOptions message from the specified reader or buffer.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @param [length] Message length if known beforehand
|
||||
* @returns GetSchemaTableSizeOptions
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): vtadmin.GetSchemaTableSizeOptions;
|
||||
|
||||
/**
|
||||
* Decodes a GetSchemaTableSizeOptions message from the specified reader or buffer, length delimited.
|
||||
* @param reader Reader or buffer to decode from
|
||||
* @returns GetSchemaTableSizeOptions
|
||||
* @throws {Error} If the payload is not a reader or valid buffer
|
||||
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
||||
*/
|
||||
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): vtadmin.GetSchemaTableSizeOptions;
|
||||
|
||||
/**
|
||||
* Verifies a GetSchemaTableSizeOptions message.
|
||||
* @param message Plain object to verify
|
||||
* @returns `null` if valid, otherwise the reason why it is not
|
||||
*/
|
||||
public static verify(message: { [k: string]: any }): (string|null);
|
||||
|
||||
/**
|
||||
* Creates a GetSchemaTableSizeOptions message from a plain object. Also converts values to their respective internal types.
|
||||
* @param object Plain object
|
||||
* @returns GetSchemaTableSizeOptions
|
||||
*/
|
||||
public static fromObject(object: { [k: string]: any }): vtadmin.GetSchemaTableSizeOptions;
|
||||
|
||||
/**
|
||||
* Creates a plain object from a GetSchemaTableSizeOptions message. Also converts values to other types if specified.
|
||||
* @param message GetSchemaTableSizeOptions
|
||||
* @param [options] Conversion options
|
||||
* @returns Plain object
|
||||
*/
|
||||
public static toObject(message: vtadmin.GetSchemaTableSizeOptions, options?: $protobuf.IConversionOptions): { [k: string]: any };
|
||||
|
||||
/**
|
||||
* Converts this GetSchemaTableSizeOptions to JSON.
|
||||
* @returns JSON object
|
||||
*/
|
||||
public toJSON(): { [k: string]: any };
|
||||
}
|
||||
|
||||
/** Properties of a GetTabletRequest. */
|
||||
interface IGetTabletRequest {
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче