forked from vitessio/vitess
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cell_info.go
175 lines (152 loc) · 5.23 KB
/
cell_info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package topo
import (
"fmt"
"path"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
)
// This file provides the utility methods to save / retrieve CellInfo
// in the topology server.
//
// CellInfo records are not meant to be changed while the system is
// running. In a running system, a CellInfo can be added, and
// topology server implementations should be able to read them to
// access the cells upon demand. Topology server implementations can
// also read the available CellInfo at startup to build a list of
// available cells, if necessary. A CellInfo can only be removed if no
// Shard record references the corresponding cell in its Cells list.
func pathForCellInfo(cell string) string {
return path.Join(CellsPath, cell, CellInfoFile)
}
// GetCellInfoNames returns the names of the existing cells. They are
// sorted by name.
func (ts *Server) GetCellInfoNames(ctx context.Context) ([]string, error) {
entries, err := ts.globalCell.ListDir(ctx, CellsPath, false /*full*/)
switch {
case IsErrType(err, NoNode):
return nil, nil
case err == nil:
return DirEntriesToStringArray(entries), nil
default:
return nil, err
}
}
// GetCellInfo reads a CellInfo from the global Conn.
func (ts *Server) GetCellInfo(ctx context.Context, cell string, strongRead bool) (*topodatapb.CellInfo, error) {
conn := ts.globalCell
if !strongRead {
conn = ts.globalReadOnlyCell
}
// Read the file.
filePath := pathForCellInfo(cell)
contents, _, err := conn.Get(ctx, filePath)
if err != nil {
return nil, err
}
// Unpack the contents.
ci := &topodatapb.CellInfo{}
if err := proto.Unmarshal(contents, ci); err != nil {
return nil, err
}
return ci, nil
}
// CreateCellInfo creates a new CellInfo with the provided content.
func (ts *Server) CreateCellInfo(ctx context.Context, cell string, ci *topodatapb.CellInfo) error {
// Pack the content.
contents, err := proto.Marshal(ci)
if err != nil {
return err
}
// Save it.
filePath := pathForCellInfo(cell)
_, err = ts.globalCell.Create(ctx, filePath, contents)
return err
}
// UpdateCellInfoFields is a high level helper method to read a CellInfo
// object, update its fields, and then write it back. If the write fails due to
// a version mismatch, it will re-read the record and retry the update.
// If the update method returns ErrNoUpdateNeeded, nothing is written,
// and nil is returned.
func (ts *Server) UpdateCellInfoFields(ctx context.Context, cell string, update func(*topodatapb.CellInfo) error) error {
filePath := pathForCellInfo(cell)
for {
ci := &topodatapb.CellInfo{}
// Read the file, unpack the contents.
contents, version, err := ts.globalCell.Get(ctx, filePath)
switch {
case err == nil:
if err := proto.Unmarshal(contents, ci); err != nil {
return err
}
case IsErrType(err, NoNode):
// Nothing to do.
default:
return err
}
// Call update method.
if err = update(ci); err != nil {
if IsErrType(err, NoUpdateNeeded) {
return nil
}
return err
}
// Pack and save.
contents, err = proto.Marshal(ci)
if err != nil {
return err
}
if _, err = ts.globalCell.Update(ctx, filePath, contents, version); !IsErrType(err, BadVersion) {
// This includes the 'err=nil' case.
return err
}
}
}
// DeleteCellInfo deletes the specified CellInfo.
// We first make sure no Shard record points to the cell.
func (ts *Server) DeleteCellInfo(ctx context.Context, cell string) error {
// Get all keyspaces.
keyspaces, err := ts.GetKeyspaces(ctx)
if err != nil {
return fmt.Errorf("GetKeyspaces() failed: %v", err)
}
// For each keyspace, make sure no shard points at the cell.
for _, keyspace := range keyspaces {
shards, err := ts.FindAllShardsInKeyspace(ctx, keyspace)
if err != nil {
return fmt.Errorf("FindAllShardsInKeyspace(%v) failed: %v", keyspace, err)
}
for shard, si := range shards {
if si.HasCell(cell) {
return fmt.Errorf("cell %v is used by shard %v/%v, cannot remove it. Use 'vtctl RemoveShardCell' to remove unused cells in a Shard", cell, keyspace, shard)
}
}
}
filePath := pathForCellInfo(cell)
return ts.globalCell.Delete(ctx, filePath, nil)
}
// GetKnownCells returns the list of known cells.
// For now, it just lists the 'cells' directory in the global topology server.
// TODO(alainjobart) once the cell map is migrated to this generic
// package, we can do better than this.
func (ts *Server) GetKnownCells(ctx context.Context) ([]string, error) {
// Note we use the global read-only cell here, as the result
// is not time sensitive.
entries, err := ts.globalReadOnlyCell.ListDir(ctx, CellsPath, false /*full*/)
if err != nil {
return nil, err
}
return DirEntriesToStringArray(entries), nil
}