go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/sync/dispatcher/buffer/options.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package buffer 16 17 import ( 18 "time" 19 20 "go.chromium.org/luci/common/errors" 21 "go.chromium.org/luci/common/retry" 22 ) 23 24 // Options configures policy for the Buffer. 25 // 26 // See Defaults for default values. 27 type Options struct { 28 // [OPTIONAL] The maximum number of outstanding leases permitted. 29 // 30 // Attempting additional leases (with LeaseOne) while at the maximum will 31 // return nil. 32 // 33 // Requirement: Must be > 0 34 MaxLeases int 35 36 // [OPTIONAL] The maximum number of items to allow in a Batch before making it 37 // available to lease. 38 // 39 // Special value -1: unlimited 40 // Requirement: Must be == -1 (i.e. cut batches on BatchAgeMax/BatchSizeMax), 41 // or > 0 42 BatchItemsMax int 43 44 // [OPTIONAL] The maximum number of "size units" to allow in a Batch before 45 // making it available to lease. 46 // 47 // The units used here are arbitrary and are only checked vs the value 48 // provided to AddNoBlock. 49 // 50 // Size is explicitly provided to AddNoBlock by the caller. 51 // 52 // Inserting an item which exceeds BatchSizeMax will result in ErrItemTooLarge. 53 // It's up to the caller to ensure that this doesn't happen. 54 // 55 // Special value -1: unlimited 56 // Requirement: Must be == -1 (i.e. cut batches on BatchAgeMax/BatchItemsMax), 57 // or > 0 58 BatchSizeMax int 59 60 // [OPTIONAL] The maximum amount of time to wait before queuing a Batch for 61 // transmission. Note that batches are only cut by time when a worker is ready 62 // to process them (i.e. LeaseOne is invoked). 63 // 64 // Requirement: Must be > 0 65 BatchAgeMax time.Duration 66 67 // [OPTIONAL] Sets the policy for the Buffer around how many items the Buffer 68 // is allowed to hold, and what happens when that number is reached. 69 FullBehavior FullBehavior 70 71 // [OPTIONAL] If true, ensures that the next available batch is always the one 72 // with the oldest data. 73 // 74 // If this is false (the default), batches will be leased in the order that 75 // they're available to send; If a Batch has a retry with a high delay, it's 76 // possible that the next leased Batch actually contains newer data than 77 // a later batch. 78 // 79 // NOTE: if this is combined with high Retry values, it can lead to a 80 // head-of-line blocking situation. 81 // 82 // Requirement: May only be true if MaxLeases == 1 83 FIFO bool 84 85 // [OPTIONAL] Each batch will have a retry.Iterator assigned to it from this 86 // retry.Factory. 87 // 88 // When a Batch is NACK'd, it will be retried at "now" plus the Duration 89 // returned by the retry.Iterator. 90 // 91 // If the retry.Iterator returns retry.Stop, the Batch will be silently 92 // dropped. 93 Retry retry.Factory 94 } 95 96 // Defaults defines the defaults for Options when it contains 0-valued 97 // fields. 98 // 99 // DO NOT ASSIGN/WRITE TO THIS STRUCT. 100 var Defaults = Options{ 101 MaxLeases: 4, 102 BatchItemsMax: 20, 103 BatchSizeMax: -1, 104 BatchAgeMax: 10 * time.Second, 105 FullBehavior: &BlockNewItems{ 106 MaxItems: 1000, 107 }, 108 Retry: func() retry.Iterator { 109 return &retry.ExponentialBackoff{ 110 Limited: retry.Limited{ 111 Delay: 200 * time.Millisecond, // initial delay 112 Retries: -1, // no retry cap 113 }, 114 Multiplier: 1.2, 115 MaxDelay: 60 * time.Second, 116 } 117 }, 118 } 119 120 // normalize validates that Options is well formed and populates defaults 121 // which are missing. 122 func (o *Options) normalize() error { 123 switch { 124 case o.MaxLeases == 0: 125 o.MaxLeases = Defaults.MaxLeases 126 case o.MaxLeases > 0: 127 default: 128 return errors.Reason("MaxLeases must be > 0: got %d", o.MaxLeases).Err() 129 } 130 131 switch { 132 case o.BatchItemsMax == -1: 133 case o.BatchItemsMax == 0: 134 o.BatchItemsMax = Defaults.BatchItemsMax 135 case o.BatchItemsMax > 0: 136 default: 137 return errors.Reason("BatchItemsMax must be > 0 or == -1: got %d", o.BatchItemsMax).Err() 138 } 139 140 switch { 141 case o.BatchSizeMax == -1: 142 case o.BatchSizeMax == 0: 143 o.BatchSizeMax = Defaults.BatchSizeMax 144 case o.BatchSizeMax > 0: 145 default: 146 return errors.Reason("BatchSizeMax must be > 0 or == -1: got %d", o.BatchSizeMax).Err() 147 } 148 149 switch { 150 case o.BatchAgeMax == 0: 151 o.BatchAgeMax = Defaults.BatchAgeMax 152 case o.BatchAgeMax > 0: 153 default: 154 return errors.Reason("BatchAgeMax must be > 0: got %s", o.BatchAgeMax).Err() 155 } 156 157 if o.FIFO && o.MaxLeases != 1 { 158 return errors.Reason("FIFO is true, but MaxLeases != 1: got %d", o.MaxLeases).Err() 159 } 160 161 if o.FullBehavior == nil { 162 o.FullBehavior = Defaults.FullBehavior 163 } 164 165 if o.Retry == nil { 166 o.Retry = Defaults.Retry 167 } 168 169 return errors.Annotate(o.FullBehavior.Check(*o), "FullBehavior.Check").Err() 170 } 171 172 func (o *Options) batchItemsGuess() int { 173 if o.BatchItemsMax > 0 { 174 return o.BatchItemsMax 175 } 176 return 10 177 } 178 179 func (o *Options) checkItemSize(itemSize int) error { 180 if itemSize < 0 { 181 // We don't ever allow negative sizes. 182 return ErrItemTooSmall 183 } 184 185 switch { 186 case o.BatchSizeMax == -1: 187 case itemSize == 0: 188 return ErrItemTooSmall 189 case itemSize > o.BatchSizeMax: 190 return ErrItemTooLarge 191 } 192 return nil 193 }