diff --git a/go/vt/vtorc/inst/shard_dao.go b/go/vt/vtorc/inst/shard_dao.go index 5501ba5d7b9..7fe8df110f9 100644 --- a/go/vt/vtorc/inst/shard_dao.go +++ b/go/vt/vtorc/inst/shard_dao.go @@ -41,18 +41,6 @@ func ReadShardNames(keyspaceName string) (shardNames []string, err error) { return shardNames, err } -// ReadAllShardNames reads the names of all vitess shards by keyspace. -func ReadAllShardNames() (shardNames map[string][]string, err error) { - shardNames = make(map[string][]string) - query := `select keyspace, shard from vitess_shard` - err = db.QueryVTOrc(query, nil, func(row sqlutils.RowMap) error { - ks := row.GetString("keyspace") - shardNames[ks] = append(shardNames[ks], row.GetString("shard")) - return nil - }) - return shardNames, err -} - // ReadShardPrimaryInformation reads the vitess shard record and gets the shard primary alias and timestamp. func ReadShardPrimaryInformation(keyspaceName, shardName string) (primaryAlias string, primaryTimestamp string, err error) { if err = topo.ValidateKeyspaceName(keyspaceName); err != nil { diff --git a/go/vt/vtorc/inst/shard_dao_test.go b/go/vt/vtorc/inst/shard_dao_test.go index 8cf67f10ee6..0077b3d64af 100644 --- a/go/vt/vtorc/inst/shard_dao_test.go +++ b/go/vt/vtorc/inst/shard_dao_test.go @@ -108,13 +108,6 @@ func TestSaveReadAndDeleteShard(t *testing.T) { require.NoError(t, err) require.Equal(t, []string{tt.shardName}, shardNames) - // ReadAllShardNames - allShardNames, err := ReadAllShardNames() - require.NoError(t, err) - ksShards, found := allShardNames[tt.keyspaceName] - require.True(t, found) - require.Equal(t, []string{tt.shardName}, ksShards) - // DeleteShard require.NoError(t, DeleteShard(tt.keyspaceName, tt.shardName)) _, _, err = ReadShardPrimaryInformation(tt.keyspaceName, tt.shardName) diff --git a/go/vt/vtorc/inst/tablet_dao.go b/go/vt/vtorc/inst/tablet_dao.go index d4498e65239..bd8115de969 100644 --- a/go/vt/vtorc/inst/tablet_dao.go +++ b/go/vt/vtorc/inst/tablet_dao.go @@ -74,6 +74,50 @@ func ReadTablet(tabletAlias string) (*topodatapb.Tablet, error) { return tablet, nil } +// ReadTabletCountsByCell returns the count of tablets watched by cell. +// The backend query uses an index by "cell": cell_idx_vitess_tablet. +func ReadTabletCountsByCell() (map[string]int64, error) { + tabletCounts := make(map[string]int64) + query := `SELECT + cell, + COUNT() AS count + FROM + vitess_tablet + GROUP BY + cell` + err := db.QueryVTOrc(query, nil, func(row sqlutils.RowMap) error { + cell := row.GetString("cell") + tabletCounts[cell] = row.GetInt64("count") + return nil + }) + return tabletCounts, err +} + +// ReadTabletCountsByKeyspaceShard returns the count of tablets watched by keyspace/shard. +// The backend query uses an index by "keyspace, shard": ks_idx_vitess_tablet. +func ReadTabletCountsByKeyspaceShard() (map[string]map[string]int64, error) { + tabletCounts := make(map[string]map[string]int64) + query := `SELECT + keyspace, + shard, + COUNT() AS count + FROM + vitess_tablet + GROUP BY + keyspace, + shard` + err := db.QueryVTOrc(query, nil, func(row sqlutils.RowMap) error { + keyspace := row.GetString("keyspace") + shard := row.GetString("shard") + if _, found := tabletCounts[keyspace]; !found { + tabletCounts[keyspace] = make(map[string]int64) + } + tabletCounts[keyspace][shard] = row.GetInt64("count") + return nil + }) + return tabletCounts, err +} + // SaveTablet saves the tablet record against the instanceKey. func SaveTablet(tablet *topodatapb.Tablet) error { tabletp, err := prototext.Marshal(tablet) diff --git a/go/vt/vtorc/inst/tablet_dao_test.go b/go/vt/vtorc/inst/tablet_dao_test.go index a876d857ace..d5de0eb268b 100644 --- a/go/vt/vtorc/inst/tablet_dao_test.go +++ b/go/vt/vtorc/inst/tablet_dao_test.go @@ -91,3 +91,56 @@ func TestSaveAndReadTablet(t *testing.T) { }) } } + +func TestReadTabletCountsByCell(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + + for i := 0; i < 100; i++ { + require.NoError(t, SaveTablet(&topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: uint32(i), + }, + Keyspace: "test", + Shard: "-", + })) + } + tabletCounts, err := ReadTabletCountsByCell() + require.NoError(t, err) + require.Equal(t, map[string]int64{"cell1": 100}, tabletCounts) +} + +func TestReadTabletCountsByKeyspaceShard(t *testing.T) { + // Clear the database after the test. The easiest way to do that is to run all the initialization commands again. + defer func() { + db.ClearVTOrcDatabase() + }() + + var uid uint32 + for _, shard := range []string{"-40", "40-80", "80-c0", "c0-"} { + for i := 0; i < 100; i++ { + require.NoError(t, SaveTablet(&topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: uid, + }, + Keyspace: "test", + Shard: shard, + })) + uid++ + } + } + tabletCounts, err := ReadTabletCountsByKeyspaceShard() + require.NoError(t, err) + require.Equal(t, map[string]map[string]int64{ + "test": { + "-40": 100, + "40-80": 100, + "80-c0": 100, + "c0-": 100, + }, + }, tabletCounts) +} diff --git a/go/vt/vtorc/logic/keyspace_shard_discovery.go b/go/vt/vtorc/logic/keyspace_shard_discovery.go index 56fe551eb49..861af7a64a4 100644 --- a/go/vt/vtorc/logic/keyspace_shard_discovery.go +++ b/go/vt/vtorc/logic/keyspace_shard_discovery.go @@ -22,7 +22,6 @@ import ( "golang.org/x/exp/maps" - "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo" @@ -30,27 +29,6 @@ import ( "vitess.io/vitess/go/vt/vtorc/inst" ) -var statsShardsWatched = stats.NewGaugesFuncWithMultiLabels("ShardsWatched", - "Keyspace/shards currently watched", - []string{"Keyspace", "Shard"}, - getShardsWatchedStats) - -// getShardsWatchedStats returns the keyspace/shards watched in a format for stats. -func getShardsWatchedStats() map[string]int64 { - shardsWatched := make(map[string]int64) - allShardNames, err := inst.ReadAllShardNames() - if err != nil { - log.Errorf("Failed to read all shard names: %+v", err) - return shardsWatched - } - for ks, shards := range allShardNames { - for _, shard := range shards { - shardsWatched[ks+"."+shard] = 1 - } - } - return shardsWatched -} - // refreshAllKeyspacesAndShardsMu ensures RefreshAllKeyspacesAndShards // is not executed concurrently. var refreshAllKeyspacesAndShardsMu sync.Mutex diff --git a/go/vt/vtorc/logic/tablet_discovery.go b/go/vt/vtorc/logic/tablet_discovery.go index d90247409aa..3a1ed153e11 100644 --- a/go/vt/vtorc/logic/tablet_discovery.go +++ b/go/vt/vtorc/logic/tablet_discovery.go @@ -31,6 +31,7 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/external/golib/sqlutils" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" @@ -54,10 +55,48 @@ var ( // This is populated by parsing `--clusters_to_watch` flag. shardsToWatch map[string][]*topodatapb.KeyRange + // tablet stats + statsTabletsWatchedByCell = stats.NewGaugesFuncWithMultiLabels( + "TabletsWatchedByCell", + "Number of tablets watched by cell", + []string{"Cell"}, + getTabletsWatchedByCellStats, + ) + statsTabletsWatchedByShard = stats.NewGaugesFuncWithMultiLabels( + "TabletsWatchedByShard", + "Number of tablets watched by keyspace/shard", + []string{"Keyspace", "Shard"}, + getTabletsWatchedByShardStats, + ) + // ErrNoPrimaryTablet is a fixed error message. ErrNoPrimaryTablet = errors.New("no primary tablet found") ) +// getTabletsWatchedByCellStats returns the number of tablets watched by cell in stats format. +func getTabletsWatchedByCellStats() map[string]int64 { + tabletCountsByCell, err := inst.ReadTabletCountsByCell() + if err != nil { + log.Errorf("Failed to read tablet counts by cell: %+v", err) + } + return tabletCountsByCell +} + +// getTabletsWatchedByShardStats returns the number of tablets watched by keyspace/shard in stats format. +func getTabletsWatchedByShardStats() map[string]int64 { + tabletsWatchedByShard := make(map[string]int64) + tabletCountsByKS, err := inst.ReadTabletCountsByKeyspaceShard() + if err != nil { + log.Errorf("Failed to read tablet counts by shard: %+v", err) + } + for keyspace, countsByShard := range tabletCountsByKS { + for shard, tabletCount := range countsByShard { + tabletsWatchedByShard[keyspace+"."+shard] = tabletCount + } + } + return tabletsWatchedByShard +} + // RegisterFlags registers the flags required by VTOrc func RegisterFlags(fs *pflag.FlagSet) { fs.StringSliceVar(&clustersToWatch, "clusters_to_watch", clustersToWatch, "Comma-separated list of keyspaces or keyspace/keyranges that this instance will monitor and repair. Defaults to all clusters in the topology. Example: \"ks1,ks2/-80\"")