Skip to content

LAC: smoothing tokens request and keep in high level (#10997) - #11034

Merged
JaySon-Huang merged 2 commits into
pingcap:release-8.5-20260811-v8.5.7from
yongman:release-8.5-20260811-v8.5.7-hotfix
Aug 11, 2026
Merged

LAC: smoothing tokens request and keep in high level (#10997)#11034
JaySon-Huang merged 2 commits into
pingcap:release-8.5-20260811-v8.5.7from
yongman:release-8.5-20260811-v8.5.7-hotfix

Conversation

@yongman

@yongman yongman commented Aug 11, 2026

Copy link
Copy Markdown
Member

This is a cherry-pick of #10997

What problem does this PR solve?

Issue Number: close #10996

Summary

This change improves TiFlash Local Admission Controller token refill behavior to keep the local token bucket near a high watermark without requesting a large amount of tokens in a single GAC request.

Problem

The previous acquire calculation was based only on predicted consumption:

acquire_tokens = max(smoothed_speed * 5s * 1.1 - remaining_tokens, 0)

When the smoothed consumption speed was underestimated, a small positive token balance could make acquire_tokens zero. The local balance would then remain low and could be exhausted by a traffic burst, causing unexpected throttling.

Always refilling directly to the full bucket capacity would avoid this problem, but could transfer and retain too many tokens in TiFlash at once, reducing the tokens available to other clients such as TiDB.

Changes

  • Added a proactive refill watermark at 80% of the local high watermark.
  • Added a one-second refill check interval in normal mode.
  • Included proactive refill checks in addition to the existing low-token and consumption-report triggers.
  • Added incremental token acquisition for normal refills:
deficit = high_watermark - remaining_tokens

fallback_batch = min(
    5000,
    high_watermark * 20%
)

incremental_batch = max(
    smoothed_consumption_speed * 1s * 1.1,
    fallback_batch
)

acquire_tokens = min(deficit, incremental_batch)
  • Preserved emergency refill behavior when the bucket reaches the existing low-token threshold. In that case, the incremental limit is bypassed to avoid request throttling.
  • Before the first GAC token response, the Resource Group fill_rate is used as the local high watermark.
  • After the first GAC response, the capacity assigned by GAC to the current client is used as the high watermark.
  • Added has_gac_capacity state to distinguish the global Resource Group burst limit from the capacity assigned to the local client.
  • Added a read-only TokenBucket::getCapacity() accessor.
  • Kept the low-token threshold based on the actual post-grant token balance. This prevents a capacity increase from immediately classifying the bucket as low-token and triggering a large emergency refill.
  • Preserved the existing five-second consumption reporting period and GAC target request period.
  • Did not change RU accounting, token deduction, GAC grant handling, or trickle-mode semantics.

Resulting Behavior

  • TiFlash starts refilling before the local bucket reaches a critically low balance.
  • Normal refill requests are spread across smaller requests instead of immediately filling the entire capacity.
  • High-throughput workloads can still request approximately one second of predicted consumption per refill.
  • Low-token conditions retain an emergency path that prioritizes avoiding unexpected query throttling.
  • A newly started TiFlash instance does not use the global Resource Group burst limit as its initial local refill target.
  • Unused tokens are less likely to be transferred from GAC to TiFlash in one large request, reducing the impact on other clients sharing the Resource Group.

##Test
During bench tpch workload, after acquire tokens from GAC, the remaining_tokens keeps close to the high watermark.
image

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

None

Summary by CodeRabbit

  • Bug Fixes

    • Improved resource token refilling for more consistent capacity availability.
    • Added capacity-aware refill behavior, including incremental requests and full refills when resources run low.
    • Improved handling of capacity supplied through admission control responses.
    • Prevented background maintenance activity from interfering with stable data operations.
  • Tests

    • Added coverage for startup refills, incremental replenishment, predicted consumption, and low-capacity recovery.

close pingcap#10996\n\nSigned-off-by: yongman <yming0221@gmail.com>

Signed-off-by: yongman <yming0221@gmail.com>
@ti-chi-bot ti-chi-bot Bot added release-note-none Denotes a PR that doesn't merit a release note. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Local admission control now uses one-second, capacity-aware token refills. It tracks GAC capacity, limits incremental requests, supports low-token full refills, and adds refill tests. A segment read test now suppresses segment update checks during stable-data merging.

Changes

Local admission token refill

Layer / File(s) Summary
Refill contracts and calculation
dbms/src/Flash/ResourceControl/LocalAdmissionController.h, dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp, dbms/src/Flash/ResourceControl/TokenBucket.h
Resource groups track refill timing and GAC capacity. Refill eligibility and high-watermark calculations use bucket capacity and bounded token requests.
GAC refill integration
dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp
GAC updates record assigned capacity. GAC requests now include eligible periodic refills.
Refill behavior tests
dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp
Tests cover startup timing, incremental refill, predicted consumption, and full refill for low-token buckets.

Segment read test stability

Layer / File(s) Summary
Stable data merge setup
dbms/src/Storages/DeltaMerge/tests/gtest_segment_read_task.cpp
The test temporarily enables skip_check_segment_update while writing and merging stable data, then disables it with a scope guard.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: type/cherry-pick-for-release-8.5

Poem

A rabbit counts tokens, one hop at a time,
Refill bells ring on a one-second chime.
Low buckets get plenty, not just a small share,
GAC marks capacity waiting there.
Tests twitch their noses: the numbers align.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The DeltaMerge segment-read test change is unrelated to the linked Local Admission Controller issue #10996. Remove the unrelated DeltaMerge test change or explain its dependency on the Local Admission Controller fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Local Admission Controller token-request smoothing change.
Description check ✅ Passed The description covers the problem, implementation, testing, side effects, and release note, although manual steps are limited.
Linked Issues check ✅ Passed The changes address issue #10996 by proactively refilling tokens and reducing pending or undersized GAC requests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
dbms/src/Flash/ResourceControl/LocalAdmissionController.h (1)

287-287: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use camelCase for newly added variables.

  • dbms/src/Flash/ResourceControl/LocalAdmissionController.h#L287-L287: Rename has_gac_capacity to hasGacCapacity.
  • dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp#L139-L170: Rename new local variables such as refill_threshold and high_watermark to camelCase.
  • dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp#L22-L113: Rename new test-local variables such as fill_rate and consumed_tokens to camelCase.

As per coding guidelines, “Method and variable names should use camelCase.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dbms/src/Flash/ResourceControl/LocalAdmissionController.h` at line 287,
Rename the newly added variables to camelCase: change has_gac_capacity to
hasGacCapacity in
dbms/src/Flash/ResourceControl/LocalAdmissionController.h:287-287, rename locals
such as refill_threshold and high_watermark in
dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp:139-170, and rename
test locals such as fill_rate and consumed_tokens in
dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp:22-113;
update all references consistently.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp`:
- Line 39: Update the shouldRefillToken assertion in the relevant test to pass
std::chrono::milliseconds(500) after start_time instead of
ResourceGroup::REFILL_TOKEN_INTERVAL / 2, ensuring the check uses a non-zero
sub-second duration.

---

Nitpick comments:
In `@dbms/src/Flash/ResourceControl/LocalAdmissionController.h`:
- Line 287: Rename the newly added variables to camelCase: change
has_gac_capacity to hasGacCapacity in
dbms/src/Flash/ResourceControl/LocalAdmissionController.h:287-287, rename locals
such as refill_threshold and high_watermark in
dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp:139-170, and rename
test locals such as fill_rate and consumed_tokens in
dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp:22-113;
update all references consistently.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4550d572-01de-4973-aa9e-8420726ea7e8

📥 Commits

Reviewing files that changed from the base of the PR and between 7eaf8c4 and fb977ab.

📒 Files selected for processing (4)
  • dbms/src/Flash/ResourceControl/LocalAdmissionController.cpp
  • dbms/src/Flash/ResourceControl/LocalAdmissionController.h
  • dbms/src/Flash/ResourceControl/TokenBucket.h
  • dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp

group.smooth_ru_consumption_speed = 0;
group.consumeResource(consumed_tokens, 0);

EXPECT_FALSE(group.shouldRefillToken(start_time + ResourceGroup::REFILL_TOKEN_INTERVAL / 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

According to the C++ standard library, what value does std::chrono::seconds{1} / 2 produce?

💡 Result:

In the C++ standard library, the expression std::chrono::seconds{1} / 2 produces a std::chrono::seconds object with a count of 0 [1][2][3]. When a std::chrono::duration (such as std::chrono::seconds) is divided by a scalar value, the division is performed on the internal tick count of the duration [1][4]. The result of this operation is a new std::chrono::duration object of the same type [1][2][3]. In this case: 1. std::chrono::seconds{1} has an internal tick count of 1. 2. The division operator performs integer division: 1 / 2, which equals 0. 3. The resulting object is std::chrono::seconds{0}.

Citations:


🏁 Script executed:

#!/bin/bash
set -e
file="dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp"
rg -n -C 8 "REFILL_TOKEN_INTERVAL|shouldRefillToken|start_time" "$file"
rg -n -C 4 "REFILL_TOKEN_INTERVAL" dbms/src/Flash/ResourceControl
python3 - <<'PY'
from datetime import timedelta

# Model the relevant duration arithmetic: std::chrono::seconds has an
# integral tick representation, so division by an integer truncates.
seconds_count = 1
print("seconds{1} / 2 count:", seconds_count // 2)
print("500ms count in seconds:", timedelta(milliseconds=500).total_seconds())
PY

Repository: pingcap/tiflash

Length of output: 10265


Use a non-zero sub-second duration.

ResourceGroup::REFILL_TOKEN_INTERVAL / 2 evaluates to zero seconds. The assertion therefore checks start_time, not a time before the refill interval. Use std::chrono::milliseconds(500) instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dbms/src/Flash/ResourceControl/tests/gtest_local_admission_controller.cpp` at
line 39, Update the shouldRefillToken assertion in the relevant test to pass
std::chrono::milliseconds(500) after start_time instead of
ResourceGroup::REFILL_TOKEN_INTERVAL / 2, ensuring the check uses a non-zero
sub-second duration.

@ti-chi-bot

ti-chi-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: JaySon-Huang

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Aug 11, 2026
@ti-chi-bot

ti-chi-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

[LGTM Timeline notifier]

Timeline:

  • 2026-08-11 02:16:44.082254556 +0000 UTC m=+3099190.118349632: ☑️ agreed by JaySon-Huang.

@ti-chi-bot ti-chi-bot Bot added the approved label Aug 11, 2026
@yongman

yongman commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

/test pull-unit-test

…te+mergeDeltaAll (pingcap#10898)

close pingcap#10897\n\nSigned-off-by: JaySon-Huang <tshent@qq.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
dbms/src/Storages/DeltaMerge/tests/gtest_segment_read_task.cpp (1)

656-657: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename fp_guard to follow the C++ naming rule.

Line 656 introduces a local variable with snake_case. Rename it to fpGuard. Keep the scope guard in this block.

As per coding guidelines, “Method and variable names should use camelCase.”

Proposed fix
-        auto fp_guard
+        auto fpGuard
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dbms/src/Storages/DeltaMerge/tests/gtest_segment_read_task.cpp` around lines
656 - 657, Rename the local scope-guard variable in the test block from fp_guard
to fpGuard, while preserving the existing ext::make_scope_guard behavior and
scope.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@dbms/src/Storages/DeltaMerge/tests/gtest_segment_read_task.cpp`:
- Around line 656-657: Rename the local scope-guard variable in the test block
from fp_guard to fpGuard, while preserving the existing ext::make_scope_guard
behavior and scope.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7634d53d-2fd5-4c52-a3f0-9f15c61fe961

📥 Commits

Reviewing files that changed from the base of the PR and between fb977ab and 4b7dbc2.

📒 Files selected for processing (1)
  • dbms/src/Storages/DeltaMerge/tests/gtest_segment_read_task.cpp

@jebter

jebter commented Aug 11, 2026

Copy link
Copy Markdown

/retest

@JaySon-Huang
JaySon-Huang merged commit 3ca432f into pingcap:release-8.5-20260811-v8.5.7 Aug 11, 2026
4 of 5 checks passed
@yongman
yongman deleted the release-8.5-20260811-v8.5.7-hotfix branch August 11, 2026 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved needs-1-more-lgtm Indicates a PR needs 1 more LGTM. release-note-none Denotes a PR that doesn't merit a release note. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants