Skip to content

Commit

Permalink
perf: improve perf by refactoring code to be cleaner and more efficie…
Browse files Browse the repository at this point in the history
…nt (#559)

Co-authored-by: Dan Shappir <dan.s@nextinsurance.com>
  • Loading branch information
Dan Shappir and Dan Shappir authored May 10, 2023
1 parent 126d4f3 commit 1df34ed
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 68 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed

- Cleanup code and refactor to be more efficient

### Breaking

- drop support for Node.js versions 10, 12 and 17
Expand Down
75 changes: 36 additions & 39 deletions lib/histogram.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class Histogram extends Metric {
this.hashMap = {
[hashObject({})]: createBaseValues(
{},
Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
this.bucketValues,
this.bucketExemplars,
),
};
}
Expand All @@ -84,16 +84,17 @@ class Histogram extends Metric {

updateExemplar(labels, value, exemplarLabels) {
const hash = hashObject(labels);
const b = findBound(this.upperBounds, value);
if (!isObject(this.hashMap[hash].bucketExemplars[b])) {
this.hashMap[hash].bucketExemplars[b] = new Exemplar();
const bound = findBound(this.upperBounds, value);
const { bucketExemplars } = this.hashMap[hash];
let exemplar = bucketExemplars[bound];
if (!isObject(exemplar)) {
exemplar = new Exemplar();
bucketExemplars[bound] = exemplar;
}
this.hashMap[hash].bucketExemplars[b].validateExemplarLabelSet(
exemplarLabels,
);
this.hashMap[hash].bucketExemplars[b].labelSet = exemplarLabels;
this.hashMap[hash].bucketExemplars[b].value = value;
this.hashMap[hash].bucketExemplars[b].timestamp = nowTimestamp();
exemplar.validateExemplarLabelSet(exemplarLabels);
exemplar.labelSet = exemplarLabels;
exemplar.value = value;
exemplar.timestamp = nowTimestamp();
}

async get() {
Expand Down Expand Up @@ -134,8 +135,8 @@ class Histogram extends Metric {
const hash = hashObject(labels);
this.hashMap[hash] = createBaseValues(
labels,
Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
this.bucketValues,
this.bucketExemplars,
);
}

Expand Down Expand Up @@ -218,8 +219,8 @@ function observe(labels) {
if (!valueFromMap) {
valueFromMap = createBaseValues(
labelValuePair.labels,
Object.assign({}, this.bucketValues),
Object.assign({}, this.bucketExemplars),
this.bucketValues,
this.bucketExemplars,
);
}

Expand All @@ -239,43 +240,39 @@ function observe(labels) {
function createBaseValues(labels, bucketValues, bucketExemplars) {
return {
labels,
bucketValues,
bucketExemplars,
bucketValues: { ...bucketValues },
bucketExemplars: { ...bucketExemplars },
sum: 0,
count: 0,
};
}

function convertLabelsAndValues(labels, value) {
if (!isObject(labels)) {
return {
value: labels,
labels: {},
};
}
return {
labels,
value,
};
return isObject(labels)
? {
labels,
value,
}
: {
value: labels,
labels: {},
};
}

function extractBucketValuesForExport(histogram) {
const name = `${histogram.name}_bucket`;
return bucketData => {
const buckets = [];
let acc = 0;
for (const upperBound of histogram.upperBounds) {
const buckets = histogram.upperBounds.map(upperBound => {
acc += bucketData.bucketValues[upperBound];
const lbls = { le: upperBound };
buckets.push(
setValuePair(
lbls,
acc,
`${histogram.name}_bucket`,
bucketData.bucketExemplars[upperBound],
bucketData.labels,
),
return setValuePair(
{ le: upperBound },
acc,
name,
bucketData.bucketExemplars[upperBound],
bucketData.labels,
);
}
});
return { buckets, data: bucketData };
};
}
Expand Down
53 changes: 24 additions & 29 deletions lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ class Registry {
const defaultLabels =
Object.keys(this._defaultLabels).length > 0 ? this._defaultLabels : null;

const isOpenMetrics =
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE;

for (const val of metric.values || []) {
let { metricName = name, labels = {} } = val;
const { sharedLabels = {} } = val;
if (
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE &&
metric.type === 'counter'
) {
if (isOpenMetrics && metric.type === 'counter') {
metricName = `${metricName}_total`;
}

Expand All @@ -58,13 +58,7 @@ class Registry {

// We have to flatten these separately to avoid duplicate labels appearing
// between the base labels and the shared labels
const formattedLabels = [];
for (const [n, v] of Object.entries(labels)) {
if (Object.prototype.hasOwnProperty.call(sharedLabels, n)) {
continue;
}
formattedLabels.push(`${n}="${escapeLabelValue(v)}"`);
}
const formattedLabels = formatLabels(labels, sharedLabels);

const flattenedShared = flattenSharedLabels(sharedLabels);
const labelParts = [...formattedLabels, flattenedShared].filter(Boolean);
Expand All @@ -74,7 +68,7 @@ class Registry {
)}`;

const { exemplar } = val;
if (exemplar && this.contentType === Registry.OPENMETRICS_CONTENT_TYPE) {
if (exemplar && isOpenMetrics) {
const formattedExemplars = formatLabels(exemplar.labelSet);
fullMetricLine += ` # {${formattedExemplars.join(
',',
Expand All @@ -87,25 +81,21 @@ class Registry {
}

async metrics() {
const promises = [];
const isOpenMetrics =
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE;

for (const metric of this.getMetricsAsArray()) {
if (
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE &&
metric.type === 'counter'
) {
const promises = this.getMetricsAsArray().map(metric => {
if (isOpenMetrics && metric.type === 'counter') {
metric.name = standardizeCounterName(metric.name);
}
promises.push(this.getMetricsAsString(metric));
}
return this.getMetricsAsString(metric);
});

const resolves = await Promise.all(promises);

if (this.contentType === Registry.OPENMETRICS_CONTENT_TYPE) {
return `${resolves.join('\n')}\n# EOF\n`;
} else {
return `${resolves.join('\n\n')}\n`;
}
return isOpenMetrics
? `${resolves.join('\n')}\n# EOF\n`
: `${resolves.join('\n\n')}\n`;
}

registerMetric(metric) {
Expand Down Expand Up @@ -212,10 +202,15 @@ class Registry {
}
}

function formatLabels(labels) {
return Object.entries(labels).map(
([n, v]) => `${n}="${escapeLabelValue(v)}"`,
);
function formatLabels(labels, exclude) {
const { hasOwnProperty } = Object.prototype;
const formatted = [];
for (const [name, value] of Object.entries(labels)) {
if (!exclude || !hasOwnProperty.call(exclude, name)) {
formatted.push(`${name}="${escapeLabelValue(value)}"`);
}
}
return formatted;
}

const sharedLabelCache = new WeakMap();
Expand Down

0 comments on commit 1df34ed

Please sign in to comment.