This is an automated email from the ASF dual-hosted git repository.
hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 44da2ee1058 [opt](partition) Add global dynamic partition drop
protection (#68844)
44da2ee1058 is described below
commit 44da2ee105878448b40705e1029861d4b9f71a8c
Author: Gavin Chou <[email protected]>
AuthorDate: Tue Oct 13 14:16:31 2026 +0800
[opt](partition) Add global dynamic partition drop protection (#68844)
Problem Summary: Some operational workflows need a cluster-wide guard
that temporarily prevents background dynamic partition cleanup while
allowing partition creation to continue.
### Release note
Add the global `enable_dynamic_partition_drop_protection` variable to
pause automatic partition drops without disabling dynamic partition
creation.
---
.../doris/clone/DynamicPartitionScheduler.java | 30 +++++--
.../java/org/apache/doris/qe/SessionVariable.java | 14 ++++
.../main/java/org/apache/doris/qe/VariableMgr.java | 30 +++++++
.../doris/catalog/DynamicPartitionTableTest.java | 97 ++++++++++++++++++++++
.../java/org/apache/doris/qe/VariableMgrTest.java | 26 ++++++
5 files changed, 191 insertions(+), 6 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java
b/fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java
index d552d0cb88e..c467bb6765f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/clone/DynamicPartitionScheduler.java
@@ -59,6 +59,8 @@ import
org.apache.doris.nereids.trees.plans.commands.info.AddPartitionOp;
import org.apache.doris.nereids.trees.plans.commands.info.DropPartitionOp;
import org.apache.doris.nereids.util.DateUtils;
import org.apache.doris.persist.PartitionPersistInfo;
+import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.qe.VariableMgr;
import org.apache.doris.thrift.TStorageMedium;
import com.google.common.annotations.VisibleForTesting;
@@ -787,13 +789,17 @@ public class DynamicPartitionScheduler extends
MasterDaemon {
executeFirstTime);
}
clearDropPartitionFailedMsg(olapTable.getId());
- if (olapTable.getPartitionRetentionCount() > 0) {
- // Handle AUTO PARTITION cleanup based on
partition.retention_count
- dropPartitionOps = getDropPartitionOpForAutoPartition(db,
olapTable);
+ if (VariableMgr.isDynamicPartitionDropProtectionEnabled()) {
+ logDynamicPartitionDropProtection(db, olapTable);
} else {
- // Handle dynamic partition cleanup
- dropPartitionOps = getDropPartitionOpForDynamic(db,
olapTable, partitionColumn,
- partitionFormat);
+ if (olapTable.getPartitionRetentionCount() > 0) {
+ // Handle AUTO PARTITION cleanup based on
partition.retention_count
+ dropPartitionOps =
getDropPartitionOpForAutoPartition(db, olapTable);
+ } else {
+ // Handle dynamic partition cleanup
+ dropPartitionOps = getDropPartitionOpForDynamic(db,
olapTable, partitionColumn,
+ partitionFormat);
+ }
}
tableName = olapTable.getName();
} catch (Exception e) {
@@ -811,6 +817,10 @@ public class DynamicPartitionScheduler extends
MasterDaemon {
continue;
}
try {
+ if (VariableMgr.isDynamicPartitionDropProtectionEnabled())
{
+ logDynamicPartitionDropProtection(db, olapTable);
+ break;
+ }
Env.getCurrentEnv().dropPartition(db, olapTable,
dropPartitionOp);
} catch (Exception e) {
recordDropPartitionFailedMsg(db.getFullName(), tableName,
e.getMessage(), olapTable.getId());
@@ -1013,6 +1023,14 @@ public class DynamicPartitionScheduler extends
MasterDaemon {
createOrUpdateRuntimeInfo(tableId, CREATE_PARTITION_MSG,
DEFAULT_RUNTIME_VALUE);
}
+ private void logDynamicPartitionDropProtection(Database db, OlapTable
olapTable) {
+ LOG.warn("Skip automatic partition drop for db [{}-{}], table [{}-{}]
because global session variable "
+ + "'{}' is enabled. Existing partitions are retained;
set it to false to resume automatic "
+ + "partition cleanup",
+ db.getId(), db.getName(), olapTable.getId(),
olapTable.getName(),
+ SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION);
+ }
+
private void recordDropPartitionFailedMsg(String dbName, String tableName,
String msg, long tableId) {
LOG.warn("dynamic drop partition failed: {}, db: {}, table: {}", msg,
dbName, tableName);
createOrUpdateRuntimeInfo(tableId, DYNAMIC_PARTITION_STATE,
State.ERROR.toString());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 1cdfc01674a..3f04a2b4ac2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -204,6 +204,8 @@ public class SessionVariable implements Serializable,
Writable {
public static final String ENABLE_REWRITE_ELEMENT_AT_TO_SLOT =
"enable_rewrite_element_at_to_slot";
public static final String ENABLE_ODBC_TRANSCATION =
"enable_odbc_transcation";
public static final String ENABLE_BINARY_SEARCH_FILTERING_PARTITIONS =
"enable_binary_search_filtering_partitions";
+ public static final String ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION =
+ "enable_dynamic_partition_drop_protection";
public static final String CACHE_SORTED_PARTITION_INTERVAL_SECOND =
"cache_sorted_partition_interval_second";
public static final String SKIP_PRUNE_PREDICATE = "skip_prune_predicate";
public static final String ENABLE_SQL_CACHE = "enable_sql_cache";
@@ -1531,6 +1533,18 @@ public class SessionVariable implements Serializable,
Writable {
)
public boolean enableBinarySearchFilteringPartitions = true;
+ @VarAttrDef.VarAttr(
+ name = ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION,
+ flag = VarAttrDef.GLOBAL,
+ description = "Whether to prevent background dynamic partition
tasks from automatically dropping "
+ + "partitions. OFF by default."
+ )
+ private boolean enableDynamicPartitionDropProtection = false;
+
+ public boolean isEnableDynamicPartitionDropProtection() {
+ return enableDynamicPartitionDropProtection;
+ }
+
@VarAttrDef.VarAttr(
name = CACHE_SORTED_PARTITION_INTERVAL_SECOND,
fuzzy = false,
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
index 5d2e505684a..ec4bc17148e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
@@ -178,6 +178,36 @@ public class VariableMgr {
return defaultSessionVariable;
}
+ public static boolean isDynamicPartitionDropProtectionEnabled() {
+ rlock.lock();
+ try {
+ return
defaultSessionVariable.isEnableDynamicPartitionDropProtection();
+ } finally {
+ rlock.unlock();
+ }
+ }
+
+ /**
+ * Sets a persistent global-only session variable while constructing an
image.
+ *
+ * <p>The caller is responsible for persisting the resulting image. This
method intentionally does not write an
+ * edit log because image construction may happen without a running edit
log.</p>
+ */
+ public static void setGlobalSessionVariableForImage(String name, String
value) throws DdlException {
+ wlock.lock();
+ try {
+ VarContext ctx = getVarContext(name);
+ Preconditions.checkArgument(ctx != null, "Unknown session
variable: %s", name);
+ Preconditions.checkArgument(ctx.getObj() == defaultSessionVariable,
+ "Not a session variable: %s", name);
+ Preconditions.checkArgument((ctx.getFlag() & VarAttrDef.GLOBAL) !=
0,
+ "Session variable is not global-only: %s", name);
+ setValue(defaultSessionVariable, new
SessionVariableField(ctx.getField()), value);
+ } finally {
+ wlock.unlock();
+ }
+ }
+
// Set value to a variable
private static boolean setValue(Object obj, SessionVariableField
sessionVariableField, String value)
throws DdlException {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/DynamicPartitionTableTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/DynamicPartitionTableTest.java
index eee11b90fbf..28240e22bb2 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/catalog/DynamicPartitionTableTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/DynamicPartitionTableTest.java
@@ -30,18 +30,22 @@ import org.apache.doris.common.Pair;
import org.apache.doris.common.UserException;
import org.apache.doris.common.util.DynamicPartitionUtil;
import org.apache.doris.common.util.TimeUtils;
+import org.apache.doris.mysql.authenticate.TestLogAppender;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.trees.plans.commands.AlterTableCommand;
import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.SessionVariable;
import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.qe.VariableMgr;
import org.apache.doris.system.Backend;
import org.apache.doris.thrift.TStorageMedium;
import org.apache.doris.utframe.TestWithFeService;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
+import org.apache.logging.log4j.Level;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -108,6 +112,99 @@ public class DynamicPartitionTableTest extends
TestWithFeService {
}
}
+ private void setDynamicPartitionDropProtection(boolean enabled) throws
DdlException {
+ VariableMgr.setGlobalSessionVariableForImage(
+ SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION,
Boolean.toString(enabled));
+ }
+
+ @Test
+ public void testDynamicPartitionDropProtection() throws Exception {
+ String createOlapTblStmt = "CREATE TABLE
test.`dynamic_partition_drop_protection` (\n"
+ + " `k1` date NULL\n"
+ + ") ENGINE=OLAP\n"
+ + "DUPLICATE KEY(`k1`)\n"
+ + "PARTITION BY RANGE (`k1`) ()\n"
+ + "DISTRIBUTED BY HASH(`k1`) BUCKETS 1\n"
+ + "PROPERTIES (\n"
+ + "\"replication_num\" = \"1\",\n"
+ + "\"dynamic_partition.enable\" = \"false\",\n"
+ + "\"dynamic_partition.start\" = \"-1\",\n"
+ + "\"dynamic_partition.end\" = \"1\",\n"
+ + "\"dynamic_partition.time_unit\" = \"day\",\n"
+ + "\"dynamic_partition.prefix\" = \"p\",\n"
+ + "\"dynamic_partition.buckets\" = \"1\"\n"
+ + ");";
+ createTableStmt(createOlapTblStmt);
+
+ Database db =
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+ OlapTable table = (OlapTable)
db.getTableOrAnalysisException("dynamic_partition_drop_protection");
+ alterTable("ALTER TABLE test.dynamic_partition_drop_protection ADD
PARTITION p_old "
+ + "VALUES [('2000-01-01'), ('2000-01-02'))");
+
+ try {
+ setDynamicPartitionDropProtection(true);
+ alterTable("ALTER TABLE test.dynamic_partition_drop_protection SET
"
+ + "('dynamic_partition.enable' = 'true')");
+ try (TestLogAppender appender =
TestLogAppender.attach(DynamicPartitionScheduler.class, Level.WARN)) {
+ Env.getCurrentEnv().getDynamicPartitionScheduler()
+ .executeDynamicPartitionFirstTime(db.getId(),
table.getId());
+ Assertions.assertTrue(appender.contains(Level.WARN,
+ "table [" + table.getId() + "-" + table.getName()
+ + "] because global session variable '"
+ +
SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION + "' is enabled"));
+ }
+
+ Assertions.assertTrue(table.getPartitionNames().contains("p_old"));
+ Assertions.assertTrue(table.getPartitionNames().size() > 1,
+ "Dynamic partition creation should continue while drop
protection is enabled");
+
+ setDynamicPartitionDropProtection(false);
+ Env.getCurrentEnv().getDynamicPartitionScheduler()
+ .executeDynamicPartitionFirstTime(db.getId(),
table.getId());
+
Assertions.assertFalse(table.getPartitionNames().contains("p_old"));
+ } finally {
+ setDynamicPartitionDropProtection(false);
+ }
+ }
+
+ @Test
+ public void testAutoPartitionRetentionDropProtection() throws Exception {
+ String createOlapTblStmt = "CREATE TABLE
test.`auto_partition_drop_protection` (\n"
+ + " `k1` datetime NOT NULL\n"
+ + ") ENGINE=OLAP\n"
+ + "DUPLICATE KEY(`k1`)\n"
+ + "AUTO PARTITION BY RANGE (date_trunc(k1, 'day')) ()\n"
+ + "DISTRIBUTED BY HASH(`k1`) BUCKETS 1\n"
+ + "PROPERTIES (\n"
+ + "\"replication_num\" = \"1\",\n"
+ + "\"partition.retention_count\" = \"1\"\n"
+ + ");";
+ createTableStmt(createOlapTblStmt);
+
+ Database db =
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+ OlapTable table = (OlapTable)
db.getTableOrAnalysisException("auto_partition_drop_protection");
+ alterTable("ALTER TABLE test.auto_partition_drop_protection ADD
PARTITION p_old "
+ + "VALUES [('2000-01-01'), ('2000-01-02'))");
+ alterTable("ALTER TABLE test.auto_partition_drop_protection ADD
PARTITION p_new "
+ + "VALUES [('2020-01-01'), ('2020-01-02'))");
+
+ try {
+ setDynamicPartitionDropProtection(true);
+ Env.getCurrentEnv().getDynamicPartitionScheduler()
+ .executeDynamicPartitionFirstTime(db.getId(),
table.getId());
+ Assertions.assertTrue(table.getPartitionNames().contains("p_old"));
+ Assertions.assertTrue(table.getPartitionNames().contains("p_new"));
+
+ setDynamicPartitionDropProtection(false);
+ Env.getCurrentEnv().getDynamicPartitionScheduler()
+ .executeDynamicPartitionFirstTime(db.getId(),
table.getId());
+
Assertions.assertFalse(table.getPartitionNames().contains("p_old"));
+ Assertions.assertTrue(table.getPartitionNames().contains("p_new"));
+ } finally {
+ setDynamicPartitionDropProtection(false);
+ }
+ }
+
@Test
public void testNormal() throws Exception {
String createOlapTblStmt = "CREATE TABLE
test.`dynamic_partition_normal` (\n"
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
index 455e9801e9a..f584d59572a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
@@ -87,6 +87,32 @@ public class VariableMgrTest extends TestWithFeService {
Assertions.assertThrows(DdlException.class, () ->
VariableMgr.setVar(null, setVar));
}
+ @Test
+ public void testDynamicPartitionDropProtectionIsGlobalOnly() throws
Exception {
+ try {
+ VariableMgr.setGlobalSessionVariableForImage(
+ SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION,
"false");
+
Assertions.assertFalse(VariableMgr.isDynamicPartitionDropProtectionEnabled());
+
+ SetOptionsCommand sessionStmt = (SetOptionsCommand)
UtFrameUtils.parseStmt(
+ "set enable_dynamic_partition_drop_protection=true",
connectContext);
+ Assertions.assertThrows(DdlException.class, () ->
sessionStmt.run(connectContext, null));
+
+ VariableMgr.setGlobalSessionVariableForImage(
+ SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION,
"true");
+
Assertions.assertTrue(VariableMgr.isDynamicPartitionDropProtectionEnabled());
+
Assertions.assertTrue(VariableMgr.newSessionVariable().isEnableDynamicPartitionDropProtection());
+
+ SetOptionsCommand globalStmt = (SetOptionsCommand)
UtFrameUtils.parseStmt(
+ "set global
enable_dynamic_partition_drop_protection=false", connectContext);
+ globalStmt.run(connectContext, null);
+
Assertions.assertFalse(VariableMgr.isDynamicPartitionDropProtectionEnabled());
+ } finally {
+ VariableMgr.setGlobalSessionVariableForImage(
+ SessionVariable.ENABLE_DYNAMIC_PARTITION_DROP_PROTECTION,
"false");
+ }
+ }
+
@Test
public void testAuthenticationPolicyIsTheReadOnlyMysqlDefault() throws
Exception {
SessionVariable sessionVariable = new SessionVariable();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]