forked from vitessio/vitess
-
Notifications
You must be signed in to change notification settings - Fork 1
/
cell_info.go
145 lines (126 loc) · 4 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
package topo
import (
"fmt"
"path"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
// This file provides the utility methods to save / retrieve CellInfo
// in the topology Backend.
//
// 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.
const (
cellsPath = "cells"
)
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.ListDir(ctx, GlobalCell, cellsPath)
switch err {
case ErrNoNode:
return nil, nil
case nil:
return entries, nil
default:
return nil, err
}
}
// GetCellInfo reads a CellInfo from the Backend.
func (ts Server) GetCellInfo(ctx context.Context, cell string) (*topodatapb.CellInfo, error) {
// Read the file.
filePath := pathForCellInfo(cell)
contents, _, err := ts.Get(ctx, GlobalCell, 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.Create(ctx, GlobalCell, 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.Get(ctx, GlobalCell, filePath)
switch err {
case nil:
if err := proto.Unmarshal(contents, ci); err != nil {
return err
}
case ErrNoNode:
// Nothing to do.
default:
return err
}
// Call update method.
if err = update(ci); err != nil {
if err == ErrNoUpdateNeeded {
return nil
}
return err
}
// Pack and save.
contents, err = proto.Marshal(ci)
if err != nil {
return err
}
if _, err = ts.Update(ctx, GlobalCell, filePath, contents, version); err != ErrBadVersion {
// 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.Delete(ctx, GlobalCell, filePath, nil)
}