Skip to content

Commit

Permalink
Support bigint metrics returned by the scuba client
Browse files Browse the repository at this point in the history
Issue: CLDSRV-606
  • Loading branch information
williamlardier committed Jan 16, 2025
1 parent 15e07e4 commit c008606
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 62 deletions.
7 changes: 5 additions & 2 deletions lib/api/apiUtils/quotas/quotaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ function _evaluateQuotas(
return parallelDone(err);
}
if (!isMetricStale(bucketMetrics, 'bucket', bucket.getName(), action, inflight, log) &&
bucketMetrics.bytesTotal + inflightForCheck > bucketQuota) {
BigInt(bucketMetrics.bytesTotal || 0) + BigInt(inflightForCheck || 0) >
BigInt(bucketQuota || 0)) {
log.debug('Bucket quota exceeded', {
bucket: bucket.getName(),
action,
Expand All @@ -147,8 +148,10 @@ function _evaluateQuotas(
if (err || inflight < 0) {
return parallelDone(err);
}
// Metrics are served as BigInt strings
if (!isMetricStale(accountMetrics, 'account', account.account, action, inflight, log) &&
accountMetrics.bytesTotal + inflightForCheck > accountQuota) {
BigInt(accountMetrics.bytesTotal || 0) + BigInt(inflightForCheck || 0) >
BigInt(accountQuota || 0)) {
log.debug('Account quota exceeded', {
accountId: account.account,
action,
Expand Down
36 changes: 18 additions & 18 deletions tests/quota/awsNodeSdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
next => abortMPU(bucket, key, uploadId, 0, next),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0);
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0n);
return next();
},
next => deleteBucket(bucket, next),
Expand Down Expand Up @@ -553,13 +553,13 @@ function multiObjectDelete(bucket, keys, size, callback) {
}, next),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => restoreObject(bucket, key, 0, next),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => deleteVersionID(bucket, key, vID, size, next),
Expand Down Expand Up @@ -590,7 +590,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size * 2);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size * 2));
return next();
},
next => wait(inflightFlushFrequencyMS * 2, next),
Expand Down Expand Up @@ -643,7 +643,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
next => wait(inflightFlushFrequencyMS * 2, next),
// Should still have 0 as inflight
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0);
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0n);
return next();
},
next => wait(inflightFlushFrequencyMS * 2, next),
Expand Down Expand Up @@ -683,7 +683,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size - 100);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size - 100));
return next();
},
next => deleteObject(bucket, key, size, next),
Expand Down Expand Up @@ -715,7 +715,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0);
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0n);
return next();
},
next => deleteBucket(bucket, next),
Expand Down Expand Up @@ -752,7 +752,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0);
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0n);
return next();
},
next => multiObjectDelete(bucket, [`${key}1`, `${key}2`], size * 10, err => {
Expand Down Expand Up @@ -786,7 +786,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => deleteVersionID(bucket, key, vID, size, err => {
Expand All @@ -795,7 +795,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
], done);
Expand All @@ -818,7 +818,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => fakeMetadataArchive(bucket, key, vID, {
Expand All @@ -832,7 +832,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
return next();
}),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => deleteVersionID(bucket, key, vID, size, next),
Expand All @@ -857,7 +857,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
}),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => fakeMetadataArchive(bucket, key, vID, {
Expand All @@ -876,7 +876,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
return next();
}),
next => {
assert.strictEqual(scuba.getInflightsForBucket(bucket), size);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(size));
return next();
},
next => deleteVersionID(bucket, key, vID, size, next),
Expand Down Expand Up @@ -932,7 +932,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
// Verify all parts are counted in inflights
assert.strictEqual(scuba.getInflightsForBucket(bucket), totalSize);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(totalSize));
return next();
},
next => {
Expand All @@ -954,7 +954,7 @@ function multiObjectDelete(bucket, keys, size, callback) {
next => {
// Verify inflights reduced by dropped part
const expectedInflights = usedParts * partSize;
assert.strictEqual(scuba.getInflightsForBucket(bucket), expectedInflights);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(expectedInflights));
return next();
},
next => deleteObject(bucket, key, usedParts * partSize, next),
Expand Down Expand Up @@ -1002,14 +1002,14 @@ function multiObjectDelete(bucket, keys, size, callback) {
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
// Verify all parts are counted in inflights
assert.strictEqual(scuba.getInflightsForBucket(bucket), totalSize);
assert.strictEqual(scuba.getInflightsForBucket(bucket), BigInt(totalSize));
return next();
},
next => abortMPU(bucket, key, uploadId, totalSize, next),
next => wait(inflightFlushFrequencyMS * 2, next),
next => {
// Verify inflights reduced to zero after abort
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0);
assert.strictEqual(scuba.getInflightsForBucket(bucket), 0n);
return next();
},
next => deleteBucket(bucket, next),
Expand Down
60 changes: 30 additions & 30 deletions tests/unit/api/apiUtils/quotas/quotaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ describe('validateQuotas (buckets)', () => {

it('should return errors.QuotaExceeded if quota is exceeded', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.yields(null, result2);
Expand All @@ -117,10 +117,10 @@ describe('validateQuotas (buckets)', () => {

it('should not return QuotaExceeded if quotas are exceeded but operation is creating a delete marker', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -143,10 +143,10 @@ describe('validateQuotas (buckets)', () => {

it('should not return QuotaExceeded if the quotas are exceeded but operation is a delete', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -169,10 +169,10 @@ describe('validateQuotas (buckets)', () => {

it('should not return QuotaExceeded if the quotas are exceeded but operation is a delete with version', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -195,10 +195,10 @@ describe('validateQuotas (buckets)', () => {

it('should decrease the inflights by deleting data, and go below 0 to unblock operations', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -221,10 +221,10 @@ describe('validateQuotas (buckets)', () => {

it('should return null if quota is not exceeded', done => {
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -249,10 +249,10 @@ describe('validateQuotas (buckets)', () => {
it('should not include the inflights in the request if they are disabled', done => {
config.quota.enableInflights = false;
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -276,10 +276,10 @@ describe('validateQuotas (buckets)', () => {

it('should evaluate the quotas and not update the inflights when isStorageReserved is true', done => {
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand Down Expand Up @@ -386,10 +386,10 @@ describe('validateQuotas (with accounts)', () => {

it('should return errors.QuotaExceeded if quota is exceeded', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -416,10 +416,10 @@ describe('validateQuotas (with accounts)', () => {

it('should not return QuotaExceeded if the quotas are exceeded but operation is a delete', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -445,10 +445,10 @@ describe('validateQuotas (with accounts)', () => {

it('should decrease the inflights by deleting data, and go below 0 to unblock operations', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -474,10 +474,10 @@ describe('validateQuotas (with accounts)', () => {

it('should return null if quota is not exceeded', done => {
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -503,10 +503,10 @@ describe('validateQuotas (with accounts)', () => {

it('should return quota exceeded if account and bucket quotas are different', done => {
const result1 = {
bytesTotal: 150,
bytesTotal: BigInt(150),
};
const result2 = {
bytesTotal: 120,
bytesTotal: BigInt(120),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -524,10 +524,10 @@ describe('validateQuotas (with accounts)', () => {

it('should update the request with one function per action to clear quota updates', done => {
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand All @@ -553,10 +553,10 @@ describe('validateQuotas (with accounts)', () => {

it('should evaluate the quotas and not update the inflights when isStorageReserved is true', done => {
const result1 = {
bytesTotal: 80,
bytesTotal: BigInt(80),
};
const result2 = {
bytesTotal: 90,
bytesTotal: BigInt(90),
};
QuotaService._getLatestMetricsCallback.yields(null, result1);
QuotaService._getLatestMetricsCallback.onCall(1).yields(null, result2);
Expand Down
Loading

0 comments on commit c008606

Please sign in to comment.