google.golang.org/grpc@v1.72.2/xds/internal/balancer/outlierdetection/callcounter.go (about) 1 /* 2 * 3 * Copyright 2022 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package outlierdetection 19 20 import ( 21 "sync/atomic" 22 ) 23 24 type bucket struct { 25 numSuccesses uint32 26 numFailures uint32 27 } 28 29 func newCallCounter() *callCounter { 30 cc := &callCounter{ 31 inactiveBucket: &bucket{}, 32 } 33 cc.activeBucket.Store(&bucket{}) 34 return cc 35 } 36 37 // callCounter has two buckets, which each count successful and failing RPC's. 38 // The activeBucket is used to actively count any finished RPC's, and the 39 // inactiveBucket is populated with this activeBucket's data every interval for 40 // use by the Outlier Detection algorithm. 41 type callCounter struct { 42 // activeBucket updates every time a call finishes (from picker passed to 43 // Client Conn), so protect pointer read with atomic load of the pointer 44 // so picker does not have to grab a mutex per RPC, the critical path. 45 activeBucket atomic.Pointer[bucket] 46 inactiveBucket *bucket 47 } 48 49 func (cc *callCounter) clear() { 50 cc.activeBucket.Store(&bucket{}) 51 cc.inactiveBucket = &bucket{} 52 } 53 54 // "When the timer triggers, the inactive bucket is zeroed and swapped with the 55 // active bucket. Then the inactive bucket contains the number of successes and 56 // failures since the last time the timer triggered. Those numbers are used to 57 // evaluate the ejection criteria." - A50. 58 func (cc *callCounter) swap() { 59 ib := cc.inactiveBucket 60 *ib = bucket{} 61 ab := cc.activeBucket.Swap(ib) 62 cc.inactiveBucket = &bucket{ 63 numSuccesses: atomic.LoadUint32(&ab.numSuccesses), 64 numFailures: atomic.LoadUint32(&ab.numFailures), 65 } 66 }