func zkResolveWildcards(wr *wrangler.Wrangler, args []string) ([]string, error) { zkts, ok := wr.TopoServer().(*zktopo.Server) if !ok { return args, nil } return zk.ResolveWildcards(zkts.GetZConn(), args) }
// StartActionLoop will start the action loop for a fake tablet, // using ft.FakeMysqlDaemon as the backing mysqld. func (ft *FakeTablet) StartActionLoop(t *testing.T, wr *wrangler.Wrangler) { if ft.Agent != nil { t.Fatalf("Agent for %v is already running", ft.Tablet.Alias) } // Listen on a random port var err error ft.Listener, err = net.Listen("tcp", ":0") if err != nil { t.Fatalf("Cannot listen: %v", err) } port := ft.Listener.Addr().(*net.TCPAddr).Port // create a test agent on that port, and re-read the record // (it has new ports and IP) ft.Agent = tabletmanager.NewTestActionAgent(wr.TopoServer(), ft.Tablet.Alias, port, ft.FakeMysqlDaemon) ft.Tablet = ft.Agent.Tablet().Tablet // create the RPC server ft.RpcServer = rpcplus.NewServer() gorpctmserver.RegisterForTest(ft.RpcServer, ft.Agent) // create the HTTP server, serve the server from it handler := http.NewServeMux() bsonrpc.ServeCustomRPC(handler, ft.RpcServer, false) httpServer := http.Server{ Handler: handler, } go httpServer.Serve(ft.Listener) }
func commandDemoteMaster(wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { if err := subFlags.Parse(args); err != nil { return err } if subFlags.NArg() != 1 { return fmt.Errorf("action DemoteMaster requires <tablet alias|zk tablet path>") } tabletAlias, err := tabletParamToTabletAlias(subFlags.Arg(0)) if err != nil { return err } tabletInfo, err := wr.TopoServer().GetTablet(tabletAlias) if err != nil { return err } return wr.TabletManagerClient().DemoteMaster(wr.Context(), tabletInfo) }
func commandPruneActionLogs(wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { keepCount := subFlags.Int("keep-count", 10, "count to keep") if err := subFlags.Parse(args); err != nil { return err } if subFlags.NArg() == 0 { return fmt.Errorf("action PruneActionLogs requires <zk action log path> ...") } paths, err := zkResolveWildcards(wr, subFlags.Args()) if err != nil { return err } zkts, ok := wr.TopoServer().(*zktopo.Server) if !ok { return fmt.Errorf("PruneActionLogs requires a zktopo.Server") } var errCount sync2.AtomicInt32 wg := sync.WaitGroup{} for _, zkActionLogPath := range paths { wg.Add(1) go func(zkActionLogPath string) { defer wg.Done() purgedCount, err := zkts.PruneActionLogs(zkActionLogPath, *keepCount) if err == nil { wr.Logger().Infof("%v pruned %v", zkActionLogPath, purgedCount) } else { wr.Logger().Errorf("%v pruning failed: %v", zkActionLogPath, err) errCount.Add(1) } }(zkActionLogPath) } wg.Wait() if errCount.Get() > 0 { return fmt.Errorf("some errors occurred, check the log") } return nil }
func keyspacesWithOverlappingShards(wr *wrangler.Wrangler) ([]map[string]string, error) { keyspaces, err := wr.TopoServer().GetKeyspaces() if err != nil { return nil, err } wg := sync.WaitGroup{} mu := sync.Mutex{} // protects result result := make([]map[string]string, 0, len(keyspaces)) rec := concurrency.AllErrorRecorder{} for _, keyspace := range keyspaces { wg.Add(1) go func(keyspace string) { defer wg.Done() osList, err := topotools.FindOverlappingShards(wr.TopoServer(), keyspace) if err != nil { rec.RecordError(err) return } mu.Lock() for _, os := range osList { result = append(result, map[string]string{ "Keyspace": os.Left[0].Keyspace(), "Shard": os.Left[0].ShardName(), }) } mu.Unlock() }(keyspace) } wg.Wait() if rec.HasErrors() { return nil, rec.Error() } if len(result) == 0 { return nil, fmt.Errorf("There are no keyspaces with overlapping shards") } return result, nil }
// findHealthyRdonlyEndPoint returns a random healthy endpoint. // Since we don't want to use them all, we require at least 2 servers // are healthy. func findHealthyRdonlyEndPoint(wr *wrangler.Wrangler, cell, keyspace, shard string) (topo.TabletAlias, error) { endPoints, err := wr.TopoServer().GetEndPoints(cell, keyspace, shard, topo.TYPE_RDONLY) if err != nil { return topo.TabletAlias{}, fmt.Errorf("GetEndPoints(%v,%v,%v,rdonly) failed: %v", cell, keyspace, shard, err) } healthyEndpoints := make([]topo.EndPoint, 0, len(endPoints.Entries)) for _, entry := range endPoints.Entries { if len(entry.Health) == 0 { healthyEndpoints = append(healthyEndpoints, entry) } } if len(healthyEndpoints) <= 1 { return topo.TabletAlias{}, fmt.Errorf("Not enough endpoints to chose from in (%v,%v/%v), have %v healthy ones", cell, keyspace, shard, len(healthyEndpoints)) } // random server in the list is what we want index := rand.Intn(len(healthyEndpoints)) return topo.TabletAlias{ Cell: cell, Uid: healthyEndpoints[index].Uid, }, nil }
// keyspacesWithServedFrom returns all the keyspaces that have ServedFrom set // to one value. func keyspacesWithServedFrom(wr *wrangler.Wrangler) ([]string, error) { keyspaces, err := wr.TopoServer().GetKeyspaces() if err != nil { return nil, err } wg := sync.WaitGroup{} mu := sync.Mutex{} // protects result result := make([]string, 0, len(keyspaces)) rec := concurrency.AllErrorRecorder{} for _, keyspace := range keyspaces { wg.Add(1) go func(keyspace string) { defer wg.Done() ki, err := wr.TopoServer().GetKeyspace(keyspace) if err != nil { rec.RecordError(err) return } if len(ki.ServedFromMap) > 0 { mu.Lock() result = append(result, keyspace) mu.Unlock() } }(keyspace) } wg.Wait() if rec.HasErrors() { return nil, rec.Error() } if len(result) == 0 { return nil, fmt.Errorf("There are no keyspaces with ServedFrom") } return result, nil }
// findChecker: // - find a rdonly instance in the keyspace / shard // - mark it as checker // - tag it with our worker process func findChecker(wr *wrangler.Wrangler, cleaner *wrangler.Cleaner, cell, keyspace, shard string) (topo.TabletAlias, error) { tabletAlias, err := findHealthyRdonlyEndPoint(wr, cell, keyspace, shard) if err != nil { return topo.TabletAlias{}, err } // We add the tag before calling ChangeSlaveType, so the destination // vttablet reloads the worker URL when it reloads the tablet. ourURL := servenv.ListeningURL.String() wr.Logger().Infof("Adding tag[worker]=%v to tablet %v", ourURL, tabletAlias) if err := wr.TopoServer().UpdateTabletFields(tabletAlias, func(tablet *topo.Tablet) error { if tablet.Tags == nil { tablet.Tags = make(map[string]string) } tablet.Tags["worker"] = ourURL return nil }); err != nil { return topo.TabletAlias{}, err } // we remove the tag *before* calling ChangeSlaveType back, so // we need to record this tag change after the change slave // type change in the cleaner. defer wrangler.RecordTabletTagAction(cleaner, tabletAlias, "worker", "") wr.Logger().Infof("Changing tablet %v to 'checker'", tabletAlias) wr.ResetActionTimeout(30 * time.Second) if err := wr.ChangeType(tabletAlias, topo.TYPE_CHECKER, false /*force*/); err != nil { return topo.TabletAlias{}, err } // Record a clean-up action to take the tablet back to rdonly. // We will alter this one later on and let the tablet go back to // 'spare' if we have stopped replication for too long on it. wrangler.RecordChangeSlaveTypeAction(cleaner, tabletAlias, topo.TYPE_RDONLY) return tabletAlias, nil }
// shardsWithSources returns all the shards that have SourceShards set // with no Tables list. func shardsWithSources(wr *wrangler.Wrangler) ([]map[string]string, error) { keyspaces, err := wr.TopoServer().GetKeyspaces() if err != nil { return nil, err } wg := sync.WaitGroup{} mu := sync.Mutex{} // protects result result := make([]map[string]string, 0, len(keyspaces)) rec := concurrency.AllErrorRecorder{} for _, keyspace := range keyspaces { wg.Add(1) go func(keyspace string) { defer wg.Done() shards, err := wr.TopoServer().GetShardNames(keyspace) if err != nil { rec.RecordError(err) return } for _, shard := range shards { wg.Add(1) go func(keyspace, shard string) { defer wg.Done() si, err := wr.TopoServer().GetShard(keyspace, shard) if err != nil { rec.RecordError(err) return } if len(si.SourceShards) > 0 && len(si.SourceShards[0].Tables) == 0 { mu.Lock() result = append(result, map[string]string{ "Keyspace": keyspace, "Shard": shard, }) mu.Unlock() } }(keyspace, shard) } }(keyspace) } wg.Wait() if rec.HasErrors() { return nil, rec.Error() } if len(result) == 0 { return nil, fmt.Errorf("There are no shards with SourceShards") } return result, nil }