Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutate labels to avoid excessive object cloning. #220

Merged
merged 1 commit into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Fixed performance by avoiding `Object.assign` on hot paths, as well as
mutating objects when appropriate.

## [11.1.2] - 2018-09-19

### Changed
Expand Down
33 changes: 17 additions & 16 deletions lib/histogram.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,17 @@ function convertLabelsAndValues(labels, value) {

function extractBucketValuesForExport(histogram) {
return bucketData => {
const buckets = histogram.upperBounds.map(
createBucketValues(bucketData, histogram)
);
const buckets = [];
const bucketLabelNames = Object.keys(bucketData.labels);
let acc = 0;
for (const upperBound of histogram.upperBounds) {
acc += bucketData.bucketValues[upperBound];
const lbls = { le: upperBound };
for (const labelName of bucketLabelNames) {
lbls[labelName] = bucketData.labels[labelName];
}
buckets.push(setValuePair(lbls, acc, `${histogram.name}_bucket`));
}
return { buckets, data: bucketData };
};
}
Expand All @@ -293,24 +301,17 @@ function addSumAndCountForExport(histogram) {
return (acc, d) => {
acc.push(...d.buckets);

const infLabel = Object.assign({ le: '+Inf' }, d.data.labels);
acc.push(setValuePair(infLabel, d.data.count, `${histogram.name}_bucket`));
acc.push(setValuePair(d.data.labels, d.data.sum, `${histogram.name}_sum`));
const infLabel = { le: '+Inf' };
for (const label of Object.keys(d.data.labels)) {
infLabel[label] = d.data.labels[label];
}
acc.push(
setValuePair(infLabel, d.data.count, `${histogram.name}_bucket`),
setValuePair(d.data.labels, d.data.sum, `${histogram.name}_sum`),
setValuePair(d.data.labels, d.data.count, `${histogram.name}_count`)
);
return acc;
};
}

function createBucketValues(bucket, histogram) {
let acc = 0;
return upperBound => {
acc += bucket.bucketValues[upperBound];
const lbls = Object.assign({ le: upperBound }, bucket.labels);
const valuePair = setValuePair(lbls, acc, `${histogram.name}_bucket`);
return valuePair;
};
}

module.exports = Histogram;
79 changes: 45 additions & 34 deletions lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,46 @@ class Registry {
const opts = Object.assign({}, defaultMetricsOpts, conf);
const item = metric.get();
const name = escapeString(item.name);
let help = escapeString(item.help);
help = ['#', 'HELP', name, help].join(' ');
const type = ['#', 'TYPE', name, item.type].join(' ');

const values = (item.values || []).reduce((valAcc, val) => {
const merged = Object.assign({}, this._defaultLabels, val.labels);
const help = `# HELP ${name} ${escapeString(item.help)}`;
const type = `# TYPE ${name} ${item.type}`;
const defaultLabelNames = Object.keys(this._defaultLabels);

let values = '';
for (const val of item.values || []) {
val.labels = val.labels || {};
for (const labelName of defaultLabelNames) {
val.labels[labelName] =
val.labels[labelName] || this._defaultLabels[labelName];
}

const labels = Object.keys(merged).map(
key => `${key}="${escapeLabelValue(merged[key])}"`
);
let labels = '';
for (const key of Object.keys(val.labels)) {
labels += `${key}="${escapeLabelValue(val.labels[key])}",`;
}

let metricName = val.metricName || item.name;
if (labels.length) {
metricName += `{${labels.join(',')}}`;
if (labels) {
metricName += `{${labels.replace(/,$/, '')}}`;
}

const line = [metricName, getValueAsString(val.value)];
if (opts.timestamps) {
line.push(val.timestamp);
let line = `${metricName} ${getValueAsString(val.value)}`;
if (opts.timestamps && val.timestamp) {
line += ` ${val.timestamp}`;
}
valAcc += line.join(' ').trim();
valAcc += '\n';
return valAcc;
}, '');
values += `${line.trim()}\n`;
}

const acc = [help, type, values].join('\n');
return acc;
return `${help}\n${type}\n${values}`.trim();
}

metrics(opts) {
return this.getMetricsAsArray()
.map(metric => this.getMetricAsPrometheusString(metric, opts))
.join('\n');
let metrics = '';

for (const metric of this.getMetricsAsArray()) {
metrics += `${this.getMetricAsPrometheusString(metric, opts)}\n\n`;
}

return metrics.replace(/\n$/, '');
}

registerMetric(metricFn) {
Expand All @@ -83,21 +90,25 @@ class Registry {
}

getMetricsAsJSON() {
return this.getMetricsAsArray().map(metric => {
const metrics = [];
const defaultLabelNames = Object.keys(this._defaultLabels);

for (const metric of this.getMetricsAsArray()) {
const item = metric.get();
if (!item.values) {
return item;

if (item.values) {
for (const val of item.values) {
for (const labelName of defaultLabelNames) {
val.labels[labelName] =
val.labels[labelName] || this._defaultLabels[labelName];
}
}
}

item.values = item.values.map(val =>
// Avoid mutation and merge metric labels with registry default labels
Object.assign({}, val, {
labels: Object.assign({}, this._defaultLabels, val.labels)
})
);
metrics.push(item);
}

return item;
});
return metrics;
}

removeSingleMetric(name) {
Expand Down