Skip to content

Forward X-Scal-Request-Uids on Scuba quota requests#6220

Open
delthas wants to merge 5 commits into
development/9.4from
improvement/CLDSRV-946/forward-requids-scuba
Open

Forward X-Scal-Request-Uids on Scuba quota requests#6220
delthas wants to merge 5 commits into
development/9.4from
improvement/CLDSRV-946/forward-requids-scuba

Conversation

@delthas

@delthas delthas commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Forward the werelogs req_id chain (log.getSerializedUids()) as the X-Scal-Request-Uids header on cloudserver's Scuba utilization requests, so Scuba can correlate quota lookups back to the originating cloudserver request. traceparent already flows; the req_id chain did not.

ScubaClientImpl.getUtilizationMetrics now takes a log parameter and, when the logger exposes getSerializedUids(), merges the header into the per-call options (arsenal RESTClient convention). A missing/mock logger never sends an undefined header. The quota (_evaluateQuotas) and Veeam capacity-metrics call sites pass log; the background health-check path is intentionally left untouched.

Issue: CLDSRV-946

@bert-e

bert-e commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Hello delthas,

My role is to assist you with the merge of this
pull request. Please type @bert-e help to get information
on this process, or consult the user documentation.

Available options
name description privileged authored
/after_pull_request Wait for the given pull request id to be merged before continuing with the current one.
/bypass_author_approval Bypass the pull request author's approval
/bypass_build_status Bypass the build and test status
/bypass_commit_size Bypass the check on the size of the changeset TBA
/bypass_incompatible_branch Bypass the check on the source branch prefix
/bypass_jira_check Bypass the Jira issue check
/bypass_peer_approval Bypass the pull request peers' approval
/bypass_leader_approval Bypass the pull request leaders' approval
/approve Instruct Bert-E that the author has approved the pull request. ✍️
/create_pull_requests Allow the creation of integration pull requests.
/create_integration_branches Allow the creation of integration branches.
/no_octopus Prevent Wall-E from doing any octopus merge and use multiple consecutive merge instead
/unanimity Change review acceptance criteria from one reviewer at least to all reviewers
/wait Instruct Bert-E not to run until further notice.
Available commands
name description privileged
/help Print Bert-E's manual in the pull request.
/status Print Bert-E's current status in the pull request TBA
/clear Remove all comments from Bert-E from the history TBA
/retry Re-start a fresh build TBA
/build Re-start a fresh build TBA
/force_reset Delete integration branches & pull requests, and restart merge process from the beginning.
/reset Try to remove integration branches unless there are commits on them which do not appear on the source branch.

Status report is not available.

@scality scality deleted a comment from bert-e Jul 6, 2026
@bert-e

bert-e commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Waiting for approval

The following approvals are needed before I can proceed with the merge:

  • the author

  • 2 peers

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
9664 2 9662 0
View the top 3 failed test(s) by shortest run time
"after each" hook for "should fail if trying to overwrite a delete marker"::MPU with x-scal-s3-version-id header With v4 signature "after each" hook for "should fail if trying to overwrite a delete marker"
Stack Traces | 0.016s run time
We encountered an internal error. Please try again.
should log correct bucketGetTagging operation with all required fields::Server Access Logs - File Output With v4 signature should log correct bucketGetTagging operation with all required fields
Stack Traces | 0.03s run time
Expected 3 log entries, got 4

4 !== 3
should log correct objectGetLegalHold operation with all required fields::Server Access Logs - File Output With default signature should log correct objectGetLegalHold operation with all required fields
Stack Traces | 0.051s run time
Expected 5 log entries, got 6

6 !== 5
View the full list of 1 ❄️ flaky test(s)
"after each" hook for "should fail if trying to overwrite a delete marker"::MPU with x-scal-s3-version-id header With default signature "after each" hook for "should fail if trying to overwrite a delete marker"

Flake rate in main: 90.12% (Passed 24 times, Failed 219 times)

