Skip to content

Commit

Permalink
[Main][Task]24841107: Refactor throttleMgr to support multiple messag…
Browse files Browse the repository at this point in the history
…e keys (#2133)

* update throttlemgr v3

* update shrinkwrap
  • Loading branch information
Karlie-777 committed Aug 18, 2023
1 parent 0e06711 commit bd20503
Show file tree
Hide file tree
Showing 9 changed files with 935 additions and 462 deletions.
1 change: 0 additions & 1 deletion .aiAutoMinify.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"eStorageType",
"FieldType",
"eDistributedTracingModes",
"IThrottleMsgKey",
"eRequestHeaders",
"DataPointType",
"DependencyKind",
Expand Down
545 changes: 275 additions & 270 deletions common/config/rush/npm-shrinkwrap.json

Large diffs are not rendered by default.

641 changes: 534 additions & 107 deletions shared/AppInsightsCommon/Tests/Unit/src/ThrottleMgr.tests.ts

Large diffs are not rendered by default.

16 changes: 0 additions & 16 deletions shared/AppInsightsCommon/src/Enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,3 @@ export const DistributedTracingModes = createEnumStyle<typeof eDistributedTracin
W3C: eDistributedTracingModes.W3C
});
export type DistributedTracingModes = number | eDistributedTracingModes;

export const enum IThrottleMsgKey {
/**
* Default Message key for non pre-defined message
*/
default = 0,
/**
* Message key for ikey Deprecation
*/
ikeyDeprecate = 1,
/**
* Message key for cdn Deprecation
*/
cdnDeprecate = 2
}

6 changes: 6 additions & 0 deletions shared/AppInsightsCommon/src/Interfaces/IConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IConfiguration, ICustomProperties, isNullOrUndefined } from "@microsoft
import { DistributedTracingModes } from "../Enums";
import { IRequestContext } from "./IRequestContext";
import { IStorageBuffer } from "./IStorageBuffer";
import { IThrottleMgrConfig } from "./IThrottleMgr";

/**
* Configuration settings for how telemetry is sent
Expand Down Expand Up @@ -371,6 +372,11 @@ export interface IConfig {
* should not be excluded.
*/
addIntEndpoints?: boolean;

/**
* [Optional] set throttle mgr configuration
*/
throttleMgrCfg?: IThrottleMgrConfig;
}

