vitess.io/vitess@v0.16.2/go/vt/wrangler/keyspace.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package wrangler 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "strings" 24 "sync" 25 "time" 26 27 "vitess.io/vitess/go/vt/proto/binlogdata" 28 29 "vitess.io/vitess/go/sqltypes" 30 "vitess.io/vitess/go/vt/concurrency" 31 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 32 "vitess.io/vitess/go/vt/topo" 33 "vitess.io/vitess/go/vt/topo/topoproto" 34 "vitess.io/vitess/go/vt/topotools" 35 "vitess.io/vitess/go/vt/vterrors" 36 ) 37 38 const ( 39 // DefaultFilteredReplicationWaitTime is the default value for argument filteredReplicationWaitTime. 40 DefaultFilteredReplicationWaitTime = 30 * time.Second 41 ) 42 43 // keyspace related methods for Wrangler 44 45 // validateNewWorkflow ensures that the specified workflow doesn't already exist 46 // in the keyspace. 47 func (wr *Wrangler) validateNewWorkflow(ctx context.Context, keyspace, workflow string) error { 48 allshards, err := wr.ts.FindAllShardsInKeyspace(ctx, keyspace) 49 if err != nil { 50 return err 51 } 52 var wg sync.WaitGroup 53 allErrors := &concurrency.AllErrorRecorder{} 54 for _, si := range allshards { 55 if si.PrimaryAlias == nil { 56 allErrors.RecordError(fmt.Errorf("shard has no primary: %v", si.ShardName())) 57 continue 58 } 59 wg.Add(1) 60 go func(si *topo.ShardInfo) { 61 defer wg.Done() 62 63 primary, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) 64 if err != nil { 65 allErrors.RecordError(vterrors.Wrap(err, "validateWorkflowName.GetTablet")) 66 return 67 } 68 validations := []struct { 69 query string 70 msg string 71 }{{ 72 fmt.Sprintf("select 1 from _vt.vreplication where db_name=%s and workflow=%s", encodeString(primary.DbName()), encodeString(workflow)), 73 fmt.Sprintf("workflow %s already exists in keyspace %s on tablet %d", workflow, keyspace, primary.Alias.Uid), 74 }, { 75 fmt.Sprintf("select 1 from _vt.vreplication where db_name=%s and message='FROZEN' and workflow_sub_type != %d", encodeString(primary.DbName()), binlogdata.VReplicationWorkflowSubType_Partial), 76 fmt.Sprintf("found previous frozen workflow on tablet %d, please review and delete it first before creating a new workflow", 77 primary.Alias.Uid), 78 }} 79 for _, validation := range validations { 80 p3qr, err := wr.tmc.VReplicationExec(ctx, primary.Tablet, validation.query) 81 if err != nil { 82 allErrors.RecordError(vterrors.Wrap(err, "validateWorkflowName.VReplicationExec")) 83 return 84 } 85 if p3qr != nil && len(p3qr.Rows) != 0 { 86 allErrors.RecordError(vterrors.Wrap(fmt.Errorf(validation.msg), "validateWorkflowName.VReplicationExec")) 87 return 88 } 89 } 90 }(si) 91 } 92 wg.Wait() 93 return allErrors.AggrError(vterrors.Aggregate) 94 } 95 96 func (wr *Wrangler) printShards(ctx context.Context, si []*topo.ShardInfo) error { 97 for _, si := range si { 98 wr.Logger().Printf(" Shard: %v\n", si.ShardName()) 99 if len(si.SourceShards) != 0 { 100 wr.Logger().Printf(" Source Shards: %v\n", si.SourceShards) 101 } 102 ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) 103 if err != nil { 104 return err 105 } 106 qr, err := wr.tmc.VReplicationExec(ctx, ti.Tablet, fmt.Sprintf("select * from _vt.vreplication where db_name=%v", encodeString(ti.DbName()))) 107 if err != nil { 108 return err 109 } 110 res := sqltypes.Proto3ToResult(qr) 111 if len(res.Rows) != 0 { 112 wr.Logger().Printf(" VReplication:\n") 113 for _, row := range res.Rows { 114 wr.Logger().Printf(" %v\n", row) 115 } 116 } 117 wr.Logger().Printf(" Is Primary Serving: %v\n", si.IsPrimaryServing) 118 if len(si.TabletControls) != 0 { 119 wr.Logger().Printf(" Tablet Controls: %v\n", si.TabletControls) 120 } 121 } 122 return nil 123 } 124 125 func (wr *Wrangler) getPrimaryPositions(ctx context.Context, shards []*topo.ShardInfo) (map[*topo.ShardInfo]string, error) { 126 mu := sync.Mutex{} 127 result := make(map[*topo.ShardInfo]string) 128 129 wg := sync.WaitGroup{} 130 rec := concurrency.AllErrorRecorder{} 131 for _, si := range shards { 132 wg.Add(1) 133 go func(si *topo.ShardInfo) { 134 defer wg.Done() 135 wr.Logger().Infof("Gathering primary position for %v", topoproto.TabletAliasString(si.PrimaryAlias)) 136 ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) 137 if err != nil { 138 rec.RecordError(err) 139 return 140 } 141 142 pos, err := wr.tmc.PrimaryPosition(ctx, ti.Tablet) 143 if err != nil { 144 rec.RecordError(err) 145 return 146 } 147 148 wr.Logger().Infof("Got primary position for %v", topoproto.TabletAliasString(si.PrimaryAlias)) 149 mu.Lock() 150 result[si] = pos 151 mu.Unlock() 152 }(si) 153 } 154 wg.Wait() 155 return result, rec.Error() 156 } 157 158 func (wr *Wrangler) waitForFilteredReplication(ctx context.Context, sourcePositions map[*topo.ShardInfo]string, destinationShards []*topo.ShardInfo, waitTime time.Duration) error { 159 wg := sync.WaitGroup{} 160 rec := concurrency.AllErrorRecorder{} 161 for _, si := range destinationShards { 162 wg.Add(1) 163 go func(si *topo.ShardInfo) { 164 defer wg.Done() 165 ctx, cancel := context.WithTimeout(ctx, waitTime) 166 defer cancel() 167 168 var pos string 169 for _, sourceShard := range si.SourceShards { 170 // find the position it should be at 171 for s, sp := range sourcePositions { 172 if s.Keyspace() == sourceShard.Keyspace && s.ShardName() == sourceShard.Shard { 173 pos = sp 174 break 175 } 176 } 177 178 // and wait for it 179 wr.Logger().Infof("Waiting for %v to catch up", topoproto.TabletAliasString(si.PrimaryAlias)) 180 ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) 181 if err != nil { 182 rec.RecordError(err) 183 return 184 } 185 186 if err := wr.tmc.VReplicationWaitForPos(ctx, ti.Tablet, int(sourceShard.Uid), pos); err != nil { 187 if strings.Contains(err.Error(), "not found") { 188 wr.Logger().Infof("%v stream %d was not found. Skipping wait.", topoproto.TabletAliasString(si.PrimaryAlias), sourceShard.Uid) 189 } else { 190 rec.RecordError(err) 191 } 192 } else { 193 wr.Logger().Infof("%v caught up", topoproto.TabletAliasString(si.PrimaryAlias)) 194 } 195 } 196 }(si) 197 } 198 wg.Wait() 199 return rec.Error() 200 } 201 202 // refreshPrimaryTablets will just RPC-ping all the primary tablets with RefreshState 203 func (wr *Wrangler) refreshPrimaryTablets(ctx context.Context, shards []*topo.ShardInfo) error { 204 wg := sync.WaitGroup{} 205 rec := concurrency.AllErrorRecorder{} 206 for _, si := range shards { 207 wg.Add(1) 208 go func(si *topo.ShardInfo) { 209 defer wg.Done() 210 wr.Logger().Infof("RefreshState primary %v", topoproto.TabletAliasString(si.PrimaryAlias)) 211 ti, err := wr.ts.GetTablet(ctx, si.PrimaryAlias) 212 if err != nil { 213 rec.RecordError(err) 214 return 215 } 216 217 if err := wr.tmc.RefreshState(ctx, ti.Tablet); err != nil { 218 rec.RecordError(err) 219 } else { 220 wr.Logger().Infof("%v responded", topoproto.TabletAliasString(si.PrimaryAlias)) 221 } 222 }(si) 223 } 224 wg.Wait() 225 return rec.Error() 226 } 227 228 // updateShardRecords updates the shard records based on 'from' or 'to' direction. 229 func (wr *Wrangler) updateShardRecords(ctx context.Context, keyspace string, shards []*topo.ShardInfo, cells []string, servedType topodatapb.TabletType, isFrom bool, clearSourceShards bool) (err error) { 230 return topotools.UpdateShardRecords(ctx, wr.ts, wr.tmc, keyspace, shards, cells, servedType, isFrom, clearSourceShards, wr.Logger()) 231 } 232 233 // updateFrozenFlag sets or unsets the Frozen flag for primary migration. This is performed 234 // for all primary tablet control records. 235 func (wr *Wrangler) updateFrozenFlag(ctx context.Context, shards []*topo.ShardInfo, value bool) (err error) { 236 for i, si := range shards { 237 updatedShard, err := wr.ts.UpdateShardFields(ctx, si.Keyspace(), si.ShardName(), func(si *topo.ShardInfo) error { 238 tc := si.GetTabletControl(topodatapb.TabletType_PRIMARY) 239 if tc != nil { 240 tc.Frozen = value 241 return nil 242 } 243 // This shard does not have a tablet control record, adding one to set frozen flag 244 tc = &topodatapb.Shard_TabletControl{ 245 TabletType: topodatapb.TabletType_PRIMARY, 246 Frozen: value, 247 } 248 si.TabletControls = append(si.TabletControls, tc) 249 return nil 250 }) 251 if err != nil { 252 return err 253 } 254 255 shards[i] = updatedShard 256 } 257 return nil 258 } 259 260 func encodeString(in string) string { 261 buf := bytes.NewBuffer(nil) 262 sqltypes.NewVarChar(in).EncodeSQL(buf) 263 return buf.String() 264 }