vitess.io/vitess@v0.16.2/go/vt/topo/cell_info.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 topo 18 19 import ( 20 "context" 21 "path" 22 "strings" 23 24 "google.golang.org/protobuf/proto" 25 "k8s.io/apimachinery/pkg/util/sets" 26 27 "vitess.io/vitess/go/vt/vterrors" 28 29 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 30 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 31 ) 32 33 // This file provides the utility methods to save / retrieve CellInfo 34 // in the topology server. 35 // 36 // CellInfo records are not meant to be changed while the system is 37 // running. In a running system, a CellInfo can be added, and 38 // topology server implementations should be able to read them to 39 // access the cells upon demand. Topology server implementations can 40 // also read the available CellInfo at startup to build a list of 41 // available cells, if necessary. A CellInfo can only be removed if no 42 // Shard record references the corresponding cell in its Cells list. 43 44 func pathForCellInfo(cell string) string { 45 return path.Join(CellsPath, cell, CellInfoFile) 46 } 47 48 // GetCellInfoNames returns the names of the existing cells. They are 49 // sorted by name. 50 func (ts *Server) GetCellInfoNames(ctx context.Context) ([]string, error) { 51 entries, err := ts.globalCell.ListDir(ctx, CellsPath, false /*full*/) 52 switch { 53 case IsErrType(err, NoNode): 54 return nil, nil 55 case err == nil: 56 return DirEntriesToStringArray(entries), nil 57 default: 58 return nil, err 59 } 60 } 61 62 // GetCellInfo reads a CellInfo from the global Conn. 63 func (ts *Server) GetCellInfo(ctx context.Context, cell string, strongRead bool) (*topodatapb.CellInfo, error) { 64 conn := ts.globalCell 65 if !strongRead { 66 conn = ts.globalReadOnlyCell 67 } 68 // Read the file. 69 filePath := pathForCellInfo(cell) 70 contents, _, err := conn.Get(ctx, filePath) 71 if err != nil { 72 return nil, err 73 } 74 75 // Unpack the contents. 76 ci := &topodatapb.CellInfo{} 77 if err := proto.Unmarshal(contents, ci); err != nil { 78 return nil, err 79 } 80 return ci, nil 81 } 82 83 // CreateCellInfo creates a new CellInfo with the provided content. 84 func (ts *Server) CreateCellInfo(ctx context.Context, cell string, ci *topodatapb.CellInfo) error { 85 // Pack the content. 86 contents, err := proto.Marshal(ci) 87 if err != nil { 88 return err 89 } 90 91 // Save it. 92 filePath := pathForCellInfo(cell) 93 _, err = ts.globalCell.Create(ctx, filePath, contents) 94 return err 95 } 96 97 // UpdateCellInfoFields is a high level helper method to read a CellInfo 98 // object, update its fields, and then write it back. If the write fails due to 99 // a version mismatch, it will re-read the record and retry the update. 100 // If the update method returns ErrNoUpdateNeeded, nothing is written, 101 // and nil is returned. 102 func (ts *Server) UpdateCellInfoFields(ctx context.Context, cell string, update func(*topodatapb.CellInfo) error) error { 103 filePath := pathForCellInfo(cell) 104 for { 105 ci := &topodatapb.CellInfo{} 106 107 // Read the file, unpack the contents. 108 contents, version, err := ts.globalCell.Get(ctx, filePath) 109 switch { 110 case err == nil: 111 if err := proto.Unmarshal(contents, ci); err != nil { 112 return err 113 } 114 case IsErrType(err, NoNode): 115 // Nothing to do. 116 default: 117 return err 118 } 119 120 // Call update method. 121 if err = update(ci); err != nil { 122 if IsErrType(err, NoUpdateNeeded) { 123 return nil 124 } 125 return err 126 } 127 128 // Pack and save. 129 contents, err = proto.Marshal(ci) 130 if err != nil { 131 return err 132 } 133 if _, err = ts.globalCell.Update(ctx, filePath, contents, version); !IsErrType(err, BadVersion) { 134 // This includes the 'err=nil' case. 135 return err 136 } 137 } 138 } 139 140 // DeleteCellInfo deletes the specified CellInfo. 141 // We first try to make sure no Shard record points to the cell, 142 // but we'll continue regardless if 'force' is true. 143 func (ts *Server) DeleteCellInfo(ctx context.Context, cell string, force bool) error { 144 srvKeyspaces, err := ts.GetSrvKeyspaceNames(ctx, cell) 145 switch { 146 case err == nil: 147 if len(srvKeyspaces) != 0 && !force { 148 return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cell %v has serving keyspaces. Before deleting, delete keyspace with DeleteKeyspace, or use -force to continue anyway.", cell) 149 } 150 case IsErrType(err, NoNode): 151 // Nothing to do. 152 default: 153 if !force { 154 return vterrors.Wrap(err, "can't list SrvKeyspace entries in the cell; use -force flag to continue anyway (e.g. if cell-local topo was already permanently shut down)") 155 } 156 157 select { 158 case <-ctx.Done(): 159 // If our context has expired and we got an error back from 160 // GetSrvKeyspaceNames, we assume that call failed because the 161 // local cell topo was down. If force=true, then we make a new 162 // background context to cleanup from the global topo. Otherwise a 163 // local-down-topo scenario would mean we never can delete it. 164 // (see https://github.com/vitessio/vitess/issues/8220). 165 var cancel context.CancelFunc 166 ctx, cancel = context.WithTimeout(context.Background(), RemoteOperationTimeout) 167 defer cancel() 168 default: 169 // Context still has some time left, no need to make a new one. 170 } 171 } 172 173 filePath := pathForCellInfo(cell) 174 return ts.globalCell.Delete(ctx, filePath, nil) 175 } 176 177 // GetKnownCells returns the list of known cells. 178 // For now, it just lists the 'cells' directory in the global topology server. 179 // TODO(alainjobart) once the cell map is migrated to this generic 180 // package, we can do better than this. 181 func (ts *Server) GetKnownCells(ctx context.Context) ([]string, error) { 182 // Note we use the global read-only cell here, as the result 183 // is not time sensitive. 184 entries, err := ts.globalReadOnlyCell.ListDir(ctx, CellsPath, false /*full*/) 185 if err != nil { 186 return nil, err 187 } 188 return DirEntriesToStringArray(entries), nil 189 } 190 191 // ExpandCells takes a comma-separated list of cells and returns an array of cell names 192 // Aliases are expanded and an empty string returns all cells 193 func (ts *Server) ExpandCells(ctx context.Context, cells string) ([]string, error) { 194 var ( 195 err error 196 inputCells []string 197 outputCells = sets.New[string]() // Use a set to dedupe if the input cells list includes an alias and a cell in that alias. 198 ) 199 200 if cells == "" { 201 inputCells, err = ts.GetCellInfoNames(ctx) 202 if err != nil { 203 return nil, err 204 } 205 } else { 206 inputCells = strings.Split(cells, ",") 207 } 208 209 expandCell := func(ctx context.Context, cell string) error { 210 shortCtx, cancel := context.WithTimeout(ctx, RemoteOperationTimeout) 211 defer cancel() 212 213 _, err := ts.GetCellInfo(shortCtx, cell, false /* strongRead */) 214 if err != nil { 215 // Not a valid cell name. Check whether it is an alias. 216 shortCtx, cancel := context.WithTimeout(ctx, RemoteOperationTimeout) 217 defer cancel() 218 219 alias, err2 := ts.GetCellsAlias(shortCtx, cell, false /* strongRead */) 220 if err2 != nil { 221 return err // return the original err to indicate the cell does not exist 222 } 223 224 // Expand the alias cells list into the final set. 225 outputCells.Insert(alias.Cells...) 226 return nil 227 } 228 229 // Valid cell. 230 outputCells.Insert(cell) 231 return nil 232 } 233 234 for _, cell := range inputCells { 235 cell2 := strings.TrimSpace(cell) 236 if err := expandCell(ctx, cell2); err != nil { 237 return nil, err 238 } 239 } 240 241 return sets.List(outputCells), nil 242 }