Stack Traces | 0.014s run time
We encountered an internal error. Please try again.

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@delthas
delthas force-pushed the improvement/CLDSRV-946/forward-requids-scuba branch from d4798d7 to 22ff966 Compare July 6, 2026 14:28
Comment on lines +269 to +349
(apiName, done) => {
// Object copy operations first check the target object,
// meaning the source object, containing the current bytes,
// is checked second. This logic handles these APIs calls by
// ensuring the bytes are positives (i.e., not an object
// replacement).
if (actionNeedQuotaCheckCopy(apiName, apiMethod)) {
// eslint-disable-next-line no-param-reassign
inflight = Math.abs(inflight);
} else if (!actionNeedQuotaCheck[apiName] && !actionWithDataDeletion[apiName]) {
return done();
}
// When inflights are disabled, the sum of the current utilization metrics
// and the current bytes are compared with the quota. The current bytes
// are not sent to the utilization service. When inflights are enabled,
// the sum of the current utilization metrics only are compared with the
// quota. They include the current inflight bytes sent in the request.
let _inflights = shouldSendInflights ? inflight : undefined;
const inflightForCheck = shouldSendInflights ? 0 : inflight;
return _evaluateQuotas(
bucketQuota,
accountQuota,
bucket,
account,
_inflights,
inflightForCheck,
apiName,
log,
(err, _bucketQuotaExceeded, _accountQuotaExceeded) => {
if (err) {
return done(err);
}

bucketQuotaExceeded = _bucketQuotaExceeded;
accountQuotaExceeded = _accountQuotaExceeded;
bucketQuotaExceeded = _bucketQuotaExceeded;
accountQuotaExceeded = _accountQuotaExceeded;

// Inflights are inverted: in case of cleanup, we just re-issue
// the same API call.
if (_inflights) {
_inflights = -_inflights;
}
// Inflights are inverted: in case of cleanup, we just re-issue
// the same API call.
if (_inflights) {
_inflights = -_inflights;
}

request.finalizerHooks.push((errorFromAPI, _done) => {
const code = (bucketQuotaExceeded || accountQuotaExceeded) ? 429 : 200;
const quotaCleanUpStartTime = process.hrtime.bigint();
// Quotas are cleaned only in case of error in the API
async.waterfall([
cb => {
if (errorFromAPI) {
return _evaluateQuotas(bucketQuota, accountQuota, bucket, account, _inflights,
null, apiName, log, cb);
}
return cb();
},
], () => {
monitorQuotaEvaluationDuration(apiMethod, type, code, quotaEvaluationDuration +
Number(process.hrtime.bigint() - quotaCleanUpStartTime));
return _done();
request.finalizerHooks.push((errorFromAPI, _done) => {
const code = bucketQuotaExceeded || accountQuotaExceeded ? 429 : 200;
const quotaCleanUpStartTime = process.hrtime.bigint();
// Quotas are cleaned only in case of error in the API
async.waterfall(
[
cb => {
if (errorFromAPI) {
return _evaluateQuotas(
bucketQuota,
accountQuota,
bucket,
account,
_inflights,
null,
apiName,
log,
cb,
);
}
return cb();
},
],
() => {
monitorQuotaEvaluationDuration(
apiMethod,
type,
code,
quotaEvaluationDuration + Number(process.hrtime.bigint() - quotaCleanUpStartTime),
);
return _done();
},
);
});
});

return done();
});
}, err => {
quotaEvaluationDuration = Number(process.hrtime.bigint() - requestStartTime);
if (err) {
log.warn('Error getting metrics from the quota service, allowing the request', {
error: err.name,
description: err.message,
});
}
if (!actionWithDataDeletion[apiMethod] &&
(bucketQuotaExceeded || accountQuotaExceeded)) {
return callback(errors.QuotaExceeded);
}
return callback();
});
return done();
},
);
},
Comment on lines +317 to +332
cb => {
if (errorFromAPI) {
return _evaluateQuotas(
bucketQuota,
accountQuota,
bucket,
account,
_inflights,
null,
apiName,
log,
cb,
);
}
return cb();
},
Comment thread lib/utilization/scuba/wrapper.js Fixed
Formatting-only baseline: run Prettier on the files this PR modifies so the
subsequent functional and async/await changes stay prettier-clean without
mixing in unrelated reformatting. Also shortens one over-long test title (drops
a redundant word) so a reflowed line stays within the eslint max-len of 120. No
behavior change.

