fix [Feature]: Support delete volume only when dentry count is zero.

if volForceDeletion param set to true, volume can be deleted directly.
otherwise, volumes can be deleted only if it's latest dentry count is less than or equal to the volDeletionDentryThreshold

Signed-off-by: jax <jax.jiang@openpie.com>
This commit is contained in:
jax 2023-11-04 22:17:32 +08:00 committed by jiaruo bai
parent b6c4e1b633
commit f8f7a70f69
5 changed files with 106 additions and 67 deletions

View File

@ -3,40 +3,42 @@
CubeFS 使用 **JSON** 作为配置文件的格式.
| 配置项 | 类型 | 描述 | 必需 | 默认值 |
| 配置项 | 类型 | 描述 | 必需 | 默认值 |
|:-------------------------------------|:--------|:--------------------------------------------|:------|:------------|
| role | string | 进程的角色,值只能是 master | 是 | |
| ip | string | 主机ip | 是 | |
| listen | string | http服务监听的端口号 | 是 | |
| prof | string | golang pprof 端口号 | 是 | |
| id | string | 区分不同的master节点 | 是 | |
| peers | string | raft复制组成员信息 | 是 | |
| logDir | string | 日志文件存储目录 | 是 | |
| logLevel | string | 日志级别 | 否 | error |
| retainLogs | string | 保留多少条raft日志. | 是 | |
| walDir | string | raft wal日志存储目录. | 是 | |
| storeDir | string | RocksDB数据存储目录.此目录必须存在,如果目录不存在,无法启动服务 | 是 | |
| clusterName | string | 集群名字 | 是 | |
| ebsAddr | string | 纠删码子系统的地址,使用纠删码子系统时需配置 | 否 | |
| exporterPort | int | prometheus获取监控数据端口 | 否 | |
| consulAddr | string | consul注册地址供prometheus exporter使用 | 否 | |
| metaNodeReservedMem | string | 元数据节点预留内存大小,单位:字节 | 否 | 1073741824 |
| heartbeatPort | string | raft心跳通信端口 | 否 | 5901 |
| replicaPort | string | raft数据传输端口 | 否 | 5902 |
| nodeSetCap | string | NodeSet的容量 | 否 | 18 |
| missingDataPartitionInterval | string | 当此时间段内没有收到副本的心跳该副本被认为已丢失单位s | 否 | 24h |
| dataPartitionTimeOutSec | string | 当此时间段内没有收到副本的心跳该副本被认为非存活单位s | 否 | 10min |
| numberOfDataPartitionsToLoad | string | 一次最多检查多少数据分片 | 否 | 40 |
| secondsToFreeDataPartitionAfterLoad | string | 在多少秒之后开始释放由加载数据分片任务占用的内存 | 否 | 300 |
| tickInterval | string | 检查心跳和选举超时的计时器间隔单位ms | 否 | 500 |
| electionTick | string | 在计时器重置多少次时,选举超时 | 否 | 5 |
| bindIp | bool | 是否仅在主机ip上监听连接 | 否 | false |
| faultDomain | bool | 是否启用故障域 | 否 | false |
| faultDomainBuildAsPossible | bool | 若可用的故障域数量少于预期的故障域数量是否仍尽可能地去构建nodeSetGroup | 否 | false |
| faultDomainGrpBatchCnt | string | 可用的故障域数量 | 否 | 3 |
| dpNoLeaderReportIntervalSec | string | 数据分片没有leader时多久上报一次单位s | 否 | 60 |
| mpNoLeaderReportIntervalSec | string | 元数据分片没有leader时多久上报一次单位s | 否 | 60 |
| maxQuotaNumPerVol | string | 单个卷最大的配额数 | 否 | 100 |
| role | string | 进程的角色,值只能是 master | 是 | |
| ip | string | 主机ip | 是 | |
| listen | string | http服务监听的端口号 | 是 | |
| prof | string | golang pprof 端口号 | 是 | |
| id | string | 区分不同的master节点 | 是 | |
| peers | string | raft复制组成员信息 | 是 | |
| logDir | string | 日志文件存储目录 | 是 | |
| logLevel | string | 日志级别 | 否 | error |
| retainLogs | string | 保留多少条raft日志. | 是 | |
| walDir | string | raft wal日志存储目录. | 是 | |
| storeDir | string | RocksDB数据存储目录.此目录必须存在,如果目录不存在,无法启动服务 | 是 | |
| clusterName | string | 集群名字 | 是 | |
| ebsAddr | string | 纠删码子系统的地址,使用纠删码子系统时需配置 | 否 | |
| exporterPort | int | prometheus获取监控数据端口 | 否 | |
| consulAddr | string | consul注册地址供prometheus exporter使用 | 否 | |
| metaNodeReservedMem | string | 元数据节点预留内存大小,单位:字节 | 否 | 1073741824 |
| heartbeatPort | string | raft心跳通信端口 | 否 | 5901 |
| replicaPort | string | raft数据传输端口 | 否 | 5902 |
| nodeSetCap | string | NodeSet的容量 | 否 | 18 |
| missingDataPartitionInterval | string | 当此时间段内没有收到副本的心跳该副本被认为已丢失单位s | 否 | 24h |
| dataPartitionTimeOutSec | string | 当此时间段内没有收到副本的心跳该副本被认为非存活单位s | 否 | 10min |
| numberOfDataPartitionsToLoad | string | 一次最多检查多少数据分片 | 否 | 40 |
| secondsToFreeDataPartitionAfterLoad | string | 在多少秒之后开始释放由加载数据分片任务占用的内存 | 否 | 300 |
| tickInterval | string | 检查心跳和选举超时的计时器间隔单位ms | 否 | 500 |
| electionTick | string | 在计时器重置多少次时,选举超时 | 否 | 5 |
| bindIp | bool | 是否仅在主机ip上监听连接 | 否 | false |
| faultDomain | bool | 是否启用故障域 | 否 | false |
| faultDomainBuildAsPossible | bool | 若可用的故障域数量少于预期的故障域数量是否仍尽可能地去构建nodeSetGroup | 否 | false |
| faultDomainGrpBatchCnt | string | 可用的故障域数量 | 否 | 3 |
| dpNoLeaderReportIntervalSec | string | 数据分片没有leader时多久上报一次单位s | 否 | 60 |
| mpNoLeaderReportIntervalSec | string | 元数据分片没有leader时多久上报一次单位s | 否 | 60 |
| maxQuotaNumPerVol | string | 单个卷最大的配额数 | 否 | 100 |
| volForceDeletion | bool | 非空的卷是否可以删除 | 否 | true |
| volDeletionDentryThreshold | int | 如果非空的卷不可以直接删除, 该参数定义了一个阈值只有一个卷的dentry个数小于等于该阈值时才可以被删除 | 否 | 0 |
## 配置示例

View File

@ -3,40 +3,42 @@
CubeFS uses **JSON** as the format of the configuration file.
| Configuration Item | Type | Description | Required | Default Value |
|:-------------------------------------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------|:----------|:---------------|
| role | string | The role of the process, the value can only be master | Yes | |
| ip | string | Host IP address | Yes | |
| listen | string | Port number on which the HTTP service listens | Yes | |
| prof | string | Golang pprof port number | Yes | |
| id | string | Distinguish different master nodes | Yes | |
| peers | string | Raft replication group member information | Yes | |
| logDir | string | Directory for storing log files | Yes | |
| logLevel | string | Log level | No | error |
| retainLogs | string | How many Raft logs to keep. | Yes | |
| walDir | string | Directory for storing Raft WAL logs. | Yes | |
| storeDir | string | Directory for storing RocksDB data. This directory must exist. If the directory does not exist, the service cannot be started. | Yes | |
| clusterName | string | Cluster name | Yes | |
| ebsAddr | string | Address of the erasure coding subsystem. This must be configured when using the erasure coding subsystem. | No | |
| exporterPort | int | Port for Prometheus to obtain monitoring data | No | |
| consulAddr | string | Consul registration address, used by Prometheus exporter | No | |
| metaNodeReservedMem | string | Reserved memory size for metadata nodes, in bytes | No | 1073741824 |
| heartbeatPort | string | Raft heartbeat communication port | No | 5901 |
| replicaPort | string | Raft data transmission port | No | 5902 |
| nodeSetCap | string | Capacity of NodeSet | No | 18 |
| missingDataPartitionInterval | string | If no heartbeat is received during this time period, the replica is considered lost, in seconds | No | 24h |
| dataPartitionTimeOutSec | string | If no heartbeat is received during this time period, the replica is considered not alive, in seconds | No | 10min |
| numberOfDataPartitionsToLoad | string | Maximum number of data partitions to check at a time | No | 40 |
| secondsToFreeDataPartitionAfterLoad | string | After how many seconds to start releasing the memory occupied by the loaded data partition task | No | 300 |
| tickInterval | string | Timer interval for checking heartbeat and election timeout, in milliseconds | No | 500 |
| electionTick | string | How many times the timer is reset before the election times out | No | 5 |
| bindIp | bool | Whether to listen for connections only on the host IP | No | false |
| faultDomain | bool | Whether to enable fault domain | No | false |
| faultDomainBuildAsPossible | bool | Whether to still try to build a nodeSetGroup as much as possible if the number of available fault domains is less than the expected number | No | false |
| faultDomainGrpBatchCnt | string | Number of available fault domains | No | 3 |
| dpNoLeaderReportIntervalSec | string | How often to report when data partitions has no leader, unit: s | No | 60 |
| mpNoLeaderReportIntervalSec | string | How often to report when meta partitions has no leader, unit: s | No | 60 |
| maxQuotaNumPerVol | string | Maximum quota number per volume | No | 100 |
| Configuration Item | Type | Description | Required | Default Value |
|:------------------------------------|:-------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------|:--------------|
| role | string | The role of the process, the value can only be master | Yes | |
| ip | string | Host IP address | Yes | |
| listen | string | Port number on which the HTTP service listens | Yes | |
| prof | string | Golang pprof port number | Yes | |
| id | string | Distinguish different master nodes | Yes | |
| peers | string | Raft replication group member information | Yes | |
| logDir | string | Directory for storing log files | Yes | |
| logLevel | string | Log level | No | error |
| retainLogs | string | How many Raft logs to keep. | Yes | |
| walDir | string | Directory for storing Raft WAL logs. | Yes | |
| storeDir | string | Directory for storing RocksDB data. This directory must exist. If the directory does not exist, the service cannot be started. | Yes | |
| clusterName | string | Cluster name | Yes | |
| ebsAddr | string | Address of the erasure coding subsystem. This must be configured when using the erasure coding subsystem. | No | |
| exporterPort | int | Port for Prometheus to obtain monitoring data | No | |
| consulAddr | string | Consul registration address, used by Prometheus exporter | No | |
| metaNodeReservedMem | string | Reserved memory size for metadata nodes, in bytes | No | 1073741824 |
| heartbeatPort | string | Raft heartbeat communication port | No | 5901 |
| replicaPort | string | Raft data transmission port | No | 5902 |
| nodeSetCap | string | Capacity of NodeSet | No | 18 |
| missingDataPartitionInterval | string | If no heartbeat is received during this time period, the replica is considered lost, in seconds | No | 24h |
| dataPartitionTimeOutSec | string | If no heartbeat is received during this time period, the replica is considered not alive, in seconds | No | 10min |
| numberOfDataPartitionsToLoad | string | Maximum number of data partitions to check at a time | No | 40 |
| secondsToFreeDataPartitionAfterLoad | string | After how many seconds to start releasing the memory occupied by the loaded data partition task | No | 300 |
| tickInterval | string | Timer interval for checking heartbeat and election timeout, in milliseconds | No | 500 |
| electionTick | string | How many times the timer is reset before the election times out | No | 5 |
| bindIp | bool | Whether to listen for connections only on the host IP | No | false |
| faultDomain | bool | Whether to enable fault domain | No | false |
| faultDomainBuildAsPossible | bool | Whether to still try to build a nodeSetGroup as much as possible if the number of available fault domains is less than the expected number | No | false |
| faultDomainGrpBatchCnt | string | Number of available fault domains | No | 3 |
| dpNoLeaderReportIntervalSec | string | How often to report when data partitions has no leader, unit: s | No | 60 |
| mpNoLeaderReportIntervalSec | string | How often to report when meta partitions has no leader, unit: s | No | 60 |
| maxQuotaNumPerVol | string | Maximum quota number per volume | No | 100 |
| volForceDeletion | bool | the non-empty volume can be deleted directly or not | No | true |
| volDeletionDentryThreshold | int | if the non-empty volume can't be deleted directly , this param define a threshold , only volumes with a dentry count that is less than or equal to the threshold can be deleted | No | 0 |
## Configuration Example

