Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
This change creates an option to skip checking if metric descriptors exist before
sending metrics. Skipping the metric descriptor check can prevent the clients from
getting rate limited by Google when a large number are all started at the same time.
  • Loading branch information
sethrwebster committed Sep 19, 2023
1 parent c0b521b commit c5f11a4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export interface ExporterOptions {
* monitoring.googleapis.com:443.
*/
apiEndpoint?: string;
/**
* Assume all metric descriptors have already been created and publish
* metrics without checking. This can prevent hitting a rate limit in Google
* when a large number of clients are all started up at the same time.
*/
skipDescriptorCheck?: boolean;
}

export interface Credentials {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class MetricExporter implements PushMetricExporter {
private _projectId: string | void | Promise<string | void>;
private readonly _metricPrefix: string;
private readonly _auth: GoogleAuth;
private readonly skipMetricDescriptorCheck: boolean;

static readonly DEFAULT_METRIC_PREFIX: string = 'workload.googleapis.com';

Expand All @@ -73,6 +74,7 @@ export class MetricExporter implements PushMetricExporter {

constructor(options: ExporterOptions = {}) {
this._metricPrefix = options.prefix ?? MetricExporter.DEFAULT_METRIC_PREFIX;
this.skipMetricDescriptorCheck = !!options.skipDescriptorCheck;

this._auth = new GoogleAuth({
credentials: options.credentials,
Expand Down Expand Up @@ -145,7 +147,9 @@ export class MetricExporter implements PushMetricExporter {
const timeSeries: TimeSeries[] = [];
for (const scopeMetric of resourceMetrics.scopeMetrics) {
for (const metric of scopeMetric.metrics) {
const isRegistered = await this._registerMetricDescriptor(metric);
const isRegistered =
this.skipMetricDescriptorCheck ||
(await this._registerMetricDescriptor(metric));
if (isRegistered) {
timeSeries.push(
...createTimeSeries(metric, resource, this._metricPrefix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,68 @@ describe('MetricExporter', () => {
});
});

assert.strictEqual(metricDescriptorsGet.callCount, 1);
assert.strictEqual(metricDescriptorCreate.callCount, 1);
assert.strictEqual(timeSeries.callCount, 1);
assert.deepStrictEqual(result.code, ExportResultCode.SUCCESS);

// Second time around, MetricDescriptorCreate.create() should be skipped
metricDescriptorCreate.resetHistory();
metricDescriptorsGet.resetHistory();
timeSeries.resetHistory();
result = await new Promise<ExportResult>(resolve => {
exporter.export(resourceMetrics, result => {
resolve(result);
});
});

assert.strictEqual(metricDescriptorsGet.callCount, 0);
assert.strictEqual(metricDescriptorCreate.callCount, 0);
assert.strictEqual(timeSeries.callCount, 1);
assert.deepStrictEqual(result.code, ExportResultCode.SUCCESS);
});

it('should skip fetching the MetricDescriptors when skipDescriptorCheck is set', async () => {
const exporterSkipDescriptorCreate = new MetricExporter({
skipDescriptorCheck: true,
});
sinon.replace(
exporterSkipDescriptorCreate['_monitoring'].projects
.metricDescriptors,
'create',
metricDescriptorCreate as sinon.SinonSpy
);
sinon.replace(
exporterSkipDescriptorCreate['_monitoring'].projects
.metricDescriptors,
'get',
metricDescriptorsGet as sinon.SinonSpy
);
sinon.replace(
exporterSkipDescriptorCreate['_monitoring'].projects.timeSeries,
'create',
timeSeries as any
);
sinon.replace(
exporterSkipDescriptorCreate['_auth'],
'getClient',
() => {
if (getClientShouldFail) {
throw new Error('fail');
}
return {} as any;
}
);

resourceMetrics = await generateMetricsData();

const result = await new Promise<ExportResult>(resolve => {
exporterSkipDescriptorCreate.export(resourceMetrics, result => {
resolve(result);
});
});

assert.strictEqual(metricDescriptorsGet.callCount, 0);
assert.strictEqual(metricDescriptorCreate.callCount, 0);
assert.strictEqual(timeSeries.callCount, 1);
assert.deepStrictEqual(result.code, ExportResultCode.SUCCESS);
Expand Down

0 comments on commit c5f11a4

Please sign in to comment.