# drive9 quota guide

Last verified: 2026-08-21.

This guide shows how to read and update Drive9 tenant quota from the CLI and
HTTP API in TiDBCloud Mode.

## What quota tracks

Drive9 exposes these user-settable quota settings:

| Field | Meaning |
| --- | --- |
| `max_storage_size` | Maximum confirmed plus reserved file storage size, in Mi. Stored in Drive9. |
| `max_file_size` | Maximum single file size, in Mi. Stored in Drive9. Must not exceed the server `DRIVE9_MAX_UPLOAD_BYTES` limit. |
| `max_file_count` | Maximum file count. Stored in Drive9. `0` means unlimited. |
| `max_media_llm_files` | Maximum confirmed image and audio files admitted to LLM extraction. `0` disables this limit. Updating it requires an enabled tenant-specific `image` extract config. |
| `max_video_llm_files` | Maximum video extraction calls recorded for the tenant. `0` disables this limit. Updating it requires an enabled tenant-specific `video` extract config. |
| `tidbcloud_spending_limit` | Dedicated `tidb_cloud_native` cluster monthly spending limit. Omitted for `tidb_cloud_native_shared`. |

Quota responses include these usage counters:

| Field | Meaning |
| --- | --- |
| `storage_bytes` | Confirmed file storage bytes. |
| `reserved_bytes` | Bytes reserved by active uploads. |
| `file_count` | Current file count used by quota admission. Active upload reservations for new files may be included until they complete or abort. |
| `media_file_count` | Current confirmed image and audio file count used by LLM extraction quota admission. |
| `video_file_count` | Current video extraction count used by LLM extraction quota admission. |

For dedicated `tidb_cloud_native` tenants, the quota API retains
`tidbcloud_spending_limit` compatibility. GET reads the last persisted value
from Drive9's Meta DB and never fetches the cluster quota from TiDB Cloud. SET
physically patches the TiDB Cloud cluster and persists the effective physical
target returned by the native provisioner. Dedicated create and pool-claim
paths also apply their requested or configured physical target. When a paid
pool claim does not provide an explicit target, Drive9 restores the
provisioner's default physical target and persists the effective target returned
by the native provisioner in the Meta DB. Free dedicated clusters use a
physical monthly target of zero.

For `tidb_cloud_native_shared`, the field is omitted from GET responses. Create
and SET accept the field for compatibility but ignore it, including an explicit
zero. Other quota fields in the same request are still applied. Shared physical
policy remains internal in `db_pool.spending_limit` and is not a per-tenant
quota.

## Permissions and supported modes

Tenant list/get and quota update require TiDB Cloud API keys. A Drive9 tenant
API key is not accepted for these admin operations.
Server-side default TiDB Cloud credentials configured for tenant provision or
deprovision are not used as a fallback by quota read or update. Callers must
supply TiDB Cloud credentials on each quota request; those credentials may be
the same keys as the server defaults when passed explicitly.

Only `tidb_cloud_native` and `tidb_cloud_native_shared` tenants support quota
update through this API. Other tenant providers use their configured defaults.

Tenant list/get validates that the supplied TiDB Cloud API key can access the
tenant's cluster. Drive9 caches successful API-key-to-cluster authorization in
process for up to one hour to avoid calling the TiDB Cloud OpenAPI on every
read. Empty managed-cluster list results are not retained, and successful tenant
create/delete paths invalidate the full-list cache for the submitted
credentials. The quota response itself is assembled from Drive9's local tenant,
quota config, and usage tables after authorization succeeds.

For a native tenant, quota update patches Drive9's TiDB Cloud cluster labels to
confirm that the API key has cluster write permission, physically patches a
requested spending limit, then writes the Drive9 quota fields. Shared tenants
authorize against their physical pool's TiDB Cloud organization and update only
tenant-local storage, file, and LLM quota fields.

Changing `max_media_llm_files` or `max_video_llm_files` requires an enabled
tenant-specific extract config for the corresponding media type. A missing row
means the process default is in use and cannot be paired with a tenant-specific
LLM limit. A row with `enabled=false` is also rejected.

## CLI

Use TiDB Cloud credentials to list TiDBCloud Mode tenants with quota:

```bash
drive9 admin tenant list \
  --region-code aws-ap-southeast-1 \
  --tidbcloud-public-key <tidbcloud-public-key> \
  --tidbcloud-private-key <tidbcloud-private-key>
```

Use tenant get when you already have the Drive9 tenant id:

```bash
drive9 admin tenant get \
  --region-code aws-ap-southeast-1 \
  --tenant-id tnt_abc123 \
  --tidbcloud-public-key <tidbcloud-public-key> \
  --tidbcloud-private-key <tidbcloud-private-key>
```

Example output:

```text
TENANT_ID   STATUS  KIND  MAX_STORAGE  MAX_FILE_SIZE  MAX_FILE_COUNT  SPENDING_LIMIT  STORAGE_USED  RESERVED  FILE_COUNT
tnt_abc123  active  live  102400 Mi    1024 Mi        100000          10000           1.0 MiB       0 B       12
```

Set quota with `drive9 admin tenant set-quota`. Only TiDBCloud Mode supports
quota set. Pass at least one of `--max-storage-size`, `--max-file-size`,
`--max-file-count`, or `--tidbcloud-spending-limit`. The spending-limit flag
changes physical quota only for dedicated `tidb_cloud_native` tenants; shared
tenants accept and ignore it. Use the HTTP API for the tenant-specific LLM
limits.

