Minn Fong
04/29/2026, 8:15 PMRocky
04/29/2026, 8:15 PMINSERT tasks) and another for analytical queries (SELECT).
* Isolate by User: Map specific users or roles to resource groups using Classifiers.
Example: Creating a group for analysts with strict limits:
sql
CREATE RESOURCE GROUP analyst_group
TO (role='analyst_role')
WITH (
"cpu_core_limit" = "10", -- Max CPU cores this group can use per BE
"mem_limit" = "30%", -- Max % of memory per BE
"concurrency_limit" = "10", -- Max concurrent queries for this group
"big_query_cpu_second_limit" = "100", -- Kill query if it uses >100s of CPU time
"big_query_scan_rows_limit" = "1000000", -- Kill query if it scans >1M rows
"big_query_mem_limit" = "2147483648" -- Kill query if it uses >2GB RAM
);
2. Guarding Against "Rogue" Queries
To prevent a single massive query from taking down a node, use the big_query_* parameters within a resource group. These act as a "circuit breaker":
* `big_query_cpu_second_limit`: Automatically cancels a query if its cumulative CPU time exceeds this limit.
* `big_query_mem_limit`: Cancels a query if its memory consumption on a single BE exceeds this value.
* `big_query_scan_rows_limit`: Limits the number of rows a query can scan.
3. Query Queuing
When the cluster is under high load (high CPU or memory usage), you can enable Query Queues to buffer incoming requests instead of letting them overwhelm the system.
* Global Queue: SET GLOBAL enable_query_queue_select = true;
* Thresholds: You can set query_queue_concurrency_limit, query_queue_mem_used_pct_limit, and query_queue_cpu_used_permille_limit.
4. Protecting Ingestion Specifically
To ensure ingestion health is prioritized over rogue user queries:
1. Define an Ingestion Resource Group: Use query_type in ('insert') in the classifier.
2. Use `exclusive_cpu_cores`: If you want to guarantee resources for ingestion, you can use exclusive_cpu_cores for the ingestion group. This ensures those cores are reserved and cannot be "stolen" by analytical queries.
3. Adjust `load_mem_limit`: You can set a session or global limit for memory used by loading tasks specifically via SET GLOBAL load_mem_limit = <bytes>;.
5. Other Quotas
* Connection Quotas: Limit the number of connections a single user can open:
ALTER USER 'rogue_user' SET PROPERTIES ("max_user_connections" = "5");
* Storage Quotas: Limit the data volume for a database:
ALTER DATABASE example_db SET DATA QUOTA 10T;
By combining Resource Groups (to cap the "rogue" user's impact) and Big Query Limits (to kill runaway queries), you can effectively shield your ingestion pipeline and overall cluster health.
References
• administration/management/monitor_manage_big_queries.md
• administration/management/resource_management/Query_management.md
• administration/management/resource_management/query_queues.mdMinn Fong
04/29/2026, 8:18 PMr6in.4xlarge instances, how much resources should I allocate users but still not impact query performance?Rocky
04/29/2026, 8:18 PMr6in.4xlarge instances (16 vCPUs, 128 GiB RAM) to protect ingestion.
1. The Strategy: "Reserved Capacity" for Ingestion
On a 16 vCPU machine, you want to ensure that even if a user sends a massive query, the background loading and system tasks always have breathing room.
Recommended Allocation for `r6in.4xlarge`:
* System/Ingestion (Reserved): 4-6 vCPUs
* User Queries (Resource Group): 10-12 vCPUs
2. Implementation Example
You should create a specific resource group for your users. By using exclusive_cpu_cores, you provide "hard" isolation—the user queries literally cannot touch the cores reserved for the system/ingestion.
sql
CREATE RESOURCE GROUP user_queries
TO (role='analyst') -- or map by user
WITH (
-- CPU: Use 10 out of 16 vCPUs. The remaining 6 are reserved for ingestion.
"exclusive_cpu_cores" = "10",
-- Memory: Limit users to 70% of the BE memory (approx 90GB of your 128GB).
"mem_limit" = "70%",
-- Safety Valve: Kill any single query that exceeds 20GB of RAM on one node.
"big_query_mem_limit" = "21474836480",
-- Safety Valve: Kill any query using more than 60s of total CPU time.
"big_query_cpu_second_limit" = "60",
-- Concurrency: Limit to 20 simultaneous queries to prevent thread exhaustion.
"concurrency_limit" = "20"
);
3. Why this works for Ingestion Health
* Ingestion Priority: Ingestion tasks (INSERT, ROUTINE LOAD) typically run in the default resource group or a dedicated ingestion group. If you cap the user_queries group to 10 cores, the BE will always have 6 cores available to handle the high-throughput networking and disk I/O required for r6in instances during ingestion.
* Spillover Protection: If you notice ingestion is still slow, you can specifically create an ingestion group with exclusive_cpu_cores to "guarantee" those resources.
4. Adjusting for Query Performance
To ensure you aren't hurting performance too much:
1. Monitor the "Default" Group: Use SELECT * FROM information_schema.resource_group_usage; to see if your ingestion is actually using those 6 reserved cores. If it's idling, you can increase the user group to 12 cores.
2. Use Query Queues: Instead of failing queries when the limit is hit, enable queuing. This smooths out "bursty" behavior from users without crashing the BE.
sql
SET GLOBAL enable_query_queue_select = true;
SET GLOBAL query_queue_concurrency_limit = 20;
Summary for r6in.4xlarge
Metric | User Group Setting | Reasoning
CPU | exclusive_cpu_cores = 10 | Leaves 6 cores for Ingestion and OS.
Memory | mem_limit = 0.7 | Reserves 30% (~38GB) for loading buffers and metadata.
Big Query | big_query_mem_limit = 20G | Prevents a single "rogue" join from OOMing the node.
Concurrency | concurrency_limit = 20 | Prevents "death by a thousand cuts" (too many small queries).
References
• best_practices/audit_log_resource_group.md
• administration/management/resource_management/resource_group.mdMinn Fong
04/29/2026, 8:21 PMRocky
04/29/2026, 8:22 PMexclusive_cpu_cores) is designed specifically to solve the problem you're describing: it provides strict boundaries for edge cases while still allowing resource overlapping during idle time for ingestion.
Here is how the interaction works and why it won't hurt your ingestion health:
1. Hard Limits vs. Resource Borrowing
When you set exclusive_cpu_cores = 10 for your user group on a 16-core machine:
* The Hard Boundary: The user group is "jailed" in those 10 cores. Even if they send a massive query, they cannot touch the remaining 6 cores. This creates a physical "safe zone" for your ingestion and system processes.
* The Overlap (Borrowing): By default, StarRocks has a setting called enable_resource_group_cpu_borrowing = true. This means that if your users are idle, the background ingestion tasks (which usually run in the "Shared" pool) can borrow those 10 cores to finish faster.
* The Priority: As soon as a user sends a query, the BE engine will immediately reclaim those 10 cores from the ingestion tasks and give them back to the user group.
2. Does it hurt Ingestion?
No, it actually protects it.
In a "Shared-Nothing" architecture without resource groups, a rogue query would attempt to consume 100% of the CPU on all nodes. This would starve the ingestion threads (RPC handling, memtable flushing, and I/O), leading to "RPC Timeout" or "Table not found" errors during loading.
With this configuration:
* Ingestion has a guaranteed minimum: It always has at least 6 cores (the "Shared" pool) to handle high-throughput r6in networking and disk flushes.
* Ingestion has a higher maximum: It can scale up to all 16 cores if the users aren't doing anything.
3. Understanding Memory (A different story)
Unlike CPU, Memory is a hard limit.
* If you set mem_limit = 70% for users, they will never be allowed to use more than 70% of the BE's RAM.
* This 30% "reserved" memory is vital. Ingestion requires significant memory for Memtables and Loading Buffers. By capping users at 70%, you ensure that a rogue query cannot trigger an Out-Of-Memory (OOM) event that would kill the ingestion process or the BE itself.
Recommendation for your r6in.4xlarge
Since Routine Load and Stream Load currently run in the "Shared" pool (not a specific resource group), you should configure your user group as Exclusive.
sql
-- This creates a "hard wall" for users but lets Ingestion use the whole machine when idle.
ALTER RESOURCE GROUP user_group WITH (
"exclusive_cpu_cores" = "10", -- Strict cap for users
"mem_limit" = "70%", -- Guaranteed room for loading buffers
"big_query_mem_limit" = "20G" -- Circuit breaker for rogue queries
);
Summary of Behavior
Workload | CPU Usage (Idle Cluster) | CPU Usage (Heavy User Load)
User Queries | Up to 10 cores | Exactly 10 cores (Capped)
Ingestion | Up to 16 cores (Borrowing) | 6 cores (Guaranteed)
This setup achieves exactly what you want: it enforces strict boundaries to protect ingestion from "rogue" behavior, but avoids wasting hardware during the 90% of the time when the cluster isn't under peak-loaded.
References
• administration/management/resource_management/resource_group.md
• loading/RoutineLoad.md
• sql-reference/sql-statements/loading_unloading/STREAM_LOAD.md
• sql-reference/sql-statements/loading_unloading/routine_load/CREATE_ROUTINE_LOAD.mdMinn Fong
04/29/2026, 8:38 PMRocky
04/29/2026, 8:38 PMsql
-- Shows all current active queries and the users running them
SHOW PROC '/current_queries';
Or for a broader look at all connections:
sql
-- Standard MySQL-compatible command to see who is connected
SHOW PROCESSLIST;
* The User column tells you the DB account the app is using.
* The Host column helps you verify it's the right app based on its IP address.
Step 2: Create the Resource Group with Classifiers
Once you have the username (e.g., bi_tool_user), you create the group. The TO clause is the Classifier that maps the user to this group automatically.
sql
CREATE RESOURCE GROUP analyst_group
TO
(user='bi_tool_user'), -- Matches a specific user
(role='reports_role'), -- Matches anyone with this role
(source_ip='192.168.1.0/24') -- Matches any query coming from this subnet
WITH (
"exclusive_cpu_cores" = "10",
"mem_limit" = "70%",
"big_query_mem_limit" = "20G"
);
Step 3: Verify it's working
After creating the group, any new query sent by bi_tool_user will automatically be governed by these limits. You can verify this by checking the ResourceGroup column in the query list:
sql
SHOW PROC '/current_queries';
Look for your group name in the ResourceGroup column for the queries sent by that user.
──────────
Pro-Tips for "Application Identification"
1. Dedicated Users: It is a best practice to create unique users for each application (e.g., metabase_user, python_etl_user). This makes it easy to set different quotas (e.g., your ETL user might need more memory but fewer concurrent slots).
2. IP-Based Rules: If all your users use the same generic account but connect from different servers, you can use the source_ip classifier to separate them into different resource groups.
3. Database-Based Rules: If a "rogue user" is always hitting a specific large "archive" database, you can create a classifier for that database:
sql
ALTER RESOURCE GROUP analyst_group ADD (db='archive_db');
Now, any query hitting `archive_db`—regardless of the user—will be throttled by this group's limits.
How to add/change users later
If you hire a new analyst or add a new app, you don't need to recreate the group. Just add a new classifier:
sql
ALTER RESOURCE GROUP analyst_group ADD (user='new_intern_user');
References
• administration/management/resource_management/resource_group.md
• using_starrocks/running_queries.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
References
• using_starrocks/running_queries.md
• administration/management/resource_management/query_queues.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROCESSLIST.mdMinn Fong
04/29/2026, 8:43 PMRocky
04/29/2026, 8:43 PMSHOW PROCESSLIST only shows the User because a StarRocks user is the primary identity that connects to the database. Roles are a collection of permissions granted to that user, and a single user can have multiple active roles at once, which is why there isn't a single "Role" column in the process list.
To assign each of your EC2 services its own identity and manage them with Resource Groups, follow this pattern:
1. Create a Dedicated User for Each Service
The most reliable way to distinguish between services (e.g., your "API Service" vs. your "Reporting Service") is to give each one its own StarRocks account in its connection string.
sql
-- Create a user for your specific EC2 service
CREATE USER 'service_api'@'%';
CREATE USER 'service_reports'@'%';
2. Create Roles for Permission Management
Instead of granting permissions directly to users, create Roles. This makes it easy to swap permissions later or apply the same limits to multiple users.
sql
-- 1. Create the roles
CREATE ROLE api_role;
CREATE ROLE reports_role;
-- 2. Grant permissions to the roles
GRANT SELECT ON ALL TABLES IN DATABASE prod_db TO ROLE api_role;
GRANT SELECT ON ALL TABLES IN DATABASE archive_db TO ROLE reports_role;
-- 3. Assign the roles to the users
GRANT 'api_role' TO 'service_api'@'%';
GRANT 'reports_role' TO 'service_reports'@'%';
-- 4. CRITICAL: Set these as default so the service doesn't have to run "SET ROLE"
SET DEFAULT ROLE 'api_role' TO 'service_api'@'%';
SET DEFAULT ROLE 'reports_role' TO 'service_reports'@'%';
3. Connect the User/Role to your Resource Group
Now you can use the Classifiers we discussed earlier to map these to your resource limits. You can map by user (most common) or by role.
sql
-- Map by Role (anyone with this role is limited)
ALTER RESOURCE GROUP user_group ADD (role='reports_role');
-- OR Map by User
ALTER RESOURCE GROUP user_group ADD (user='service_reports');
4. How to see which Role is active
Since it's not in SHOW PROCESSLIST, you can check the active role for any specific session by running:
* For your current session: SELECT current_role();
* To see what roles a user has: SHOW GRANTS FOR 'service_reports'@'%';
Why you don't see roles in Processlist
In StarRocks' RBAC (Role-Based Access Control) system, a user like service_reports might have 3 different roles: db_admin, reports_role, and public. Since all three can be active simultaneously to give the user the "union" of those permissions, a single column in the process list wouldn't be able to display them clearly.
Summary of Workflow for EC2
1. EC2 Side: Update your application's .env or config file to use a unique StarRocks user (e.g., user=service_api).
2. StarRocks Side: Create that user and grant it a specific Role.
3. Resource Group: Create a Classifier for that user or role.
4. Verification: Run SHOW PROCESSLIST to see the User column. If it says service_api, your resource group rules are being applied.
References
• administration/management/resource_management/resource_group.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROCESSLIST.md
• sql-reference/sql-statements/user-account-management/SET_DEFAULT_ROLE.md
• sql-reference/sql-statements/user-account-management/SHOW_ROLES.mdMinn Fong
04/29/2026, 8:52 PMRocky
04/29/2026, 8:52 PMapp_service_1).
* Create the Role: Create a role that defines what the app can do (e.g., api_read_role).
* The Link: Grant the role to the user and set it as the Default.
sql
GRANT 'api_read_role' TO 'app_service_1'@'%';
SET DEFAULT ROLE 'api_read_role' TO 'app_service_1'@'%';
2. The Application Configuration (EC2 Side)
Your application environment variables should define the User, not the Role:
* STARROCKS_USER=app_service_1
* STARROCKS_PASSWORD=your_password
* STARROCKS_HOST=your_fe_ip
3. The Resource Group Mapping
You can now tie the Resource Group to either the User or the Role.
* Matching by User: This is the most direct way if one service = one user.
sql
ALTER RESOURCE GROUP user_group ADD (user='app_service_1');
* Matching by Role: This is better if you have 10 different EC2 services that should all share the same 10-core limit. You just give them all the same role.
sql
ALTER RESOURCE GROUP user_group ADD (role='api_read_role');
4. About the "HTTP Connection"
While StarRocks supports a RESTful SQL API (over HTTP), most EC2 applications (Java, Python, Go) use the MySQL protocol (Port 9030) via a standard driver (JDBC/SQLAlchemy/etc).
* If using MySQL Protocol: Use the logic above.
* If using HTTP SQL API: You still pass the user and password in the Basic Auth header. StarRocks will identify the user and apply the Resource Group rules exactly the same way.
Summary Checklist
1. Define a unique User per application/service in your EC2 env vars.
2. Assign a Role to that user and make it the DEFAULT ROLE so it's always active.
3. Add a Classifier to your Resource Group using either the user name or the role name.
4. Verify: Run SHOW PROCESSLIST to see the User column and confirm the application is connecting with the correct identity.
This setup ensures that even if one application goes "rogue," its CPU usage is physically jailed to its assigned cores, protecting your ingestion and other services.
References
• administration/management/resource_management/resource_group.md
• sql-reference/sql-statements/user-account-management/SET_DEFAULT_ROLE.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
• faq/resource_isolation_faq.md
References
• faq/resource_isolation_faq.md
• administration/management/resource_management/resource_group.mdMinn Fong
04/29/2026, 8:56 PMRocky
04/29/2026, 8:57 PMmysql_user and mysql_role resources.
* Resource Groups: Because "Resource Group" is a custom StarRocks SQL extension (not standard MySQL), you cannot use the MySQL provider for it. Instead, use a null_resource with a local-exec provisioner to run the SQL command via a MySQL client.
Example Terraform snippet:
hcl
# Manage the User
resource "mysql_user" "app_service" {
user = "service_api"
host = "%"
plaintext_password = var.app_password
}
# Manage the Resource Group (Using local-exec)
resource "null_resource" "starrocks_resource_group" {
provisioner "local-exec" {
command = <<EOT
mysql -h ${var.sr_host} -P 9030 -u root -p${var.root_password} -e \
"CREATE RESOURCE GROUP IF NOT EXISTS api_group TO (user='service_api') WITH ('exclusive_cpu_cores' = '5');"
EOT
}
}
──────────
2. HTTP Connection & Authentication
You are correct regarding the HTTP connection. If you are using the HTTP SQL API (introduced in v3.2) to query the FE node:
* Authentication: You must provide the user and password in the Basic Auth header.
* Role Activation: The HTTP SQL API currently supports a limited set of statements (SELECT, SHOW, EXPLAIN, KILL). It may not support the SET ROLE command in the same way a persistent MySQL session does.
* The Solution: Use SET DEFAULT ROLE. By assigning a default role to the user, StarRocks automatically activates that role (and its associated Resource Group mapping) the moment the HTTP request authenticates.
Example Curl Command for your EC2 Service:
bash
curl -X POST 'http://<fe_ip>:8030/api/v1/catalogs/default_catalog/databases/prod_db/sql' \
-u 'service_api:your_password' \
-d '{"query": "SELECT * FROM my_table LIMIT 10;"}' \
--header "Content-Type: application/json"
Key Takeaways for your EC2 Setup
1. Identity: Each EC2 service should have a unique StarRocks User (defined in your service's .env).
2. Permissions: Create a Role, grant it permissions, and SET DEFAULT ROLE for that user.
3. Throttling: Create a Resource Group with a Classifier that matches that specific user or role.
4. Automation: Use Terraform's MySQL provider for users/roles and local-exec for the Resource Group commands.
References
• administration/management/resource_management/resource_group.md
• sql-reference/sql-statements/user-account-management/SET_DEFAULT_ROLE.md
• using_starrocks/running_queries.md
• administration/management/resource_management/query_queues.md
• sql-reference/sql-statements/cluster-management/nodes_processes/SHOW_PROCESSLIST.md
• faq/resource_isolation_faq.mdMinn Fong
04/29/2026, 9:00 PMRocky
04/29/2026, 9:00 PMsql
-- Define the role
CREATE ROLE service_api_role;
-- Grant necessary permissions to the role
GRANT SELECT ON ALL TABLES IN DATABASE prod_db TO ROLE service_api_role;
Step 2: Create the User
Create a unique user identity for your EC2 application.
sql
-- Create the user (identifies the specific service)
CREATE USER 'ec2_api_service'@'%' IDENTIFIED BY 'your_secure_password';
Step 3: Link Role to User (and Set Default)
You must grant the role to the user and—most importantly for HTTP connections—set it as the Default Role. This ensures the role (and its associated resource limits) is automatically active when the app connects.
sql
-- 1. Assign the role to the user
GRANT 'service_api_role' TO USER 'ec2_api_service'@'%';
-- 2. Make it active by default
ALTER USER 'ec2_api_service'@'%' DEFAULT ROLE 'service_api_role';
Step 4: Create the Resource Group
Define the physical hardware limits (CPU/Memory).
sql
CREATE RESOURCE GROUP api_service_group
WITH (
"exclusive_cpu_cores" = "4", -- Reserve 4 cores for this group
"mem_limit" = "20%", -- Limit to 20% of BE memory
"concurrency_limit" = "50" -- Max 50 simultaneous queries
);
Step 5: Assign Identity to Resource Group (Classifiers)
In StarRocks, you "assign" a role or user to a resource group by creating a Classifier. This tells StarRocks: "If a query comes from this role/user, run it in this specific group."
You can do this during creation (Step 4) or add it to an existing group:
sql
-- Option A: Map by Role (Recommended if multiple users share the same role)
ALTER RESOURCE GROUP api_service_group ADD (role='service_api_role');
-- Option B: Map by User (Direct mapping)
ALTER RESOURCE GROUP api_service_group ADD (user='ec2_api_service');
──────────
Course of Action Summary Table
Step | Action | SQL Command | Why?
1 | Create Role | CREATE ROLE | Defines what the service is allowed to do.
2 | Create User | CREATE USER | Provides the login identity for your EC2 .env.
3 | Link User/Role | SET DEFAULT ROLE | Ensures the identity is "active" immediately on login.
4 | Create Group | CREATE RESOURCE GROUP | Sets the physical "jail" (CPU/RAM limits).
5 | Link Group | ALTER...ADD (role='...') | Tells the engine to put that role's queries into that group.
Verification
Once your EC2 application connects, you can verify the setup is working by running:
sql
-- See which user is connected (matches Step 2)
SHOW PROCESSLIST;
-- See which resource group is currently being used by active queries
SELECT * FROM information_schema.resource_group_usage;
References
• administration/user_privs/authorization/User_privilege.md
• administration/management/resource_management/resource_group.md
• sql-reference/sql-statements/user-account-management/SET_DEFAULT_ROLE.md
References
• administration/user_privs/authorization/User_privilege.md
• administration/management/resource_management/resource_group.mdMinn Fong
04/29/2026, 9:06 PMRocky
04/29/2026, 9:06 PMINSERT INTO ... SELECT and Broker Load (can be classified using query_type = 'insert').
* Not Supported: Routine Load (Kafka), Stream Load (HTTP), and Schema Change. These run as background/system tasks and do not currently "hit" the resource group classifier logic.
2. How to "Reserve" for Ingestion anyway
Because background tasks like Routine Load and Stream Load aren't managed by resource groups, you can't explicitly "put them in a jail." Instead, you do the opposite: you cage the queries.
If you want to ensure ingestion always has at least 20% of your CPU, you should:
1. Create a Resource Group for all queries (using a role or the default_wg).
2. Use exclusive_cpu_cores to hard-limit queries to 80% of your total cores.
3. The remaining 20% of cores are now "untouchable" by the query engine, leaving them available for the background ingestion processes (Routine/Stream Load).
Example SQL to "Cage" queries:
sql
-- Modify the default group to only allow queries to use 40 cores on a 64-core machine
ALTER RESOURCE GROUP default_wg WITH (
"exclusive_cpu_cores" = "40"
);
3. Why the "User + Role" approach is still better
If you just limit the "whole system" to leave room for ingestion, you run into two problems:
1. Resource Waste: If no ingestion is happening, those reserved cores sit idle and cannot be used to speed up a big query.
2. The "Rogue Query" Problem: One bad user can still write a query that eats up all the CPU in your "query cage," killing performance for all other users, even if ingestion is safe.
Recommended Course of Action (The "Best of Both Worlds")
I recommend a hybrid approach:
1. For Ingestion (Routine/Stream Load): Use BE configurations to protect memory.
* load_process_max_memory_limit_percent (Default is usually 60%).
2. For Ingestion (Broker/Insert): Create a specific "Ingestion Group" using query_type = 'insert'.
sql
CREATE RESOURCE GROUP ingestion_wg
TO (query_type in ('insert'))
WITH ("cpu_weight" = "10", "mem_limit" = "30%");
3. For Users: Stick to the Role-based plan we discussed. This allows you to say: "Service A gets 5 cores, Service B gets 10 cores, and if a data scientist runs a massive join, they only get 2 cores."
Summary Table: What to use where
Workload | Managed by Resource Group? | Recommendation
Routine Load / Stream Load | No | Use BE config for memory; Cage queries for CPU.
Broker Load / INSERT INTO | Yes | Use query_type = 'insert' in a Resource Group.
App Queries (EC2) | Yes | Use Role-based Resource Groups (as discussed).
By defining the user/role, you aren't just "bothering with users"—you are building a multi-tenant safety net. It ensures that "Service A" doesn't starve "Service B," and that both of them together don't starve your critical data ingestion.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• administration/management/BE_configuration.md
References
• administration/management/resource_management/resource_group.md
• loading/RoutineLoad.mdMinn Fong
04/30/2026, 1:38 PMRocky
04/30/2026, 1:38 PMexclusive_cpu_cores. These cores are "locked" to that group.
* Shared Cores: Any CPU core not assigned to an exclusive group. This is where Ingestion (Routine/Stream Load) and the default_wg run.
Does the Reserved CPU Overlap?
No. exclusive_cpu_cores are additive, not overlapping.
* If you have a 16 vCPU machine:
* User A: exclusive_cpu_cores = 4
* User B: exclusive_cpu_cores = 4
* Total Reserved: 8 cores.
* Remaining Shared Cores: 8 cores (16 total - 8 reserved).
* Constraint: The sum of all exclusive_cpu_cores cannot exceed vCPU - 1. StarRocks forces you to leave at least 1 core for the "Shared" pool.
To protect Ingestion: Simply ensure your total exclusive_cpu_cores for all users leaves enough "Shared Cores" for your loading tasks. If your ingestion needs 4 cores to stay healthy, and you have 16 total, only allocate a maximum of 12 cores to your user groups.
2. The Memory Strategy: mem_pool
If you want to map users 1-1 but manage their memory globally, you have two choices:
* Individual Isolation: Give each group a mem_limit = '10%'. If User A hits 10%, their query fails, even if User B is idle.
* Shared Memory Pool (v4.0+): Put all users into one mem_pool.
sql
-- Both users share a 40% bucket. If User B is idle, User A can use all 40%.
-- But together, they can never exceed 40%.
CREATE RESOURCE GROUP user_a_rg WITH ("mem_limit" = "40%", "mem_pool" = "query_tier");
CREATE RESOURCE GROUP user_b_rg WITH ("mem_limit" = "40%", "mem_pool" = "query_tier");
Recommended Configuration Example
On a 16 vCPU machine, to ensure 4 cores are always available for ingestion:
sql
-- 1. Create Group for User A (Gets 4 cores exclusively)
CREATE RESOURCE GROUP rg_user_a
TO (user='user_a')
WITH (
"exclusive_cpu_cores" = "4",
"mem_limit" = "20%"
);
-- 2. Create Group for User B (Gets 4 cores exclusively)
CREATE RESOURCE GROUP rg_user_b
TO (user='user_b')
WITH (
"exclusive_cpu_cores" = "4",
"mem_limit" = "20%"
);
-- RESULT:
-- User A: Has 4 cores.
-- User B: Has 4 cores.
-- Ingestion/System: Has 8 cores (the "Shared" pool).
-- NOTE: Ingestion will NEVER be touched by User A or User B's queries.
Important "Borrowing" Note
By default, if User A is idle, StarRocks allows "Shared" tasks (like ingestion) to borrow User A's 4 cores. This is efficient. However, if you want to be extremely strict and ensure User A's cores stay cold when they aren't querying, you can disable borrowing:
sql
UPDATE information_schema.be_configs
SET VALUE = "false"
WHERE NAME = "enable_resource_group_cpu_borrowing";
Summary of Action for You
1. Calculate Ingestion Needs: Determine how many cores your Stream/Routine loads need (e.g., 4 cores).
2. Define User Groups: Create your 1-1 groups using exclusive_cpu_cores.
3. Check the Sum: Ensure Sum(exclusive_cpu_cores) <= Total_vCPUs - Ingestion_Cores.
4. Memory: Use mem_limit per group to prevent a single user from triggering an OOM on the whole BE.
References
• faq/resource_isolation_faq.mdMinn Fong
04/30/2026, 1:42 PMRocky
04/30/2026, 1:42 PMn cores to exclusive groups; the limit is n-1).
2. The Solution for "Many Services": Soft Isolation (cpu_weight)
If you have 20 services, do not use exclusive_cpu_cores. Use cpu_weight instead.
How it works:
Instead of saying "Service A gets 2 physical cores," you say "Service A has a weight of 10."
* If Service A is the only one running, it can use all available cores in the Shared Pool.
* If Service B (weight 10) starts running at the same time, the system will split the CPU 50/50.
* Crucially: You can have 100 services all using weights, and you will never "run out of cores." They will simply throttle each other proportionally based on their weight.
3. How to ensure Ingestion is fine (The "Inverse" Logic)
Since you can't put Ingestion in a cage, you create a cage for everything else.
Step 1: Create a "Global User Pool" with a Weight
Instead of 1-1 mapping for every single user, group your "regular" services into a shared resource group.
sql
CREATE RESOURCE GROUP general_service_rg
WITH (
"cpu_weight" = "10", -- High weight for query performance
"mem_limit" = "60%" -- Leave 40% for system/ingestion
);
-- Map all your service roles to this one group
ALTER RESOURCE GROUP general_service_rg ADD (role='service_role_1');
ALTER RESOURCE GROUP general_service_rg ADD (role='service_role_2');
Step 2: Use the "CPU Buffer" Trick
If you want to be 100% sure Ingestion always has 4 cores available on a 16-core machine:
1. Do not give your services exclusive_cpu_cores.
2. Create one "Dummy" or "Administrative" group with exclusive_cpu_cores = 11.
3. Assign your users to a Shared Group (using cpu_weight).
4. Because the system now only has 5 cores left in the "Shared Pool" (16 total - 11 exclusive), your weighted services and your Ingestion will compete for those 5 cores.
Summary: The "Best Practice" for Many Services
If you have a high number of services:
1. Don't use Exclusive Cores for the services. It is too rigid.
2. Use cpu_weight to allow services to share the CPU.
3. Protect Ingestion via Memory: Use mem_limit on your resource groups to ensure queries don't trigger a global Out-of-Memory (OOM) that kills the ingestion threads.
4. Protect Ingestion via `concurrency_limit`: Limit how many queries can run at once. This prevents "CPU death by a thousand cuts" where too many small queries starve the background ingestion.
Rule of Thumb:
* Exclusive Cores: Use only for your #1 most critical, latency-sensitive app (e.g., your Executive Dashboard).
* CPU Weight: Use for everything else (including your "many services"). This/That services"). This" services). This" services).
ReferencesMinn Fong
04/30/2026, 6:22 PMRocky
04/30/2026, 6:23 PMSELECT queries and INSERT INTO ... SELECT / Broker Load jobs (which are essentially big distributed queries).
Stream Load is different:
* Lightweight Path: It is an HTTP-based write directly to the storage engine (MemTable). It uses dedicated thread pools on the Backend (BE) rather than the general Pipeline threads.
* No Classifier Check: The BE code that handles the incoming Stream Load data stream doesn't "check-in" with the Resource Manager to see which CPU bucket it belongs to. It simply runs in the system's Shared Pool.
2. How to "Guarantee" Ingestion Resources (The Workaround)
Since you cannot put Stream Load into a "Reserved Cage," you must cage everything else.
Think of your CPU as two separate buckets:
1. The Exclusive Pool: Cores you explicitly give to Resource Groups.
2. The Shared Pool: Cores left over. All Stream Load and Routine Load run here.
If you have a 16 vCPU machine and you want to ensure ingestion always has 4 cores available:
Step A: Fence off the Querying Users
You create a resource group for your query applications and give them a hard "Exclusive" limit.
sql
-- This group will "steal" 12 cores away from the system.
-- Queries in this group CANNOT use the remaining 4 cores.
CREATE RESOURCE GROUP query_app_rg
TO (role='my_query_role')
WITH (
"exclusive_cpu_cores" = "12",
"mem_limit" = "60%"
);
Step B: The Result
* Query Apps: Limited to 12 cores. They can never spike to use the full 16.
* Ingestion: Because it has no resource group, it defaults to the Shared Pool.
* The Shared Pool: Has exactly 4 cores left (16 total - 12 exclusive).
Because your queries are physically forbidden from touching those 4 cores, Ingestion now has a 4-core guarantee.
3. Other "Knobs" for Stream Load
Since you are looking to protect ingestion specifically for an application, you should also look at these BE Configurations (in be.conf):
* load_process_max_memory_limit_percent (Default 30%): This is the global "safety valve" for all ingestion. If your Stream Load application is huge, you might want to increase this to ensure it doesn't fail when memory is tight.
* push_worker_count_high_priority: Controls the number of threads dedicated to handling ingestion tasks.
* Merge Commit (v3.4+): If your application sends many small Stream Loads, enable Merge Commit. This combines many small loads into one transaction, which drastically reduces the CPU/IO spent on "Compaction" (the background cleanup that usually slows down a busy system).
Summary
You can't give Ingestion a Resource Group because it doesn't "live" in the Resource Group world. But you can guarantee its resources by using exclusive_cpu_cores to restrict your query users, effectively leaving a "hole" in the CPU capacity that only Ingestion can fill.
References
• administration/management/resource_management/resource_group.md
• loading/StreamLoad.md
• administration/management/BE_configuration.md
References
• loading/Loading_intro.md
• faq/resource_isolation_faq.md
• administration/management/resource_management/resource_group.mdRocky
04/30/2026, 6:23 PMMinn Fong
04/30/2026, 6:25 PMRocky
04/30/2026, 6:25 PMexclusive_cpu_cores, you are essentially carving the machine into pieces.
* How it protects Ingestion: You don't give ingestion a group; instead, you give everyone else a group. The cores you don't assign to users automatically become the Shared Pool. This is where Ingestion (Stream Load) and the default_wg live.
* The "Bursting" behavior: By default, the configuration enable_resource_group_cpu_borrowing is set to true. This means:
* Ingestion CAN burst: If ingestion needs more than the Shared Pool cores, it can borrow idle cores from the users' exclusive groups.
* Users CANNOT burst: Users are strictly capped. Even if ingestion is totally idle, a user with 2 exclusive cores will never use 3.
Verdict: Use this if your primary goal is to ensure ingestion is never "starved" by a massive query spike from a user.
The "Weighted" Architecture (Soft Isolation)
If you want both users and ingestion to be able to use 100% of the CPU when the machine is idle, you should use cpu_weight.
* How it works: You don't reserve cores. Instead, you give each user a "Weight" (e.g., cpu_weight = 1).
* The default_wg priority: The default_wg (where ingestion and system tasks live) has a default weight equal to the total number of vCPUs on your BE.
* Protection: If you have 10 users with weight = 1 and a 16-core machine, the total weight is 26 ($10 + 16$).
* Ingestion/System gets $16/26$ (~61%) of the CPU when everyone is fighting for resources.
* Users get $1/26$ (~4%) each.
* Bursting: If only one user is active, they can take all 16 cores. If only ingestion is active, it takes all 16 cores.
Verdict: Use this if you want maximum efficiency and are okay with users occasionally competing with ingestion during the rare moments when everyone spikes at once.
Addressing your specific concerns
1. "Does that mean ingestion can't access shared pool?"
Actually, ingestion is the Shared Pool. In StarRocks, anything not in an exclusive group is "Shared." The concern is usually the opposite: ensuring queries don't eat the shared pool.
2. "Why can't I give ingestion exclusive cores?"
Because Stream Load is an internal system task. It doesn't use the standard SQL execution pipeline that the Resource Manager monitors. It uses dedicated background thread pools. Since it doesn't have a "User" or "Query ID" in the same way a SELECT query does, the classifier can't catch it and put it in a cage.
3. Recommendations for your 70% spikes:
Since you have many services and occasional spikes, Soft Isolation (cpu_weight) is typically better. It prevents you from "running out" of cores and allows your idle capacity to be used by whichever service needs it most at that millisecond.
Summary Checklist
* For 1-1 mapping + Bursting: Use CREATE RESOURCE GROUP ... WITH ("cpu_weight" = "1").
* To protect Ingestion in this setup: Set a mem_limit (e.g., 5-10%) on each user group. This ensures no single user query can trigger an Out-of-Memory event that kills the ingestion threads.
* To monitor: Use SELECT * FROM information_schema.resource_group_usage to see which users are hitting their limits during your 70% spikes.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
References
• faq/resource_isolation_faq.mdRocky
04/30/2026, 6:25 PMMinn Fong
04/30/2026, 6:28 PMRocky
04/30/2026, 6:28 PMdefault_wg
In StarRocks, there is a hidden resource group called default_wg. This is where:
* Ingestion (Stream Load, Routine Load) runs.
* System tasks (Compaction, Metadata sync) run.
* Any user not explicitly assigned to a resource group runs.
By default, the cpu_weight of default_wg is equal to the number of vCPUs on the BE.
* On your 16-core machine, default_wg has a weight of 16.
2. The Calculation (Total Weight = 26)
When you create 10 resource groups (one for each service/user) and give each a weight of 1, the scheduler looks at the total "tickets" in the system:
* 10 Users/Groups: $10 \times 1 = 10$ tickets
* default_wg: $16$ tickets (Automatic)
* TOTAL: 26 tickets
3. Why users don't get 10%
In a period of full contention (when every service and ingestion are all trying to use 100% CPU at the exact same time), the resources are split like this:
Group | Weight | Percentage Share | Core Equivalent
System (Ingestion) | 16 | $16 / 26 \approx \mathbf{61.5\%}$ | ~9.8 Cores
User 1 | 1 | $1 / 26 \approx \mathbf{3.8\%}$ | ~0.6 Cores
User 2 | 1 | $1 / 26 \approx \mathbf{3.8\%}$ | ~0.6 Cores
... | ... | ... | ...
User 10 | 1 | $1 / 26 \approx \mathbf{3.8\%}$ | ~0.6 Cores
This is actually a safety feature. It ensures that even if all 10 of your services go crazy with queries, the System/Ingestion group still retains the majority (~61%) of the CPU power because its weight (16) is much higher than the individual user weights (1).
4. How to get the "10% per user" you want
If you want your users to have more "clout" relative to ingestion, you have two choices:
Option A: Lower the System Weight
You can manually lower the weight of the system group so it doesn't "hog" so many tickets.
sql
ALTER RESOURCE GROUP default_wg WITH ("cpu_weight" = "2");
* Now: 10 (Users) + 2 (System) = 12 total tickets.
* Each User: $1/12 \approx 8.3\%$
* System: $2/12 \approx 16\%$
Option B: Increase User Weights
Give each user a weight equal to the core count if you want them to be "equal" to the system.
sql
-- Do this for each of your 10 groups
ALTER RESOURCE GROUP user_service_1 WITH ("cpu_weight" = "16");
* Now: $(10 \times 16) + 16 = 176$ total tickets.
* Each User: $16/176 \approx 9.1\%$ (Very close to your 10% goal).
Why this is better than "Hard" cores:
In your scenario (50% average load, 70% spikes), if a user has a weight of 1, they are only "throttled" to 3.8% if, and only if, the machine is at 100% capacity.
If the machine is at 50% capacity, that user can "burst" and use all 50% of the remaining idle CPU. The weight only matters when there is a line at the door. This flexibility is why Soft Isolation is the recommended architecture for the "many services" use case.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• best_practices/audit_log_resource_group.mdMinn Fong
04/30/2026, 6:31 PMRocky
04/30/2026, 6:32 PMdefault_wg (ingestion/system) is only using 30% of its "60% share," the remaining 30% isn't wasted. The StarRocks scheduler immediately offers those idle cycles to any other active group.
* Single User Burst: If only one user is querying and ingestion is at its 50% baseline, that single user can easily burst to consume all remaining CPU capacity (the other 50%).
2. Why default_wg having 16 Weight is your "Safety Buffer"
Think of the 16 tickets assigned to default_wg as a Priority Reserve rather than a hard limit.
* When Ingestion Spikes: If a massive Stream Load hits and the system utilization jumps toward 90%, the 16 weight of default_wg ensures it can "push back" against the query users. It says: "I have 16 tickets, you only have 1; I get to use the CPU first."
* When Ingestion is Quiet: When ingestion is doing nothing, those 16 tickets are effectively "off the table." The total "tickets" in the system drops from 26 down to 10 (your users). Now each user's weight of 1 represents $1/10$ (10%) of the total available power.
3. The "Unmanaged" Ingestion Factor (Crucial Detail)
As established, Stream Load is not currently managed by the Resource Group scheduler (it doesn't appear in the cpu_weight table). It runs in background thread pools (push_worker).
Because Stream Load is "invisible" to the resource groups:
1. Ingestion is the "Noisy Neighbor": It will take the CPU it needs directly from the OS.
2. Resource Groups are the "Self-Governing Tenants": Your 10 users share whatever is left over after the OS handles ingestion.
3. The Result: By giving users a cpu_weight of 1, you are telling the StarRocks query engine: "When we are fighting for the CPU that ingestion hasn't taken, split it evenly among these 10 services."
Architecture Recommendation
For your specific 70% spike environment, the Soft Isolation (Weights) approach is better than Hard Isolation (Exclusive Cores) for the following reason:
If you used Exclusive Cores (Hard Isolation) to reserve 4 cores for ingestion, those 4 cores would be legally forbidden from being used by queries. Even if ingestion was 0% utilized and a user was waiting on a huge query, those 4 cores would sit idle.
Stick with Soft Isolation:
* Set default_wg weight to your core count (16).
* Set each User group weight to 1.
* The Benefit: Your users can burst to use 100% of the machine at midnight when ingestion is idle, but ingestion is protected by the "Priority Reserve" when things get busy during the day.
Summary of the Math for your 50% Load:
* Total Capacity: 16 Cores
* Ingestion (at 50%): Consumes 8 Cores
* Remaining: 8 Cores
* Active User 1: Can use all 8 remaining cores ($100\%$ of the available slack).
* If Users 1 and 2 query together: They will each get 4 cores ($1/2$ of the available slack).
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• best_practices/audit_log_resource_group.md
References
• best_practices/audit_log_resource_group.mdMinn Fong
04/30/2026, 6:34 PMRocky
04/30/2026, 6:34 PMdefault_wg (and thus your ingestion protection) shrinks. If you add 20 users each with a weight of 1, the default_wg (weight 16) now only commands ~44% of the CPU during a "traffic jam."
To avoid the "constant tuning" trap as your service grows, here are the three best ways to architecture this:
1. The "High-Floor" Strategy (Recommended)
Instead of leaving default_wg at the default (16), set it to a much higher number that makes individual user additions "noise" in the math.
* Action: ALTER RESOURCE GROUP default_wg WITH ("cpu_weight" = "100");
* With 10 users (weight 1): Ingestion share = $100 / 110 = \mathbf{91\%}$
* With 50 users (weight 1): Ingestion share = $100 / 150 = \mathbf{66\%}$
* Result: You don't have to touch the configuration again until you have dozens of new services. This ensures that the system/ingestion always has the "biggest stick" in the room.
2. The "Tiered" Architecture
Instead of 1-1 mapping for every service, group your services into Tiers (Gold, Silver, Bronze).
* Create 3 resource groups: prod_high, prod_med, prod_low.
* Assign your 20 users to these 3 groups using classifiers.
* Why this scales: The total number of resource groups stays at 3, regardless of whether you have 20 users or 200 users. Your default_wg share remains perfectly stable.
3. Re-evaluating "Hard" vs "Soft" Isolation for Scaling
If your goal is to never tune the ingestion protection, the Hard Isolation (Exclusive Cores) for users is actually more "set and forget."
* Architecture:
* Total cores: 16.
* Create 10 User Groups with exclusive_cpu_cores = 1.
* Result: 10 cores are reserved for users. The remaining 6 cores are the Shared Pool (where ingestion lives).
* Scaling to 20 Users: You literally cannot. The system will stop you because $20 > 16$.
* The Trade-off: Hard isolation forces you to think about capacity planning before you add a service, whereas Soft isolation (Weights) lets you add infinite services but dilutes the power of each one.
Rocky's Summary Recommendation
For a growing environment with many services:
1. Use Soft Isolation (cpu_weight) to allow bursting.
2. Boost the default_wg weight to 100 immediately. This gives you a massive safety buffer so that your ingestion remains the highest priority even as you scale from 10 to 50 services.
3. Monitor with this query:
sql
-- Check the current relative percentages of all groups
SELECT name, cpu_weight,
(cpu_weight / SUM(cpu_weight) OVER()) * 100 as potential_cpu_percentage
FROM information_schema.resource_group_usage;
This way, you only need to tune the values once or twice a year rather than every time a new service is onboard.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md
References
• administration/management/resource_management/resource_group.md
• sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md
• faq/resource_isolation_faq.md
References
• faq/resource_isolation_faq.mdMinn Fong
04/30/2026, 6:37 PMRocky
04/30/2026, 6:37 PMcpu_weight limit that changes how you should implement this "big stick" strategy.
1. The Maximum Weight Limit
In StarRocks, the cpu_weight parameter has a specific constraint: its value range is (0, number of CPU cores].
* On your 16-core machine, the maximum weight you can assign to any group (including default_wg) is 16.
* You cannot set it to 100. If you try, the system will likely throw a validation error.
2. How to achieve the "1% Worst-Case" (The Reverse Strategy)
Since you can't push default_wg up to 100, you achieve the exact same "safeguard" by pushing the user weights down.
Instead of 10 users with Weight 1 and a System with Weight 16, do this:
* System (default_wg): Keep at 16 (its max).
* Users/Services: Set each to 0.1 (or the smallest allowed increment, typically 1 or a small decimal depending on your StarRocks version).
The Math at 100% Contention (with 20 users at weight 0.1):
* Total weight = 16 (System) + 2 (20 users × 0.1) = 18.
* System Share: $16 / 18 = \mathbf{88.8\%}$
* User Share: $0.1 / 18 = \mathbf{0.5\%}$
This effectively gives ingestion the "Big Stick" you were looking for.
3. Will queries still work? (The "Borrowing" Reality)
Yes. This is the beauty of the Soft Isolation architecture. Your assessment of the 50% idle capacity is spot on:
* During Normal Operation (50% Ingestion Load): Even if a user only has a "guaranteed" share of 0.5%, they are not restricted to it. Because the scheduler is work-conserving, that user can borrow every single idle cycle. If ingestion is using 8 cores and the other 8 are idle, a single query can "burst" and use all 8 idle cores.
* During a Crisis (100% Ingestion Spike): If a massive data load suddenly requires all 16 cores, the scheduler will immediately claw back the "borrowed" cores from the users. The user's query won't necessarily fail, but it will slow down significantly as it is compressed into its 0.5% "reserve" to make room for the ingestion.
4. Is this the easiest safeguard?
Yes, it is the safest "hands-off" configuration. By keeping the user weights very low and the default_wg weight at the core count:
1. Ingestion is Protected: You never have to worry about a "rogue" query (like a massive SELECT * without a limit) killing your real-time data pipelines.
2. Resources aren't Wasted: You aren't "locking" cores away. Users get the full power of the 16-core machine whenever ingestion isn't using it.
3. Low Maintenance: As you grow from 10 to 20 to 50 users, you don't need to keep re-tuning. Even at 50 users, the system still prioritizes the default_wg because its weight (16) remains dominant over the sum of many small user weights.
Summary Recommendation
To implement your plan on a 16-core machine:
1. Verify default_wg is at 16 (it should be by default).
2. Set your user-facing resource groups to a weight of 1 (or smaller if your version allows decimals).
3. If you have 20 users at weight 1, ingestion is guaranteed ~44% ($16/36$).
4. If you want ingestion to have a higher guarantee (like 80%+), you should simply keep the number of resource groups low by grouping users into Tiers (e.g., prod_group, test_group) rather than one group per user.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md
References
• faq/resource_isolation_faq.mdRocky
04/30/2026, 6:37 PMMinn Fong
04/30/2026, 6:40 PMRocky
04/30/2026, 6:40 PMcpu_weight, here is the breakdown of how StarRocks handles these "Soft Isolation" scenarios:
1. Maximum Granularity of cpu_weight
The granularity of cpu_weight is integer-based.
* Data Type: It is an INT. You cannot set it to 0.1 or 0.5. The minimum valid value is 1.
* Value Range: The range is (0, avg_be_cpu_cores]. This means on your 16-core machine, the maximum weight you can assign to any single group (including default_wg) is 16.
The Impact on your "1% Target":
Because the minimum weight is 1 and the maximum is 16, you cannot technically achieve a "1% guarantee" if you have 20 individual user groups.
* With 20 users (weight 1 each) and default_wg (weight 16):
* Total weight = 36.
* Each user's worst-case share is $1/36 \approx \mathbf{2.8\%}$.
* Ingestion's worst-case share is $16/36 \approx \mathbf{44.4\%}$.
2. The "Bursty User" vs. Idle Ingestion
If Ingestion is allocated 90% via weights but is only using 0% (idle), and a User with a 1% weight starts a massive query:
* Borrowing Logic: The StarRocks scheduler is work-conserving. It does not care that the user is "only allocated 1%." If the other 99% of the "weight tickets" aren't being used by anyone else, the active user can borrow up to 100% of the CPU.
* Response to Ingestion: The moment an ingestion task arrives, the scheduler will immediately (within milliseconds) reclaim those cycles. The user's query will be squeezed back down toward its 2.8% share to allow the ingestion to take its 44.4% (or more, if other users are idle).
3. Multiple Users Contending for Idle CPU
This is where the "Shared Pool" logic becomes very fair. Imagine Ingestion is idle, but User A and User B (both weight 1) both start heavy queries:
1. Redistribution of Idle Capacity: The 16 weight "tickets" from the idle default_wg are effectively ignored by the scheduler.
2. Relative Weighting: The scheduler looks at the active groups. Since User A has 1 ticket and User B has 1 ticket, they share the total CPU 50/50.
3. The Formula: Your Share = (Your Weight) / (Sum of weights of all ACTIVE groups).
4. Scaling to 10 Users: If 10 users are active and ingestion is idle, each user gets $1/10$ (10%) of the total CPU.
Rocky's Architecture Tip for "Infinite" Scaling
If you want to ensure Ingestion always has a "Big Stick" (e.g., >80% protection) regardless of how many users you add, you should stop creating 1 resource group per user. Instead:
* Group users into a "Query Pool": Create one resource group called user_queries with cpu_weight = 2.
* Map all 20+ users to this one group using classifiers.
* The Result:
* default_wg (Ingestion) weight = 16.
* user_queries group weight = 2.
* Ingestion is now guaranteed $16/18$ (88.9%) of the CPU forever, no matter if you have 20, 100, or 500 users.
_ The 500 users will simply fight amongst themselves _within* that 2-weight share during high load, but they will never be able to collectively "drown out" the ingestion.
Summary
* Max Granularity: Integer (1 is the minimum).
* Max Weight: Core count (16).
* Single User Burst: Can take 100% if everyone else is idle.
* Multi-User Contention: Idle CPU is shared proportionally based on the weights of the groups currently running queries.
References
• administration/management/resource_management/resource_group.md
• faq/resource_isolation_faq.md
• sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
ReferencesRocky
04/30/2026, 6:40 PM