-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgroup_config.go
More file actions
278 lines (220 loc) · 9.68 KB
/
group_config.go
File metadata and controls
278 lines (220 loc) · 9.68 KB
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
Copyright 2026 The ARCORIS Authors
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 agreed to 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 bufferpool
import (
"strings"
"arcoris.dev/bufferpool/internal/multierr"
)
const (
// DefaultGroupName is the diagnostic name used when a group config does not
// provide an explicit name.
DefaultGroupName = "default-group"
// errGroupConfigInvalidPolicy identifies invalid group policy input.
errGroupConfigInvalidPolicy = "bufferpool.PoolGroupConfig: invalid group policy"
// errGroupConfigInvalidPartitioning identifies invalid partitioning input.
errGroupConfigInvalidPartitioning = "bufferpool.PoolGroupConfig: invalid partitioning policy"
// errGroupConfigNoPoolsOrPartitions rejects groups with no runtime topology.
errGroupConfigNoPoolsOrPartitions = "bufferpool.PoolGroupConfig: at least one pool or partition must be configured"
// errGroupConfigEmptyPoolName rejects unnamed group-level Pools.
errGroupConfigEmptyPoolName = "bufferpool.PoolGroupConfig: pool name must not be empty"
// errGroupConfigDuplicatePool rejects duplicate group-level Pool names.
errGroupConfigDuplicatePool = "bufferpool.PoolGroupConfig: duplicate pool name"
// errGroupConfigInvalidPool wraps invalid group-level Pool construction config.
errGroupConfigInvalidPool = "bufferpool.PoolGroupConfig: invalid pool config"
// errGroupConfigEmptyPartitionName rejects unnamed group-local partitions.
errGroupConfigEmptyPartitionName = "bufferpool.PoolGroupConfig: partition name must not be empty"
// errGroupConfigDuplicatePartition rejects duplicate group-local partition names.
errGroupConfigDuplicatePartition = "bufferpool.PoolGroupConfig: duplicate partition name"
// errGroupConfigInvalidPartition wraps invalid owned partition construction config.
errGroupConfigInvalidPartition = "bufferpool.PoolGroupConfig: invalid partition config"
// errGroupConfigPartitionNameMismatch rejects divergent group and partition names.
errGroupConfigPartitionNameMismatch = "bufferpool.PoolGroupConfig: partition config name must match group partition name"
)
// PoolGroupConfig configures one PoolGroup.
//
// PoolGroupConfig is the construction boundary above PoolPartition. It composes
// group policy, partitioning policy, group-level Pool configs, and optional
// explicit PoolPartition configs. It contains no runtime state; scheduler
// fields are construction policy that NewPoolGroup translates into owner-local
// runtime only after validation and full initialization.
type PoolGroupConfig struct {
// Name is diagnostic metadata for this group.
Name string
// Policy defines group-level control behavior.
Policy PoolGroupPolicy
// Partitioning controls automatic partition count and Pool placement when
// Pools is used as the managed construction input.
Partitioning PoolGroupPartitioningPolicy
// Pools is the normal managed-mode input. PoolGroup assigns these Pools to
// owned PoolPartitions and builds a pool-name routing directory.
Pools []GroupPoolConfig
// Partitions is the deterministic set of explicitly configured partitions.
// It remains available for advanced layouts and for explicit per-Pool
// placement when Partitioning.Mode is PoolGroupPartitioningModeExplicit.
Partitions []GroupPartitionConfig
}
// GroupPoolConfig configures one Pool owned by a PoolGroup.
type GroupPoolConfig struct {
// Name is the group-global Pool name used for managed routing.
Name string
// Config is the Pool construction config. If Config.Name is empty,
// normalization sets it to Name.
Config PoolConfig
// Partition is the group-local partition name for explicit placement.
Partition string
// Priority is metadata reserved for later budget allocation.
Priority PoolPriority
}
// GroupPartitionConfig configures one PoolPartition owned by a PoolGroup.
type GroupPartitionConfig struct {
// Name is the group-local partition name.
Name string
// Config is the PoolPartition construction config. If Config.Name is empty,
// normalization sets it to Name.
Config PoolPartitionConfig
}
// DefaultPoolGroupConfig returns the package default group config.
//
// The default has no pools or partitions because a group without runtime
// topology is invalid. Supplying Pools uses automatic partitioning by default.
func DefaultPoolGroupConfig() PoolGroupConfig {
return PoolGroupConfig{Name: DefaultGroupName, Policy: DefaultPoolGroupPolicy(), Partitioning: DefaultPoolGroupPartitioningPolicy()}
}
// Normalize returns c with group defaults applied.
func (c PoolGroupConfig) Normalize() PoolGroupConfig {
normalized := c
normalized.Name = normalizeGroupName(normalized.Name)
normalized.Policy = normalized.Policy.Normalize()
normalized.Partitioning = normalized.Partitioning.Normalize()
if len(normalized.Pools) > 0 {
pools := make([]GroupPoolConfig, len(normalized.Pools))
for index, poolConfig := range normalized.Pools {
pools[index] = poolConfig.Normalize()
}
normalized.Pools = pools
}
if len(normalized.Partitions) > 0 {
partitions := make([]GroupPartitionConfig, len(normalized.Partitions))
for index, partitionConfig := range normalized.Partitions {
partitions[index] = partitionConfig.Normalize()
}
normalized.Partitions = partitions
}
return normalized
}
// Validate validates normalized group construction configuration.
func (c PoolGroupConfig) Validate() error {
normalized := c.Normalize()
var err error
if policyErr := normalized.Policy.Validate(); policyErr != nil {
multierr.AppendInto(&err, wrapError(ErrInvalidOptions, policyErr, errGroupConfigInvalidPolicy))
}
if _, _, assignmentErr := newGroupPartitionAssignments(normalized); assignmentErr != nil {
multierr.AppendInto(&err, assignmentErr)
}
return err
}
// validateGroupPartitions validates explicit partition config structure.
func validateGroupPartitions(partitions []GroupPartitionConfig) error {
var err error
seen := make(map[string]struct{}, len(partitions))
for _, partitionConfig := range partitions {
if partitionConfig.Name == "" {
multierr.AppendInto(&err, newError(ErrInvalidOptions, errGroupConfigEmptyPartitionName))
continue
}
if _, exists := seen[partitionConfig.Name]; exists {
multierr.AppendInto(&err, newError(ErrInvalidOptions, errGroupConfigDuplicatePartition+": "+partitionConfig.Name))
continue
}
seen[partitionConfig.Name] = struct{}{}
if nameErr := validateGroupPartitionNameMatch(partitionConfig); nameErr != nil {
multierr.AppendInto(&err, nameErr)
continue
}
if partitionErr := partitionConfig.Config.Validate(); partitionErr != nil {
multierr.AppendInto(&err, wrapError(ErrInvalidOptions, partitionErr, errGroupConfigInvalidPartition+": "+partitionConfig.Name))
}
}
return err
}
// IsZero reports whether c contains no explicit construction settings.
func (c PoolGroupConfig) IsZero() bool {
return strings.TrimSpace(c.Name) == "" &&
c.Policy.IsZero() &&
c.Partitioning.IsZero() &&
len(c.Pools) == 0 &&
len(c.Partitions) == 0
}
// Normalize returns p with defaults applied.
func (p GroupPoolConfig) Normalize() GroupPoolConfig {
normalized := p
normalized.Name = strings.TrimSpace(normalized.Name)
normalized.Partition = strings.TrimSpace(normalized.Partition)
normalized.Priority = normalized.Priority.Normalize()
poolConfig := normalized.Config
if normalized.Name != "" && isPoolConfigNameUnset(poolConfig.Name) {
poolConfig.Name = normalized.Name
}
normalized.Config = poolConfig.Normalize()
return normalized
}
// Normalize returns p with defaults applied.
func (p GroupPartitionConfig) Normalize() GroupPartitionConfig {
normalized := p
normalized.Name = strings.TrimSpace(normalized.Name)
partitionConfig := normalized.Config
partitionName := strings.TrimSpace(partitionConfig.Name)
if normalized.Name != "" && (partitionName == "" || partitionName == DefaultPartitionName) {
partitionConfig.Name = normalized.Name
}
normalized.Config = partitionConfig.Normalize()
return normalized
}
// validateGroupPartitionNameMatch rejects divergent group-local and diagnostic names.
func validateGroupPartitionNameMatch(config GroupPartitionConfig) error {
if config.Name == "" || config.Config.Name == "" || config.Config.Name == config.Name {
return nil
}
return newError(ErrInvalidOptions, errGroupConfigPartitionNameMismatch+": "+config.Name+" != "+config.Config.Name)
}
// normalizeGroupName applies the group diagnostic-name default.
func normalizeGroupName(name string) string {
name = strings.TrimSpace(name)
if name == "" {
return DefaultGroupName
}
return name
}
// cloneGroupConfig returns a caller-owned copy of normalized group config.
func cloneGroupConfig(config PoolGroupConfig) PoolGroupConfig {
config.Policy = config.Policy.Normalize()
config.Partitioning = config.Partitioning.Normalize()
if len(config.Pools) > 0 {
pools := make([]GroupPoolConfig, len(config.Pools))
for index, poolConfig := range config.Pools {
pools[index] = poolConfig
pools[index].Config.Policy = clonePoolPolicy(poolConfig.Config.Policy)
}
config.Pools = pools
}
if len(config.Partitions) > 0 {
partitions := make([]GroupPartitionConfig, len(config.Partitions))
for index, partitionConfig := range config.Partitions {
partitions[index] = partitionConfig
partitions[index].Config = clonePartitionConfig(partitionConfig.Config)
}
config.Partitions = partitions
}
return config
}