Issue: CLDSRV-946
@delthas
delthas force-pushed the improvement/CLDSRV-946/forward-requids-scuba branch from 22ff966 to e2084c3 Compare July 6, 2026 15:04
Comment thread lib/utilization/scuba/wrapper.js Fixed
Comment thread lib/utilization/scuba/wrapper.js Outdated
Thread the werelogs logger into ScubaClientImpl.getUtilizationMetrics and,
when available, forward the serialized req_id chain (log.getSerializedUids())
as the X-Scal-Request-Uids header on Scuba utilization requests, mirroring the
arsenal RESTClient convention. This lets Scuba correlate quota lookups with the
originating cloudserver request. The header is only added when the logger
exposes getSerializedUids(), so mock/absent loggers never send an undefined
value.

Quota (_evaluateQuotas) and Veeam capacity-metrics call sites now pass log; the
background health check path is intentionally left untouched.

Issue: CLDSRV-946
@delthas
delthas force-pushed the improvement/CLDSRV-946/forward-requids-scuba branch 2 times, most recently from 8b634e9 to c0b8f6f Compare July 7, 2026 14:42
@delthas
delthas marked this pull request as ready for review July 7, 2026 14:44
@delthas
delthas requested review from a team, benzekrimaha and maeldonn July 8, 2026 09:38
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread lib/routes/veeam/utils.js Outdated
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread lib/utilization/scuba/wrapper.js Outdated
Comment thread tests/unit/quotas/scuba/wrapper.js
Convert getUtilizationMetrics to an async function that awaits the parent
ScubaClient.getLatestMetrics directly. It keeps a dual callback+async interface:
callers can await the returned promise, and when a callback is supplied a thin
trampoline delegates to the async version and adapts the result back via
.then(onFulfilled, onRejected). This preempts the CodeQL "callback-style
function" alert on the handler this PR modifies while leaving callers unchanged.
The metrics duration is recorded in a finally block so both the success and
error paths are covered.

Drop the now-unused util.callbackify helper (and the util import), and have the
Veeam capacity-metrics path await getUtilizationMetrics directly instead of
re-promisifying it.

getUtilizationMetrics is an internal Scuba client method, not a CORS-bearing S3
API handler, so the trampoline does not carry additionalResHeaders.

Unit tests now stub the parent ScubaClient.prototype.getLatestMetrics. The quota
metrics-failure test expects the account branch to also be queried: since the
Scuba client resolves asynchronously (as in production), the parallel account
lookup runs even when the bucket lookup errors.

Issue: CLDSRV-946
@delthas
delthas force-pushed the improvement/CLDSRV-946/forward-requids-scuba branch from c0b8f6f to 273de92 Compare July 13, 2026 14:42
Comment thread lib/utilization/scuba/wrapper.js Fixed
@delthas
delthas requested review from benzekrimaha and maeldonn July 13, 2026 14:53
Comment thread tests/unit/quotas/scuba/wrapper.js
Comment thread lib/utilization/scuba/wrapper.js Outdated
Drop the dual-mode callback trampoline from ScubaClientImpl.getUtilizationMetrics
(the `.then()`-based callback branch and the trailing `callback` parameter), and
migrate the only remaining callback caller — the bucketQuota/accountQuota tasks
of _evaluateQuotas' async.parallel — to await the promise directly (async.parallel
accepts async-function tasks). veeam already awaited it.

Removes the `.then()` inside the async method and the awkward callback-as-last-arg
signature raised in review; behaviour is unchanged (the quota tests stub
getLatestMetrics directly and pass as-is).

