> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asktable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 运维与容量规划

> 了解 AskTable 默认数据库连接容量，并排查 DATABASE_BUSY。

<Check>
  默认配置适合常见的单机部署。没有反复出现 `DATABASE_BUSY`，也没有增加 Web 或 Worker 进程时，无需修改连接池。
</Check>

## 默认连接容量

| 项目                              |                        默认值 |
| ------------------------------- | -------------------------: |
| Web 进程                          |                        `1` |
| 每个 Web 进程的主库连接上限                | `20`（连接池 `10` + 临时连接 `10`） |
| Worker 进程                       |                        `1` |
| 每个 Worker 进程的主库连接上限             |   `10`（连接池 `5` + 临时连接 `5`） |
| 每个进程的 Workbook 连接上限             |                       `10` |
| 内置 PostgreSQL `max_connections` |                      `100` |

默认启用 Workbook 时，应用的保守连接峰值是 `51`：

```text theme={null}
Web 20 + Worker 10 + Workbook 20 + LISTEN/NOTIFY 1 = 51
```

PostgreSQL 上限 `100` 已包含足够余量。`POOL_SIZE` 只是连接池保留的连接数，不是连接上限；连接上限还要加上 `MAX_OVERFLOW`。

## 遇到 DATABASE\_BUSY

`DATABASE_BUSY` 表示 AskTable 暂时没有拿到数据库连接，或者 PostgreSQL 已达到连接上限。页面会自动重试，仍未恢复时可以手动重试。

如果错误反复出现：

1. 查看日志，确认来自 Web 请求还是后台刷新。
2. 后台刷新繁忙时，先降低 `AT_WORKER_MAX_JOBS`。
3. PostgreSQL 已报连接数耗尽时，不要继续增大连接池。
4. 只有确认 PostgreSQL 仍有余量、且本地连接池等待超时时，才调大对应的 Web 或 Worker 池。

## 增加进程时

每增加一个 Web 进程，会增加一套 Web 主库池、最多一套 Workbook 池和一个 `LISTEN/NOTIFY` 连接。每增加一个 Worker 进程，会增加一套 Worker 主库池和最多一套 Workbook 池。

调整顺序建议是：缩短事务、降低后台并发、调整对应角色的连接池，最后才提高 PostgreSQL 上限。始终为迁移和人工排障保留至少 10 个连接。

<AccordionGroup>
  <Accordion title="完整配置参考">
    所有配置都是静态环境变量，修改后需要重启对应进程。

    | 配置                          |         默认值 | 说明                         |
    | --------------------------- | ----------: | -------------------------- |
    | `AT_SERVER_WORKERS`         |         `1` | Web 进程数；命令行 `--workers` 优先 |
    | `AT_WEB_DB_POOL_SIZE`       |        `10` | 每个 Web 进程保留的主库连接数          |
    | `AT_WEB_DB_MAX_OVERFLOW`    |        `10` | Web 池满时可临时增加的连接数           |
    | `AT_WORKER_NUM_PROCS`       |         `1` | Worker 进程数                 |
    | `AT_WORKER_MAX_JOBS`        |        `10` | 每个 Worker 同时执行的最大任务数       |
    | `AT_WORKER_DB_POOL_SIZE`    |         `5` | 每个 Worker 进程保留的主库连接数       |
    | `AT_WORKER_DB_MAX_OVERFLOW` |         `5` | Worker 池满时可临时增加的连接数        |
    | `AT_DB_POOL_TIMEOUT`        |        `30` | 等待主库连接的最长秒数                |
    | `AT_DB_POOL_RECYCLE`        |       `300` | 主库连接回收秒数                   |
    | `WORKBOOK_PG_DSN`           | 本地主库中的独立数据库 | 非空时启用；显式设为空可关闭             |
    | `AT_WORKBOOK_POOL_MIN`      |         `1` | 每个进程保留的最少 Workbook 连接数     |
    | `AT_WORKBOOK_POOL_MAX`      |        `10` | 每个进程最多使用的 Workbook 连接数     |

    Web 和 Worker 的池容量分别配置；等待和回收时间由两类进程共用。所有池参数必须为非负数，且 Workbook 必须满足 `0 <= MIN <= MAX`。
  </Accordion>

  <Accordion title="精确计算连接上限">
    `B` 表示启用 Workbook 时的 `AT_WORKBOOK_POOL_MAX`，未启用时为 `0`。

    ```text theme={null}
    应用连接上限 =
      AT_SERVER_WORKERS
        × (AT_WEB_DB_POOL_SIZE + AT_WEB_DB_MAX_OVERFLOW + B + 1)
      + AT_WORKER_NUM_PROCS
        × (AT_WORKER_DB_POOL_SIZE + AT_WORKER_DB_MAX_OVERFLOW + B)
    ```

    应用连接上限加至少 10 个运维连接后，必须小于 PostgreSQL 可供普通用户使用的连接数。后者等于 `max_connections` 减去 `reserved_connections` 和 `superuser_reserved_connections`。
  </Accordion>

  <Accordion title="查看连接占用">
    查看 PostgreSQL 上限：

    ```sql theme={null}
    SHOW max_connections;
    SHOW reserved_connections;
    SHOW superuser_reserved_connections;
    ```

    查看连接来源和长事务：

    ```sql theme={null}
    SELECT application_name, state, count(*) AS connections,
           max(now() - xact_start) AS longest_transaction
    FROM pg_stat_activity
    WHERE datname IN ('asktable', 'asktable_workbook')
    GROUP BY application_name, state
    ORDER BY connections DESC;
    ```

    查看容器日志：

    ```bash theme={null}
    docker compose logs --since=30m asktable | grep -E 'DATABASE_BUSY|creating postgresql pool|refresh_table_task'
    docker compose logs --since=30m asktable-pg
    ```
  </Accordion>
</AccordionGroup>

<Note>
  使用外部 PostgreSQL 时，以数据库服务商提供的实际连接上限为准，AskTable 不会自动修改它。
</Note>