export class ConfigurationManager {
Expand Down
6 changes: 0 additions & 6 deletions shared/AppInsightsCommon/src/Interfaces/IThrottleMgr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { IThrottleMsgKey } from "../Enums";

/**
* Identifies limit number/percentage of items sent per time
Expand Down Expand Up @@ -48,11 +47,6 @@ export interface IThrottleInterval {
* Identifies basic config
*/
export interface IThrottleMgrConfig {
/**
* Identifies message key to be used for local storage key
* Default: IThrottleMsgKey.default
*/
msgKey?: IThrottleMsgKey;

/**
* Identifies if throttle is disabled
Expand Down
177 changes: 117 additions & 60 deletions shared/AppInsightsCommon/src/ThrottleMgr.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
IAppInsightsCore, IDiagnosticLogger, IUnloadHookContainer, _eInternalMessageId, _throwInternal, arrForEach, arrIndexOf,
createDynamicConfig, createUnloadHookContainer, eLoggingSeverity, isNotNullOrUndefined, isNullOrUndefined, onConfigChange, randomValue,
safeGetLogger, strTrim
IAppInsightsCore, IConfiguration, IDiagnosticLogger, _eInternalMessageId, _throwInternal, arrIndexOf, eLoggingSeverity,
isNotNullOrUndefined, isNullOrUndefined, onConfigChange, randomValue, safeGetLogger, strTrim
} from "@microsoft/applicationinsights-core-js";
import { IThrottleMsgKey } from "./Enums";
import { arrForEach, objForEachKey } from "@nevware21/ts-utils";
import { IThrottleInterval, IThrottleLocalStorageObj, IThrottleMgrConfig, IThrottleResult } from "./Interfaces/IThrottleMgr";
import { utlCanUseLocalStorage, utlGetLocalStorage, utlSetLocalStorage } from "./StorageHelperFuncs";
import { IConfig } from "./applicationinsights-common";

const THROTTLE_STORAGE_PREFIX = "appInsightsThrottle";

Expand All @@ -16,25 +16,25 @@ interface SendMsgParameter {
}

export class ThrottleMgr {
public canThrottle: () => boolean;
public sendMessage: (msgID: _eInternalMessageId, message: string, severity?: eLoggingSeverity) => IThrottleResult | null;
public canThrottle: (msgId: _eInternalMessageId | number) => boolean;
public sendMessage: (msgId: _eInternalMessageId, message: string, severity?: eLoggingSeverity) => IThrottleResult | null;
public getConfig: () => IThrottleMgrConfig;
public isTriggered: () => boolean; // this function is to get previous triggered status
public isTriggered: (msgId: _eInternalMessageId | number) => boolean; // this function is to get previous triggered status
public isReady: () => boolean
public onReadyState: (isReady?: boolean) => boolean;
public flush: () => boolean;
public onReadyState: (isReady?: boolean, flushAll?: boolean) => boolean;
public flush: (msgId: _eInternalMessageId | number) => boolean;
public flushAll: () => boolean;
public config: IThrottleMgrConfig;

constructor(config?: IThrottleMgrConfig, core?: IAppInsightsCore, namePrefix?: string, unloadHookContainer?: IUnloadHookContainer) {
constructor(core: IAppInsightsCore, namePrefix?: string) {
let _self = this;
let _canUseLocalStorage: boolean;
let _logger: IDiagnosticLogger | null | undefined;
let _config: IThrottleMgrConfig;
let _localStorageName: string | null;
let _localStorageObj: IThrottleLocalStorageObj | null | undefined;
let _isTriggered: boolean; //_isTriggered is to make sure that we only trigger throttle once a day
let _localStorageObj: {[msgKey: number]: IThrottleLocalStorageObj | null | undefined};
let _isTriggered: {[msgKey: number]: boolean}; //_isTriggered is to make sure that we only trigger throttle once a day
let _namePrefix: string;
let _queue: Array<SendMsgParameter>;
let _queue: {[msgKey: number]: Array<SendMsgParameter>};
let _isReady: boolean = false;
let _isSpecificDaysGiven: boolean = false;

Expand All @@ -55,17 +55,18 @@ export class ThrottleMgr {
* because we only allow triggering sendMessage() once a day.
* @returns if the current date is the valid date to send message
*/
_self.canThrottle = (): boolean => {
return _canThrottle(_config, _canUseLocalStorage, _localStorageObj);
_self.canThrottle = (msgId: _eInternalMessageId | number ): boolean => {
let localObj = _getLocalStorageObjByKey(msgId);
return _canThrottle(_config, _canUseLocalStorage, localObj);
}

/**
* Check if throttle is triggered on current day(UTC)
* if canThrottle returns false, isTriggered will return false
* @returns if throttle is triggered on current day(UTC)
*/
_self.isTriggered = (): boolean => {
return _isTriggered;
_self.isTriggered = (msgId: _eInternalMessageId | number): boolean => {
return _isTrigger(msgId);
}

/**
Expand All @@ -79,14 +80,15 @@ export class ThrottleMgr {
}

/**
* Flush all message in queue with isReady state set to true.
* Flush all message with given message key in queue with isReady state set to true.
* @returns if message queue is flushed
*/
_self.flush = (): boolean => {
_self.flush = (msgId: _eInternalMessageId | number): boolean => {
try {
if (_isReady && _queue.length > 0) {
let items = _queue.slice(0);
_queue = [];
let queue = _getQueueByKey(msgId);
if (queue && queue.length > 0) {
let items = queue.slice(0);
_queue[msgId] = []
arrForEach(items, (item: SendMsgParameter) => {
_flushMessage(item.msgID, item.message, item.severity, false);
});
Expand All @@ -98,43 +100,71 @@ export class ThrottleMgr {
return false;
}

/**
* Flush all message in queue with isReady state set to true.
* @returns if message queue is flushed
*/
_self.flushAll = (): boolean => {
try {
if (_queue) {
let result = true;
objForEachKey(_queue, (key) => {
let isFlushed = _self.flush(parseInt(key));
result = result && isFlushed;
});
return result;
}

} catch(err) {
// eslint-disable-next-line no-empty
}
return false;
}

/**
* Set isReady State
* if isReady set to true, message queue will be flushed automatically.
* @param isReady isReady State
* @pa
* @returns if message queue is flushed
*/
_self.onReadyState = (isReady?: boolean): boolean => {
_self.onReadyState = (isReady?: boolean, flushAll: boolean = true): boolean => {
_isReady = isNullOrUndefined(isReady)? true : isReady;
return _self.flush();
if (_isReady && flushAll) {
return _self.flushAll();
}
return null;
}

_self.sendMessage = (msgID: _eInternalMessageId, message: string, severity?: eLoggingSeverity): IThrottleResult | null => {
_self.sendMessage = (msgID: _eInternalMessageId | number, message: string, severity?: eLoggingSeverity): IThrottleResult | null => {
return _flushMessage(msgID, message, severity, true);

}

function _flushMessage(msgID: _eInternalMessageId, message: string, severity?: eLoggingSeverity, saveUnsentMsg?: boolean) {
function _flushMessage(msgID: _eInternalMessageId | number, message: string, severity?: eLoggingSeverity, saveUnsentMsg?: boolean) {
if (_isReady) {
let isSampledIn = _canSampledIn();
if (!isSampledIn) {
return;
}
let canThrottle = _canThrottle(_config, _canUseLocalStorage, _localStorageObj);
let localStorageObj = _getLocalStorageObjByKey(msgID);
let canThrottle = _canThrottle(_config, _canUseLocalStorage, localStorageObj);
let throttled = false;
let number = 0;
let isTriggered = _isTrigger(msgID);
try {
if (canThrottle && !_isTriggered) {
number = Math.min(_config.limit.maxSendNumber, _localStorageObj.count + 1);
_localStorageObj.count = 0;
if (canThrottle && !isTriggered) {
number = Math.min(_config.limit.maxSendNumber, localStorageObj.count + 1);
localStorageObj.count = 0;
throttled = true;
_isTriggered = true;
_localStorageObj.preTriggerDate = new Date();
_isTriggered[msgID] = true;
localStorageObj.preTriggerDate = new Date();
} else {
_isTriggered = canThrottle;
_localStorageObj.count += 1;
_isTriggered[msgID] = canThrottle;
localStorageObj.count += 1;
}
_resetLocalStorage(_logger, _localStorageName, _localStorageObj);
let localStorageName = _getLocalStorageName(msgID);
_resetLocalStorage(_logger, localStorageName, localStorageObj);
for (let i = 0; i < number; i++) {
_sendMessage(msgID, _logger, message, severity);
}
Expand All @@ -147,7 +177,8 @@ export class ThrottleMgr {
} as IThrottleResult;
} else {
if (!!saveUnsentMsg) {
_queue.push({
let queue = _getQueueByKey(msgID);
queue.push({
msgID: msgID,
message: message,
severity: severity
Expand All @@ -159,21 +190,18 @@ export class ThrottleMgr {

function _initConfig() {
_logger = safeGetLogger(core);
_isTriggered = false;
_queue = [];
_isTriggered = {};
_localStorageObj = {};
_queue = {};
_namePrefix = isNotNullOrUndefined(namePrefix)? namePrefix : "";
unloadHookContainer = unloadHookContainer || createUnloadHookContainer();
// Make sure the root config is dynamic as it may be the global config
config = createDynamicConfig(config as any || {}, null, _logger).cfg;

let unloadHook = onConfigChange(config, () => {
core.addUnloadHook(onConfigChange<IConfig & IConfiguration>(core.config, (details) => {
let coreConfig = details.cfg;
_canUseLocalStorage = utlCanUseLocalStorage();

let configMgr = config || {};
let configMgr = coreConfig.throttleMgrCfg || {};
_config = {} as any;
_config.disabled = !!configMgr.disabled;

_config.msgKey = configMgr.msgKey || IThrottleMsgKey.default;

let configInterval = configMgr.interval || {};
_isSpecificDaysGiven = configInterval?.daysOfMonth && configInterval?.daysOfMonth.length > 0;
Expand All @@ -185,16 +213,8 @@ export class ThrottleMgr {
maxSendNumber: configMgr.limit?.maxSendNumber || 1
};
_config.limit = limit;
_localStorageName = _getLocalStorageName(_config.msgKey, _namePrefix);

if (_canUseLocalStorage && _localStorageName) {
_localStorageObj = _getLocalStorageObj(utlGetLocalStorage(_logger, _localStorageName), _logger, _localStorageName);
}
if (_localStorageObj) {
_isTriggered = _isTriggeredOnCurDate(_localStorageObj.preTriggerDate);
}
});
unloadHookContainer && unloadHookContainer.add(unloadHook);

}));
}

function _getIntervalConfig(interval: IThrottleInterval) {
Expand Down Expand Up @@ -244,7 +264,7 @@ export class ThrottleMgr {
return false;
}

function _getLocalStorageName(msgKey: IThrottleMsgKey, prefix?: string) {
function _getLocalStorageName(msgKey: _eInternalMessageId | number, prefix?: string) {
let fix = isNotNullOrUndefined(prefix)? prefix : "";
if (msgKey) {
return THROTTLE_STORAGE_PREFIX + fix + "-" + msgKey;
Expand Down Expand Up @@ -276,15 +296,15 @@ export class ThrottleMgr {
} as IThrottleLocalStorageObj;
if (value) {
let obj = JSON.parse(value);
return {
let curObj = {
date: _getThrottleDate(obj.date) || storageObj.date,
count: obj.count || storageObj.count,
preTriggerDate: obj.preTriggerDate? _getThrottleDate(obj.preTriggerDate) : undefined
} as IThrottleLocalStorageObj;
return curObj;
} else {
_resetLocalStorage(logger, storageName, storageObj);
return storageObj;

}
} catch(e) {
// eslint-disable-next-line no-empty
Expand Down Expand Up @@ -341,5 +361,42 @@ export class ThrottleMgr {
function _canSampledIn() {
return randomValue(1000000) <= _config.limit.samplingRate;
}

function _getLocalStorageObjByKey(key: _eInternalMessageId | number) {
try {
let curObj = _localStorageObj[key];
if (!curObj) {
let localStorageName = _getLocalStorageName(key, _namePrefix);
curObj = _getLocalStorageObj(utlGetLocalStorage(_logger, localStorageName), _logger, localStorageName);
_localStorageObj[key] = curObj;
}
return _localStorageObj[key];

} catch (e) {
// eslint-disable-next-line no-empty
}
return null;
}

function _isTrigger(key: _eInternalMessageId | number) {
let isTrigger = _isTriggered[key];
if (isNullOrUndefined(isTrigger)) {
isTrigger = false;
let localStorageObj = _getLocalStorageObjByKey(key);
if (localStorageObj) {
isTrigger = _isTriggeredOnCurDate(localStorageObj.preTriggerDate);
}
_isTriggered[key] = isTrigger;
}
return _isTriggered[key];
}

function _getQueueByKey(key: _eInternalMessageId | number) {
_queue = _queue || {};
if (isNullOrUndefined(_queue[key])) {
_queue[key] = [];
}
return _queue[key];
}
}
}
2 changes: 1 addition & 1 deletion shared/AppInsightsCommon/src/applicationinsights-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export { IPropertiesPlugin } from "./Interfaces/IPropertiesPlugin";
export { IUser, IUserContext } from "./Interfaces/Context/IUser";
export { ITelemetryTrace, ITraceState } from "./Interfaces/Context/ITelemetryTrace";
export { IRequestContext } from "./Interfaces/IRequestContext";
export { eDistributedTracingModes, DistributedTracingModes, IThrottleMsgKey } from "./Enums";
export { eDistributedTracingModes, DistributedTracingModes } from "./Enums";
export { stringToBoolOrDefault, msToTimeSpan, getExtensionByName, isCrossOriginError } from "./HelperFuncs";
export {
isBeaconsSupported as isBeaconApiSupported,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ export const enum _eInternalMessageId {
InMemoryStorageBufferFull = 105,
InstrumentationKeyDeprecation = 106,
ConfigWatcherException = 107,
DynamicConfigException = 108
DynamicConfigException = 108,
DefaultThrottleMsgKey = 109
}

export type _InternalMessageId = number | _eInternalMessageId;

0 comments on commit bd20503

Please sign in to comment.