Skip to content

Commit d6daabf

Browse files
committed
Correct the presence write interval recommendation, which broke the active-user count
Active presence counts users whose timestamp falls in the last 180 seconds, a window fixed in the code. Four pages recommended 300 to 500 seconds, and the reference claimed that stayed inside the window. It does not: at those values a user's timestamp ages past the window before the next write is permitted, so they leave the count and return when the write lands. Someone watching the active-user metric sees a sawtooth rather than a steady figure, which is what was reported in #1358. The recommendation is now 60 to 120 seconds, with the reason stated so nobody raises it again, and every page says the value must stay below 180. The advice to set a value at all is unchanged, since on current releases the write is unthrottled until an interval is configured.
1 parent 3e56375 commit d6daabf

4 files changed

Lines changed: 4 additions & 4 deletions

File tree

docs/getting-started/advanced-topics/scaling.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ ENABLE_ORJSON=True
472472

473473
:::warning Two settings people forget, and then their scaled deployment stalls
474474
- **`THREAD_POOL_SIZE=2000`**: Open WebUI offloads blocking work (DB calls, file I/O, sync handlers) to a thread pool whose default concurrency ceiling is only **40**. At scale, once 40 blocking operations are in flight every further request **queues**, and the whole app appears to freeze even though CPU/RAM look fine. `2000` is a *lower* bound for large instances; it is a concurrency ceiling, **not** a CPU/thread count, so a high value is not a contention risk. Never lower it. (The only exception is genuinely tiny hardware, which is not a "scaled deployment".)
475-
- **`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL=300`**: presence tracking writes each user's `last_active_at` to the database. **Unset (the default) means this write is unthrottled, roughly one `UPDATE` + `COMMIT` per authenticated request.** At scale that is a continuous flood of tiny write transactions that saturates the connection pool for no functional gain. Set it to `300` to `500` seconds; it is mandatory for large/production deployments and free performance everywhere else.
475+
- **`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL=300`**: presence tracking writes each user's `last_active_at` to the database. **Unset (the default) means this write is unthrottled, roughly one `UPDATE` + `COMMIT` per authenticated request.** At scale that is a continuous flood of tiny write transactions that saturates the connection pool for no functional gain. Set it to `60` to `120` seconds. Keep it below `180`, the width of the active-presence window, or users age out of the active count between writes and the figure oscillates. It is mandatory for large/production deployments and free performance everywhere else.
476476

477477
Both are read once at startup and are not configurable from the Admin UI. See [Performance → Database Optimization](/troubleshooting/performance#-database-optimization) and [Performance → High-Concurrency](/troubleshooting/performance#-high-concurrency--network-optimization).
478478
:::

docs/reference/env-configuration.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7458,7 +7458,7 @@ When `DATABASE_URL` is not explicitly set, Open WebUI will attempt to construct
74587458
74597459
Setting a positive interval collapses thousands of these writes into at most one per user per interval. This is **free performance for any setup** and is **required** for large/production deployments; leaving it unset is a common, avoidable database bottleneck.
74607460
7461-
- **Recommended:** a few hundred seconds, e.g. `300` to `500`. Presence accuracy stays within the interval, which is well inside the 3-minute active window.
7461+
- **Recommended:** `60` to `120` seconds. **Keep it below `180`**: active presence counts users whose timestamp falls in the last 180 seconds, so an interval at or above that lets a user age out of the count before the next write is allowed. They then drop out and reappear, which shows up as a sawtooth in the active-user figure and the metric that reports it rather than a steady number.
74627462
- Applies regardless of database backend, and applies on weak hardware too (it only *reduces* writes, there is no downside to setting it on a Raspberry Pi).
74637463
- Read once at startup; not a `ConfigVar` and not changeable from the Admin UI.
74647464
:::

docs/troubleshooting/multi-replica.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Before troubleshooting specific errors, ensure your deployment meets these **abs
2020
5. **External Vector Database (Required):** The default ChromaDB uses a local SQLite-backed `PersistentClient` that is **not safe for multi-worker or multi-replica deployments**. SQLite connections are not fork-safe, and concurrent writes from multiple processes will crash workers instantly. You **must** use a dedicated external Vector DB (e.g., [PGVector](/reference/env-configuration#pgvector_db_url), [MariaDB Vector](/reference/env-configuration#mariadb_vector_db_url), Milvus, Qdrant) via [`VECTOR_DB`](/reference/env-configuration#vector_db), or run ChromaDB as a [separate HTTP server](/reference/env-configuration#chroma_http_host).
2121
6. **Database Session Sharing (Optional):** For PostgreSQL deployments with adequate resources, consider enabling [`DATABASE_ENABLE_SESSION_SHARING=True`](/reference/env-configuration#database_enable_session_sharing) to improve performance under high concurrency.
2222
7. **Thread Pool Ceiling:** Set [`THREAD_POOL_SIZE=2000`](/reference/env-configuration#thread_pool_size) (or higher). The default concurrency ceiling for blocking operations is only **40**; at multi-user scale this is exhausted quickly and the app appears to **freeze** while CPU/RAM look fine. It is a ceiling, not a thread/CPU count: a high value is not a contention risk. Never lower it.
23-
8. **Throttle Presence Writes:** Set [`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL=300`](/reference/env-configuration#database_user_active_status_update_interval) (300 to 500s). Unset (the default) means each user's `last_active_at` is written **on essentially every request**: a continuous flood of tiny `UPDATE`/`COMMIT` transactions that saturates the connection pool at scale for no functional benefit.
23+
8. **Throttle Presence Writes:** Set [`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL=60`](/reference/env-configuration#database_user_active_status_update_interval) (60 to 120s, and below 180 so users do not age out of the active count between writes). Unset (the default) means each user's `last_active_at` is written **on essentially every request**: a continuous flood of tiny `UPDATE`/`COMMIT` transactions that saturates the connection pool at scale for no functional benefit.
2424

2525
---
2626

docs/troubleshooting/performance.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Open WebUI tracks online/"active" presence by writing each user's `last_active_a
9292

9393
- **Env Var**: `DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL=300`
9494
- **Default**: unset (**unthrottled, writes on every request**)
95-
- **Recommendation**: Set a positive interval in seconds. `300` to `500` is a good range. This collapses thousands of writes into at most one per user per interval. It is **free performance for any setup** and is effectively **mandatory for large/production deployments**; leaving it unset is a common, avoidable database bottleneck. There is no downside on weak hardware either: it only *reduces* writes. See [`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL`](/reference/env-configuration#database_user_active_status_update_interval).
95+
- **Recommendation**: Set a positive interval in seconds. `60` to `120` is a good range, and it must stay below `180`, the width of the active-presence window, or users age out of the count between writes and the active-user figure oscillates instead of holding steady. This collapses thousands of writes into at most one per user per interval. It is **free performance for any setup** and is effectively **mandatory for large/production deployments**; leaving it unset is a common, avoidable database bottleneck. There is no downside on weak hardware either: it only *reduces* writes. See [`DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL`](/reference/env-configuration#database_user_active_status_update_interval).
9696

9797
### Database Session Sharing
9898

0 commit comments

Comments
 (0)