@@ -112,7 +112,7 @@ type Config struct {
112112// that operate at this priority level.
113113type PriorityBandConfig struct {
114114 // Priority is the unique numerical priority level for this band.
115- // Convention: Lower numerical values indicate lower priority.
115+ // Convention: Highest numeric value corresponds to highest priority (centered on 0) .
116116 // Required.
117117 Priority int
118118
@@ -140,20 +140,6 @@ type PriorityBandConfig struct {
140140 MaxBytes uint64
141141}
142142
143- // NewConfig performs validation and initialization, returning a guaranteed-valid `Config` object.
144- // This is the required constructor for creating a new configuration. It applies provided functional options (primarily
145- // for testing) and does not mutate the input `cfg`.
146- func NewConfig (cfg Config , opts ... configOption ) (* Config , error ) {
147- newCfg := cfg .deepCopy ()
148- for _ , opt := range opts {
149- opt (newCfg )
150- }
151- if err := newCfg .validateAndApplyDefaults (); err != nil {
152- return nil , err
153- }
154- return newCfg , nil
155- }
156-
157143// =============================================================================
158144// Shard-Level Configuration
159145// =============================================================================
@@ -205,52 +191,55 @@ func (sc *ShardConfig) getBandConfig(priority int) (*ShardPriorityBandConfig, er
205191
206192// --- Validation and Defaulting ---
207193
208- // validateAndApplyDefaults checks the global configuration for validity (including plugin compatibility) and mutates
209- // the receiver to populate any empty fields with system defaults. It also initializes internal lookup maps.
210- func (c * Config ) validateAndApplyDefaults () error {
194+ // ValidateAndApplyDefaults checks the global configuration for validity and then creates a new `Config` object,
195+ // populating any empty fields with system defaults.
196+ // It does not mutate the receiver.
197+ func (c * Config ) ValidateAndApplyDefaults () (* Config , error ) {
198+ cfg := c .deepCopy ()
199+
211200 // Apply defaults to top-level fields.
212- if c .InitialShardCount <= 0 {
213- c .InitialShardCount = defaultInitialShardCount
201+ if cfg .InitialShardCount <= 0 {
202+ cfg .InitialShardCount = defaultInitialShardCount
214203 }
215- if c .FlowGCTimeout <= 0 {
216- c .FlowGCTimeout = defaultFlowGCTimeout
204+ if cfg .FlowGCTimeout <= 0 {
205+ cfg .FlowGCTimeout = defaultFlowGCTimeout
217206 }
218- if c .EventChannelBufferSize <= 0 {
219- c .EventChannelBufferSize = defaultEventChannelBufferSize
207+ if cfg .EventChannelBufferSize <= 0 {
208+ cfg .EventChannelBufferSize = defaultEventChannelBufferSize
220209 }
221210
222211 // Ensure the DI factories are initialized for production use if `NewConfig` was called without options.
223- if c .interFlowDispatchPolicyFactory == nil {
224- c .interFlowDispatchPolicyFactory = inter .NewPolicyFromName
212+ if cfg .interFlowDispatchPolicyFactory == nil {
213+ cfg .interFlowDispatchPolicyFactory = inter .NewPolicyFromName
225214 }
226- if c .intraFlowDispatchPolicyFactory == nil {
227- c .intraFlowDispatchPolicyFactory = intra .NewPolicyFromName
215+ if cfg .intraFlowDispatchPolicyFactory == nil {
216+ cfg .intraFlowDispatchPolicyFactory = intra .NewPolicyFromName
228217 }
229- if c .queueFactory == nil {
230- c .queueFactory = queue .NewQueueFromName
218+ if cfg .queueFactory == nil {
219+ cfg .queueFactory = queue .NewQueueFromName
231220 }
232221
233- if len (c .PriorityBands ) == 0 {
234- return errors .New ("config validation failed: at least one priority band must be defined" )
222+ if len (cfg .PriorityBands ) == 0 {
223+ return nil , errors .New ("config validation failed: at least one priority band must be defined" )
235224 }
236225
237226 // Validate and default each priority band.
238227 priorities := make (map [int ]struct {})
239228 priorityNames := make (map [string ]struct {})
240- c .priorityBandMap = make (map [int ]* PriorityBandConfig , len (c .PriorityBands ))
229+ cfg .priorityBandMap = make (map [int ]* PriorityBandConfig , len (cfg .PriorityBands ))
241230
242- for i := range c .PriorityBands {
243- band := & c .PriorityBands [i ]
231+ for i := range cfg .PriorityBands {
232+ band := & cfg .PriorityBands [i ]
244233 if _ , exists := priorities [band .Priority ]; exists {
245- return fmt .Errorf ("config validation failed: duplicate priority level %d found" , band .Priority )
234+ return nil , fmt .Errorf ("config validation failed: duplicate priority level %d found" , band .Priority )
246235 }
247236 priorities [band .Priority ] = struct {}{}
248237
249238 if band .PriorityName == "" {
250- return fmt .Errorf ("config validation failed: PriorityName is required for priority band %d" , band .Priority )
239+ return nil , fmt .Errorf ("config validation failed: PriorityName is required for priority band %d" , band .Priority )
251240 }
252241 if _ , exists := priorityNames [band .PriorityName ]; exists {
253- return fmt .Errorf ("config validation failed: duplicate priority name %q found" , band .PriorityName )
242+ return nil , fmt .Errorf ("config validation failed: duplicate priority name %q found" , band .PriorityName )
254243 }
255244 priorityNames [band .PriorityName ] = struct {}{}
256245
@@ -267,12 +256,12 @@ func (c *Config) validateAndApplyDefaults() error {
267256 band .MaxBytes = defaultPriorityBandMaxBytes
268257 }
269258
270- if err := c .validateBandCompatibility (* band ); err != nil {
271- return err
259+ if err := cfg .validateBandCompatibility (* band ); err != nil {
260+ return nil , err
272261 }
273- c .priorityBandMap [band .Priority ] = band
262+ cfg .priorityBandMap [band .Priority ] = band
274263 }
275- return nil
264+ return cfg , nil
276265}
277266
278267// validateBandCompatibility verifies that a band's configured queue type has the necessary capabilities.
@@ -423,6 +412,18 @@ func withQueueFactory(factory queueFactory) configOption {
423412 }
424413}
425414
415+ // newConfig creates a new validated and defaulted `Config` object.
416+ // It applies provided test-only functional options before validation and defaulting.
417+ // It does not mutate the input `cfg`.
418+ // test-only
419+ func newConfig (cfg Config , opts ... configOption ) (* Config , error ) {
420+ newCfg := cfg .deepCopy ()
421+ for _ , opt := range opts {
422+ opt (newCfg )
423+ }
424+ return newCfg .ValidateAndApplyDefaults ()
425+ }
426+
426427// --- Internal Utilities ---
427428
428429// deepCopy creates a deep copy of the `Config` object.
@@ -436,7 +437,6 @@ func (c *Config) deepCopy() *Config {
436437 FlowGCTimeout : c .FlowGCTimeout ,
437438 EventChannelBufferSize : c .EventChannelBufferSize ,
438439 PriorityBands : make ([]PriorityBandConfig , len (c .PriorityBands )),
439- priorityBandMap : make (map [int ]* PriorityBandConfig , len (c .PriorityBands )),
440440 interFlowDispatchPolicyFactory : c .interFlowDispatchPolicyFactory ,
441441 intraFlowDispatchPolicyFactory : c .intraFlowDispatchPolicyFactory ,
442442 queueFactory : c .queueFactory ,
@@ -445,11 +445,14 @@ func (c *Config) deepCopy() *Config {
445445 // PriorityBandConfig contains only value types, so a slice copy is sufficient for a deep copy.
446446 copy (newCfg .PriorityBands , c .PriorityBands )
447447
448- // Crucial: We must rebuild the map and take the address of the elements within the new slice (`newCfg.PriorityBands`)
449- // to ensure the map pointers are correct for the newly created `Config` instance.
450- for i := range newCfg .PriorityBands {
451- band := & newCfg .PriorityBands [i ]
452- newCfg .priorityBandMap [band .Priority ] = band
448+ if c .priorityBandMap != nil {
449+ newCfg .priorityBandMap = make (map [int ]* PriorityBandConfig , len (c .PriorityBands ))
450+ // Crucial: We must rebuild the map and take the address of the elements within the new slice (`newCfg.PriorityBands`)
451+ // to ensure the map pointers are correct for the newly created `Config` instance.
452+ for i := range newCfg .PriorityBands {
453+ band := & newCfg .PriorityBands [i ]
454+ newCfg .priorityBandMap [band .Priority ] = band
455+ }
453456 }
454457 return newCfg
455458}
0 commit comments