Skip to content

Seed zero-value quota metric on bucket create / quota enable - #6221

Merged
bert-e merged 3 commits into
development/9.4from
improvement/CLDSRV-949/seed-empty-bucket-quota-metric
Jul 28, 2026
Merged

Seed zero-value quota metric on bucket create / quota enable#6221
bert-e merged 3 commits into
development/9.4from
improvement/CLDSRV-949/seed-empty-bucket-quota-metric

Conversation

@delthas

@delthas delthas commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Context

Bucket/account quota enforcement reads utilization metrics from Scuba, which serves the __infostore collection. Those documents are written today only by the s3utils count-items cron (every 6–24h). A freshly created bucket — or a bucket that has a quota enabled while empty — has no metric document until the next cron run, so validateQuotas_evaluateQuotas gets NoSuchEntity from Scuba and fails open: the quota is not enforced for up to 24h (ARTESCA-17063).

Change

Seed a zero-value bucket metric document in __infostore at the moments CloudServer knows the bucket is empty, so the quota is enforceable immediately:

  1. On createBucket — the bucket is empty by definition, so seed unconditionally.
  2. On bucketUpdateQuota — seed only when the bucket is verifiably empty (a maxKeys:1 DelimiterVersions listing). Never default to zero for a bucket that may hold uncounted data (that would silently under-enforce).

