Wikis get slow as content grows. Pages render slower; searches lag; admin operations time out. Most wikis hit performance limits before they hit content limits.
This page covers where wikis get slow and the patterns for fixing it.
Wiki markup → HTML conversion. For complex pages with macros, plugins, transclusions, this can be slow.
Indexing many pages; querying with complex filters. Search performance often the most-visible bottleneck.
Page reads, writes, history queries. Standard database concerns. See DatabasePerformanceMonitoringHub.
Pages with thousands of lines, hundreds of links, embedded content. Each render is expensive.
Each plugin adds overhead. Plugin-heavy wikis are slower than minimalist ones.
Page lists, sidebars showing recent changes, categorical views. As page count grows, these get slow.
Rendered HTML cached. Re-rendering only when source changes.
For most wikis, this is essential. Without it, every page view re-renders.
Parts of a page (sidebar, navigation, transclusions) cached separately. Updates to one part don't invalidate the whole.
Common queries cached. Avoid hitting the database for every page navigation.
For mostly-read public wikis, CDN caches rendered pages. Massive performance win.
For internal wikis, CDN may not apply.
When does cached content become stale? Page edited; plugin reconfigured; user permissions changed.
Cache invalidation strategies:
For wikis, event-based is usually right — cache until something changes.
For long pages, load only the visible portion initially. Load more as user scrolls.
For wiki sidebars: load related content on demand.
Page lists, search results: paginated. Don't return 10,000 items.
Plugins that don't need to render synchronously: defer. Load page; load plugin output asynchronously.
For very expensive operations, render in background. Show "rendering..." until done.
Missing indexes on commonly-queried fields slow everything. Profile slow queries; add indexes.
Database connections expensive. Pool them. See DatabaseConnectionSecurity.
Search is often the slowest piece.
Index pages incrementally. Don't rebuild from scratch on each change.
Indexes get large. Plan for it.
Common queries optimized. Filters that match many pages need indexes.
For large wikis, dedicated search (Elasticsearch, Solr) outperforms database-backed search dramatically.
See ElasticsearchFundamentals and WikiSearchOptimization.
Pages with thousands of lines render slowly. Consider:
Pages including content from many other pages. Each transclusion adds rendering time.
A page using 20 plugins. Each plugin runs on render. Combined, very slow.
Audit plugin usage. Remove unused; combine where possible.
Page A links to B which links back to A. Plugins that follow links can recurse.
Limit recursion depth.
How many pages can the wiki handle before performance degrades?
For most wikis: a few thousand pages is fine. Tens of thousands need work. Hundreds of thousands need real engineering.
Reads scale well; writes scale less. Many concurrent edits compete for locks.
For most internal wikis, concurrent-user count is limited; this isn't an issue.
More plugins = more risk. Audit; remove dead.
Page render times; search times; database query times. Monitor.
Database slow-query log. Find slow operations; optimize.
For specific slow pages, profile to find the bottleneck.
Performance degrades over time. Periodic review catches drift.
ehcache-wikantik.xml in the wikantik-cache module: the rendering and HTML caches hold up to 10K entries with a 1-hour TTL, the page, page-text and page-history caches 10K entries with a 60-second TTL, and the attachment and attachment-collection caches 1K entries. wikantik.cache.enable (default true) is the master switch. Metrics for every cache, including hit rates, appear on the Prometheus /metrics endpoint.wikantik-cache-memcached is an opt-in Memcached adapter for multi-node deployments. It is not wired into the WAR: override the CachingManager mapping in classmappings.xml, put the jar on the container classpath, and set wikantik.cache.memcached.servers.wikantik.search.dense.backend defaults to lucene-hnsw, an in-process index; pgvector reads through to the database, and inmemory is the older brute-force index. See HybridRetrieval.BackpressureFilter caps in-flight requests at WIKANTIK_MAX_INFLIGHT_REQUESTS (default 390, deliberately just under Tomcat's maxThreads, which is 400 on the docker1 deployment) and answers the excess with 503 and Retry-After: 1 instead of queueing; /api/health and /metrics are exempt. RateLimitFilter adds per-client-IP limits: 25 requests per second by default, and a tighter tier (3 per client, 10 global) for the embedding-heavy /api/bundle, /api/search and /sparql. Loopback callers are never limited. The limits are set with the WIKANTIK_RATELIMIT_* environment variables.bin/loadtest.sh smoke|load|stress, and docs/developer/LoadTesting.md in the repository covers pairing a run with JFR recordings, Prometheus and thread dumps. docs/developer/ScalingCharacterization.md records a sweep that found the knee for a single 16-core host.For most wikis:
Cluster Hub: Wikantik Platform Development Hub: Architecture, MCP Protocols, and Agent Briefings (wikantik-development)