Coverage Report

Created: 2026-08-26 11:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_throttle_state_machine.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <cstdint>
21
#include <map>
22
#include <mutex>
23
#include <string_view>
24
#include <utility>
25
#include <vector>
26
27
namespace doris::cloud {
28
29
// ============== Common Types ==============
30
31
// Load-related RPC types that need table-level QPS statistics
32
enum class LoadRelatedRpc : size_t {
33
    PREPARE_ROWSET,
34
    COMMIT_ROWSET,
35
    UPDATE_TMP_ROWSET,
36
    UPDATE_PACKED_FILE_INFO,
37
    UPDATE_DELETE_BITMAP,
38
    COUNT
39
};
40
41
// Get the name string for a LoadRelatedRpc type
42
std::string_view load_related_rpc_name(LoadRelatedRpc rpc);
43
44
// ============== Data Structures ==============
45
46
// QPS snapshot: the current QPS of a table on a specific RPC type
47
struct RpcQpsSnapshot {
48
    LoadRelatedRpc rpc_type;
49
    int64_t table_id;
50
    double current_qps;
51
};
52
53
// Throttle action: describes what action should be taken
54
struct RpcThrottleAction {
55
    enum class Type { SET_LIMIT, REMOVE_LIMIT };
56
57
    Type type;
58
    LoadRelatedRpc rpc_type;
59
    int64_t table_id;
60
    double qps_limit {0}; // only meaningful for SET_LIMIT
61
    // Discard queued reservations when applying a downgraded SET_LIMIT action.
62
    bool reset_reservation {false};
63
};
64
65
// ============== ThrottleStateMachine ==============
66
67
// Parameters for throttle state machine
68
struct RpcThrottleParams {
69
    int top_k = 3;          // Number of top tables to throttle on each upgrade
70
    double ratio = 0.5;     // Decay ratio for throttle upgrade
71
    double floor_qps = 1.0; // Floor value for table-level QPS limit
72
73
0
    bool operator==(const RpcThrottleParams& other) const {
74
0
        return top_k == other.top_k && ratio == other.ratio && floor_qps == other.floor_qps;
75
0
    }
76
};
77
78
// Pure state machine for throttle upgrade/downgrade decisions
79
// - No time awareness: caller drives events via on_upgrade/on_downgrade
80
// - No config dependency: all parameters passed via constructor/update_params
81
// - No side effects: only returns action descriptions, doesn't touch throttler
82
// - Deterministically testable: same event sequence -> same output
83
class RpcThrottleStateMachine {
84
public:
85
    explicit RpcThrottleStateMachine(RpcThrottleParams params);
86
87
    // Runtime update parameters, takes effect on next on_upgrade
88
    // Note: existing upgrade history is NOT recalculated
89
    void update_params(RpcThrottleParams params);
90
91
    // Process a throttle upgrade event
92
    // qps_snapshot: current QPS snapshot for each (rpc, table), provided by caller
93
    // Returns: list of actions to execute
94
    std::vector<RpcThrottleAction> on_upgrade(const std::vector<RpcQpsSnapshot>& qps_snapshot);
95
96
    // Process a throttle downgrade event (undo the most recent upgrade)
97
    // Returns: list of actions to execute
98
    std::vector<RpcThrottleAction> on_downgrade();
99
100
    // Query current state
101
    size_t upgrade_level() const; // Current upgrade level
102
    double get_current_limit(LoadRelatedRpc rpc_type, int64_t table_id) const; // 0 = no limit
103
    RpcThrottleParams get_params() const;
104
105
private:
106
    mutable std::mutex _mtx;
107
108
    RpcThrottleParams _params;
109
110
    // Upgrade history for downgrade rollback
111
    // changes: (rpc_type, table_id) -> (old_limit, new_limit)
112
    struct UpgradeRecord {
113
        std::map<std::pair<LoadRelatedRpc, int64_t>, std::pair<double, double>> changes;
114
    };
115
    std::vector<UpgradeRecord> _upgrade_history;
116
117
    // Current active limits for all (rpc, table)
118
    std::map<std::pair<LoadRelatedRpc, int64_t>, double> _current_limits;
119
};
120
121
// ============== ThrottleCoordinator ==============
122
123
// Coordinator parameters
124
struct ThrottleCoordinatorParams {
125
    // Minimum ticks between upgrades
126
    int upgrade_cooldown_ticks = 10;
127
    // Ticks after last MS_BUSY to trigger downgrade
128
    int downgrade_after_ticks = 60;
129
130
0
    bool operator==(const ThrottleCoordinatorParams& other) const {
131
0
        return upgrade_cooldown_ticks == other.upgrade_cooldown_ticks &&
132
0
               downgrade_after_ticks == other.downgrade_after_ticks;
133
0
    }
134
};
135
136
// Pure timing control for upgrade/downgrade triggers
137
// - No time awareness: based on tick count, driven by caller
138
// - No config dependency: all parameters passed via constructor/update_params
139
//
140
// Tick semantics:
141
// - 1 tick = 1 millisecond (fixed unit)
142
// - upgrade_cooldown_ticks and downgrade_after_ticks are in milliseconds
143
// - The tick thread advances time by 1000 ticks (1 second) each iteration
144
class RpcThrottleCoordinator {
145
public:
146
    explicit RpcThrottleCoordinator(ThrottleCoordinatorParams params);
147
148
    // Runtime update parameters, takes effect on subsequent report_ms_busy/tick calls
149
    // Note: existing tick counts are NOT reset
150
    void update_params(ThrottleCoordinatorParams params);
151
152
    // Report a MS_BUSY event
153
    // Returns true if upgrade should be triggered
154
    bool report_ms_busy();
155
156
    // Advance by specified number of ticks (caller decides actual time between ticks)
157
    // Returns true if downgrade should be triggered
158
    bool tick(int64_t ticks = 1);
159
160
    // Tell coordinator whether there are pending upgrades that can be downgraded
161
    // Called by the state machine consumer after upgrade/downgrade
162
    void set_has_pending_upgrades(bool has);
163
164
    // Query state
165
    int64_t ticks_since_last_ms_busy() const;
166
    int64_t ticks_since_last_upgrade() const;
167
    ThrottleCoordinatorParams get_params() const;
168
169
private:
170
    mutable std::mutex _mtx;
171
172
    ThrottleCoordinatorParams _params;
173
    // Counters saturate at their decision thresholds. The MS_BUSY counter is reset
174
    // when there is no pending upgrade history, so neither counter grows unbounded.
175
    int64_t _ticks_since_last_ms_busy = -1; // -1 means inactive or never received
176
    int64_t _ticks_since_last_upgrade = -1; // -1 means never upgraded
177
    bool _has_pending_upgrades = false;     // Whether there are upgrade records to downgrade
178
};
179
180
} // namespace doris::cloud