Skip to content

CLDSRV-979: Stop retaining a request logger on rate limit token buckets - #6259

Merged
bert-e merged 5 commits into
development/9.3from
bugfix/CLDSRV-979-rate-limit-pinned-request-logger
Aug 20, 2026
Merged

CLDSRV-979: Stop retaining a request logger on rate limit token buckets#6259
bert-e merged 5 commits into
development/9.3from
bugfix/CLDSRV-979-rate-limit-pinned-request-logger

Conversation

@anurag4DSB

@anurag4DSB anurag4DSB commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Intent: why does this change exist?

With account rate limiting on, cloudserver held about 80MB per account per connector and only gave it back on restart (RD-2240). The retained bytes turned out to be buffered werelogs entries, not rate limiting state.

what's affected, including downstream?

lib/api/apiUtils/rateLimit/ only — token buckets and the refill job, for both the account and bucket resource classes. refillIfNeeded() now takes the logger as an argument; the refill job is its only production caller. Nothing else in the request path changes, and there is no config, schema or dependency change.

Intended change: what's different after this PR?

Token buckets no longer store the request-scoped logger; the refill job passes the long-lived server logger, which writes through instead of buffering. Idle eviction is keyed on the last request that consulted a bucket rather than on lastRefillTime and an empty buffer, both of which the refill job itself kept from ever being true.

Two consequences worth flagging. Eviction becoming reachable also makes the pre-existing fail-open-on-create path reachable: a resource idle more than 60s gets bufferSize local tokens before its first Redis grant, about 0.8 RPS of slack against a 60 RPS limit, with Redis still holding the GCRA emptyAt state. And it removes a latent log bomb, a single error-level write on a pinned logger used to flush its whole buffer at once (measured: 20,000 buffered debug calls emit nothing, one error() emits 20,001 lines), which refillIfNeeded's own catch block could trigger on any Redis failure.

Verification: how do we know this worked, or how would we know if it didn't?

Reproduced end-to-end on development/9.3 with a local Vault-free stack (the platform-default limit path, which is RD-2240's TS16 case) plus Redis, then measured buffered entries in-process across every worker.

At clusters=10 with 50 accounts, stock held 500 token buckets all pinning a logger, 838,638 buffered entries and 950MB of worker heap growth, releasing none of it. Patched: same 500 peak buckets, 0 pinned loggers, 0 buffered entries, 4MB growth, and all 500 buckets evicted after idle. Single-worker RSS over 4 minutes went from an unbounded 247→1873MB to a flat plateau at 559MB.

Five new regression tests were written first and confirmed failing against stock. Rate limit suite 186 passing; full unit suite 5119 passing against a 5114 baseline, with the same 15 pre-existing unrelated failures (bucketPut quota seeding, ScubaClientImpl); lint clean.

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Hello anurag4dsb,

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

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Incorrect fix version

The Fix Version/s in issue CLDSRV-979 contains:

  • None

Considering where you are trying to merge, I ignored possible hotfix versions and I expected to find:

  • 9.3.17

  • 9.4.2

Please check the Fix Version/s of CLDSRV-979, or the target
branch of this pull request.

Comment thread tests/unit/api/apiUtils/rateLimit/tokenBucket.js
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 85.31%. Comparing base (ed1cce7) to head (9d912a2).
⚠️ Report is 5 commits behind head on development/9.3.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
lib/api/apiUtils/rateLimit/refillJob.js 83.33% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
lib/api/apiUtils/rateLimit/tokenBucket.js 98.68% <100.00%> (+15.78%) ⬆️
lib/api/apiUtils/rateLimit/refillJob.js 92.10% <83.33%> (+2.63%) ⬆️

... and 1 file with indirect coverage changes

@@                 Coverage Diff                 @@
##           development/9.3    #6259      +/-   ##
===================================================
+ Coverage            85.20%   85.31%   +0.10%     
===================================================
  Files                  206      206              
  Lines                13434    13434              
===================================================
+ Hits                 11447    11461      +14     
+ Misses                1987     1973      -14     
Flag Coverage Δ
file-ft-tests 68.40% <50.00%> (+0.01%) ⬆️
file-ft-tests-null-compat 68.91% <50.00%> (+0.01%) ⬆️
kmip-ft-tests 28.36% <18.75%> (ø)
mongo-v0-ft-tests 69.59% <50.00%> (+0.03%) ⬆️
mongo-v1-ft-tests 69.54% <50.00%> (+0.01%) ⬆️
multiple-backend 36.82% <18.75%> (ø)
s3c-ft-tests-v0 64.00% <18.75%> (-0.03%) ⬇️
s3c-ft-tests-v0-null-compat 64.06% <18.75%> (ø)
s3c-ft-tests-v1 64.00% <18.75%> (+0.02%) ⬆️
sur-tests 36.78% <18.75%> (ø)
sur-tests-inflights 37.79% <18.75%> (-0.03%) ⬇️
unit 71.27% <93.75%> (+0.08%) ⬆️
utapi-v2-tests 34.61% <18.75%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Request integration branches

Waiting for integration branch creation to be requested by the user.

To request integration branches, please comment on this pull request with the following command:

/create_integration_branches

Alternatively, the /approve and /create_pull_requests commands will automatically
create the integration branches.

@anurag4DSB
anurag4DSB force-pushed the bugfix/CLDSRV-979-rate-limit-pinned-request-logger branch 3 times, most recently from feb5e77 to 153567e Compare August 20, 2026 14:32
@anurag4DSB

Copy link
Copy Markdown
Contributor Author

/approve

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Integration data created

I have created the integration data for the additional destination branches.

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

You can set option create_pull_requests if you need me to create
integration pull requests in addition to integration branches, with:

@bert-e create_pull_requests

The following options are set: approve

@bert-e

bert-e commented Aug 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 options are set: approve

this.lastRefillTime = Date.now();
// Last request that consulted this bucket. Idle eviction keys on this
// rather than lastRefillTime, which the refill job keeps fresh.
this.lastAccessTime = Date.now();

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 don't think the addition of lastAccessTime is needed.
lastRefillTime is only set when a bucket is refilled and a refill is not done unless the tokens are below a threshold which prevents it from being updated unless tokens are being used.

@tmacro
tmacro force-pushed the bugfix/CLDSRV-979-rate-limit-pinned-request-logger branch from 153567e to 43c600a Compare August 20, 2026 19:17
@tmacro

tmacro commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

/reset

@scality scality deleted a comment from bert-e Aug 20, 2026
Comment on lines +37 to +44
const promise = bucket
.refillIfNeeded(logger)
.then(bucketRefilled => {
// Check if refill actually happened
if (bucketRefilled) {
refilled++;
}
})
anurag4DSB and others added 5 commits August 20, 2026 12:25
WorkerTokenBucket stored the werelogs logger of the first request that
touched a resource, and the 100ms refill job then logged through it for
the lifetime of the process. RequestLogger buffers every entry it is
handed in RequestLogger.entries and only drains when something logs at
or above the dump threshold ('error'), so the refill chatter accumulated
forever - about 80MB per account per connector on a 10-worker deployment,
reclaimed only by restarting cloudserver. S3C runs at logLevel info, so
the buffered debug/trace lines were never even printed.

The logger is now supplied per call to refillIfNeeded() and never stored.
The refill job passes the long-lived server logger, which writes through
and drops sub-level entries instead of buffering them.

The existing tests could not have caught this: they all pass a sinon
stub as the logger, so the werelogs buffering that is the bug is never
exercised. The new retention tests assert the invariants directly - no
retained request logger, and refills logged through the caller's logger.
The unit environment has no rate limit Redis instance (the feature is
disabled at Config load), so every refill test bounced off isReady()
into the catch block and the grant, denial, disconnected and slow paths
were never executed - codecov flagged exactly those lines.

tokenBucket now reads rateLimitClient.instance at call time instead of
destructuring it at module load, which is behaviour-identical in
production (the instance is created once, before the first request) and
lets tests substitute a fake client. Five new cases cover each outcome;
tokenBucket.js line coverage goes from 83% to 98%, leaving only the
defensive requested <= 0 guard, unreachable while refillThreshold is
below bufferSize.
@tmacro
tmacro force-pushed the bugfix/CLDSRV-979-rate-limit-pinned-request-logger branch from e121b87 to 9d912a2 Compare August 20, 2026 19:26
@scality scality deleted a comment from bert-e Aug 20, 2026
@scality scality deleted a comment from bert-e Aug 20, 2026
@scality scality deleted a comment from bert-e Aug 20, 2026
@scality scality deleted a comment from bert-e Aug 20, 2026
@scality scality deleted a comment from bert-e Aug 20, 2026
@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

History mismatch

Merge commit #76080b58abd36ae825d193f603749ddc974d1b55 on the integration branch
w/9.4/bugfix/CLDSRV-979-rate-limit-pinned-request-logger is merging a branch which is neither the current
branch bugfix/CLDSRV-979-rate-limit-pinned-request-logger nor the development branch
development/9.4.

It is likely due to a rebase of the branch bugfix/CLDSRV-979-rate-limit-pinned-request-logger and the
merge is not possible until all related w/* branches are deleted or updated.

Please use the reset command to have me reinitialize these branches.

The following options are set: approve

@tmacro

tmacro commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

/reset

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Reset complete

I have successfully deleted this pull request's integration branches.

The following options are set: approve

@scality scality deleted a comment from bert-e Aug 20, 2026
@bert-e

bert-e commented Aug 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 options are set: approve

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Build failed

The build for commit did not succeed in branch bugfix/CLDSRV-979-rate-limit-pinned-request-logger

The following options are set: approve

@bert-e

bert-e commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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

  • ✔️ development/9.3

  • ✔️ 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

This pull request did not target the following hotfix branch(es) so they
were left untouched:

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

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

Goodbye anurag4dsb.

The following options are set: approve

@bert-e
bert-e merged commit 9d912a2 into development/9.3 Aug 20, 2026
59 of 61 checks passed
@bert-e
bert-e deleted the bugfix/CLDSRV-979-rate-limit-pinned-request-logger branch August 20, 2026 22:02
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