Accessing content properties on DefaultContentPropertyManager before an updating transaction is finalised leads to stale values living in the cache

XMLWordPrintable

    • Severity 3 - Minor
    • RtB - Service ops & Tech entropy

      Issue Summary

      `DefaultContentPropertyManager` can return stale cached property values after `setStringProperty()` is called, due to a cache invalidation race condition. The removeFromCache() call in DefaultContentPropertyManager.java executes inside the @Transactional method boundary — before the transaction commits. A concurrent getStringProperty() call during this window triggers a read-through cache load from the database, which still contains the pre-commit (old) value. This stale value is then cached and persists until the cache entry expires (TTL = 24h) or the "Content Properties" cache is manually flushed.

      Steps to Reproduce

      1. Deploy a Confluence DC instance (single node or cluster) running 9.4+
      2. Using a Marketplace app or custom plugin, call contentPropertyManager.setStringProperty() to update a property value
      3. Concurrently (from another thread or HTTP request), call contentPropertyManager.getStringProperty() to fetch the same property while step 2 is in-flight
      4. After step 2 completes, call contentPropertyManager.getStringProperty() again for the same property and compare to that to the value in the database (in the `os_propertyentry` table)

      Expected Results

      The value returned from `getStringProperty` matches the value in the database

      Actual Results

      The value returned from `getStringProperty` is the previous value

      Workaround

      Manually flush the "Content Properties" cache via Confluence Admin → Cache Management after observing stale reads. This cleans up the currently invalid state in the cache, but it does not prevent it from continuing to read-through-and-store stale values.

      Alternatively, apps can migrate from ContentPropertyManager to ContentPropertyService which uses a completely different storage path (CustomContentEntityObject / contentproperties table) and is not affected by this cache bug.

              Assignee:
              Unassigned
              Reporter:
              Tom Smallridge
              Votes:
              13 Vote for this issue
              Watchers:
              7 Start watching this issue

                Created:
                Updated: