Performance Limits & Capacity Planning¶
This document outlines the practical performance limits of MetaVox when managing metadata across Nextcloud Team Folders. It covers files per view, column limits, concurrent user capacity, and hardware scaling considerations. Where applicable, limits are compared to SharePoint Online as a reference point.
Summary¶
| Dimension | MetaVox Recommended Limit | MetaVox Hard Limit | SharePoint Online |
|---|---|---|---|
| Files per view | 5,000 | None (gradual degradation) | 5,000 (blocked above) |
| Fields per Team Folder | Unlimited | Unlimited (EAV model) | Limited by 8,000-byte row size |
| Active filters per view | 10 | ~15 before noticeable slowdown | 12 join operations (blocked above) |
| Displayed columns | 10–20 | No hard limit | Limited by row size |
| Concurrent users (small folder) | 10–100 (hardware dependent) | N/A | N/A |
| Concurrent users (large folder) | 2–20 (hardware dependent) | N/A | N/A |
Key takeaway: SharePoint enforces hard thresholds — queries above 5,000 items are refused. MetaVox has no hard limits; performance degrades gradually. The primary bottleneck above 5,000 files is the Nextcloud browser-side file list, not MetaVox itself. Use filtered Views to keep per-view file counts under 5,000 for the best experience.
SharePoint Online Reference Limits¶
SharePoint Online enforces the following hard limits (Microsoft documentation):
| Constraint | Limit | Behavior when exceeded |
|---|---|---|
| List View Threshold | 5,000 items per view | Query is blocked |
| Admin override threshold | 20,000 items | Query is blocked |
| Max items per list/library | 30 million | Cannot add more items |
| Row size limit | 8,000 bytes per item | Cannot add more columns |
| Lookup join threshold | 12 join operations | Query is blocked (8+ lookup columns) |
| Unique permissions per list | 50,000 (recommended: 5,000) | Performance degradation |
| Max file size | 250 GB | Upload rejected |
The List View Threshold is SharePoint's most impactful limit: any view, sort, or filter operation that touches more than 5,000 non-indexed items is blocked entirely — not slowed down, but refused by the server.
MetaVox: Files Per View¶
MetaVox does not enforce a hard item-per-view threshold. Instead, performance degrades gradually as folder size increases. There are four independent bottleneck tiers:
Bottleneck 1: HTTP Batch Loading¶
MetaVox loads file metadata in bulk at folder open. All file IDs are extracted from Nextcloud's dirContents Vue property and fetched in chunks of 200 per API request (MetaVoxColumns.js). Since dirContents may populate asynchronously, MetaVox polls for new entries for up to 10 seconds and fetches any uncached files in additional batches.
| Files in folder | API calls at open | Estimated load time | Scroll behavior |
|---|---|---|---|
| 200 | 1 | ~300ms | Instant (cached) |
| 1,000 | 5 | ~1.5s | Instant (cached) |
| 5,000 | 25 | ~8s | Instant (cached) |
| 10,000 | 50 | ~15s | Instant (cached) |
Note: All metadata is loaded in the background after folder open. Once the initial bulk load completes, scrolling through the file list does not trigger additional API calls — all data is served from the in-memory cache. The MutationObserver detects virtual scroll row recycling (via
data-cy-files-list-row-fileidattribute changes) and fills cells from the cache instantly.
Bottleneck 2: Nextcloud File List¶
Nextcloud's Files app loads all files in a folder into a client-side dirContents array. This is the primary limiting factor and is not MetaVox-specific:
| Files in folder | Nextcloud behavior |
|---|---|
| < 1,000 | Instant load |
| 1,000–5,000 | 1–2 second load |
| 5,000–10,000 | Noticeable delay, UI becomes sluggish |
| 10,000–50,000 | Multi-second load, browser memory pressure |
| 50,000+ | Browser may hang or crash |
Bottleneck 3: SQL Filtering & Sorting¶
MetaVox uses an Entity-Attribute-Value (EAV) model with self-JOINs for filtering (FilterService.php). With proper database indexes (added in migration Version20250101000010), query performance scales logarithmically:
| Metadata records | 1 filter | 3 filters | 5 filters |
|---|---|---|---|
| 10,000 | < 10ms | < 20ms | < 50ms |
| 100,000 | < 50ms | < 100ms | < 200ms |
| 1,000,000 | ~200ms | ~400ms | ~800ms |
| 10,000,000 | ~1s | ~2s | ~4s |
Important: These numbers assume the database indexes from
Version20250101000010are active. Without indexes, performance drops by 40–100x.
Bottleneck 4: DOM Rendering¶
Nextcloud 33+ uses virtual scrolling for the file list. MetaVox uses a MutationObserver that listens for both new rows (childList) and row recycling (attributes on data-cy-files-list-row-fileid). When a row is recycled during scrolling, the metadata cells are instantly updated from the in-memory cache without any API call. Only ~15–50 rows exist in the DOM at any time. This is not a bottleneck regardless of folder size.
Combined Practical Limits¶
| Files per view | MetaVox status | SharePoint status |
|---|---|---|
| < 1,000 | Excellent | OK |
| 1,000–5,000 | Good | OK (approaching threshold) |
| 5,000–10,000 | Usable with delays | Blocked (above threshold) |
| 10,000–50,000 | Slow, not recommended | Blocked |
| 50,000+ | Unworkable | Blocked |
Key difference: SharePoint blocks queries above 5,000 items. MetaVox degrades gradually — performance gets worse, but operations are never refused.
Column & Field Limits¶
MetaVox Field Architecture¶
MetaVox uses an EAV (Entity-Attribute-Value) database model. Metadata fields are stored as rows, not as database columns. This means:
- No hardcoded maximum on the number of fields per Team Folder
- Adding fields does not alter the database schema
- Each field value is stored as a TEXT type (no fixed byte limit per value)
| Constraint | MetaVox | SharePoint |
|---|---|---|
| Max fields/columns per list | Unlimited (EAV model) | Limited by 8,000-byte row size |
| Field name length | 255 characters | 255 characters |
| Field value size | TEXT (unbounded) | Varies by column type |
| Lookup/join threshold | No hard limit | 12 join operations |
| Field types | 10 (text, number, textarea, date, select, multiselect, checkbox, url, user, filelink) | 30+ |
Column Display¶
The frontend renders metadata columns via DOM injection with horizontal scrolling. There is no hardcoded column display limit:
- Minimum column width: 60px
- Column widths are calculated dynamically based on field label length and type
- Horizontal scrolling with sticky headers is supported
- Column widths are persisted per user
Practical Column Limits¶
While there is no hard limit, performance considerations apply:
| Active columns | Effect |
|---|---|
| 1–10 | No impact |
| 10–20 | Wider horizontal scroll area, still performant |
| 20–30 | API response size increases (~500 bytes per file per column) |
| 30+ | Consider using Views to show subsets of columns |
Filter Performance vs. Column Count¶
Each active filter in a view adds one SQL JOIN operation to the query. This is the primary scaling factor for column count:
| Active filters | SQL JOINs | Performance impact |
|---|---|---|
| 1–5 | 2–6 | Negligible with indexes |
| 5–10 | 6–11 | Minimal, < 200ms at 100K records |
| 10–15 | 11–16 | Noticeable, queries may exceed 500ms |
| 15+ | 16+ | Not recommended, consider restructuring |
Comparison: SharePoint blocks queries when more than 12 join operations are required (triggered by 8+ lookup/person/workflow columns). MetaVox does not block but slows down beyond ~15 active filters.
Concurrent Users¶
Per-User Server Load¶
MetaVox injects initialization data (groupfolders, fields, views) inline into the HTML via Nextcloud's IInitialState mechanism. This eliminates separate API calls for the initial page load. When navigating to a different folder within the same session, a single /api/init call retrieves all needed data.
Each user opening a folder with metadata columns generates:
| Operation | API calls | Database queries |
|---|---|---|
| Folder open (first load) | 0 (inline via IInitialState) | Executed during PHP page render |
| Folder open (navigation) | 1 (/api/init) |
~4 (groupfolders, fields, views, permissions) |
| Metadata bulk load (all files) | 1–3 (chunks of 200) | 1 per chunk |
| Filter values (lazy, on dropdown open) | 0–1 | 0–1 (cached 300s server-side) |
| Total per folder open | 1–4 | ~5–8 |
Data Consistency & Real-Time Sync¶
When notify_push is installed and configured, MetaVox supports real-time collaboration:
- Cell locking: When a user starts editing a cell, it is locked for other users (30-second TTL via Redis). Other users see a lock indicator with the editor's name.
- Real-time push: Metadata changes are pushed to all users viewing the same groupfolder via WebSocket (notify_push). No polling required.
- Presence tracking: MetaVox tracks which users are actively viewing a groupfolder. Push events are only sent to active viewers, reducing unnecessary traffic.
- Graceful degradation: Without notify_push, MetaVox falls back to last-write-wins with eventual consistency via cache expiry.
See Real-Time Sync for full details.
Concurrent User Capacity¶
Capacity depends heavily on server configuration and folder size:
Small folders (< 500 files)¶
| Server configuration | Concurrent users | Status |
|---|---|---|
| Single core, 8 GB RAM | 10–15 | Good |
| 4 cores, 16 GB RAM + Redis | 40–60 | Good |
| 4 cores + separate database | 80–100 | Good |
Medium folders (500–5,000 files)¶
| Server configuration | Concurrent users | Status |
|---|---|---|
| Single core, 8 GB RAM | 3–5 | Slow |
| 4 cores, 16 GB RAM + Redis | 15–25 | Acceptable |
| 4 cores + separate database | 30–50 | Good |
Large folders (5,000+ files)¶
| Server configuration | Concurrent users | Status |
|---|---|---|
| Single core, 8 GB RAM | 1–2 | Very slow |
| 4 cores, 16 GB RAM + Redis | 5–10 | Slow |
| 4 cores + separate database | 10–20 | Acceptable |
Note: PHP processes requests single-threaded. On a single-core server, requests are queued sequentially. Multiple cores allow PHP-FPM to handle requests in parallel.
Hardware Scaling Impact¶
What helps¶
| Upgrade | Impact | Why |
|---|---|---|
| Database on separate server | High | SQL queries no longer compete with PHP for CPU/RAM |
| RAM (8 → 16+ GB) | High | Larger database buffer pool, more PHP workers |
| SSD/NVMe storage | High | Faster database I/O, especially for JOINs |
| Redis/APCu caching | High | MetaVox caches metadata (30s TTL) and field definitions (600s TTL) |
| More CPU cores | Medium | Helps with concurrent users (parallel PHP-FPM workers), limited benefit for single-user performance |
What doesn't help¶
| Change | Why it has limited effect |
|---|---|
| More CPU cores (single user) | PHP is single-threaded per request |
| More network bandwidth | API calls are small (20–100 KB per batch) |
| CDN / edge caching | Metadata is dynamic and user-specific |
Estimated Practical Limits by Hardware Tier¶
| Hardware | Files per view | Concurrent users | Active filters |
|---|---|---|---|
| Entry (1 core, 8 GB, local DB) | ~5,000 | ~10 | ~5 |
| Standard (4 cores, 16 GB, Redis) | ~8,000 | ~40 | ~10 |
| Recommended (4 cores, 16 GB, separate DB, Redis) | ~10,000–15,000 | ~80 | ~15 |
Reminder: The primary bottleneck above ~5,000 files is Nextcloud's client-side file list, not MetaVox. Hardware upgrades improve MetaVox's backend processing but cannot eliminate the browser-side limitation.
Best Practices¶
-
Use filtered Views to keep the number of visible files per view under 5,000. The MetaVox backend can store and query millions of metadata records — the limit is in the browser rendering.
-
Ensure database indexes are active. The indexes added in migration
Version20250101000010improve filter query performance by 40–100x. Verify they exist on themetavox_file_gf_metatable. -
Enable Redis or APCu caching in your Nextcloud configuration. MetaVox uses distributed caching for metadata (30s TTL) and field definitions (600s TTL).
-
Limit active filters per view to 10 or fewer. Each filter adds a SQL JOIN. Performance remains excellent up to ~10 filters but degrades beyond 15.
-
Use the Views feature to show only relevant columns per use case, rather than displaying all fields at once. This reduces API response sizes and improves rendering performance.
-
For multi-user editing, install notify_push for real-time sync and cell locking. Without it, last-write-wins applies.
-
Consider folder structure. Splitting large collections across multiple folders (each under 5,000 files) is more effective than relying on hardware upgrades alone.