github.com/m3db/m3@v1.5.0/src/dbnode/client/fetch_state.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package client 22 23 import ( 24 "errors" 25 "fmt" 26 "sync" 27 28 "github.com/m3db/m3/src/dbnode/encoding" 29 "github.com/m3db/m3/src/dbnode/namespace" 30 "github.com/m3db/m3/src/dbnode/storage/index" 31 "github.com/m3db/m3/src/dbnode/topology" 32 "github.com/m3db/m3/src/dbnode/x/xpool" 33 xerrors "github.com/m3db/m3/src/x/errors" 34 "github.com/m3db/m3/src/x/ident" 35 "github.com/m3db/m3/src/x/serialize" 36 xtime "github.com/m3db/m3/src/x/time" 37 ) 38 39 type fetchStateType byte 40 41 const ( 42 fetchTaggedFetchState fetchStateType = iota 43 aggregateFetchState 44 ) 45 46 const ( 47 maxUint = ^uint(0) 48 maxInt = int(maxUint >> 1) 49 ) 50 51 var ( 52 errFetchStateStillProcessing = errors.New("[invariant violated] fetch " + 53 "state is still processing, unable to create response") 54 ) 55 56 type fetchState struct { 57 sync.Cond 58 sync.Mutex 59 refCounter 60 61 fetchTaggedOp *fetchTaggedOp 62 aggregateOp *aggregateOp 63 64 nsID ident.ID 65 tagResultAccumulator fetchTaggedResultAccumulator 66 err error 67 68 pool fetchStatePool 69 70 // NB: stateType determines which type of op this fetchState 71 // is used for - fetchTagged or Aggregate. 72 stateType fetchStateType 73 74 done bool 75 } 76 77 func newFetchState(pool fetchStatePool) *fetchState { 78 f := &fetchState{ 79 tagResultAccumulator: newFetchTaggedResultAccumulator(), 80 pool: pool, 81 } 82 f.destructorFn = f.close // Set refCounter completion as close 83 f.L = f // Set the embedded condition locker to the embedded mutex 84 return f 85 } 86 87 func (f *fetchState) close() { 88 if f.nsID != nil { 89 f.nsID.Finalize() 90 f.nsID = nil 91 } 92 if f.fetchTaggedOp != nil { 93 f.fetchTaggedOp.decRef() 94 f.fetchTaggedOp = nil 95 } 96 if f.aggregateOp != nil { 97 f.aggregateOp.decRef() 98 f.aggregateOp = nil 99 } 100 f.err = nil 101 f.done = false 102 f.tagResultAccumulator.Clear() 103 104 if f.pool == nil { 105 return 106 } 107 f.pool.Put(f) 108 } 109 110 func (f *fetchState) ResetFetchTagged( 111 startTime xtime.UnixNano, 112 endTime xtime.UnixNano, 113 op *fetchTaggedOp, topoMap topology.Map, 114 majority int, 115 consistencyLevel topology.ReadConsistencyLevel, 116 ) { 117 op.incRef() // take a reference to the provided op 118 f.fetchTaggedOp = op 119 f.stateType = fetchTaggedFetchState 120 f.tagResultAccumulator.Reset(startTime, endTime, topoMap, majority, consistencyLevel) 121 } 122 123 func (f *fetchState) ResetAggregate( 124 startTime xtime.UnixNano, 125 endTime xtime.UnixNano, 126 op *aggregateOp, topoMap topology.Map, 127 majority int, 128 consistencyLevel topology.ReadConsistencyLevel, 129 ) { 130 op.incRef() // take a reference to the provided op 131 f.aggregateOp = op 132 f.stateType = aggregateFetchState 133 f.tagResultAccumulator.Reset(startTime, endTime, topoMap, majority, consistencyLevel) 134 } 135 136 func (f *fetchState) completionFn( 137 result interface{}, 138 resultErr error, 139 ) { 140 if IsBadRequestError(resultErr) { 141 // Wrap with invalid params and non-retryable so it is 142 // not retried. 143 resultErr = xerrors.NewInvalidParamsError(resultErr) 144 resultErr = xerrors.NewNonRetryableError(resultErr) 145 } 146 147 f.Lock() 148 defer func() { 149 f.Unlock() 150 f.decRef() // release ref held onto by the hostQueue (via op.completionFn) 151 }() 152 153 if f.done { 154 // i.e. we've already failed, no need to continue processing any additional 155 // responses we receive 156 return 157 } 158 159 var ( 160 done bool 161 err error 162 ) 163 switch r := result.(type) { 164 case fetchTaggedResultAccumulatorOpts: 165 done, err = f.tagResultAccumulator.AddFetchTaggedResponse(r, resultErr) 166 case aggregateResultAccumulatorOpts: 167 done, err = f.tagResultAccumulator.AddAggregateResponse(r, resultErr) 168 default: 169 // should never happen 170 done = true 171 err = fmt.Errorf( 172 "[invariant violated] expected result to be one of %v, received: %v", 173 []string{"fetchTaggedResultAccumulatorOpts", "aggregateResultAccumulatorOpts"}, 174 result) 175 } 176 177 if done { 178 f.markDoneWithLock(err) 179 } 180 } 181 182 func (f *fetchState) markDoneWithLock(err error) { 183 f.done = true 184 f.err = err 185 f.Signal() 186 } 187 188 func (f *fetchState) asTaggedIDsIterator( 189 pools fetchTaggedPools, 190 limit int, 191 ) (TaggedIDsIterator, FetchResponseMetadata, error) { 192 f.Lock() 193 defer f.Unlock() 194 195 if expected := fetchTaggedFetchState; f.stateType != expected { 196 return nil, FetchResponseMetadata{}, 197 fmt.Errorf("unexpected fetch state: expected=%v, actual=%v", 198 expected, f.stateType) 199 } 200 201 if !f.done { 202 return nil, FetchResponseMetadata{}, errFetchStateStillProcessing 203 } 204 205 if err := f.err; err != nil { 206 return nil, FetchResponseMetadata{}, err 207 } 208 209 if limit == 0 { 210 limit = maxInt 211 } 212 return f.tagResultAccumulator.AsTaggedIDsIterator(limit, pools) 213 } 214 215 func (f *fetchState) asEncodingSeriesIterators( 216 pools fetchTaggedPools, 217 descr namespace.SchemaDescr, 218 opts index.IterationOptions, 219 limit int, 220 ) (encoding.SeriesIterators, FetchResponseMetadata, error) { 221 f.Lock() 222 defer f.Unlock() 223 224 if expected := fetchTaggedFetchState; f.stateType != expected { 225 return nil, FetchResponseMetadata{}, 226 fmt.Errorf("unexpected fetch state: expected=%v, actual=%v", 227 expected, f.stateType) 228 } 229 230 if !f.done { 231 return nil, FetchResponseMetadata{}, errFetchStateStillProcessing 232 } 233 234 if err := f.err; err != nil { 235 return nil, FetchResponseMetadata{}, err 236 } 237 238 if limit == 0 { 239 limit = maxInt 240 } 241 return f.tagResultAccumulator.AsEncodingSeriesIterators(limit, pools, descr, opts) 242 } 243 244 func (f *fetchState) asAggregatedTagsIterator(pools fetchTaggedPools, limit int) ( 245 AggregatedTagsIterator, FetchResponseMetadata, error) { 246 f.Lock() 247 defer f.Unlock() 248 249 if expected := aggregateFetchState; f.stateType != expected { 250 return nil, FetchResponseMetadata{}, 251 fmt.Errorf("unexpected fetch state: expected=%v, actual=%v", 252 expected, f.stateType) 253 } 254 255 if !f.done { 256 return nil, FetchResponseMetadata{}, errFetchStateStillProcessing 257 } 258 259 if err := f.err; err != nil { 260 return nil, FetchResponseMetadata{}, err 261 } 262 263 if limit == 0 { 264 limit = maxInt 265 } 266 return f.tagResultAccumulator.AsAggregatedTagsIterator(limit, pools) 267 } 268 269 // NB(prateek): this is backed by the sessionPools struct, but we're restricting it to a narrow 270 // interface to force the fetchTagged code-paths to be explicit about the pools they need access 271 // to. The alternative is to either expose the sessionPools struct (which is a worse abstraction), 272 // or make a new concrete implemtation (which requires an extra alloc). Chosing the best of the 273 // three options and leaving as the interface below. 274 type fetchTaggedPools interface { 275 MultiReaderIteratorArray() encoding.MultiReaderIteratorArrayPool 276 MultiReaderIterator() encoding.MultiReaderIteratorPool 277 SeriesIterator() encoding.SeriesIteratorPool 278 CheckedBytesWrapper() xpool.CheckedBytesWrapperPool 279 ID() ident.Pool 280 ReaderSliceOfSlicesIterator() *readerSliceOfSlicesIteratorPool 281 TagDecoder() serialize.TagDecoderPool 282 }