Skip to content

fix: make ApiTokenAttribute Serializable for Ehcache L2 cache#24002

Merged
janhenrikoverland merged 1 commit into
masterfrom
fix/apitoken-serializable
May 27, 2026
Merged

fix: make ApiTokenAttribute Serializable for Ehcache L2 cache#24002
janhenrikoverland merged 1 commit into
masterfrom
fix/apitoken-serializable

Conversation

@netroms

@netroms netroms commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • ApiTokenAttribute now implements Serializable, fixing 500 errors on /api/me and /api/apiTokens when PATs exist
  • Regression caused by ehcache bump from 3.10.8 to 3.12.0 (chore(deps): bump org.ehcache:ehcache from 3.10.8 to 3.12.0 in /dhis-2 #23834), which uses SerializingCopier requiring Java serialization
  • ApiToken has <cache usage="read-write"/> in its HBM mapping, so the L2 cache serialization path is hit on every insert/load

AI Assisted

Ehcache 3.12.0 uses SerializingCopier which requires Java
serialization. ApiTokenAttribute (and subclasses MethodAllowedList,
IpAllowedList, RefererAllowedList) lacked Serializable, causing
500 errors on /api/me and /api/apiTokens when PATs exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@janhenrikoverland
janhenrikoverland merged commit fbfb9d0 into master May 27, 2026
18 checks passed
@janhenrikoverland
janhenrikoverland deleted the fix/apitoken-serializable branch May 27, 2026 09:03
enricocolasante added a commit that referenced this pull request Jul 16, 2026
The Ehcache 3.12 bump (#23834 / #24002) switched the on-heap L2 store to the
JSR-107 store-by-value default, implemented by SerializingCopier: a full Java
serialize/deserialize round-trip on every cache get and putFromLoad. Profiling a
production tracker import (DHIS2-21800) showed this dominates: ~50% of the thread
profile serializing entities under the AbstractReadWriteAccess ReentrantReadWriteLock
(READ_WRITE strategy), manifesting as long "waited" time (park on the lock, not
blocked) and heavy transient allocation that drives frequent young-gen GC; it also
made requests hold their DB connection for the whole duration, draining the pool.

Configure an explicit IdentityCopier on defaultCacheTemplate so the mapped entity
and query regions store by reference, overriding the JSR-107 by-value semantics
(Ehcache logs "overwriting JSR-107 by-value semantics"). This restores the pre-3.12
(Ehcache 2.x) behaviour and eliminates the redundant copy: Hibernate's L2 contract
already isolates callers at the type level - entity CacheEntry state is deep-copied
on disassemble/assemble (e.g. JsonBinaryType.assemble -> deepCopy) and all immutable
custom UserTypes share safely.

Add EhcacheStoreByReferenceTest, which loads the real ehcache.xml through the JSR-107
path (as JCacheRegionFactory does, storeByValue=true) and asserts a get returns the
same instance that was put. Update CachedEntityJsonbSerializableTest's Javadoc: the
Serializable requirement still holds (UserType#disassemble returns Serializable and
Hibernate stores a Serializable[]), and that test keeps running store-by-value via an
empty cache config, so it remains a strict guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants