Skip to content

Commit

Permalink
Fallback to Statsig API (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
kenny-statsig authored Jul 31, 2024
1 parent f9db271 commit 01df1ea
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
11 changes: 6 additions & 5 deletions src/StatsigOptions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { IDataAdapter } from './interfaces/IDataAdapter';
import { STATSIG_API, STATSIG_CDN } from './utils/StatsigFetcher';

const DEFAULT_API = 'https://statsigapi.net/v1';
const DEFAULT_API_FOR_DOWNLOAD_CONFIG_SPECS = 'https://api.statsigcdn.com/v1';
const DEFAULT_RULESETS_SYNC_INTERVAL = 10 * 1000;
const MIN_RULESETS_SYNC_INTERVAL = 5 * 1000;
const DEFAULT_ID_LISTS_SYNC_INTERVAL = 60 * 1000;
Expand Down Expand Up @@ -34,6 +33,7 @@ export type ExplicitStatsigOptions = {
api: string;
apiForDownloadConfigSpecs: string;
apiForGetIdLists: string;
fallbackToStatsigAPI: boolean;
bootstrapValues: string | null;
environment: StatsigEnvironment | null;
rulesUpdatedCallback: RulesUpdatedCallback | null;
Expand Down Expand Up @@ -65,15 +65,16 @@ export function OptionsWithDefaults(
): ExplicitStatsigOptions {
return {
api: normalizeUrl(
getString(opts, 'api', DEFAULT_API) ?? DEFAULT_API,
getString(opts, 'api', STATSIG_API) ?? STATSIG_API,
) as string,
apiForDownloadConfigSpecs:
normalizeUrl(
getString(opts, 'apiForDownloadConfigSpecs', opts.api ?? null),
) ?? DEFAULT_API_FOR_DOWNLOAD_CONFIG_SPECS,
) ?? STATSIG_CDN,
apiForGetIdLists:
normalizeUrl(getString(opts, 'apiForGetIdLists', opts.api ?? null)) ??
DEFAULT_API,
STATSIG_API,
fallbackToStatsigAPI: getBoolean(opts, 'fallbackToStatsigAPI', false),
bootstrapValues: getString(opts, 'bootstrapValues', null),
environment: opts.environment
? (getObject(opts, 'environment', {}) as StatsigEnvironment)
Expand Down
11 changes: 8 additions & 3 deletions src/__tests__/CustomDcsUrl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ describe('Check custom DCS url', () => {
disableDiagnostics: true,
});
const secretKey = 'secret-123';
const errorBoundary = new ErrorBoundary(secretKey, options, "sessionid-1")
const errorBoundary = new ErrorBoundary(secretKey, options, 'sessionid-1');
const fetcher = new StatsigFetcher(secretKey, options);
const logger = new LogEventProcessor(fetcher,errorBoundary, options);
const logger = new LogEventProcessor(fetcher, errorBoundary, options);
const store = new SpecStore(fetcher, options);
Diagnostics.initialize({ logger });

Expand All @@ -37,7 +37,12 @@ describe('Check custom DCS url', () => {
logger.log(new LogEvent('test'));
await logger.flush();

expect(spy).toHaveBeenCalledWith('GET', customUrl + dcsPath + `/${secretKey}.json?sinceTime=0`, undefined);
expect(spy).toHaveBeenCalledWith(
'GET',
customUrl + dcsPath + `/${secretKey}.json?sinceTime=0`,
undefined,
undefined,
);
expect(spy).not.toHaveBeenCalledWith(
'POST',
customUrl + '/get_id_lists',
Expand Down
48 changes: 36 additions & 12 deletions src/utils/StatsigFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import safeFetch from './safeFetch';
import StatsigContext from './StatsigContext';

const retryStatusCodes = [408, 500, 502, 503, 504, 522, 524, 599];
export const STATSIG_API = 'https://statsigapi.net/v1';
export const STATSIG_CDN = 'https://api.statsigcdn.com/v1';

type RequestOptions = Partial<{
retries: number;
retryURL: string;
backoff: number | RetryBackoffFunc;
isRetrying: boolean;
signal: AbortSignal;
Expand All @@ -25,9 +28,9 @@ type RequestOptions = Partial<{
}>;

export default class StatsigFetcher {
private api: string;
private apiForDownloadConfigSpecs: string;
private apiForGetIdLists: string;
private fallbackToStatsigAPI: boolean;
private sessionID: string;
private leakyBucket: Record<string, number>;
private pendingTimers: NodeJS.Timer[];
Expand All @@ -42,9 +45,9 @@ export default class StatsigFetcher {
errorBoundry: ErrorBoundary,
sessionID: string,
) {
this.api = options.api;
this.apiForDownloadConfigSpecs = options.apiForDownloadConfigSpecs;
this.apiForGetIdLists = options.apiForGetIdLists;
this.fallbackToStatsigAPI = options.fallbackToStatsigAPI;
this.sessionID = sessionID;
this.leakyBucket = {};
this.pendingTimers = [];
Expand All @@ -66,16 +69,30 @@ export default class StatsigFetcher {
}

public async downloadConfigSpecs(sinceTime: number): Promise<Response> {
return await this.get(
this.apiForDownloadConfigSpecs +
'/download_config_specs' +
`/${this.sdkKey}.json` +
`?sinceTime=${sinceTime}`,
);
const path =
'/download_config_specs' +
`/${this.sdkKey}.json` +
`?sinceTime=${sinceTime}`;
const url = this.apiForDownloadConfigSpecs + path;

let options: RequestOptions | undefined;
if (this.fallbackToStatsigAPI) {
options = { retries: 1, retryURL: STATSIG_CDN + path };
}

return await this.get(url, options);
}

public async getIDLists(sinceTime?: number): Promise<Response> {
return await this.post(this.apiForGetIdLists + '/get_id_lists', {});
public async getIDLists(): Promise<Response> {
const path = '/get_id_lists';
const url = this.apiForGetIdLists + path;

let options: RequestOptions | undefined;
if (this.fallbackToStatsigAPI) {
options = { retries: 1, retryURL: STATSIG_API + path };
}

return await this.post(url, {}, options);
}

public dispatch(
Expand All @@ -95,7 +112,7 @@ export default class StatsigFetcher {
}

public async get(url: string, options?: RequestOptions): Promise<Response> {
return await this.request('GET', url, options);
return await this.request('GET', url, undefined, options);
}

public async request(
Expand All @@ -105,6 +122,7 @@ export default class StatsigFetcher {
options?: RequestOptions,
): Promise<Response> {
const {
retryURL = url,
retries = 0,
backoff = 1000,
isRetrying = false,
Expand Down Expand Up @@ -172,7 +190,13 @@ export default class StatsigFetcher {
.then((localRes) => {
res = localRes;
if ((!res.ok || retryStatusCodes.includes(res.status)) && retries > 0) {
return this._retry(method, url, body, retries - 1, backoffAdjusted);
return this._retry(
method,
retryURL,
body,
retries - 1,
backoffAdjusted,
);
} else if (!res.ok) {
return Promise.reject(
new Error(
Expand Down

0 comments on commit 01df1ea

Please sign in to comment.