fix(content-drive): user-cache sizing + warm-up to cut per-row DB lookups (#37186) - #37394
Conversation
…ydration (#37186) Adds a sequential warm-up pass in BrowserAPIImpl that resolves every distinct modUser/owner id a page needs before hydrateContentletsInParallel fans rows into concurrent chunks, eliminating the thundering-herd race on UserFactoryImpl#loadUserById. Also wraps the two unguarded loadUserById calls in DefaultTransformStrategy#addVersionProperties (modUser, locked-by) so an orphaned user id degrades one row instead of failing the whole listing.
…n thrashing (#37186) FR-002: cache.userdotcmscache.size was effectively 1000 (commented out, falling back to the shared cache.default.size), which thrashes continuously on the 1,828-active-user reference instance. Line 519 (email region) is left untouched — it's a dead region due to a key-mismatch bug in UserCacheImpl, so raising it would only reserve memory nothing reads back.
…nsform (#37186) User Story 2 (optional, kept per developer decision): addVersionProperties now reuses the modUserName addAuditProperties already resolved for the same row instead of calling loadUserById a second time for the same id. Once FR-001's warm-up makes the id cache-warm, the repeat call was already a cache hit, not a DB round trip -- this trims call-count/CPU overhead, and matters on its own for any caller that doesn't warm up first.
|
Claude finished @ihoffmann-dot's task in 3m 0s —— View job Code Review — PR #37394 (content-drive user-cache sizing + warm-up)
I traced the full transform/hydration path and verified the cache region key, the dedup logic, and the warm-up placement against the surrounding code. New Issues
Notes (non-blocking, no action required)
No blocking issues. The two medium items are test-robustness concerns that only bite if integration test concurrency changes — worth a glance but they don't block merge. · |
…erAPITest (#37186) - ContentletDataGen#user(User) is a no-op on the no-categories persist path (it always uses the static system user as the acting user, per ContentletDataGen.java:317-326), so the warm-up test's per-author assignment and the orphan test's modUser were both silently ignored. Reassign modUser/owner directly via SQL after creation instead. - The orphan test's raw delete of the deleted user's row was blocked by two FKs: users_cms_roles.fkusers_cms_roles2 (role assignment) and contentlet.fk_user_contentlet, now correctly referenced once modUser is actually set. Delete the role assignment first, and disable/ restore user_'s triggers (where Postgres registers the parent-side RI check for a DELETE) around the user delete.
Summary
cache.userdotcmscache.sizefrom the effectively-1000 default to 4000 (FR-002), sized against a reference ~1,828 active-user base.BrowserAPIImpl#warmUpUserCache) that resolves each distinctmodUser/owneron a listing page once, ahead of parallel row hydration, cutting redundant per-rowUserAPI#loadUserByIdcalls (User Story 1).DefaultTransformStrategy#resolveModUserName, User Story 2).FR-004a): a listing row whosemodUser/lockedBy user was deleted no longer fails the whole page — it now degrades toN/Afor that row only.@VisibleForTestingDB-lookup counter (UserFactoryImpl) used only by the new integration test to assert warm-up actually reduces lookups to one per distinct author.Test plan
./mvnw test -pl :dotcms-core -Dtest=UserFactoryImplTest,BrowserAPIImplTest,UserCacheConfigTest,DefaultTransformStrategyTestjust test-integration-ide./mvnw verify -pl :dotcms-integration -Dcoreit.test.skip=false -Dit.test=BrowserAPITestSystem.out/System.getProperty/System.getenvintroduced (checked via diff at implementation time)Branched off the approved spec branch per this repo's Spec-Kit flow (spec.md-only in PR1, not merged to
mainyet).🤖 Generated with Claude Code
This PR fixes: #37186