# 基于目录的集群配额

## Directory-Based Cluster Quota Overview

Alluxio 允许管理员对目录设置配额，以限制该目录中的文件在 Alluxio 集群所有worker上使用的总缓存空间 。

<figure><img src="https://2035737952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvkkQB4Z2EZgJPE9ShfBC%2Fuploads%2Fgit-blob-4b38430bb03183d62648a2cf0b4acc0aa74aa4e5%2Fdirectory-based-quota-architecture.png?alt=media" alt=""><figcaption></figcaption></figure>

ETCD 仅存储配额定义。当前的配额使用情况不会存储在 ETCD 中，也不会在 ETCD 中更新。

Worker 通过查询 ETCD 来同步最新的配额定义。 Worker 根据配额定义来追踪目录中文件的当前缓存使用情况。

Coordinator 负责定期向 worker 轮询最新的配额使用情况，并通过汇总每个 worker 的使用情况形成集群范围的配额使用视图。 如果所有 worker 的总使用量超出了某个配额定义的限制，Coordinator 会检测到，并向所有 worker 发送缓存控制命令，来处理配额超限的情况。 有关缓存控制命令的更多详细信息，请参见 [此处](#配额超限)。

请确保在启用基于目录的集群配额功能时，coordinator 处于运行中。 如果 coordinator 未运行，添加/删除/更新/列出配额的命令将会失败。 Worker 将继续运行，但配额定义不会被强制执行。因此，某些配额可能会被无限地超额使用。

要启用基于目录的集群配额功能，请在 `conf/alluxio-site.properties` 文件中设置以下属性：

```properties
# 启用所有组件的基于目录的集群配额功能 
alluxio.quota.enabled=true

# 配置 Coordinator 地址
alluxio.coordinator.address=<host>:<port>
```

## 基础操作

### 添加配额定义

你可以通过指定目录路径和配额大小在 Alluxio 中添加配额定义。 请注意，路径是一个不带协议（scheme)的 Alluxio 路径，而非 UFS 路径（如`s3://bucket`）。

当添加配额定义时，请确保目录在 Alluxio 中是可解析的。 也就是说，目标目录必须位于现有的 Alluxio 挂载点下，并且该目录必须存在于 UFS 中。 在下面的示例中，Alluxio 命名空间中有两个已有挂载点，只能对 `/s3` 或 `/local`下的目录添加配额定义。

```console
# Alluxio 命名空间中已有两个已有挂载点
$ bin/alluxio mount list
Listing all mount points
s3a://alluxio-jliu/                           on  /s3/    properties={...}
file:///Documents/Alluxio/underFSStorage/     on  /local/ properties={...}

# 在不存在的目录上设置配额将会失败
$ bin/alluxio quota add --directory /another --quota-size 10GB
The specified quota path /another is not under any mount point! Existing mount points are:
[/s3/, /local/].
```

可以对现有挂载点下的任何目录设置配额定义。例如：

```console
# /s3/ 是 Alluxio 命名空间中的已有挂载点
# 你可以对 /s3/ 下的目录设置配额定义
$ bin/alluxio quota add --directory /s3/data/ --quota-size 10GB
Successfully added quota definition for path /s3/data/ with size 10GB.

$ bin/alluxio quota list
Alluxio path: /local, Quota capacity bytes: 11534336, Used bytes: 10486645, State: Available
Alluxio path: /s3/data, Quota capacity bytes: 10737418240, Used bytes: Calculating, State: Available
```

嵌套配额定义尚不支持。如果添加配额定义会导致嵌套配额定义，则无法添加配额定义。例如：

```console
$ bin/alluxio quota add --directory /local/data/ --quota-size 10GB
Failed to add quota definition: UNKNOWN: New quota /local/data already has a parent quota /local. Nested quota definitions are not supported in this version.
```

### 删除配额定义

你可以通过指定目录路径来删除 Alluxio 中某个目录的配额定义

```console
$ bin/alluxio quota remove --directory /s3/data
Successfully removed quota definition for path /s3/data.
```

### 更新配额定义

更新配额定义使用的参数与添加配额定义时相同。

```console
$ bin/alluxio quota update --directory /local/data/ --quota-size 100GB
```

但是，请注意，如果将配额大小更新为小于当前使用量的值，则更新会失败。 在这种情况下，我们建议在减少配额大小之前释放一些空间。

```console
$ bin/alluxio quota update --directory /local --quota-size 1MB
Loading the latest quota definitions from ETCD
Latest quota definitions loaded: {
/local=alluxioPath: "/local/"
quota: 11534336
ufsPath: "file:///Documents/Alluxio/underFSStorage/"
}
Reducing quota size for path /local from 11534336 to 1048576.
Failed to update quota: RESOURCE_EXHAUSTED: The target quota size 1048576 is smaller than the current usage 10486645. Please free up some space before reducing the quota size. Or use the --force option.
```

在这种情况下，也可使用 `--force` 选项来强制更新。但是，该做法并不推荐。 如果强制将配额容量更新为小于当前使用量的值，coordinator 将会检测到配额超限。 然后，它将触发所有 worker 上的驱逐操作，来驱逐该配额下的一些缓存。 如果这些请求试图在该目录下创建新的缓存, 根据配置，它还可能要求 worker 停止缓存或拒绝 I/O 请求。

```console
$ bin/alluxio quota update --directory /local --quota-size 1MB --force
```

### 列出当前的配额定义和使用情况

你可以通过运行以下命令来列出 Alluxio 中所有现有的配额定义及其使用情况。 请注意，如果刚刚添加了新的配额定义，worker 可能需要一些时间来扫描其现有缓存并计算该目录下的当前配额使用情况。 在此期间，使用情况将被标记为`Calculating`(正在计算）。

```console
$ bin/alluxio quota list
Alluxio path: /local, Quota capacity bytes: 11534336, Used bytes: 10486645, State: Available
Alluxio path: /s3/data, Quota capacity bytes: 10737418240, Used bytes: Calculating, State: Available
```

Alluxio 还提供可持续向 coordinator 轮询最新配额使用情况的命令：

```console
# 持续运行，直到用户按下 Ctrl+C 中断运行为止
$ bin/alluxio quota list --interval 5s
Polling quota usage summary from the master every 5s
Alluxio path: /local, Quota capacity bytes: 11534336, Used bytes: 10486645, State: Available
Alluxio path: /s3/data, Quota capacity bytes: 10737418240, Used bytes: Calculating, State: Available
Alluxio path: /local, Quota capacity bytes: 11534336, Used bytes: 10486645, State: Available
Alluxio path: /s3/data, Quota capacity bytes: 10737418240, Used bytes: Calculating, State: Available
Alluxio path: /local, Quota capacity bytes: 11534336, Used bytes: 10486645, State: Available
Alluxio path: /s3/data, Quota capacity bytes: 10737418240, Used bytes: 10486645, State: Available
```

## 进阶配置

### 配额超限

在当前的配额使用量超过配额定义时，coordinator 将指示集群中的所有 worker 驱逐该配额下的一些缓存，从而让总使用量再次低于配额限制。例如， 如果在 Alluxio 路径 `/s3/` 上已有一个 10GB 配额定义，而集群中有 2 个 worker：

There are 2 workers in the cluster.

* Worker A 当前在 `/s3/` 下持有 12GB 缓存。
* Worker B 当前在 `/s3/` 下持有 4GB 缓存。

Coordinator 将汇总两个 worker 的缓存总使用量，观察到当前总量为 16GB，超过了 10GB 的限制。 Coordinator 将向worker A 和 B 发送命令，要求它们各自驱逐占比 `(16 - 10) / 16 = 0.375` 的当前缓存。 如果驱逐成功，两台 worker 的缓存使用量将在短时间内变为：

* Worker A 目前在 `/s3/`下持有 7.5GB 缓存。
* Worker B 目前在 `/s3/`下持有 2.5GB 缓存。

可以观察到如前所述，当配额超限时，coordinator 会要求每个 worker 驱逐相同比例的缓存。这是目前支持的唯一驱逐策略。

除了驱逐之外，`alluxio.quota.limit.exceeded.action` 控制配额超限时的行为。 There are 3 supported modes:

1. `NO_CACHE` (默认): 当配额超限时，所有 worker 都会避免在该路径下添加新的缓存。 如果读取请求发生缓存未命中，worker 将通过读取 UFS 来满足请求，但不会进行缓存。 该模式阻止新缓存被添加到集群，同时在无报错的情况下完成请求。
2. `REJECT`: 当配额超限时，所有 worker 都会拒绝在该路径下的新缓存请求。 如果请求要在该路径下创建新缓存，worker将返回异常。 此模式使得 Alluxio 集群的缓存行为更类似于磁盘，即磁盘已满时会拒绝写入并报错。
3. `NOOP`: 当配额超限时，仍允许在该路径下创建新的缓存。 在这种模式下，新缓存将继续添加到worker 中，而 coordinator 会持续指示 worker 驱逐缓存以恢复配额空间。 请注意，如果新缓存的写入速度快于驱逐速度，则空间使用量可能永远无法恢复到设置的配额限制之内。

### 配额 coordinator 心跳间隔

`alluxio.quota.worker.heartbeat.interval.ms` 控制 coordinator 从集群 worker 汇总当前配额使用情况的频率。 由于coordinator 定期向 worker 轮询配额使用情况，因此它观察到的配额使用情况（也反映在 `bin/alluxio quota list` 命令中）会有一定的延迟。

```properties
# 默认值是1秒
alluxio.quota.worker.heartbeat.interval.ms=1s
```

## 加载作业中的配额

[Load 命令](https://documentation.alluxio.io/ee-ai-cn/ai-3.3/feature/cache-preloading) 会将目录或索引文件中指定的大量文件加载到 Alluxio 集群缓存中。 加载操作有可能超过配额并导致不希望出现的缓存驱逐。为避免这种情况，可以启用check（检查），从而在加载操作超出配额时拒绝该作业。

该检查将计算并比较以下三项信息：

1. UFS 中文件的总大小
2. 这些文件中已经在 Alluxio worker 中缓存了多少
3. 当前配额定义的可用容量

例如，如果索引文件指定了 100 个文件，总大小为 100GB，其中 50GB 已经在 worker 上缓存，而配额当前仅剩 10GB 可用，则 Alluxio 应该拒绝这个加载作业。 管理员应先手动释放至少 40GB 的配额空间，然后再尝试提交加载作业。

### 限制和配置

#### 启用加载作业中的配额检查步骤

加载作业中的配额检查步骤是默认禁用的，以下限制不会被强制执行。 要启用配额检查步骤，请设置以下属性：

```properties
# 默认false
alluxio.dora.load.job.must.check.quota=true
```

在Load指令上添加 `--skip-quota-check` 将覆盖由配置属性启用的配额检查

```console
# 跳过配额检查，直接加载作业
 
$ ./bin/alluxio job load --path s3://alluxio-test/data/ --submit --skip-quota-check
```

> 配额检查必须先启用才能强制执行以下的任何限制。如果配额检查被配置或命令行标志禁用，则不会进行任何检查。

#### 避免从多个有配额定义的路径下加载文件

如果加载作业涉及来自多个配额定义的文件，配额检查将拒绝该加载作业。 这适用于加载目录或加载索引文件中指定的文件列表。 如果启用了配额检查，将始终执行该限制。

#### 强制对所有加载作业应用配额定义

对配额检查进行配置后可在加载非配额定义下的路径时拒绝加载作业。此限制可用于确保所有加载作业都受到配额控制。 可以通过设置以下选项来选择性地关闭配额检查：

```properties
# 默认true
alluxio.dora.load.job.without.quota.allowed=false
```

#### 避免在一个配额定义下并发加载作业

由于配额检查仅发生加载作业开始时，如果在同一个配额下同时运行多个加载作业，可能会导致配额使用超限。 为避免这种情况，可以通过配置配额检查，只允许在设置了配额的路径中一次运行一个加载作业。 要选择开启这一限制，请设置以下属性：

```properties
# 默认 false
alluxio.dora.load.job.quota.mutual.exclusive=true
```
