@@ -61,13 +61,17 @@ type Ratio struct {
61
61
func (r Ratio ) String () string {
62
62
storeAndTypes := make ([]string , len (r .StoreIDs ))
63
63
for i := range r .StoreIDs {
64
- storeAndTypes [ i ] = "s" + strconv .Itoa (r .StoreIDs [i ])
65
- if r .Types [i ] == roachpb .NON_VOTER {
66
- storeAndTypes [ i ] += ":NON_VOTER"
64
+ s : = "s" + strconv .Itoa (r .StoreIDs [i ]) + ":"
65
+ if r .Types [i ] != roachpb .VOTER_FULL {
66
+ s += r . Types [ i ]. String ()
67
67
}
68
68
if r .StoreIDs [i ] == r .LeaseholderID {
69
- storeAndTypes [ i ] += ":leaseholder "
69
+ s += "* "
70
70
}
71
+ if l := len (s ); s [l - 1 ] == ':' {
72
+ s = s [:l - 1 ] // remove trailing ':'
73
+ }
74
+ storeAndTypes [i ] = s
71
75
}
72
76
return "{" + strings .Join (storeAndTypes , "," ) + "}:" + strconv .Itoa (r .Weight )
73
77
}
@@ -88,11 +92,16 @@ func ParseReplicaPlacement(input string) ReplicaPlacement {
88
92
pattern := `\{([^}]+)\}:(\d+)`
89
93
re := regexp .MustCompile (pattern )
90
94
95
+ // Consider input "{s1:*,s2,s3:NON_VOTER}:1 {s4:*,s5,s6}:1".
91
96
var result []Ratio
92
97
matches := re .FindAllStringSubmatch (input , - 1 )
93
98
99
+ // matches[0] will be []string{"{s1:*,s2,s3:NON_VOTER}:1", "s1:*,s2,s3:NON_VOTER", "1"}
100
+ // matches[1] will be []string{"{s4:*,s5,s6}:1", "s4:*,s5,s6", "1"}
94
101
for _ , match := range matches {
102
+ // For matches[0], stores will be []string{"s1:*","s2","s3:NON_VOTER"}.
95
103
stores := strings .Split (match [1 ], "," )
104
+ // For matches[0], weight will be 1.
96
105
weight , _ := strconv .Atoi (match [2 ])
97
106
98
107
storeSet := make ([]int , 0 )
@@ -103,24 +112,43 @@ func ParseReplicaPlacement(input string) ReplicaPlacement {
103
112
for _ , store := range stores {
104
113
store = strings .TrimSpace (store )
105
114
parts := strings .Split (store , ":" )
106
- if strings .HasPrefix (parts [0 ], "s" ) {
107
- storeID , _ := strconv .Atoi (parts [0 ][1 :])
108
- storeSet = append (storeSet , storeID )
109
-
110
- replicaType := roachpb .VOTER_FULL
111
- if len (parts ) > 1 {
112
- switch parts [1 ] {
113
- case "NON_VOTER" :
114
- replicaType = roachpb .NON_VOTER
115
- case "*" :
116
- leaseholderStoreID = storeID
117
- foundLeaseholder = true
118
- default :
119
- panic (fmt .Sprintf ("unknown replica type: %s" , parts [1 ]))
120
- }
121
- }
122
- typeSet = append (typeSet , replicaType )
115
+ if len (parts ) == 0 {
116
+ panic (fmt .Sprintf ("invalid replica placement: %s" , input ))
117
+ }
118
+ // For matches[0] and stores[0], parts will be []string{"s1","*"}.
119
+ if ! strings .HasPrefix (parts [0 ], "s" ) {
120
+ panic (fmt .Sprintf ("invalid replica placement: %s" , input ))
121
+ }
122
+ // For matches[0] and stores[0], storeID will be 1.
123
+ storeID , _ := strconv .Atoi (parts [0 ][1 :])
124
+ storeSet = append (storeSet , storeID )
125
+
126
+ // If the replica type or leaseholder is not specified, artificially
127
+ // append VOTER_FULL to parts. For matches[0] and stores[1], parts
128
+ // will be []string{"s2","VOTER_FULL"}.
129
+ if len (parts ) < 2 {
130
+ parts = append (parts , "VOTER_FULL" )
131
+ }
132
+ if len (parts ) < 2 {
133
+ panic (fmt .Sprintf ("invalid replica placement: %s" , input ))
134
+ }
135
+ // For matches[0] and stores[0], typ will be "VOTER_FULL".
136
+ typ := parts [1 ]
137
+ // If replica is a leaseholder, indicate the leaseholder store ID. Note
138
+ // that incoming voter may also be a leaseholder.
139
+ if last := len (typ ) - 1 ; typ [last ] == '*' {
140
+ leaseholderStoreID = storeID
141
+ foundLeaseholder = true
142
+ typ = typ [:last ] // remove '*'
143
+ }
144
+ if typ == "" {
145
+ typ = roachpb .VOTER_FULL .String () // default type
146
+ }
147
+ v , ok := roachpb .ReplicaType_value [typ ]
148
+ if ! ok {
149
+ panic (fmt .Sprintf ("unknown replica type: %s" , typ ))
123
150
}
151
+ typeSet = append (typeSet , roachpb .ReplicaType (v ))
124
152
}
125
153
126
154
if ! foundLeaseholder {
0 commit comments