-
Notifications
You must be signed in to change notification settings - Fork 0
/
labels.go
190 lines (159 loc) · 6.05 KB
/
labels.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package swim
import (
"errors"
"strings"
"github.com/uber/ringpop-go/util"
)
var (
// DefaultLabelOptions contain the default values to be used to limit the
// amount of data being gossipped over the network. The defaults have been
// chosen with the following assumptions.
// 1. 1000 node cluster
// 2. Every byte available is used (yes, characters are not bytes but these
// are ballpark figures to protect developers)
// 3. Worst case would have continues fullsync's, meaning the complete
// memberlist being sent over the wire 5 times a second.
// When all contiditions are met the Labels would add the following load
// (non-compressed) to the network:
// (32+128)*5*1000*5*8 = ~32mbit/s
DefaultLabelOptions = LabelOptions{
KeySize: 32,
ValueSize: 128,
Count: 5,
}
// ErrLabelSizeExceeded indicates that an operation on labels would exceed
// the configured size limits on labels. This is to prevent applications
// bloating the gossip protocol with too much data. Ringpop can be
// configured with the settings to control the amount of data that can be
// put in a nodes labels.
ErrLabelSizeExceeded = errors.New("label operation exceeds configured label limits")
// ErrLabelInternalKey is an error that is returned when an application
// tries to set a label in the internal namespace that is used by ringpop
ErrLabelInternalKey = errors.New("label can't be altered by application because it is in the internal ringpop namespace")
labelsInternalNamespacePrefix = "__"
)
// LabelOptions controlls the limits on labels. Since labels are gossiped on
// every ping/ping-req/fullsync we need to limit the amount of data an
// application stores in their labels. When needed the defaults can be
// overwritten during the construction of ringpop. This should be done with care
// to not overwhelm the network with data.
type LabelOptions struct {
// KeySize is the length a key may use at most
KeySize int
// ValueSize is the length a value may use at most
ValueSize int
// Count is the number of maximum allowed (public) labels on a node
Count int
}
func mergeLabelOptions(opts LabelOptions, def LabelOptions) LabelOptions {
return LabelOptions{
KeySize: util.SelectInt(opts.KeySize, def.KeySize),
ValueSize: util.SelectInt(opts.ValueSize, def.ValueSize),
Count: util.SelectInt(opts.Count, def.Count),
}
}
func (lo LabelOptions) validateLabel(current map[string]string, key, value string) error {
if isInternalLabel(key) {
// we ignore internal labels in the limits
return nil
}
if len(key) > lo.KeySize || len(value) > lo.ValueSize {
// if the either the key or the value of the label is bigger then
// the max allowed size an error is returned
return ErrLabelSizeExceeded
}
// keep track of all labels that are new in this operation to calculate if
// we are exceeding the maximum allowed number of labels
additionalLabelCount := 0
_, has := current[key]
if !has {
// only add a count to the countAfter if the key we are looking
// at is a new key
additionalLabelCount++
}
// get the count of the current labels, internal labels will not be counted
currentCount := countNonInternalLabels(current)
if additionalLabelCount > 0 && (currentCount+additionalLabelCount) > lo.Count {
// Only when we add additional labels we check if the new count
// (exluding internal labels) will exceed the amount of labels that is
// configured. If that is the case we will return an error
return ErrLabelSizeExceeded
}
// all is ok
return nil
}
func (lo LabelOptions) validateLabels(current map[string]string, additional map[string]string) error {
// keep track of all labels that are new in this operation to calculate if
// we are exceeding the maximum allowed number of labels
additionalLabelCount := 0
for key, value := range additional {
if isInternalLabel(key) {
// we ignore internal labels in the limits
continue
}
if len(key) > lo.KeySize || len(value) > lo.ValueSize {
// if the either the key or the value of the label is bigger then
// the max allowed size an error is returned
return ErrLabelSizeExceeded
}
_, has := current[key]
if !has {
// only add a count to the countAfter if the key we are looking
// at is a new key
additionalLabelCount++
}
}
// get the count of the current labels, internal labels will not be counted
currentCount := countNonInternalLabels(current)
if additionalLabelCount > 0 && (currentCount+additionalLabelCount) > lo.Count {
// Only when we add additional labels we check if the new count
// (exluding internal labels) will exceed the amount of labels that is
// configured. If that is the case we will return an error
return ErrLabelSizeExceeded
}
// all is ok
return nil
}
func isInternalLabel(key string) bool {
return strings.HasPrefix(key, labelsInternalNamespacePrefix)
}
func countNonInternalLabels(labels map[string]string) int {
count := 0
for key := range labels {
if isInternalLabel(key) {
continue
}
count++
}
return count
}
// NodeLabels implements the ringpop.Labels interface and proxies the calls to
// the swim.Node backing the membership protocol.
type NodeLabels struct {
node *Node
}
// Get the value of a label for this node
func (n *NodeLabels) Get(key string) (value string, has bool) {
return n.node.memberlist.GetLocalLabel(key)
}
// Set the key to a specific value. Returning an error when it failed eg. when
// the storage capacity for labels has exceed the maximum ammount. (Currently
// the storage limit is not implemented)
func (n *NodeLabels) Set(key, value string) error {
if isInternalLabel(key) {
return ErrLabelInternalKey
}
return n.node.memberlist.SetLocalLabel(key, value)
}
// Remove a key from the labels
func (n *NodeLabels) Remove(key string) (removed bool, err error) {
if isInternalLabel(key) {
return false, ErrLabelInternalKey
}
return n.node.memberlist.RemoveLocalLabels(key), nil
}
// AsMap gets a readonly copy of all the labels assigned to Node. Changes to the
// map will not be refelected in the node.
func (n *NodeLabels) AsMap() map[string]string {
return n.node.memberlist.LocalLabelsAsMap()
}