Issue: CLDSRV-946
@delthas
delthas requested a review from benzekrimaha July 21, 2026 08:30
Comment thread lib/utilization/scuba/wrapper.js Outdated

@benzekrimaha benzekrimaha left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM , but I'd strongly advice converting _healthCheck to async/await too (CodeQL .then() still open)

Convert the _healthCheck().then().catch() chain to async/await (try/catch),
removing the last .then() in the Scuba wrapper (clears the CodeQL
prefer-async and eslint prefer-await-to-then flags on this file). Behaviour is
unchanged; the _healthCheck unit tests already await it.

Issue: CLDSRV-946
@delthas

delthas commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

/approve

@bert-e

bert-e commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Build failed

The build for commit did not succeed in branch improvement/CLDSRV-946/forward-requids-scuba

The following options are set: approve

@bert-e

bert-e commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

In the queue

The changeset has received all authorizations and has been added to the
relevant queue(s). The queue(s) will be merged in the target development
branch(es) as soon as builds have passed.

The changeset will be merged in:

  • ✔️ development/9.4

The following branches will NOT be impacted:

  • development/7.10
  • development/7.4
  • development/7.70
  • development/8.8
  • development/9.0
  • development/9.1
  • development/9.2
  • development/9.3

This pull request does not target the following hotfix branch(es) so they
will be left untouched:

  • hotfix/7.6.0
  • hotfix/7.10.30
  • hotfix/7.4.2
  • hotfix/7.7.0
  • hotfix/7.70.73
  • hotfix/7.10.28
  • hotfix/7.4.3
  • hotfix/7.10.15
  • hotfix/9.0.32
  • hotfix/7.10.27
  • hotfix/9.0.7
  • hotfix/7.4.7
  • hotfix/7.70.45
  • hotfix/7.4.6
  • hotfix/7.10.0
  • hotfix/9.2.36
  • hotfix/7.4.5
  • hotfix/7.8.0
  • hotfix/7.10.4
  • hotfix/7.4.8
  • hotfix/9.2.24
  • hotfix/7.70.11
  • hotfix/7.4.10
  • hotfix/7.10.2
  • hotfix/7.10.3
  • hotfix/7.2.0
  • hotfix/7.10.1
  • hotfix/7.10.8
  • hotfix/7.70.51
  • hotfix/6.4.7
  • hotfix/7.9.0
  • hotfix/8.8.45
  • hotfix/7.4.9
  • hotfix/7.4.0
  • hotfix/7.4.4
  • hotfix/7.70.21
  • hotfix/7.10.49
  • hotfix/7.4.1

There is no action required on your side. You will be notified here once
the changeset has been merged. In the unlikely event that the changeset
fails permanently on the queue, a member of the admin team will
contact you to help resolve the matter.

IMPORTANT

Please do not attempt to modify this pull request.

  • Any commit you add on the source branch will trigger a new cycle after the
    current queue is merged.
  • Any commit you add on one of the integration branches will be lost.

If you need this pull request to be removed from the queue, please contact a
member of the admin team now.

The following options are set: approve

@bert-e

bert-e commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Queue build failed

The corresponding build for the queue failed:

  • Checkout the status page.
  • Identify the failing build and review the logs.
  • If no issue is found, re-run the build.
  • If an issue is identified, checkout the steps below to remove
    the pull request from the queue for further analysis and maybe rebase/merge.
Remove the pull request from the queue
  • Add a /wait comment on this pull request.
  • Click on login on the status page.
  • Go into the manage page.
  • Find the option called Rebuild the queue and click on it.
    Bert-E will loop again on all pull requests to put the valid ones
    in the queue again, while skipping the one with the /wait comment.
  • Wait for the new queue to merge, then merge/rebase your pull request
    with the latest changes to then work on a proper fix.
  • Once the issue is fixed, delete the /wait comment and
    follow the usual process to merge the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants