Skip to content

Commit 554c59e

Browse files
authored
HDDS-15153. [Docs] Add quota implementation deep dive (#416)
1 parent d106301 commit 554c59e

2 files changed

Lines changed: 91 additions & 1 deletion

File tree

docs/07-system-internals/02-data-operations/03-delete.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ When **`deleteKey`** is processed:
6161

6262
- **File (FSO):** Row leaves **`fileTable`** (or **`keyTable`** in non-FSO layouts) and is recorded in **`deletedTable`**.
6363
- **Directory (FSO):** Row moves from **`directoryTable`** to **`deletedDirectoryTable`**.
64-
- **Quota:** Bucket **used bytes** and **namespace** usage are updated for the **logical** delete at this stage.
64+
- **Quota:** Bucket **used bytes** and **namespace** usage are updated for the **logical** delete at this stage; retained usage stays in quota totals until **`OMKeyPurgeRequest`** clears it ([quota internals](./quota)).
6565

6666
### Directory expansion (`DirectoryDeletingService`) — FSO
6767

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
sidebar_label: Quota
3+
---
4+
5+
# Implementation of Quota
6+
7+
This page describes how Ozone enforces quotas in the Ozone Manager (OM). It complements user-facing quota documentation with an implementation-focused view. Tracked as [HDDS-15153](https://issues.apache.org/jira/browse/HDDS-15153).
8+
9+
Ozone implements quota to manage physical data usage and the number of logical entities created by users. The implementation spans multiple layers, from volume-level restrictions to fine-grained bucket tracking and background garbage collection.
10+
11+
## Types of quota
12+
13+
Ozone tracks two dimensions of quota, stored on `OmVolumeArgs` and `OmBucketInfo`:
14+
15+
- **Quota in bytes (space quota):** Limits the physical storage consumed by data blocks in the cluster, including replication.
16+
- **Quota in namespace:** Limits how many logical objects exist: buckets under a volume, or keys and directories under a bucket.
17+
18+
When a quota is unset or cleared, it uses `OzoneConsts.QUOTA_RESET`, which behaves as unbounded.
19+
20+
## Volume-level quota enforcement
21+
22+
At the volume layer, quota acts as a structural constraint across child buckets. The OM does not continuously re-sum active key sizes across the subtree to evaluate volume limits; volume space quota is enforced in terms of **per-bucket space quotas** and their aggregate.
23+
24+
[`OMVolumeSetQuotaRequest`](https://github.com/apache/ozone/blob/master/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/volume/OMVolumeSetQuotaRequest.java) applies updates from a set-quota RPC. Bucket-level quotas that participate in volume accounting are reconciled through bucket metadata updates (including [`OMBucketSetPropertyRequest`](https://github.com/apache/ozone/blob/master/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketSetPropertyRequest.java)).
25+
26+
- **Namespace:** `checkQuotaNamespaceValid()` ensures the **current bucket count** in the volume does not exceed the new volume namespace quota.
27+
- **Space:** `checkQuotaBytesValid()` requires that **every non-link bucket** in the volume has an explicit space quota set. It then ensures the **sum** of those bucket quotas does not exceed the volume’s space quota.
28+
29+
## Bucket-level metrics and tracking
30+
31+
At the bucket layer, `OmBucketInfo` records usage the OM uses for enforcement and reporting. Important fields include:
32+
33+
| Field | Role |
34+
| --- | --- |
35+
| `usedBytes` | Active space for keys currently in the bucket namespace. |
36+
| `usedNamespace` | Count of active keys and directories (per bucket semantics). |
37+
| `snapshotUsedBytes` | Space still charged against quota while blocks are retained (for example pending physical delete or snapshot retention). Added as part of [HDDS-13756](https://issues.apache.org/jira/browse/HDDS-13756). |
38+
| `snapshotUsedNamespace` | Namespace similarly retained until purge completes. |
39+
40+
When OM checks remaining headroom for an operation, it uses **total** utilization by combining active and retained usage:
41+
42+
```java
43+
// OmBucketInfo (Ozone OM helpers)
44+
public long getTotalBucketSpace() {
45+
return usedBytes + snapshotUsedBytes;
46+
}
47+
48+
public long getTotalBucketNamespace() {
49+
return usedNamespace + snapshotUsedNamespace;
50+
}
51+
```
52+
53+
## Active quota validation (write path)
54+
55+
On the write path OM blocks operations that would exceed bucket limits before committing metadata visible to clients.
56+
57+
Implementations such as `OMKeyCreateRequest`, `OMFileCreateRequest`, and `OMAllocateBlockRequest` call **`checkBucketQuotaInBytes`** and **`checkBucketQuotaInNamespace`** from [`OMKeyRequest`](https://github.com/apache/ozone/blob/master/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyRequest.java):
58+
59+
- **Block allocation:** Before granting new blocks, `checkBucketQuotaInBytes()` checks whether `getTotalBucketSpace()` plus the allocation would exceed `quotaInBytes`. If so, OM fails with `OMException.ResultCodes.QUOTA_EXCEEDED`.
60+
- **Key commit:** After a successful commit (`OMKeyCommitRequest`), OM updates usage. Overwrites decrement `usedBytes` by prior block lengths before applying the newly committed sizes.
61+
62+
## Quota reclamation and deletion (delete path)
63+
64+
Reclaiming quota is asynchronous and coordinated with background deletion (and snapshots), so totals stay consistent while blocks still exist on Datanodes.
65+
66+
### Client-driven delete
67+
68+
When a client deletes a key (`OMKeyDeleteRequest`), OM removes the key from the live namespace but **does not immediately drop total charged space** from the bucket’s perspective. Usage is moved from the “active” counters into the snapshot or pending-delete side so `getTotalBucketSpace()` stays stable until purge:
69+
70+
```java
71+
// OMKeyDeleteRequest — illustrative; see Ozone source for full context
72+
omBucketInfo.decrUsedBytes(quotaReleased, true);
73+
omBucketInfo.decrUsedNamespace(1L, true);
74+
```
75+
76+
The `true` flag routes the released bytes and namespace into the retained (`snapshot*`) counters so quota remains consumed until storage is actually reclaimed.
77+
78+
### Background purging
79+
80+
Services such as **`KeyDeletingService`** and **`SnapshotDeletingService`** drive physical deletion and related OM cleanup. When blocks are purged and OM processes **`OMKeyPurgeRequest`**, retained usage is reduced and quota becomes available for new writes:
81+
82+
```java
83+
// OMKeyPurgeRequest — illustrative; see Ozone source for full context
84+
omBucketInfo.purgeSnapshotUsedBytes(bucketPurgeKeysSize.getPurgedBytes());
85+
omBucketInfo.purgeSnapshotUsedNamespace(bucketPurgeKeysSize.getPurgedNamespace());
86+
```
87+
88+
After this step, `getTotalBucketSpace()` and `getTotalBucketNamespace()` drop, reflecting freed quota.
89+
90+
For the full delete pipeline (tables, services, and SCM), see [Implementation of Delete Operations](./delete).

0 commit comments

Comments
 (0)