This is an automated email from the ASF dual-hosted git repository.

neuyilan pushed a commit to branch multi_tenancy
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/multi_tenancy by this push:
     new 06076ebe11 [IOTDB-4836] Add spaceQuotaFrame (#7916)
06076ebe11 is described below

commit 06076ebe11da07c68b1106ba75b4513276bccbd2
Author: 任宇华 <[email protected]>
AuthorDate: Wed Nov 9 11:48:30 2022 +0800

    [IOTDB-4836] Add spaceQuotaFrame (#7916)
---
 .../org/apache/iotdb/db/qp/sql/IdentifierParser.g4 |   2 +
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |   7 +-
 .../antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4  |   8 ++
 .../iotdb/confignode/conf/ConfigNodeConfig.java    |  34 +++++++
 .../confignode/conf/ConfigNodeStartupCheck.java    |   8 ++
 .../consensus/request/ConfigPhysicalPlan.java      |   4 +
 .../consensus/request/ConfigPhysicalPlanType.java  |   5 +-
 .../request/write/quota/SetSpaceQuotaPlan.java     | 101 +++++++++++++++++++++
 .../confignode/manager/ClusterQuotaManager.java    |  62 +++++++++++++
 .../iotdb/confignode/manager/ConfigManager.java    |  18 +++-
 .../apache/iotdb/confignode/manager/IManager.java  |   3 +
 .../iotdb/confignode/persistence/QuotaInfo.java    |  67 ++++++++++++++
 .../persistence/executor/ConfigPlanExecutor.java   |  11 ++-
 .../thrift/ConfigNodeRPCServiceProcessor.java      |   6 ++
 .../request/ConfigPhysicalPlanSerDeTest.java       |  16 ++++
 .../persistence/quota/QuotaInfoTest.java           |  23 +++++
 .../java/org/apache/iotdb/db/it/IoTDBQuotaIT.java  |  58 ++++++++++++
 .../resources/conf/iotdb-common.properties         |   8 +-
 .../apache/iotdb/commons/conf/IoTDBConstant.java   |   8 ++
 .../apache/iotdb/commons/enums/SpaceQuotaType.java |  26 ++++++
 .../commons/utils/BasicStructureSerDeUtil.java     |  16 ++++
 .../apache/iotdb/db/client/ConfigNodeClient.java   |  17 ++++
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java |  50 ++++++++++
 .../org/apache/iotdb/db/conf/IoTDBDescriptor.java  |   4 +
 .../iotdb/db/localconfignode/LocalConfigNode.java  |  29 ++++--
 .../iotdb/db/mpp/plan/constant/StatementType.java  |   2 +
 .../plan/execution/config/ConfigTaskVisitor.java   |   8 ++
 .../config/executor/ClusterConfigTaskExecutor.java |  30 ++++++
 .../config/executor/IConfigTaskExecutor.java       |   3 +
 .../executor/StandaloneConfigTaskExecutor.java     |  12 +++
 .../config/sys/quota/SetSpaceQuotaTask.java        |  42 +++++++++
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       |  56 ++++++++++++
 .../db/mpp/plan/statement/StatementVisitor.java    |   5 +
 .../sys/quota/SetSpaceQuotaStatement.java          | 100 ++++++++++++++++++++
 .../apache/iotdb/db/quotas/DataNodeSizeStore.java  |  24 +++++
 .../iotdb/db/quotas/DataNodeSpaceQuotaManager.java |  34 +++++++
 .../db/quotas/SpaceViolationPolicyEnforcement.java |  23 +++++
 .../apache/iotdb/db/utils/EnvironmentUtils.java    |   4 +
 thrift-commons/src/main/thrift/common.thrift       |   7 ++
 .../src/main/thrift/confignode.thrift              |  10 ++
 40 files changed, 939 insertions(+), 12 deletions(-)

diff --git 
a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4 
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
index fb108cb96e..e72b5f8339 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4
@@ -136,6 +136,7 @@ keyWords
     | PRUNE
     | QUERIES
     | QUERY
+    | QUOTA
     | RANGE
     | READONLY
     | REGEXP
@@ -157,6 +158,7 @@ keyWords
     | SHOW
     | SLIMIT
     | SOFFSET
+    | SPACE
     | STORAGE
     | START
     | STARTTIME
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 51cb8f178d..b886dba4a2 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -40,7 +40,7 @@ ddlStatement
     | createFunction | createTrigger | createContinuousQuery
     | alterTimeseries | deleteStorageGroup | deleteTimeseries | 
deletePartition | deleteTimeseriesOfSchemaTemplate
     | dropFunction | dropTrigger | dropContinuousQuery | dropSchemaTemplate
-    | setTTL | unsetTTL | startTrigger | stopTrigger | setSchemaTemplate | 
unsetSchemaTemplate
+    | setTTL | unsetTTL | startTrigger | stopTrigger | setSchemaTemplate | 
unsetSchemaTemplate | setSpaceQuota
     | showStorageGroup | showDevices | showTimeseries | showChildPaths | 
showChildNodes
     | showFunctions | showTriggers | showContinuousQueries | showTTL | 
showAllTTL | showCluster | showRegion | showDataNodes | showConfigNodes
     | showSchemaTemplates | showNodesInSchemaTemplate
@@ -274,6 +274,11 @@ unsetSchemaTemplate
     : UNSET SCHEMA? TEMPLATE templateName=identifier FROM prefixPath
     ;
 
+// Set Space Quota
+setSpaceQuota
+    : SET SPACE QUOTA attributePair (COMMA attributePair)* ON prefixPath 
(COMMA prefixPath)*
+    ;
+
 // Start Trigger
 startTrigger
     : START TRIGGER triggerName=identifier
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
index a6141a614e..da94da2c9f 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
@@ -458,6 +458,10 @@ QUERY
     : Q U E R Y
     ;
 
+QUOTA
+    : Q U O T A
+    ;
+
 RANGE
     : R A N G E
     ;
@@ -546,6 +550,10 @@ SOFFSET
     : S O F F S E T
     ;
 
+SPACE
+    : S P A C E
+    ;
+
 STORAGE
     : S T O R A G E
     ;
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConfig.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConfig.java
index 3990b02f48..348b4f57d8 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConfig.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeConfig.java
@@ -113,6 +113,22 @@ public class ConfigNodeConfig {
   private String temporaryLibDir =
       IoTDBConstant.EXT_FOLDER_NAME + File.separator + 
IoTDBConstant.UDF_TMP_FOLDER_NAME;
 
+  /** Space quota directory, stores space quota information of each storage 
group */
+  private String spaceQuotaDir =
+      systemDir
+          + File.separator
+          + IoTDBConstant.QUOTA_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.SPACE_QUOTA_FOLDER_NAME;
+
+  /** Throttle quota directory, stores throttle quota information of each 
storage group */
+  private String throttleQuotaDir =
+      systemDir
+          + File.separator
+          + IoTDBConstant.QUOTA_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.THROTTLE_QUOTA_FOLDER_NAME;
+
   /** Time partition interval in milliseconds */
   private long timePartitionInterval = 604_800_000;
 
@@ -238,6 +254,8 @@ public class ConfigNodeConfig {
     udfLibDir = addHomeDir(udfLibDir);
     temporaryLibDir = addHomeDir(temporaryLibDir);
     triggerLibDir = addHomeDir(triggerLibDir);
+    spaceQuotaDir = addHomeDir(spaceQuotaDir);
+    throttleQuotaDir = addHomeDir(throttleQuotaDir);
   }
 
   private String addHomeDir(String dir) {
@@ -465,6 +483,22 @@ public class ConfigNodeConfig {
     this.temporaryLibDir = temporaryLibDir;
   }
 
+  public String getSpaceQuotaDir() {
+    return spaceQuotaDir;
+  }
+
+  public void setSpaceQuotaDir(String spaceQuotaDir) {
+    this.spaceQuotaDir = spaceQuotaDir;
+  }
+
+  public String getThrottleQuotaDir() {
+    return throttleQuotaDir;
+  }
+
+  public void setThrottleQuotaDir(String throttleQuotaDir) {
+    this.throttleQuotaDir = throttleQuotaDir;
+  }
+
   public int getSchemaReplicationFactor() {
     return schemaReplicationFactor;
   }
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java
index 9b91c9ca84..b23e9f474b 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/conf/ConfigNodeStartupCheck.java
@@ -114,6 +114,14 @@ public class ConfigNodeStartupCheck {
     // If consensusDir does not exist, create consensusDir
     File consensusDir = new File(CONF.getConsensusDir());
     createDirIfEmpty(consensusDir);
+
+    // If spaceQuotaDir does not exist, create spaceQuotaDir
+    File spaceQuotaDir = new File(CONF.getSpaceQuotaDir());
+    createDirIfEmpty(spaceQuotaDir);
+
+    // If throttleQuotaDir does not exist, create throttleQuotaDir
+    File throttleQuotaDir = new File(CONF.getThrottleQuotaDir());
+    createDirIfEmpty(throttleQuotaDir);
   }
 
   private void createDirIfEmpty(File dir) throws IOException {
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
index d69b620848..59c0bdb0d9 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
@@ -61,6 +61,7 @@ import 
org.apache.iotdb.confignode.consensus.request.write.partition.CreateSchem
 import 
org.apache.iotdb.confignode.consensus.request.write.partition.UpdateRegionLocationPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.DeleteProcedurePlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.UpdateProcedurePlan;
+import 
org.apache.iotdb.confignode.consensus.request.write.quota.SetSpaceQuotaPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.OfferRegionMaintainTasksPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.PollRegionMaintainTaskPlan;
@@ -379,6 +380,9 @@ public abstract class ConfigPhysicalPlan implements 
IConsensusRequest {
         case GetFunctionJar:
           plan = new GetUDFJarPlan();
           break;
+        case SET_SPACE_QUOTA:
+          plan = new SetSpaceQuotaPlan();
+          break;
         default:
           throw new IOException("unknown PhysicalPlan configPhysicalPlanType: 
" + planType);
       }
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
index d549ca450c..dd8ffdfdc4 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
@@ -136,7 +136,10 @@ public enum ConfigPhysicalPlanType {
   ACTIVE_CQ((short) 1101),
   ADD_CQ((short) 1102),
   UPDATE_CQ_LAST_EXEC_TIME((short) 1103),
-  SHOW_CQ((short) 1104);
+  SHOW_CQ((short) 1104),
+
+  /** Quota */
+  SET_SPACE_QUOTA((short) 1200);
 
   private final short planType;
 
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/quota/SetSpaceQuotaPlan.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/quota/SetSpaceQuotaPlan.java
new file mode 100644
index 0000000000..0ec2f38a99
--- /dev/null
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/quota/SetSpaceQuotaPlan.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.consensus.request.write.quota;
+
+import org.apache.iotdb.common.rpc.thrift.TSpaceQuota;
+import org.apache.iotdb.commons.utils.BasicStructureSerDeUtil;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+
+public class SetSpaceQuotaPlan extends ConfigPhysicalPlan {
+
+  private List<String> prefixPathList;
+  private TSpaceQuota spaceLimit;
+
+  public SetSpaceQuotaPlan() {
+    super(ConfigPhysicalPlanType.SET_SPACE_QUOTA);
+  }
+
+  public SetSpaceQuotaPlan(List<String> prefixPathList, TSpaceQuota 
spaceLimit) {
+    super(ConfigPhysicalPlanType.SET_SPACE_QUOTA);
+    this.prefixPathList = prefixPathList;
+    this.spaceLimit = spaceLimit;
+  }
+
+  public List<String> getPrefixPathList() {
+    return prefixPathList;
+  }
+
+  public void setPrefixPathList(List<String> prefixPathList) {
+    this.prefixPathList = prefixPathList;
+  }
+
+  public TSpaceQuota getSpaceLimit() {
+    return spaceLimit;
+  }
+
+  public void setSpaceLimit(TSpaceQuota spaceLimit) {
+    this.spaceLimit = spaceLimit;
+  }
+
+  @Override
+  protected void serializeImpl(DataOutputStream stream) throws IOException {
+    stream.writeShort(getType().getPlanType());
+    BasicStructureSerDeUtil.write(prefixPathList, stream);
+    BasicStructureSerDeUtil.write(spaceLimit.getDeviceNum(), stream);
+    BasicStructureSerDeUtil.write(spaceLimit.getTimeserieNum(), stream);
+    BasicStructureSerDeUtil.write(spaceLimit.getDisk(), stream);
+  }
+
+  @Override
+  protected void deserializeImpl(ByteBuffer buffer) throws IOException {
+    List<String> prefixPathList = 
BasicStructureSerDeUtil.readStringList(buffer);
+    int deviceNum = BasicStructureSerDeUtil.readInt(buffer);
+    int timeserieNum = BasicStructureSerDeUtil.readInt(buffer);
+    long disk = BasicStructureSerDeUtil.readLong(buffer);
+    this.prefixPathList = prefixPathList;
+    TSpaceQuota spaceLimit = new TSpaceQuota();
+    spaceLimit.setDeviceNum(deviceNum);
+    spaceLimit.setTimeserieNum(timeserieNum);
+    spaceLimit.setDisk(disk);
+    this.spaceLimit = spaceLimit;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    if (!super.equals(o)) return false;
+    SetSpaceQuotaPlan that = (SetSpaceQuotaPlan) o;
+    return Objects.equals(prefixPathList, that.prefixPathList)
+        && Objects.equals(spaceLimit, that.spaceLimit);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), prefixPathList, spaceLimit);
+  }
+}
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterQuotaManager.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterQuotaManager.java
new file mode 100644
index 0000000000..a7c4939ed2
--- /dev/null
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterQuotaManager.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.confignode.manager;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import 
org.apache.iotdb.confignode.consensus.request.write.quota.SetSpaceQuotaPlan;
+import org.apache.iotdb.confignode.persistence.QuotaInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
+import org.apache.iotdb.consensus.common.response.ConsensusWriteResponse;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: Manage quotas for storage groups
+public class ClusterQuotaManager {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(ClusterQuotaManager.class);
+
+  private final IManager configManager;
+  private final QuotaInfo quotaInfo;
+
+  public ClusterQuotaManager(IManager configManager, QuotaInfo quotaInfo) {
+    this.configManager = configManager;
+    this.quotaInfo = quotaInfo;
+  }
+
+  public TSStatus setSpaceQuota(TSetSpaceQuotaReq req) {
+    ConsensusWriteResponse response =
+        configManager
+            .getConsensusManager()
+            .write(new SetSpaceQuotaPlan(req.getStorageGroup(), 
req.getSpaceLimit()));
+    if (response.getStatus() != null) {
+      return response.getStatus();
+    } else {
+      LOGGER.warn(
+          "Unexpected error happened while setting space quota on {}: ",
+          req.getStorageGroup().toString(),
+          response.getException());
+      // consensus layer related errors
+      TSStatus res = new 
TSStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode());
+      res.setMessage(response.getErrorMessage());
+      return res;
+    }
+  }
+}
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
index e2d793958d..ed247ae940 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
@@ -82,6 +82,7 @@ import org.apache.iotdb.confignode.manager.node.NodeManager;
 import org.apache.iotdb.confignode.manager.partition.PartitionManager;
 import org.apache.iotdb.confignode.persistence.AuthorInfo;
 import org.apache.iotdb.confignode.persistence.ProcedureInfo;
+import org.apache.iotdb.confignode.persistence.QuotaInfo;
 import org.apache.iotdb.confignode.persistence.TriggerInfo;
 import org.apache.iotdb.confignode.persistence.UDFInfo;
 import org.apache.iotdb.confignode.persistence.cq.CQInfo;
@@ -123,6 +124,7 @@ import 
org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSetDataNodeStatusReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowCQResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowClusterResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowConfigNodesResp;
@@ -194,6 +196,9 @@ public class ConfigManager implements IManager {
   /** CQ */
   private final CQManager cqManager;
 
+  /** Manage quotas for storage groups */
+  private final ClusterQuotaManager clusterQuotaManager;
+
   private final PartitionRegionStateMachine stateMachine;
 
   public ConfigManager() throws IOException {
@@ -207,6 +212,7 @@ public class ConfigManager implements IManager {
     TriggerInfo triggerInfo = new TriggerInfo();
     ClusterSyncInfo syncInfo = new ClusterSyncInfo();
     CQInfo cqInfo = new CQInfo();
+    QuotaInfo quotaInfo = new QuotaInfo();
 
     // Build state machine and executor
     ConfigPlanExecutor executor =
@@ -219,7 +225,8 @@ public class ConfigManager implements IManager {
             udfInfo,
             triggerInfo,
             syncInfo,
-            cqInfo);
+            cqInfo,
+            quotaInfo);
     this.stateMachine = new PartitionRegionStateMachine(this, executor);
 
     // Build the manager module
@@ -233,6 +240,7 @@ public class ConfigManager implements IManager {
     this.loadManager = new LoadManager(this);
     this.syncManager = new SyncManager(this, syncInfo);
     this.cqManager = new CQManager(this);
+    this.clusterQuotaManager = new ClusterQuotaManager(this, quotaInfo);
   }
 
   public void initConsensusManager() throws IOException {
@@ -1310,6 +1318,14 @@ public class ConfigManager implements IManager {
         : new TShowCQResp(status, Collections.emptyList());
   }
 
+  @Override
+  public TSStatus setSpaceQuota(TSetSpaceQuotaReq req) {
+    TSStatus status = confirmLeader();
+    return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()
+        ? clusterQuotaManager.setSpaceQuota(req)
+        : status;
+  }
+
   /** Get all related schemaRegion which may contains the timeSeries matched 
by given patternTree */
   public Map<TConsensusGroupId, TRegionReplicaSet> getRelatedSchemaRegionGroup(
       PathPatternTree patternTree) {
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java 
b/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
index ac77ab2954..c71a18f043 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
@@ -82,6 +82,7 @@ import 
org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSetDataNodeStatusReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowCQResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowClusterResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowConfigNodesResp;
@@ -531,6 +532,8 @@ public interface IManager {
 
   TShowCQResp showCQ();
 
+  TSStatus setSpaceQuota(TSetSpaceQuotaReq req);
+
   TSStatus checkConfigNodeGlobalConfig(TConfigNodeRegisterReq req);
 
   TSStatus transfer(List<TDataNodeLocation> newUnknownDataList);
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/QuotaInfo.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/QuotaInfo.java
new file mode 100644
index 0000000000..026d11e13d
--- /dev/null
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/QuotaInfo.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.persistence;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.common.rpc.thrift.TSpaceQuota;
+import org.apache.iotdb.commons.snapshot.SnapshotProcessor;
+import 
org.apache.iotdb.confignode.consensus.request.write.quota.SetSpaceQuotaPlan;
+import org.apache.iotdb.rpc.RpcUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+// TODO: Store quota information of each sg
+public class QuotaInfo implements SnapshotProcessor {
+
+  private static final Logger logger = 
LoggerFactory.getLogger(QuotaInfo.class);
+  private final Map<String, TSpaceQuota> spaceQuotaLimit;
+  private final Map<String, TSpaceQuota> useSpaceQuota;
+  private final Map<Integer, Integer> regionDisk;
+
+  public QuotaInfo() {
+    spaceQuotaLimit = new HashMap<>();
+    useSpaceQuota = new HashMap<>();
+    regionDisk = new HashMap<>();
+  }
+
+  public TSStatus setSpaceQuota(SetSpaceQuotaPlan setSpaceQuotaPlan) {
+    for (String storageGroup : setSpaceQuotaPlan.getPrefixPathList()) {
+      spaceQuotaLimit.put(storageGroup, setSpaceQuotaPlan.getSpaceLimit());
+    }
+    return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS);
+  }
+
+  // TODO: add Snapshot
+  @Override
+  public boolean processTakeSnapshot(File snapshotDir) throws TException, 
IOException {
+    return false;
+  }
+
+  @Override
+  public void processLoadSnapshot(File snapshotDir) throws TException, 
IOException {}
+}
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
index b08e4059ef..886362b284 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
@@ -60,6 +60,7 @@ import 
org.apache.iotdb.confignode.consensus.request.write.partition.CreateSchem
 import 
org.apache.iotdb.confignode.consensus.request.write.partition.UpdateRegionLocationPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.DeleteProcedurePlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.UpdateProcedurePlan;
+import 
org.apache.iotdb.confignode.consensus.request.write.quota.SetSpaceQuotaPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.OfferRegionMaintainTasksPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.storagegroup.AdjustMaxRegionGroupCountPlan;
@@ -92,6 +93,7 @@ import 
org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp;
 import 
org.apache.iotdb.confignode.exception.physical.UnknownPhysicalPlanTypeException;
 import org.apache.iotdb.confignode.persistence.AuthorInfo;
 import org.apache.iotdb.confignode.persistence.ProcedureInfo;
+import org.apache.iotdb.confignode.persistence.QuotaInfo;
 import org.apache.iotdb.confignode.persistence.TriggerInfo;
 import org.apache.iotdb.confignode.persistence.UDFInfo;
 import org.apache.iotdb.confignode.persistence.cq.CQInfo;
@@ -145,6 +147,8 @@ public class ConfigPlanExecutor {
 
   private final CQInfo cqInfo;
 
+  private final QuotaInfo quotaInfo;
+
   public ConfigPlanExecutor(
       NodeInfo nodeInfo,
       ClusterSchemaInfo clusterSchemaInfo,
@@ -154,7 +158,8 @@ public class ConfigPlanExecutor {
       UDFInfo udfInfo,
       TriggerInfo triggerInfo,
       ClusterSyncInfo syncInfo,
-      CQInfo cqInfo) {
+      CQInfo cqInfo,
+      QuotaInfo quotaInfo) {
 
     this.snapshotProcessorList = new ArrayList<>();
 
@@ -183,6 +188,8 @@ public class ConfigPlanExecutor {
     this.snapshotProcessorList.add(cqInfo);
 
     this.procedureInfo = procedureInfo;
+
+    this.quotaInfo = quotaInfo;
   }
 
   public DataSet executeQueryPlan(ConfigPhysicalPlan req)
@@ -365,6 +372,8 @@ public class ConfigPlanExecutor {
         return cqInfo.activeCQ((ActiveCQPlan) physicalPlan);
       case UPDATE_CQ_LAST_EXEC_TIME:
         return cqInfo.updateCQLastExecutionTime((UpdateCQLastExecTimePlan) 
physicalPlan);
+      case SET_SPACE_QUOTA:
+        return quotaInfo.setSpaceQuota((SetSpaceQuotaPlan) physicalPlan);
       default:
         throw new UnknownPhysicalPlanTypeException(physicalPlan.getType());
     }
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
index 383d413e31..37ea98aa2c 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
@@ -124,6 +124,7 @@ import 
org.apache.iotdb.confignode.rpc.thrift.TSetDataNodeStatusReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaReplicationFactorReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetTimePartitionIntervalReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowCQResp;
@@ -765,4 +766,9 @@ public class ConfigNodeRPCServiceProcessor implements 
IConfigNodeRPCService.Ifac
   public TShowCQResp showCQ() {
     return configManager.showCQ();
   }
+
+  @Override
+  public TSStatus setSpaceQuota(TSetSpaceQuotaReq req) throws TException {
+    return configManager.setSpaceQuota(req);
+  }
 }
diff --git 
a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
 
b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
index 66a9a60350..bb4901a819 100644
--- 
a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
+++ 
b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.common.rpc.thrift.TEndPoint;
 import org.apache.iotdb.common.rpc.thrift.TNodeResource;
 import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
 import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot;
+import org.apache.iotdb.common.rpc.thrift.TSpaceQuota;
 import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
 import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
@@ -76,6 +77,7 @@ import 
org.apache.iotdb.confignode.consensus.request.write.partition.CreateDataP
 import 
org.apache.iotdb.confignode.consensus.request.write.partition.CreateSchemaPartitionPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.DeleteProcedurePlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.procedure.UpdateProcedurePlan;
+import 
org.apache.iotdb.confignode.consensus.request.write.quota.SetSpaceQuotaPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.OfferRegionMaintainTasksPlan;
 import 
org.apache.iotdb.confignode.consensus.request.write.region.PollRegionMaintainTaskPlan;
@@ -1304,4 +1306,18 @@ public class ConfigPhysicalPlanSerDeTest {
     Assert.assertEquals(plan.getTemplateId(), 
deserializedPlan.getTemplateId());
     Assert.assertEquals(plan.getPath(), deserializedPlan.getPath());
   }
+
+  @Test
+  public void setSpaceQuotaPlanTest() throws IOException {
+    TSpaceQuota spaceQuota = new TSpaceQuota();
+    spaceQuota.setDeviceNum(2);
+    spaceQuota.setTimeserieNum(3);
+    spaceQuota.setDisk(1024);
+    SetSpaceQuotaPlan plan =
+        new SetSpaceQuotaPlan(Collections.singletonList("root.sg"), 
spaceQuota);
+    SetSpaceQuotaPlan deserializedPlan =
+        (SetSpaceQuotaPlan) 
ConfigPhysicalPlan.Factory.create(plan.serializeToByteBuffer());
+    Assert.assertEquals(plan.getPrefixPathList(), 
deserializedPlan.getPrefixPathList());
+    Assert.assertEquals(plan.getSpaceLimit(), 
deserializedPlan.getSpaceLimit());
+  }
 }
diff --git 
a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/quota/QuotaInfoTest.java
 
b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/quota/QuotaInfoTest.java
new file mode 100644
index 0000000000..7c77a358e5
--- /dev/null
+++ 
b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/quota/QuotaInfoTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.persistence.quota;
+
+// TODO: Add snapshot test
+public class QuotaInfoTest {}
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBQuotaIT.java 
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBQuotaIT.java
new file mode 100644
index 0000000000..b35f9e4ce3
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBQuotaIT.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.it;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBQuotaIT {
+
+  @Before
+  public void setUp() throws Exception {
+    EnvFactory.getEnv().initBeforeTest();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanAfterTest();
+  }
+
+  @Test
+  public void setSpaceQuotaTest() throws SQLException {
+    try (Connection adminCon = EnvFactory.getEnv().getConnection();
+        Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("set space quota devices=5,timeseries=10,disk=100g on 
root.sg1;");
+    }
+  }
+}
diff --git a/node-commons/src/assembly/resources/conf/iotdb-common.properties 
b/node-commons/src/assembly/resources/conf/iotdb-common.properties
index 13a36b72b7..7b0d25fb7a 100644
--- a/node-commons/src/assembly/resources/conf/iotdb-common.properties
+++ b/node-commons/src/assembly/resources/conf/iotdb-common.properties
@@ -1134,4 +1134,10 @@
 # enable_influxdb_rpc_service=false
 
 # Datatype: int
-# influxdb_rpc_port=8086
\ No newline at end of file
+
+####################
+### Quota Configuration
+####################
+# If enabled, we can set different quotas for storage groups
+# Datatype: boolean
+# quota_enable=false
diff --git 
a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java 
b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java
index 32adf711ca..03a2f98262 100644
--- 
a/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java
+++ 
b/node-commons/src/main/java/org/apache/iotdb/commons/conf/IoTDBConstant.java
@@ -235,6 +235,11 @@ public class IoTDBConstant {
   public static final String WAL_FOLDER_NAME = "wal";
   public static final String EXT_PIPE_FOLDER_NAME = "extPipe";
 
+  // quota folder name
+  public static final String QUOTA_FOLDER_NAME = "quota";
+  public static final String SPACE_QUOTA_FOLDER_NAME = "space";
+  public static final String THROTTLE_QUOTA_FOLDER_NAME = "throttle";
+
   // mqtt
   public static final String ENABLE_MQTT = "enable_mqtt_service";
   public static final String MQTT_HOST_NAME = "mqtt_host";
@@ -288,6 +293,9 @@ public class IoTDBConstant {
   public static final String IOTDB_FOREGROUND = "iotdb-foreground";
   public static final String IOTDB_PIDFILE = "iotdb-pidfile";
 
+  // quota
+  public static final String SPACE_QUOTA_DISK = "disk";
+
   // client version number
   public enum ClientVersion {
     V_0_12,
diff --git 
a/node-commons/src/main/java/org/apache/iotdb/commons/enums/SpaceQuotaType.java 
b/node-commons/src/main/java/org/apache/iotdb/commons/enums/SpaceQuotaType.java
new file mode 100644
index 0000000000..36b23e4b0e
--- /dev/null
+++ 
b/node-commons/src/main/java/org/apache/iotdb/commons/enums/SpaceQuotaType.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.commons.enums;
+
+public enum SpaceQuotaType {
+  DISK,
+  DEVICE_NUMBER,
+  TIMESERIES_NUMBER
+}
diff --git 
a/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
 
b/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
index 49669553ab..e3832dba28 100644
--- 
a/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
+++ 
b/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
@@ -29,6 +29,7 @@ import java.util.Map;
 
 public class BasicStructureSerDeUtil {
   public static final int INT_LEN = 4;
+  public static final int LONG_LEN = 8;
 
   private BasicStructureSerDeUtil() {}
 
@@ -63,6 +64,11 @@ public class BasicStructureSerDeUtil {
     return buffer.getInt();
   }
 
+  /** read a long var from byteBuffer. */
+  public static long readLong(ByteBuffer buffer) {
+    return buffer.getLong();
+  }
+
   /**
    * write string to byteBuffer.
    *
@@ -135,6 +141,16 @@ public class BasicStructureSerDeUtil {
     return INT_LEN;
   }
 
+  /**
+   * write a long n to dataOutputStream.
+   *
+   * @return The number of bytes used to represent n.
+   */
+  public static int write(long n, DataOutputStream stream) throws IOException {
+    stream.writeLong(n);
+    return LONG_LEN;
+  }
+
   /**
    * write a map to buffer
    *
diff --git 
a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java 
b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
index 45db274090..61da2934b8 100644
--- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
+++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
@@ -91,6 +91,7 @@ import 
org.apache.iotdb.confignode.rpc.thrift.TSetDataNodeStatusReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetDataReplicationFactorReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaReplicationFactorReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetTimePartitionIntervalReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowCQResp;
@@ -1731,6 +1732,22 @@ public class ConfigNodeClient
     throw new TException(MSG_RECONNECTION_FAIL);
   }
 
+  @Override
+  public TSStatus setSpaceQuota(TSetSpaceQuotaReq req) throws TException {
+    for (int i = 0; i < RETRY_NUM; i++) {
+      try {
+        TSStatus status = client.setSpaceQuota(req);
+        if (!updateConfigNodeLeader(status)) {
+          return status;
+        }
+      } catch (TException e) {
+        configLeader = null;
+      }
+      reconnect();
+    }
+    throw new TException(MSG_RECONNECTION_FAIL);
+  }
+
   public static class Factory extends BaseClientFactory<PartitionRegionId, 
ConfigNodeClient> {
 
     public Factory(
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java 
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index 94a6703709..0ba80a1ca9 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -298,6 +298,26 @@ public class IoTDBConfig {
 
   private String schemaRegionConsensusDir = consensusDir + File.separator + 
"schema_region";
 
+  /** Space quota directory, stores space quota information of each storage 
group */
+  private String spaceQuotaDir =
+      IoTDBConstant.DEFAULT_BASE_DIR
+          + File.separator
+          + IoTDBConstant.SYSTEM_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.QUOTA_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.SPACE_QUOTA_FOLDER_NAME;
+
+  /** Throttle quota directory, stores throttle quota information of each 
storage group */
+  private String throttleQuotaDir =
+      IoTDBConstant.DEFAULT_BASE_DIR
+          + File.separator
+          + IoTDBConstant.SYSTEM_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.QUOTA_FOLDER_NAME
+          + File.separator
+          + IoTDBConstant.THROTTLE_QUOTA_FOLDER_NAME;
+
   /** Maximum MemTable number. Invalid when enableMemControl is true. */
   private int maxMemtableNumber = 0;
 
@@ -1048,6 +1068,9 @@ public class IoTDBConfig {
   private long ratisFirstElectionTimeoutMinMs = 50L;
   private long ratisFirstElectionTimeoutMaxMs = 150L;
 
+  /** Enable quotas */
+  private boolean quotaEnable = false;
+
   // customizedProperties, this should be empty by default.
   private Properties customizedProperties = new Properties();
 
@@ -1174,6 +1197,9 @@ public class IoTDBConfig {
 
     extPipeDir = addDataHomeDir(extPipeDir);
 
+    spaceQuotaDir = addDataHomeDir(spaceQuotaDir);
+    throttleQuotaDir = addDataHomeDir(throttleQuotaDir);
+
     if 
(TSFileDescriptor.getInstance().getConfig().getTSFileStorageFs().equals(FSType.HDFS))
 {
       String hdfsDir = getHdfsDir();
       queryDir = hdfsDir + File.separatorChar + queryDir;
@@ -1435,6 +1461,22 @@ public class IoTDBConfig {
     this.mqttDir = mqttDir;
   }
 
+  public String getSpaceQuotaDir() {
+    return spaceQuotaDir;
+  }
+
+  public void setSpaceQuotaDir(String spaceQuotaDir) {
+    this.spaceQuotaDir = spaceQuotaDir;
+  }
+
+  public String getThrottleQuotaDir() {
+    return throttleQuotaDir;
+  }
+
+  public void setThrottleQuotaDir(String throttleQuotaDir) {
+    this.throttleQuotaDir = throttleQuotaDir;
+  }
+
   public String getMultiDirStrategyClassName() {
     return multiDirStrategyClassName;
   }
@@ -3577,4 +3619,12 @@ public class IoTDBConfig {
   public void setRatisFirstElectionTimeoutMaxMs(long 
ratisFirstElectionTimeoutMaxMs) {
     this.ratisFirstElectionTimeoutMaxMs = ratisFirstElectionTimeoutMaxMs;
   }
+
+  public boolean isQuotaEnable() {
+    return quotaEnable;
+  }
+
+  public void setQuotaEnable(boolean quotaEnable) {
+    this.quotaEnable = quotaEnable;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java 
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index 814bf550e2..e1724821c7 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -1005,6 +1005,10 @@ public class IoTDBDescriptor {
     if (!conf.isClusterMode()) {
       
conf.setTimePartitionIntervalForRouting(conf.getTimePartitionIntervalForStorage());
     }
+
+    conf.setQuotaEnable(
+        Boolean.parseBoolean(
+            properties.getProperty("quota_enable", 
String.valueOf(conf.isQuotaEnable()))));
   }
 
   private void loadAuthorCache(Properties properties) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java 
b/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
index 998c900c02..5b0b31e567 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
@@ -153,15 +153,19 @@ public class LocalConfigNode {
   private IAuthorizer iAuthorizer;
 
   private LocalConfigNode() {
+    // create schema dir
     String schemaDir = config.getSchemaDir();
     File schemaFolder = SystemFileFactory.INSTANCE.getFile(schemaDir);
-    if (!schemaFolder.exists()) {
-      if (schemaFolder.mkdirs()) {
-        logger.info("create system folder {}", schemaFolder.getAbsolutePath());
-      } else {
-        logger.error("create system folder {} failed.", 
schemaFolder.getAbsolutePath());
-      }
-    }
+    createFolder(schemaFolder);
+
+    // create space quota dir
+    File spaceQuotaFolder = 
SystemFileFactory.INSTANCE.getFile(config.getSpaceQuotaDir());
+    createFolder(spaceQuotaFolder);
+
+    // create throttle quota dir
+    File throttleQuotaFolder = 
SystemFileFactory.INSTANCE.getFile(config.getThrottleQuotaDir());
+    createFolder(throttleQuotaFolder);
+
     try {
       iAuthorizer = BasicAuthorizer.getInstance();
     } catch (AuthException e) {
@@ -169,6 +173,17 @@ public class LocalConfigNode {
     }
   }
 
+  /** Create system folders based on paths */
+  private void createFolder(File fileFolder) {
+    if (!fileFolder.exists()) {
+      if (fileFolder.mkdirs()) {
+        logger.info("create system folder {}", fileFolder.getAbsolutePath());
+      } else {
+        logger.error("create system folder {} failed.", 
fileFolder.getAbsolutePath());
+      }
+    }
+  }
+
   // region LocalSchemaConfigManager SingleTone
   private static class LocalSchemaConfigManagerHolder {
     private static final LocalConfigNode INSTANCE = new LocalConfigNode();
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
index 411fbc5bbd..88203199f1 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
@@ -152,4 +152,6 @@ public enum StatementType {
   SHOW_TRIGGERS,
 
   DEACTIVATE_TEMPLATE,
+
+  SET_SPACE_QUOTA,
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
index 1e6aa6f61e..0fd36c18e3 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
@@ -57,6 +57,7 @@ import 
org.apache.iotdb.db.mpp.plan.execution.config.sys.FlushTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.sys.LoadConfigurationTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.sys.MergeTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.sys.SetSystemStatusTask;
+import 
org.apache.iotdb.db.mpp.plan.execution.config.sys.quota.SetSpaceQuotaTask;
 import 
org.apache.iotdb.db.mpp.plan.execution.config.sys.sync.CreatePipeSinkTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.sys.sync.CreatePipeTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.sys.sync.DropPipeSinkTask;
@@ -106,6 +107,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.sys.FlushStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.LoadConfigurationStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.MergeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.SetSystemStatusStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -295,6 +297,12 @@ public class ConfigTaskVisitor
     return new DropSchemaTemplateTask(dropSchemaTemplateStatement);
   }
 
+  @Override
+  public IConfigTask visitSetSpaceQuota(
+      SetSpaceQuotaStatement setSpaceQuotaStatement, TaskContext context) {
+    return new SetSpaceQuotaTask(setSpaceQuotaStatement);
+  }
+
   @Override
   public IConfigTask visitShowDataNodes(
       ShowDataNodesStatement showDataNodesStatement, TaskContext context) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
index 10df3d09c3..a8a8b0180a 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
@@ -22,6 +22,7 @@ package 
org.apache.iotdb.db.mpp.plan.execution.config.executor;
 import org.apache.iotdb.common.rpc.thrift.TFlushReq;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.common.rpc.thrift.TSetTTLReq;
+import org.apache.iotdb.common.rpc.thrift.TSpaceQuota;
 import org.apache.iotdb.commons.client.IClientManager;
 import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.consensus.PartitionRegionId;
@@ -57,6 +58,7 @@ import 
org.apache.iotdb.confignode.rpc.thrift.TGetTimeSlotListResp;
 import org.apache.iotdb.confignode.rpc.thrift.TGetTriggerTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TGetUDFTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TPipeSinkInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TSetSpaceQuotaReq;
 import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowCQResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowClusterResp;
@@ -121,6 +123,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchem
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowPathSetTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.UnsetSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -1069,6 +1072,33 @@ public class ClusterConfigTaskExecutor implements 
IConfigTaskExecutor {
     return future;
   }
 
+  @Override
+  public SettableFuture<ConfigTaskResult> setSpaceQuota(
+      SetSpaceQuotaStatement setSpaceQuotaStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    TSStatus tsStatus = new TSStatus();
+    TSetSpaceQuotaReq req = new TSetSpaceQuotaReq();
+    req.setStorageGroup(setSpaceQuotaStatement.getPrefixPathList());
+    TSpaceQuota spaceQuota = new TSpaceQuota();
+    spaceQuota.setDeviceNum(setSpaceQuotaStatement.getDeviceNum());
+    spaceQuota.setTimeserieNum(setSpaceQuotaStatement.getTimeSeriesNum());
+    spaceQuota.setDisk(setSpaceQuotaStatement.getDiskSize());
+    req.setSpaceLimit(spaceQuota);
+    try (ConfigNodeClient client =
+        
CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      // Send request to some API server
+      tsStatus = client.setSpaceQuota(req);
+    } catch (IOException | TException e) {
+      future.setException(e);
+    }
+    if (tsStatus.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS));
+    } else {
+      future.setException(new IoTDBException(tsStatus.message, tsStatus.code));
+    }
+    return future;
+  }
+
   @Override
   public SettableFuture<ConfigTaskResult> createPipeSink(
       CreatePipeSinkStatement createPipeSinkStatement) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
index dc79335224..ae9f0dd0ca 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
@@ -45,6 +45,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchem
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowPathSetTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.UnsetSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -128,6 +129,8 @@ public interface IConfigTaskExecutor {
   SettableFuture<ConfigTaskResult> dropSchemaTemplate(
       DropSchemaTemplateStatement dropSchemaTemplateStatement);
 
+  SettableFuture<ConfigTaskResult> setSpaceQuota(SetSpaceQuotaStatement 
setSpaceQuotaStatement);
+
   SettableFuture<ConfigTaskResult> createPipeSink(CreatePipeSinkStatement 
createPipeSinkStatement);
 
   SettableFuture<ConfigTaskResult> dropPipeSink(DropPipeSinkStatement 
dropPipeSinkStatement);
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
index a0c59d4d8b..fd8ca382f6 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
@@ -63,6 +63,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchem
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowPathSetTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import 
org.apache.iotdb.db.mpp.plan.statement.metadata.template.UnsetSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -527,6 +528,17 @@ public class StandaloneConfigTaskExecutor implements 
IConfigTaskExecutor {
     return future;
   }
 
+  @Override
+  public SettableFuture<ConfigTaskResult> setSpaceQuota(
+      SetSpaceQuotaStatement setSpaceQuotaStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    future.setException(
+        new IoTDBException(
+            "Executing unset schema template is not supported",
+            TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()));
+    return future;
+  }
+
   @Override
   public SettableFuture<ConfigTaskResult> createPipeSink(
       CreatePipeSinkStatement createPipeSinkStatement) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/sys/quota/SetSpaceQuotaTask.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/sys/quota/SetSpaceQuotaTask.java
new file mode 100644
index 0000000000..bb80bc4d9b
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/sys/quota/SetSpaceQuotaTask.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.plan.execution.config.sys.quota;
+
+import org.apache.iotdb.db.mpp.plan.execution.config.ConfigTaskResult;
+import org.apache.iotdb.db.mpp.plan.execution.config.IConfigTask;
+import 
org.apache.iotdb.db.mpp.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class SetSpaceQuotaTask implements IConfigTask {
+
+  private final SetSpaceQuotaStatement setSpaceQuotaStatement;
+
+  public SetSpaceQuotaTask(SetSpaceQuotaStatement setSpaceQuotaStatement) {
+    this.setSpaceQuotaStatement = setSpaceQuotaStatement;
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(IConfigTaskExecutor 
configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.setSpaceQuota(setSpaceQuotaStatement);
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
index a10c09e2c4..f41423c998 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
@@ -145,6 +145,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.sys.LoadConfigurationStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.MergeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.SetSystemStatusStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -2164,6 +2165,61 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     return privileges.toArray(new String[0]);
   }
 
+  // Quota
+
+  @Override
+  public Statement visitSetSpaceQuota(IoTDBSqlParser.SetSpaceQuotaContext ctx) 
{
+    SetSpaceQuotaStatement setSpaceQuotaStatement = new 
SetSpaceQuotaStatement();
+    List<IoTDBSqlParser.PrefixPathContext> prefixPathContexts = 
ctx.prefixPath();
+    List<String> paths = new ArrayList<>();
+    for (IoTDBSqlParser.PrefixPathContext prefixPathContext : 
prefixPathContexts) {
+      paths.add(parsePrefixPath(prefixPathContext).getFullPath());
+    }
+    setSpaceQuotaStatement.setPrefixPathList(paths);
+
+    Map<String, String> quotas = new HashMap<>();
+    for (IoTDBSqlParser.AttributePairContext attributePair : 
ctx.attributePair()) {
+      quotas.put(
+          parseAttributeKey(attributePair.attributeKey()),
+          parseAttributeValue(attributePair.attributeValue()));
+    }
+
+    if (quotas.containsKey(IoTDBConstant.COLUMN_DEVICES)) {
+      setSpaceQuotaStatement.setDeviceNum(
+          Integer.parseInt(quotas.get(IoTDBConstant.COLUMN_DEVICES)));
+    }
+    if (quotas.containsKey(IoTDBConstant.COLUMN_TIMESERIES)) {
+      setSpaceQuotaStatement.setTimeSeriesNum(
+          Integer.parseInt(quotas.get(IoTDBConstant.COLUMN_TIMESERIES)));
+    }
+    if (quotas.containsKey(IoTDBConstant.SPACE_QUOTA_DISK)) {
+      
setSpaceQuotaStatement.setDiskSize(parseUnit(quotas.get(IoTDBConstant.SPACE_QUOTA_DISK)));
+    }
+    return setSpaceQuotaStatement;
+  }
+
+  private long parseUnit(String data) {
+    String unit = data.substring(data.length() - 1);
+    long disk = Long.parseLong(data.substring(0, data.length() - 1));
+    switch (unit) {
+      case "M":
+      case "m":
+        return disk * 1024;
+      case "G":
+      case "g":
+        return disk * 1024 * 1024;
+      case "T":
+      case "t":
+        return disk * 1024 * 1024 * 1024;
+      case "P":
+      case "p":
+        return disk * 1024 * 1024 * 1024 * 1024;
+      default:
+        throw new SQLParserException(
+            "When setting the disk size, the unit is incorrect. Please use 
'M', 'G', 'P', 'T' as the unit");
+    }
+  }
+
   // Create Storage Group
   @Override
   public Statement 
visitCreateStorageGroup(IoTDBSqlParser.CreateStorageGroupContext ctx) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
index bd1d02330a..29ac69a2b1 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
@@ -84,6 +84,7 @@ import 
org.apache.iotdb.db.mpp.plan.statement.sys.LoadConfigurationStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.MergeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.SetSystemStatusStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement;
+import org.apache.iotdb.db.mpp.plan.statement.sys.quota.SetSpaceQuotaStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeSinkStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.CreatePipeStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.sync.DropPipeSinkStatement;
@@ -445,4 +446,8 @@ public abstract class StatementVisitor<R, C> {
       DropSchemaTemplateStatement dropSchemaTemplateStatement, C context) {
     return visitStatement(dropSchemaTemplateStatement, context);
   }
+
+  public R visitSetSpaceQuota(SetSpaceQuotaStatement setSpaceQuotaStatement, C 
context) {
+    return visitStatement(setSpaceQuotaStatement, context);
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/quota/SetSpaceQuotaStatement.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/quota/SetSpaceQuotaStatement.java
new file mode 100644
index 0000000000..318d7a28f3
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/quota/SetSpaceQuotaStatement.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.plan.statement.sys.quota;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.mpp.plan.analyze.QueryType;
+import org.apache.iotdb.db.mpp.plan.constant.StatementType;
+import org.apache.iotdb.db.mpp.plan.statement.IConfigStatement;
+import org.apache.iotdb.db.mpp.plan.statement.Statement;
+import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor;
+
+import java.util.Collections;
+import java.util.List;
+
+public class SetSpaceQuotaStatement extends Statement implements 
IConfigStatement {
+
+  private int timeSeriesNum;
+  private int deviceNum;
+  private long diskSize;
+  private String policy;
+  private List<String> prefixPathList;
+
+  /** QuotaOperator Constructor with OperatorType. */
+  public SetSpaceQuotaStatement() {
+    super();
+    statementType = StatementType.SET_SPACE_QUOTA;
+  }
+
+  public int getTimeSeriesNum() {
+    return timeSeriesNum;
+  }
+
+  public void setTimeSeriesNum(int timeSeriesNum) {
+    this.timeSeriesNum = timeSeriesNum;
+  }
+
+  public int getDeviceNum() {
+    return deviceNum;
+  }
+
+  public void setDeviceNum(int deviceNum) {
+    this.deviceNum = deviceNum;
+  }
+
+  public long getDiskSize() {
+    return diskSize;
+  }
+
+  public void setDiskSize(long diskSize) {
+    this.diskSize = diskSize;
+  }
+
+  public String getPolicy() {
+    return policy;
+  }
+
+  public void setPolicy(String policy) {
+    this.policy = policy;
+  }
+
+  public List<String> getPrefixPathList() {
+    return prefixPathList;
+  }
+
+  public void setPrefixPathList(List<String> prefixPathList) {
+    this.prefixPathList = prefixPathList;
+  }
+
+  @Override
+  public QueryType getQueryType() {
+    return QueryType.WRITE;
+  }
+
+  @Override
+  public List<PartialPath> getPaths() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
+    return visitor.visitSetSpaceQuota(this, context);
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSizeStore.java 
b/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSizeStore.java
new file mode 100644
index 0000000000..41061e7f87
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSizeStore.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.quotas;
+
+// TODO: Regularly count the disk sizes of all regions included in this 
DataNode. Save in its
+// memory.
+public class DataNodeSizeStore {}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSpaceQuotaManager.java
 
b/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSpaceQuotaManager.java
new file mode 100644
index 0000000000..776080b0af
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/quotas/DataNodeSpaceQuotaManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.quotas;
+
+import org.apache.iotdb.common.rpc.thrift.TSpaceQuota;
+
+import java.util.Map;
+
+// TODO: Store quota information of each sg
+public class DataNodeSpaceQuotaManager {
+
+  private Map<String, TSpaceQuota> spaceQuotaLimit;
+  private Map<String, TSpaceQuota> useSpaceQuota;
+  private Map<Integer, Integer> regionDisk;
+
+  public DataNodeSpaceQuotaManager() {}
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/quotas/SpaceViolationPolicyEnforcement.java
 
b/server/src/main/java/org/apache/iotdb/db/quotas/SpaceViolationPolicyEnforcement.java
new file mode 100644
index 0000000000..91b2407371
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/quotas/SpaceViolationPolicyEnforcement.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.quotas;
+
+// TODO: Store the policies to be used by each sg after exceeding the quota
+public class SpaceViolationPolicyEnforcement {}
diff --git 
a/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java 
b/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
index a97a58a74f..b98b1dc550 100644
--- a/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
+++ b/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
@@ -384,6 +384,10 @@ public class EnvironmentUtils {
       logger.error("create user and role folders failed", e);
       fail(e.getMessage());
     }
+    // create space quota
+    createDir(config.getSpaceQuotaDir());
+    // create throttle quota
+    createDir(config.getThrottleQuotaDir());
   }
 
   private static void createDir(String dir) {
diff --git a/thrift-commons/src/main/thrift/common.thrift 
b/thrift-commons/src/main/thrift/common.thrift
index e71978c6aa..94e6212511 100644
--- a/thrift-commons/src/main/thrift/common.thrift
+++ b/thrift-commons/src/main/thrift/common.thrift
@@ -122,3 +122,10 @@ struct TFilesResp {
   1: required TSStatus status
   2: required list<TFile> files
 }
+
+// quota
+struct TSpaceQuota {
+  1: optional i64 disk
+  2: optional i32 deviceNum
+  3: optional i32 timeserieNum
+}
diff --git a/thrift-confignode/src/main/thrift/confignode.thrift 
b/thrift-confignode/src/main/thrift/confignode.thrift
index fe7ca6215a..501c52e453 100644
--- a/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/thrift-confignode/src/main/thrift/confignode.thrift
@@ -556,6 +556,14 @@ struct TDeleteTimeSeriesReq{
   2: required binary pathPatternTree
 }
 
+// ====================================================
+// Quota
+// ====================================================
+struct TSetSpaceQuotaReq {
+  1: required list<string> storageGroup
+  2: required common.TSpaceQuota spaceLimit
+}
+
 // ====================================================
 // CQ
 // ====================================================
@@ -1050,5 +1058,7 @@ service IConfigNodeRPCService {
    * Return the trigger table of config leader
    */
   TShowCQResp showCQ()
+
+  common.TSStatus setSpaceQuota(TSetSpaceQuotaReq req)
 }
 

Reply via email to