Both paths:

  • are gated on config.isQuotaEnabled();
  • are best-effort / non-fatal — a seeding failure is logged and never fails the request (quota simply stays disabled until count-items, i.e. today's behavior);
  • use the new arsenal MetadataWrapper.initializeBucketCapacity (idempotent $setOnInsert, so an existing count-items document is never overwritten), keyed by the bucket's metastore creationDate — exactly what _evaluateQuotas queries with, so the create-window lookup is an exact match.

Account-level metrics are intentionally out of scope: a brand-new bucket's account may hold data in other, not-yet-counted buckets, so seeding account_<id>=0 would under-report. Empty-account coverage stays with count-items (S3UTILS-224).

Dependency

Requires ARSN-610 (arsenal#2666). The first commit here temporarily pins arsenal to that branch commit so the new method resolves; it should be replaced with a released arsenal 8.5 tag once ARSN-610 merges.

Tests

New unit tests: bucketUpdateQuota seeds on an empty bucket, does not seed on a non-empty bucket, still succeeds when seeding errors, and does not seed when quotas are disabled; bucketPut seeds on creation, stays non-fatal on seed error, and does not seed when quotas are disabled.

Issue: CLDSRV-949

@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

Comment thread package.json Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Fixed
Comment thread lib/api/bucketUpdateQuota.js Fixed
Comment thread lib/api/bucketUpdateQuota.js Fixed
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
9718 1 9717 0
View the full list of 1 ❄️ flaky test(s)
"before each" hook for "should retrieve a part copied from an MPU after the original part was overwritten"::GET object With default signature With PartNumber field uploadPartCopy overwrite "before each" hook for "should retrieve a part copied from an MPU after the original part was overwritten"

Flake rate in main: 100.00% (Passed 0 times, Failed 5 times)

Stack Traces | 30.6s run time
Connection timed out after 30000 ms

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-949/seed-empty-bucket-quota-metric branch from c463226 to 64c60e4 Compare July 6, 2026 15:13
Comment on lines +270 to -242
if (err) {
return callback(err);
}
return callback(null, data);
});
},
},
},
// Function to run upon finishing both parallel requests
(err, results) => {
if (err) {
return cb(err);
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Dismissed
Comment thread lib/api/bucketUpdateQuota.js Fixed
Comment thread lib/api/bucketUpdateQuota.js Outdated
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch 2 times, most recently from 7930f2c to 815f61c Compare July 7, 2026 08:36
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Fixed
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch 4 times, most recently from 049c059 to b0c3fc9 Compare July 7, 2026 14:17
@bert-e

bert-e commented Jul 7, 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

@scality scality deleted a comment from bert-e Jul 7, 2026
@delthas
delthas marked this pull request as ready for review July 7, 2026 14:21
@delthas
delthas requested review from a team, DarkIsDude and benzekrimaha July 8, 2026 09:39
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from b0c3fc9 to 7e7a71a Compare July 8, 2026 09:41
Comment thread package.json Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js
Comment thread lib/api/bucketUpdateQuota.js Outdated
Comment thread lib/api/bucketUpdateQuota.js Outdated
@bert-e

bert-e commented Jul 8, 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

The following reviewers are expecting changes from the author, or must review again:

Comment thread lib/api/bucketUpdateQuota.js
Comment thread lib/api/bucketUpdateQuota.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread tests/unit/api/bucketPut.js
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from a3571b1 to 68a1f28 Compare July 13, 2026 10:24

@DarkIsDude DarkIsDude 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

Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
Comment thread lib/api/bucketUpdateQuota.js Outdated
@bert-e

bert-e commented Jul 15, 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

@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from 68a1f28 to e360f5b Compare July 15, 2026 11:20

async function freshStartCreateBucket(bucket, canonicalID, log, callback) {
if (callback) {
return freshStartCreateBucket(bucket, canonicalID, log).then(() => callback(null), callback);
function cleanUpBucket(bucketMD, canonicalID, log, callback) {
async function cleanUpBucket(bucketMD, canonicalID, log, callback) {
if (callback) {
return cleanUpBucket(bucketMD, canonicalID, log).then(() => callback(null), callback);
Comment on lines +93 to +96
return bucketUpdateQuota(authInfo, request, log).then(
corsHeaders => callback(null, corsHeaders),
err => callback(err, err.code, err.additionalResHeaders),
);

@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.

I think that the current ordering still allows usage to be silently under-reported =>

  • the quota-enable path has a check-then-act race between the empty listing, metric initialization, and quota persistence;
  • bucket creation clears the transient/deleted flag before initializing the metric, exposing a similar race;
    The callback trampolines also leave the new CodeQL .then() findings open; async helpers should be promise-only, with callback adaptation at their callers IMO

}
const bucketName = bucket.getName();
try {
const list = await listObjects(bucketName, { maxKeys: 1, listingType: 'DelimiterVersions' }, log);

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.

this is check-then-act: an object can be written after this empty listing and before the quota is persisted. Since quota tracking is still disabled, that object is not reflected in Scuba, while $setOnInsert leaves the zero metric in place. This silently under-enforces the newly enabled quota until count-items runs — the same problem this PR is meant to fix. How do we prevent this race?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right that it's check-then-act, and there's no atomic fix here without a bucket lock, since the bucket is already live (unlike the creation path, which has the transient-flag gate we can seed behind).

We're accepting this deliberately: the seed is a best-effort floor, not an authoritative count — count-items is the source of truth and reconciles at its next run. The failure mode is one bucket briefly under-enforced until then, which is far narrower than the bug this PR fixes (Scuba's deep-health failing → quotas disabled cluster-wide, indefinitely). I've documented it inline at the seed call.

If you'd want a stronger guarantee it'd need a lock; otherwise I think accept-and-self-heal is the right tradeoff. Happy to discuss — leaving this open for your call.

Comment thread lib/api/apiUtils/bucket/bucketCreation.js Outdated
@bert-e

bert-e commented Jul 20, 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

The following reviewers are expecting changes from the author, or must review again:

@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from e360f5b to b060edd Compare July 21, 2026 08:41
@delthas
delthas requested a review from benzekrimaha July 21, 2026 08:48
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from b060edd to 96ee768 Compare July 21, 2026 11:50
@delthas

delthas commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

The MR is ready for me, if all OK feel free to /approve / merge without me.

@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.

Thanks, all my previous comments are addressed, and the documented accept-and-self-heal trade-off works for me.
One last issue in the error path (inline).

Comment thread lib/api/bucketUpdateQuota.js

@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.

Two last minor points, non-blocking.

Comment thread lib/api/bucketUpdateQuota.js Outdated
Comment thread lib/api/apiUtils/bucket/bucketCreation.js
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from 96ee768 to 5fd7a50 Compare July 28, 2026 08:33
delthas added 3 commits July 28, 2026 10:40
Isolates prettier reformatting of files that were already
prettier-dirty on development/9.4, so the functional change commits
that follow stay prettier-clean.

Issue: CLDSRV-949
Seed a zero-value bucket metric document in __infostore when the
bucket is known to be empty, so bucket quota checks are enforceable
immediately instead of waiting up to 24h for the periodic count-items
job (ARTESCA-17063):

- on createBucket, the bucket is empty by definition, so seed it;
- on bucketUpdateQuota, seed only when the bucket is verifiably empty
  (a maxKeys:1 DelimiterVersions listing), never defaulting to zero
  for a bucket that may hold uncounted data.

Both paths are gated on config.isQuotaEnabled() and are best-effort:
a seeding failure is logged and never fails the request. The write
uses the new arsenal MetadataWrapper.initializeBucketCapacity
(idempotent $setOnInsert), keyed by the bucket's metastore
creationDate to match the enforcement lookup.

Issue: CLDSRV-949
Migrate bucketUpdateQuota and the bucket-creation seeding helpers
(seedBucketQuotaCapacity, freshStartCreateBucket, cleanUpBucket) to
async/await, using a callback trampoline so existing callers keep working.

Seed the zero metric before persisting the quota to close the fail-open
window, probe the MPU shadow bucket so a bucket with only in-progress
uploads is not seeded to zero, log seeding failures as non-fatal warnings,
and declare the promisified metadata helpers once at module scope.

Issue: CLDSRV-949
@delthas
delthas force-pushed the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch from 5fd7a50 to 79c7731 Compare July 28, 2026 08:44
@bert-e

bert-e commented Jul 28, 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

The following reviewers are expecting changes from the author, or must review again:

@scality scality deleted a comment from bert-e Jul 28, 2026
@delthas
delthas requested a review from benzekrimaha July 28, 2026 09:32
@bert-e

bert-e commented Jul 28, 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

@delthas

delthas commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

/approve

@bert-e

bert-e commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

I have successfully merged the changeset of this pull request
into targetted development branches:

  • ✔️ development/9.4

The following branches have NOT changed:

  • 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 did not target the following hotfix branch(es) so they
were left untouched:

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

Please check the status of the associated issue CLDSRV-949.

Goodbye delthas.

The following options are set: approve

@bert-e
bert-e merged commit 79c7731 into development/9.4 Jul 28, 2026
37 checks passed
@bert-e
bert-e deleted the improvement/CLDSRV-949/seed-empty-bucket-quota-metric branch July 28, 2026 14:31
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