-
Notifications
You must be signed in to change notification settings - Fork 453
/
shard_set.go
155 lines (133 loc) · 4.2 KB
/
shard_set.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
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package sharding
import (
"errors"
"fmt"
"regexp"
"strconv"
)
const (
defaultNumShards = 1024
)
var (
// Shard range is expected to be provided in the form of startShard..endShard.
// Both startShard and endShard are inclusive. An example shard range is 0..63.
rangeRegexp = regexp.MustCompile(`^([0-9]+)(\.\.([0-9]+))?$`)
errInvalidShard = errors.New("invalid shard")
)
// ParseShardSet parses a shard set from the input string.
func ParseShardSet(s string) (ShardSet, error) {
ss := make(ShardSet, defaultNumShards)
if err := ss.ParseRange(s); err != nil {
return nil, err
}
return ss, nil
}
// MustParseShardSet parses a shard set from the input string, and panics
// if parsing is unsuccessful.
func MustParseShardSet(s string) ShardSet {
ss, err := ParseShardSet(s)
if err == nil {
return ss
}
panic(fmt.Errorf("unable to parse shard set from %s: %v", s, err))
}
// ShardSet is a collection of shards organized as a set.
// The shards contained in the set can be discontinuous.
type ShardSet map[uint32]struct{}
// UnmarshalYAML unmarshals YAML into a shard set.
// The following formats are supported:
// * StartShard..EndShard, e.g., 0..63.
// * Single shard, e.g., 5.
// * Array containing shard ranges and single shards.
func (ss *ShardSet) UnmarshalYAML(f func(interface{}) error) error {
*ss = make(ShardSet, defaultNumShards)
// If YAML contains a single string, attempt to parse out a single range.
var s string
if err := f(&s); err == nil {
return ss.ParseRange(s)
}
// Otherwise try to parse out a list of ranges or single shards.
var a []interface{}
if err := f(&a); err == nil {
for _, v := range a {
switch c := v.(type) {
case string:
if err := ss.ParseRange(c); err != nil {
return err
}
case int:
ss.Add(uint32(c))
default:
return fmt.Errorf("unexpected range %v", c)
}
}
return nil
}
// Otherwise try to parse out a single shard.
var n int
if err := f(&n); err == nil {
ss.Add(uint32(n))
return nil
}
return errInvalidShard
}
// Contains returns true if the shard set contains the given shard.
func (ss ShardSet) Contains(p uint32) bool {
_, found := ss[p]
return found
}
// Add adds the shard to the set.
func (ss ShardSet) Add(p uint32) {
ss[p] = struct{}{}
}
// AddBetween adds shards between the given min (inclusive) and max (exclusive).
func (ss ShardSet) AddBetween(minInclusive, maxExclusive uint32) {
for i := minInclusive; i < maxExclusive; i++ {
ss.Add(i)
}
}
// ParseRange parses a range of shards and adds them to the set.
func (ss ShardSet) ParseRange(s string) error {
rangeMatches := rangeRegexp.FindStringSubmatch(s)
if len(rangeMatches) != 0 {
return ss.addRange(rangeMatches)
}
return fmt.Errorf("invalid range '%s'", s)
}
func (ss ShardSet) addRange(matches []string) error {
min, err := strconv.ParseInt(matches[1], 10, 32)
if err != nil {
return err
}
max := min
if matches[3] != "" {
max, err = strconv.ParseInt(matches[3], 10, 32)
if err != nil {
return err
}
}
if min > max {
return fmt.Errorf("invalid range: %d > %d", min, max)
}
ss.AddBetween(uint32(min), uint32(max)+1)
return nil
}