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 "unsafe" 23 ) 24 25 type bucket struct { 26 numSuccesses uint32 27 numFailures uint32 28 } 29 30 func newCallCounter() *callCounter { 31 return &callCounter{ 32 activeBucket: unsafe.Pointer(&bucket{}), 33 inactiveBucket: &bucket{}, 34 } 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 unsafe.Pointer 44 // so picker does not have to grab a mutex per RPC, the critical path. 45 activeBucket unsafe.Pointer // bucket 46 inactiveBucket *bucket 47 } 48 49 func (cc *callCounter) clear() { 50 atomic.StorePointer(&cc.activeBucket, unsafe.Pointer(&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 := (*bucket)(atomic.SwapPointer(&cc.activeBucket, unsafe.Pointer(ib))) 62 cc.inactiveBucket = &bucket{ 63 numSuccesses: atomic.LoadUint32(&ab.numSuccesses), 64 numFailures: atomic.LoadUint32(&ab.numFailures), 65 } 66 } 67