Problem XWikiCacheStore derives its cache key from the wiki in the context rather than from the wiki of the document being loaded, saved or invalidated:
DocumentReference reference = doc.getDocumentReferenceWithLocale();
if (!reference.getWikiReference().equals(context.getWikiReference())) {
reference = reference.setWikiReference(context.getWikiReference());
}
return this.uidStringEntityReferenceSerializer.serialize(reference, reference);
This was introduced deliberately by XWIKI-13632, to match the Hibernate store's behaviour of loading a document from the wiki indicated in the context regardless of the wiki in the XWikiDocument. The consequence, however, is that two documents that share the same space and page name in two different wikis share a single document cache slot whenever an operation on one of them happens while the context wiki points at the other. So any loadXWikiDoc, exists or saveXWikiDoc performed with a context wiki that does not match the document's wiki reads, populates or invalidates another wiki's cache entry. In particular an invalidation that lands on the wrong key leaves a stale entry behind, and because the document cache has an unbounded lifetime that stale entry survives for the rest of the instance's life. User-visible consequence On a multi-wiki instance, pages that exist at the same path in several wikis (typically application/configuration pages shipped by an extension installed on more than one wiki) can be served from the cache in a state that does not match the database: the content of another wiki's copy, or the pre-install "does not exist" state. Anything reading such a document through the cache then behaves as if the document had no XClass and no xobjects, while REST, Solr-backed live tables and direct queries โ which do not go through this cache โ keep showing the correct data. The result is a wiki page that renders as if it were unconfigured, with no error anywhere in the logs. How it was found This is the root cause of the flickering org.xwiki.image.style.test.ui.AllIT$NestedImageStyleIT#imageStyleAdministration test (see XWIKI-24720 for a different, already-mitigated flicker of the same test). Failure rate on the CI went from about 0.2% (June-July 2026) to about 2.8% (since mid-August 2026), consistent with a load-sensitive race. The test uses @WikisSource, which runs it once on the main wiki and once on a subwiki, so Image.Style.Code.Configuration and Image.Style.Code.ConfigurationClass exist at the same path in both wikis โ exactly the collision condition. Observations:
- The failure is always NoSuchElementException: *[name='Image.Style.Code.ConfigurationClass_0_defaultStyle'] in ImageStyleAdministrationPage#getDefaultStyle.
- The archived failure screenshots show the Image Styles administration section rendered with two literal $escapetool.xml($property.translatedPrettyName) labels and with the "Default Style" select and "Force Default Style" checkbox missing, while the rest of the section (including the "Update the default image style" button and the image styles live table) renders normally.
- Frames extracted from the failure video show the section is already broken on the first visit to the administration, more than 20 seconds before the assertion fails, and it stays broken across page loads. It is therefore not a UI/wait race, and no page object change can work around it.
- Reproduced the exact same rendering on a local instance by making the Image.Style.Code.ConfigurationClass XClass invisible to the request: $configurationDoc.display('defaultStyle', 'edit') returns an empty string because XWikiDocument#display bails out on pclass == null, and $configurationObj.xWikiClass.get('defaultStyle') returns null, which is what leaks the two raw Velocity labels.
- The CI console log shows the extension provisioning into namespace [wiki:wiki1] completing 14 seconds before the main wiki run of the test started and found the main wiki broken. The two parameterized runs fail independently of each other, as a per-wiki cache collision predicts.
Notes
- Same family as XWIKI-13632 and XWIKI-24673.
- XWikiDocument#getId() deliberately excludes the wiki from the Hibernate id as well ("we don't use the wiki name in the document id's computation"), so the same page path in two wikis is also indistinguishable at the Hibernate identifier level.
- Fixing this means deciding whether the store should key on the document's own wiki (and align the context to it) instead of the reverse, which changes behaviour for every caller that relies on the context-wiki-wins semantics documented in XWIKI-13632. That decision is the point of this issue.
|