View File

@ -1289,6 +1289,27 @@ func (c *Cluster) markDeleteVol(name, authKey string, force bool) (err error) {
return proto.ErrVolNotExists
}
if !c.cfg.volForceDeletion {
volDentryCount := uint64(0)
mpsCopy := vol.cloneMetaPartitionMap()
for _, mp := range mpsCopy {
// to avoid latency, fetch latest mp dentry count from metanode
c.doLoadMetaPartition(mp)
mpDentryCount := uint64(0)
for _, response := range mp.LoadResponse {
if response.DentryCount > mpDentryCount {
mpDentryCount = response.DentryCount
}
}
volDentryCount += mpDentryCount
}
if volDentryCount > c.cfg.volDeletionDentryThreshold {
return fmt.Errorf("vol %s is not empty ! it's dentry count %d > dentry count deletion threshold %d, deletion not permitted ! ",
vol.Name, volDentryCount, c.cfg.volDeletionDentryThreshold)
}
}
if proto.IsCold(vol.VolType) && vol.totalUsedSpace() > 0 && !force {
return fmt.Errorf("ec-vol can't be deleted if ec used size not equal 0, now(%d)", vol.totalUsedSpace())
}

View File

@ -49,6 +49,9 @@ const (
disableAutoCreate = "disableAutoCreate"
cfgMonitorPushAddr = "monitorPushAddr"
intervalToScanS3Expiration = "intervalToScanS3Expiration"
cfgVolForceDeletion = "volForceDeletion"
cfgVolDeletionDentryThreshold = "volDeletionDentryThreshold"
)
// default value
@ -137,6 +140,9 @@ type clusterConfig struct {
MonitorPushAddr string
IntervalToScanS3Expiration int64
MaxConcurrentLcNodes uint64
volForceDeletion bool // when delete a volume, ignore it's dentry count or not
volDeletionDentryThreshold uint64 // in case of volForceDeletion is set to false, define the dentry count threshold to allow volume deletion
}
func newClusterConfig() (cfg *clusterConfig) {

View File

@ -363,6 +363,14 @@ func (m *Server) checkConfig(cfg *config.Config) (err error) {
m.config.MonitorPushAddr = cfg.GetString(cfgMonitorPushAddr)
m.config.volForceDeletion = cfg.GetBoolWithDefault(cfgVolForceDeletion, true)
threshold := cfg.GetInt64WithDefault(cfgVolDeletionDentryThreshold, 0)
if threshold < 0 {
return fmt.Errorf("volDeletionDentryThreshold can't be less than 0 ! ")
}
m.config.volDeletionDentryThreshold = uint64(threshold)
return
}