import { MetricType } from '../MetricType' ; import { Metric } from '../domain' ; import { LongBucketCounter } from './LongBucketCounter' ; import { Long } from '../../domain' ; const BigNumber = require ( "long" ) ; export interface IFastCompass extends Metric { param duration param subCategory record ( duration , subCategory ) ; return getMethodCountPerCategory ( startTime ? ) : Map < string , Map < number , Long > > ; return getMethodRtPerCategory ( startTime ? ) : Map < string , Map < number , Long > > ; return getCountAndRtPerCategory ( startTime ) : Map < string , Map < number , Long > > ; return getBucketInterval ( ) ; } /**
 * 控制总次数的bit数, 理论统计上限为 2 ^ (64 -38 -1) = 33554432
 * This magic number divide a long into two parts,
 * where the higher part is used to record the total number of method invocations,
 * and the lower part is used to record the total method execution time.
 * The max number of count per collecting interval will be 2 ^ (64 -38 -1) = 33554432
 */ const COUNT_OFFSET = 38 ; /**
 * 次数统计的累加基数，和rt相加得到实际更新到LongAdder的数
 * The base number of count that is added to total rt,
 * to derive a number which will be added to  LongAdder const COUNT_BASE = new BigNumber ( 1 ) . shiftLeft ( 38 ) ; /**
 * 总数和此数进行二进制与得到总rt统计
 * The base number is used to do BITWISE AND operation with the value of  LongAdder * to derive the total number of execution time
  const RT_BITWISE_AND_BASE = new BigNumber ( 1 ) . shiftLeft ( 38 ) . sub ( 1 ) ; const MAX_SUBCATEGORY_SIZE = 20 ; const DEFAULT_BUCKET_COUNT = 10 ; /**
 * 通过1个LongAdder来同时完成count和rt的累加操作
 * Java里面1个Long有64个bit, 除去最高位表示符号的1个bit，还有63个bit可以使用
 * 在一个不超过60s统计周期内，方法调用的总次数和总次数其实完全用不到63个bit
 * 所以可以将这两个统计项放到一个long里面来表示
 * 这里高位的25个bit表示统计周期内调用总次数，后38位表示总rt
 */ export class BaseFastCompass implements IFastCompass { type = MetricType . FASTCOMPASS ; bucketInterval ; numberOfBuckets ; maxCategoryCount ; subCategories : Map < string , LongBucketCounter > = new Map ( ) ; constructor ( bucketInterval = 60 , numberOfBuckets = DEFAULT_BUCKET_COUNT , maxCategoryCount = MAX_SUBCATEGORY_SIZE ) { this . bucketInterval = bucketInterval ; this . numberOfBuckets = numberOfBuckets ; this . maxCategoryCount = maxCategoryCount ; } record ( duration , subCategory ) { if ( duration < 0 || subCategory == null ) { return ; } if ( ! this . subCategories . has ( subCategory ) ) { if ( this . subCategories . size >= this . maxCategoryCount ) { return ; } this . subCategories . set ( subCategory , new LongBucketCounter ( this . bucketInterval , this . numberOfBuckets , false ) ) ; } let data = COUNT_BASE . add ( duration ) ; this . subCategories . get ( subCategory ) . update ( data ) ; } getMethodCountPerCategory ( startTime = 0 ) { let countPerCategory = new Map ( ) ; for ( let [ key , value ] of this . subCategories . entries ( ) ) { let bucketCount = new Map ( ) ; for ( let [ innerKey , innerValue ] of value . getBucketCounts ( startTime ) . entries ( ) ) { bucketCount . set ( innerKey , < Long > innerValue . shiftRight ( COUNT_OFFSET ) ) ; } countPerCategory . set ( key , bucketCount ) ; } return countPerCategory ; } getMethodRtPerCategory ( startTime = 0 ) { let rtPerCategory = new Map ( ) ; for ( let [ key , value ] of this . subCategories . entries ( ) ) { let bucketCount = new Map ( ) ; for ( let [ innerKey , innerValue ] of value . getBucketCounts ( startTime ) . entries ( ) ) { bucketCount . set ( innerKey , < Long > innerValue . and ( RT_BITWISE_AND_BASE ) ) ; } rtPerCategory . set ( key , bucketCount ) ; } return rtPerCategory ; } getBucketInterval ( ) { return this . bucketInterval ; } getCountAndRtPerCategory ( startTime = 0 ) { let countAndRtPerCategory = new Map ( ) ; for ( let [ key , value ] of this . subCategories . entries ( ) ) { let bucketCount = new Map ( ) ; for ( let [ innerKey , innerValue ] of value . getBucketCounts ( startTime ) . entries ( ) ) { bucketCount . set ( innerKey , innerValue ) ; } countAndRtPerCategory . set ( key , bucketCount ) ; } return countAndRtPerCategory ; } }