2017-05-26 04:48:44 +08:00
/*
* Stats . h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
2020-07-10 07:39:15 +08:00
# ifndef FDBRPC_STATS_H
# define FDBRPC_STATS_H
2017-05-26 04:48:44 +08:00
# pragma once
// Yet another performance statistics interface
/*
struct MyCounters {
CounterCollection cc ;
Counter foo , bar , baz ;
MyCounters ( ) : foo ( " foo " , cc ) , bar ( " bar " , cc ) , baz ( " baz " , cc ) { }
} ;
*/
2018-08-11 05:36:45 +08:00
# include <cstdint>
# include <cstddef>
2018-10-20 01:30:13 +08:00
# include "flow/flow.h"
# include "flow/TDMetric.actor.h"
2020-07-10 07:39:15 +08:00
# include "fdbrpc/ContinuousSample.h"
2017-05-26 04:48:44 +08:00
struct ICounter {
// All counters have a name and value
virtual std : : string const & getName ( ) const = 0 ;
virtual int64_t getValue ( ) const = 0 ;
// Counters may also have rate and roughness
virtual bool hasRate ( ) const = 0 ;
virtual double getRate ( ) const = 0 ;
virtual bool hasRoughness ( ) const = 0 ;
virtual double getRoughness ( ) const = 0 ;
virtual void resetInterval ( ) = 0 ;
virtual void remove ( ) { }
} ;
2019-07-09 05:01:04 +08:00
template < >
struct Traceable < ICounter * > : std : : true_type {
static std : : string toString ( ICounter const * counter ) {
if ( counter - > hasRate ( ) & & counter - > hasRoughness ( ) ) {
return format ( " %g %g %lld " , counter - > getRate ( ) , counter - > getRoughness ( ) , ( long long ) counter - > getValue ( ) ) ;
}
else {
return format ( " %lld " , ( long long ) counter - > getValue ( ) ) ;
}
}
} ;
2017-05-26 04:48:44 +08:00
struct CounterCollection {
CounterCollection ( std : : string name , std : : string id = std : : string ( ) ) : name ( name ) , id ( id ) { }
std : : vector < struct ICounter * > counters , counters_to_remove ;
~ CounterCollection ( ) { for ( auto c : counters_to_remove ) c - > remove ( ) ; }
std : : string name ;
std : : string id ;
2019-07-09 05:01:04 +08:00
void logToTraceEvent ( TraceEvent & te ) const ;
2017-05-26 04:48:44 +08:00
} ;
2018-12-01 02:46:04 +08:00
struct Counter : ICounter , NonCopyable {
2017-05-26 04:48:44 +08:00
public :
typedef int64_t Value ;
Counter ( std : : string const & name , CounterCollection & collection ) ;
void operator + = ( Value delta ) ;
void operator + + ( ) { * this + = 1 ; }
void clear ( ) ;
void resetInterval ( ) ;
std : : string const & getName ( ) const { return name ; }
Value getIntervalDelta ( ) const { return interval_delta ; }
Value getValue ( ) const { return interval_start_value + interval_delta ; }
double getRate ( ) const ; // dValue / dt
double getRoughness ( ) const ; // value deltas come in "clumps" of this many ( 1 = periodic, 2 = poisson, 10 = periodic clumps of 10 (nearly) simultaneous delta )
bool hasRate ( ) const { return true ; }
bool hasRoughness ( ) const { return true ; }
private :
std : : string name ;
double interval_start , last_event , interval_sq_time ;
Value interval_delta , interval_start_value ;
Int64MetricHandle metric ;
} ;
2019-07-09 05:01:04 +08:00
template < >
struct Traceable < Counter > : std : : true_type {
static std : : string toString ( Counter const & counter ) {
return Traceable < ICounter * > : : toString ( ( ICounter const * ) & counter ) ;
}
} ;
2017-05-26 04:48:44 +08:00
template < class F >
2018-12-01 02:46:04 +08:00
struct SpecialCounter : ICounter , FastAllocated < SpecialCounter < F > > , NonCopyable {
2017-05-26 04:48:44 +08:00
SpecialCounter ( CounterCollection & collection , std : : string const & name , F & & f ) : name ( name ) , f ( f ) { collection . counters . push_back ( this ) ; collection . counters_to_remove . push_back ( this ) ; }
virtual void remove ( ) { delete this ; }
virtual std : : string const & getName ( ) const { return name ; }
virtual int64_t getValue ( ) const { return f ( ) ; }
virtual void resetInterval ( ) { }
virtual bool hasRate ( ) const { return false ; }
virtual double getRate ( ) const { throw internal_error ( ) ; }
virtual bool hasRoughness ( ) const { return false ; }
virtual double getRoughness ( ) const { throw internal_error ( ) ; }
std : : string name ;
F f ;
} ;
template < class F >
static void specialCounter ( CounterCollection & collection , std : : string const & name , F & & f ) { new SpecialCounter < F > ( collection , name , std : : move ( f ) ) ; }
2020-10-31 07:20:08 +08:00
Future < Void > traceCounters ( std : : string const & traceEventName , UID const & traceEventID , double const & interval ,
CounterCollection * const & counters , std : : string const & trackLatestName = std : : string ( ) ,
std : : function < void ( TraceEvent & ) > const & decorator = [ ] ( TraceEvent & te ) { } ) ;
2019-02-08 05:39:22 +08:00
2018-12-01 02:46:04 +08:00
class LatencyBands {
public :
2019-02-08 05:39:22 +08:00
LatencyBands ( std : : string name , UID id , double loggingInterval ) : name ( name ) , id ( id ) , loggingInterval ( loggingInterval ) , cc ( nullptr ) , filteredCount ( nullptr ) { }
2018-12-01 02:46:04 +08:00
void addThreshold ( double value ) {
if ( value > 0 & & bands . count ( value ) = = 0 ) {
2019-01-19 08:18:34 +08:00
if ( bands . size ( ) = = 0 ) {
2019-02-08 05:39:22 +08:00
ASSERT ( ! cc & & ! filteredCount ) ;
cc = new CounterCollection ( name , id . toString ( ) ) ;
logger = traceCounters ( name , id , loggingInterval , cc , id . toString ( ) + " / " + name ) ;
filteredCount = new Counter ( " Filtered " , * cc ) ;
2019-01-19 08:18:34 +08:00
insertBand ( std : : numeric_limits < double > : : infinity ( ) ) ;
}
insertBand ( value ) ;
2018-12-01 02:46:04 +08:00
}
}
void addMeasurement ( double measurement , bool filtered = false ) {
2019-01-19 08:18:34 +08:00
if ( filtered & & filteredCount ) {
+ + ( * filteredCount ) ;
}
else if ( bands . size ( ) > 0 ) {
auto itr = bands . upper_bound ( measurement ) ;
ASSERT ( itr ! = bands . end ( ) ) ;
+ + ( * itr - > second ) ;
2018-12-01 02:46:04 +08:00
}
}
2019-01-19 08:18:34 +08:00
void clearBands ( ) {
2019-02-08 05:39:22 +08:00
logger = Void ( ) ;
2019-01-19 08:18:34 +08:00
for ( auto itr : bands ) {
delete itr . second ;
2018-12-01 02:46:04 +08:00
}
2019-01-19 08:18:34 +08:00
bands . clear ( ) ;
delete filteredCount ;
2019-02-08 05:39:22 +08:00
delete cc ;
filteredCount = nullptr ;
cc = nullptr ;
2019-01-19 08:18:34 +08:00
}
~ LatencyBands ( ) {
clearBands ( ) ;
2018-12-01 02:46:04 +08:00
}
private :
std : : map < double , Counter * > bands ;
2019-01-19 08:18:34 +08:00
Counter * filteredCount ;
2018-12-01 02:46:04 +08:00
std : : string name ;
2019-02-08 05:39:22 +08:00
UID id ;
double loggingInterval ;
CounterCollection * cc ;
Future < Void > logger ;
2019-01-19 08:18:34 +08:00
void insertBand ( double value ) {
2019-02-08 05:39:22 +08:00
bands . insert ( std : : make_pair ( value , new Counter ( format ( " Band%f " , value ) , * cc ) ) ) ;
2019-01-19 08:18:34 +08:00
}
2018-12-01 02:46:04 +08:00
} ;
2020-07-10 07:39:15 +08:00
class LatencySample {
public :
LatencySample ( std : : string name , UID id , double loggingInterval , int sampleSize ) : name ( name ) , id ( id ) , sample ( sampleSize ) , sampleStart ( now ( ) ) {
logger = recurring ( [ this ] ( ) { logSample ( ) ; } , loggingInterval ) ;
}
void addMeasurement ( double measurement ) {
sample . addSample ( measurement ) ;
}
private :
std : : string name ;
UID id ;
double sampleStart ;
ContinuousSample < double > sample ;
Future < Void > logger ;
void logSample ( ) {
TraceEvent ( name . c_str ( ) , id )
. detail ( " Count " , sample . getPopulationSize ( ) )
. detail ( " Elapsed " , now ( ) - sampleStart )
. detail ( " Min " , sample . min ( ) )
. detail ( " Max " , sample . max ( ) )
. detail ( " Mean " , sample . mean ( ) )
. detail ( " Median " , sample . median ( ) )
. detail ( " P25 " , sample . percentile ( 0.25 ) )
. detail ( " P90 " , sample . percentile ( 0.9 ) )
. detail ( " P95 " , sample . percentile ( 0.95 ) )
. detail ( " P99 " , sample . percentile ( 0.99 ) )
. detail ( " P99.9 " , sample . percentile ( 0.999 ) )
. trackLatest ( id . toString ( ) + " / " + name ) ;
sample . clear ( ) ;
sampleStart = now ( ) ;
}
} ;
2018-08-11 05:36:45 +08:00
# endif