Search before asking
Version
DORIS_BUILD_VERSION=doris-4.1.0-rc03
DORIS_BUILD_HASH=5960d4cea0e6d2a08f62b45a07a662395d2bbcfc
Java=17.0.2
Deployment=Kubernetes
Mode=Cloud / compute-storage decoupled
Authorization=Apache Ranger
Ranger version=2.7.0
access_controller_type=ranger-doris
The build hash is the same commit used by the Doris 4.1.0 release tag.
What's Wrong?
I searched existing issues and found:
PR #45645 previously made RangerDorisAccessController a singleton. However, this fix is no longer effective in Doris 4.1.0 because the new RangerDorisAccessControllerFactory directly creates a new controller for every checkpoint Env.
Every successful metadata checkpoint creates two new Ranger PolicyRefresher threads. These threads are not stopped when the temporary checkpoint Env is destroyed.
The number of threads therefore increases continuously:
PolicyRefresher count
= 1 normal refresher created by main
+ checkpoint count × 2
In our test environment:
Journal-triggered checkpoints: 3
Cloud stale-image checkpoints: 135
Total checkpoints: 138
PolicyRefresher created by main: 1
PolicyRefresher created by
leaderCheckpointer: 276
Total PolicyRefresher threads: 277
The numbers match exactly:
Thread creator distribution from the FE logs:
Count Thread
----- ------------------
276 leaderCheckpointer
1 main
The creation rate also matches the default cloud checkpoint interval. With:
cloud_checkpoint_image_stale_threshold_seconds = 3600
one cloud checkpoint is triggered every hour, and two new PolicyRefresher threads are leaked per checkpoint, resulting in approximately 48 additional refresher threads per day.
Evidence from FE Logs
A checkpoint directly creates a new Ranger plugin and PolicyRefresher:
RuntimeLogger 2026-07-07 11:10:57,122 INFO
(leaderCheckpointer|143)
[Checkpoint.doCheckpoint():122]
Trigger checkpoint since last checkpoint journal id: 5117761
is less than current finalized journal id: 5162245
RuntimeLogger 2026-07-07 11:10:57,140 INFO
(leaderCheckpointer|143)
[Checkpoint.doCheckpoint():155]
begin to generate new image: image.5162245
RuntimeLogger 2026-07-07 11:10:57,151 INFO
(leaderCheckpointer|143)
[AccessControllerManager.loadAccessControllerPlugins():102]
Found Authentication Plugin Factories: ranger-doris from class path.
RuntimeLogger 2026-07-07 11:10:57,159 INFO
(leaderCheckpointer|143)
[RangerBasePlugin.init():308]
Created PolicyRefresher Thread(PolicyRefresher(serviceName=doris)-377)
Every checkpoint leaves two additional PolicyRefresher threads running permanently.
In our production environment, the FE accumulated:
1321 PolicyRefresher threads
At the default 30-second Ranger polling interval, this generated approximately:
1321 refreshers × 2 Ranger endpoints / 30 seconds
≈ 88 requests per second
The affected endpoints included:
/service/plugins/policies/download/doris
/service/roles/download/doris
This eventually caused Ranger Admin to accumulate millions of asynchronous plugin-info update tasks:
RangerTransactionService:
scheduled = 20,751,852
pending = 5,334,642
Ranger Admin then experienced:
Old generation usage: approximately 96%
Full GC count: 5197
Full GC time: approximately 18 hours
java.lang.OutOfMemoryError: GC overhead limit exceeded
Tomcat Acceptor/Poller threads eventually disappeared, while port 6080 remained in LISTEN state with a full TCP accept backlog, making Ranger Admin appear running but inaccessible.
What You Expected?
Only one Ranger PolicyRefresher should exist per Doris FE process.
Creating or destroying a temporary checkpoint Env must not leave any long-running Ranger background threads.
How to Reproduce?
-
Deploy Doris 4.1.0 in Cloud Mode.
-
Configure Ranger authorization:
access_controller_type = ranger-doris
- Keep the default configuration:
cloud_checkpoint_image_stale_threshold_seconds = 3600
-
Start FE and wait for several cloud metadata checkpoints.
-
Count Ranger PolicyRefresher threads:
jcmd <FE_PID> Thread.print \
| grep -c '^"PolicyRefresher(serviceName=doris)'
- Count checkpoint events:
grep -R -h \
'Trigger checkpoint since' \
/opt/apache-doris/fe/log | wc -l
grep -R -h \
'Trigger checkpoint in cloud mode because latest image is expired' \
/opt/apache-doris/fe/log | wc -l
- Count PolicyRefresher creation records:
grep -R -h \
'Created PolicyRefresher Thread(PolicyRefresher(serviceName=doris)' \
/opt/apache-doris/fe/log | wc -l
The PolicyRefresher count grows by two after every successful checkpoint.
Root Cause Analysis
The observed call chain is:
Checkpoint.doCheckpoint()
-> Env.getCurrentEnv()
-> EnvFactory.createEnv(true)
-> new checkpoint Env
-> new AccessControllerManager
-> RangerDorisAccessControllerFactory.createAccessController()
-> new RangerDorisAccessController("doris")
-> RangerBasePlugin.init()
-> new PolicyRefresher
Checkpoint.doCheckpoint() obtains and destroys a checkpoint Env twice during one successful checkpoint.
The relevant Env.getCurrentEnv() bytecode is equivalent to:
if (isCheckpointThread()) {
if (CHECKPOINT == null) {
CHECKPOINT = EnvFactory.getInstance().createEnv(true);
}
return CHECKPOINT;
}
However, Env.destroyCheckpoint() only clears the static reference:
public static void destroyCheckpoint() {
if (CHECKPOINT != null) {
CHECKPOINT = null;
}
}
It does not close AccessControllerManager, clean up the Ranger plugin, or stop PolicyRefresher.
The Doris 4.1.0 factory directly creates a new controller:
https://github.com/apache/doris/blob/4.1.0/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RangerDorisAccessControllerFactory.java
@Override
public RangerDorisAccessController createAccessController(
Map<String, String> prop) {
return new RangerDorisAccessController("doris");
}
RangerDorisAccessController in Doris 4.1.0 also no longer contains the singleton getInstance() implementation introduced by PR #45645.
Anything Else?
No response
Are you willing to submit PR?
Code of Conduct
Search before asking
Version
The build hash is the same commit used by the Doris 4.1.0 release tag.
What's Wrong?
I searched existing issues and found:
PR #45645 previously made
RangerDorisAccessControllera singleton. However, this fix is no longer effective in Doris 4.1.0 because the newRangerDorisAccessControllerFactorydirectly creates a new controller for every checkpoint Env.Every successful metadata checkpoint creates two new Ranger
PolicyRefresherthreads. These threads are not stopped when the temporary checkpoint Env is destroyed.The number of threads therefore increases continuously:
In our test environment:
The numbers match exactly:
Thread creator distribution from the FE logs:
The creation rate also matches the default cloud checkpoint interval. With:
one cloud checkpoint is triggered every hour, and two new PolicyRefresher threads are leaked per checkpoint, resulting in approximately 48 additional refresher threads per day.
Evidence from FE Logs
A checkpoint directly creates a new Ranger plugin and PolicyRefresher:
Every checkpoint leaves two additional PolicyRefresher threads running permanently.
In our production environment, the FE accumulated:
At the default 30-second Ranger polling interval, this generated approximately:
The affected endpoints included:
This eventually caused Ranger Admin to accumulate millions of asynchronous plugin-info update tasks:
Ranger Admin then experienced:
Tomcat Acceptor/Poller threads eventually disappeared, while port 6080 remained in LISTEN state with a full TCP accept backlog, making Ranger Admin appear running but inaccessible.
What You Expected?
Only one Ranger PolicyRefresher should exist per Doris FE process.
Creating or destroying a temporary checkpoint Env must not leave any long-running Ranger background threads.
How to Reproduce?
Deploy Doris 4.1.0 in Cloud Mode.
Configure Ranger authorization:
access_controller_type = ranger-doriscloud_checkpoint_image_stale_threshold_seconds = 3600Start FE and wait for several cloud metadata checkpoints.
Count Ranger PolicyRefresher threads:
The PolicyRefresher count grows by two after every successful checkpoint.
Root Cause Analysis
The observed call chain is:
Checkpoint.doCheckpoint()obtains and destroys a checkpoint Env twice during one successful checkpoint.The relevant
Env.getCurrentEnv()bytecode is equivalent to:However,
Env.destroyCheckpoint()only clears the static reference:It does not close
AccessControllerManager, clean up the Ranger plugin, or stop PolicyRefresher.The Doris 4.1.0 factory directly creates a new controller:
https://github.com/apache/doris/blob/4.1.0/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/RangerDorisAccessControllerFactory.java
RangerDorisAccessControllerin Doris 4.1.0 also no longer contains the singletongetInstance()implementation introduced by PR #45645.Anything Else?
No response
Are you willing to submit PR?
Code of Conduct