```bash
drive9 admin tenant set-quota \
  --region-code aws-ap-southeast-1 \
  --tenant-id tnt_abc123 \
  --tidbcloud-public-key <tidbcloud-public-key> \
  --tidbcloud-private-key <tidbcloud-private-key> \
  --max-storage-size 102400 \
  --max-file-size 1024 \
  --max-file-count 100000 \
  --tidbcloud-spending-limit 12000
```

Use `--server` instead of `--region-code` when targeting a known Drive9 server
URL directly. If both are present, `--server` wins.

Validation rules:

- `--max-storage-size` must be positive.
- `--max-file-size` must be positive and no larger than the server
  `DRIVE9_MAX_UPLOAD_BYTES` limit.
- `--max-file-count` must be non-negative. `0` means unlimited.
- For dedicated tenants, `--tidbcloud-spending-limit` must be `0` or at least
  10 RMB, within the TiDB Cloud API wire limit. Shared tenants ignore it.

## HTTP API

Tenant get returns tenant information with quota:

```json
{
  "tenant_id": "tnt_abc123",
  "status": "active",
  "kind": "live",
  "quota": {
    "config": {
      "max_storage_size": 102400,
      "max_file_size": 1024,
      "max_file_count": 100000,
      "max_media_llm_files": 1000,
      "max_video_llm_files": 50,
      "tidbcloud_spending_limit": 10000
    },
    "usage": {
      "storage_bytes": 1048576,
      "reserved_bytes": 0,
      "file_count": 12,
      "media_file_count": 4,
      "video_file_count": 1
    }
  }
}
```

### GET /v1/admin/tenants

List authorized TiDBCloud Mode tenants. The response includes quota and usage
for each tenant.

```bash
curl -sS \
  -H "X-TiDBCloud-Public-Key: <tidbcloud-public-key>" \
  -H "X-TiDBCloud-Private-Key: <tidbcloud-private-key>" \
  "https://drive9.example.com/v1/admin/tenants?page=1&page_size=10"
```

### GET /v1/admin/tenants/{tenant-id}

Get a single authorized TiDBCloud Mode tenant. The response includes quota.

```bash
curl -sS \
  -H "X-TiDBCloud-Public-Key: <tidbcloud-public-key>" \
  -H "X-TiDBCloud-Private-Key: <tidbcloud-private-key>" \
  "https://drive9.example.com/v1/admin/tenants/tnt_abc123"
```

### POST /v1/admin/tenants/{tenant-id}/quota

Set quota for a TiDBCloud Mode tenant using TiDB Cloud credentials.

```bash
curl -sS \
  -H "Content-Type: application/json" \
  -X POST https://drive9.example.com/v1/admin/tenants/tnt_abc123/quota \
  -d '{
    "public_key": "<tidbcloud-public-key>",
    "private_key": "<tidbcloud-private-key>",
    "max_storage_size": 102400,
    "max_file_size": 1024,
    "max_file_count": 100000,
    "max_media_llm_files": 2000,
    "max_video_llm_files": 100,
    "tidbcloud_spending_limit": 12000
  }'
```

At least one quota field is required. `max_storage_size` and `max_file_size` are
in Mi; `max_file_size` must not exceed the server `DRIVE9_MAX_UPLOAD_BYTES`
limit. File-count and LLM limits must be non-negative, and `0` means unlimited.
The LLM fields require enabled tenant-specific extract configs as described
above. For dedicated `tidb_cloud_native` tenants,
`tidbcloud_spending_limit` must be non-negative, either `0` or at least 10 RMB,
and no larger than the TiDB Cloud API wire limit. Shared tenants accept and
ignore it.

## Error responses

The quota API returns JSON errors through the standard server error shape.

| Status | When it happens |
| --- | --- |
| `400 Bad Request` | Invalid JSON, missing tenant id where required, missing or partial TiDB Cloud credentials, missing all settable quota fields in a set request, or an invalid quota value. |
| `403 Forbidden` | TiDB Cloud returns unauthorized or forbidden for the supplied API key. |
| `404 Not Found` | The Drive9 tenant does not exist, quota is not enabled on this server, or TiDB Cloud cannot find the backend cluster. For the backend-cluster case, check the TiDB Cloud starter/native cluster status. |
| `409 Conflict` | The tenant provider does not support quota mutation, the tenant is provisioning or deleting, or an LLM limit lacks an enabled tenant-specific extract config. |
| `502 Bad Gateway` | TiDB Cloud returned another upstream error while authorizing the request or updating native-tenant quota labels or spending limit. |

## Notes for operators

- Server-side quota admission reads high-churn usage counters from the central
  meta store. Low-churn quota config is cached per tenant and refreshed by
  version polling.
- In cloud-native mode, small write quota mutations are recorded in the central
  meta DB `quota_mutation_log` and applied asynchronously to central quota
  state.
- Small-write storage and file-count quota admission include this process's
  pending central mutation deltas to reduce stale-central-usage undercounting,
  but concurrent writes on multiple servers may briefly over-admit. Durable
  mutation replay restores convergence for logged mutations; rare post-commit
  gaps require operational reconciliation from tenant file metadata.
- Multipart uploads use a stricter reserve-first path before writing tenant
  upload state.
- TiDB Cloud quota reads expose low-cardinality observability counters:
  `drive9_tidbcloud_rbac_cache_requests_total`,
  and `drive9_tidbcloud_openapi_requests_total`. These metrics intentionally do
  not include `tenant_id`. A quota GET may authorize against TiDB Cloud, but it
  does not fetch the cluster spending limit; the response value is read from
  the Meta DB.
- `media_file_count` and `video_file_count` remain available from the quota API,
  but Drive9 does not publish them or their limits as Prometheus quota gauges.
  Alert only on sustained TiDB Cloud OpenAPI error rate or an unexpected
  OpenAPI call-rate baseline increase.
