Repository navigation
fix: Do not load unloaded collections when building metadata audit entries [DHIS2-22235] (2.43) - #25535
Open
jason-p-pickering wants to merge 2 commits into
Open
fix: Do not load unloaded collections when building metadata audit entries [DHIS2-22235] (2.43)#25535jason-p-pickering wants to merge 2 commits into
jason-p-pickering wants to merge 2 commits into
Conversation
…tries [DHIS2-22235] (2.43)
createAuditEntry treated an unloaded PersistentCollection like an entity
proxy and read it, so every update of a data set or org unit group loaded
all of its org units after commit just to list their UIDs in the audit
entry. This breaks the method's documented rule 2 ("Do not include any lazy
HibernateProxy or PersistentCollection that is not loaded"). A collection
the request did not load is now left out; loaded collections are still
written as before.
On a copy of a production database, changing only the sharing of a data
set or org unit group with ~138k org units went from 2.7-3.3 s and a
~1.9 MB audit entry to 0.03-0.16 s and ~1 KB.
Backport of #25534.
(cherry picked from commit 786822a)
🤖 AI Assisted
…DHIS2-22235] (2.43) Review feedback: with unloaded collections left out, a loaded collection that the request emptied was also missing from the entry (putValueToMap dropped empty collections), so "removed every org unit" and "changed only the sharing" produced the same entry. A loaded empty collection is now written as [], so a missing key only means "not loaded". This matches the delete path, which already writes empty collections. Also moves the unloaded-collection check into a helper to keep createAuditEntry within the cognitive complexity limit. (cherry picked from commit 8bb35d3) 🤖 AI Assisted
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Backport of #25534 to 2.43, including the review fix (loaded empty collections written as
[]).AbstractHibernateListenerdiffers from master only in master's latershouldRefetchThroughProxychange (#25014), which is not on 2.43. The measurements below were taken on a 2.43 build.Summary
AbstractHibernateListener.createAuditEntrytreated an unloadedPersistentCollectionlike anentity proxy: it read the property through a proxy and called
isEmpty()on it, which loaded thewhole collection after commit (through a temporary session) just to write its UIDs into the audit
entry. This breaks the method's own Javadoc, rule 2: "Do not include any lazy HibernateProxy or
PersistentCollection that is not loaded."
With the default audit configuration (
audit.metadata/audit.loggerunset), every update of adata set or org unit group, even a sharing-only change, loaded all of its org units and wrote a
multi-MB audit entry.
Fix
Skip a
PersistentCollectionthat is notwasInitialized()before the proxy branch. Collectionsthe request did load are still written as UIDs.
A loaded collection that is empty (e.g. the request removed every member) is now written as
[].Before, empty collections were dropped from the entry, which with this change would have made "emptied
the collection" and "never loaded it" look the same (review feedback). So in the entry:
[]This matches the delete path, which already writes empty collections. Nothing else changes: which
objects and events are audited, the entry format, or any scalar field.
Measured (copy of a 2.43 production database, same build, sharing change only)
POST/DELETE /api/organisationUnits/{ou}/dataSets/{ds}still writes the full set (1.93 MB): thatrequest loads the set before the audit runs, so it is a loaded collection and is audited as
before. Removing that load is a separate follow-up.
On the production system, one day's audit log was 3.4 GB, 96% of it Program/DataSet UPDATE entries
of 1.5–2.25 MB, made up of org unit UID lists.
Tests
AuditHibernateListenerTest:testUnloadedCollectionIsNotLoadedOrIncluded: uses a realPersistentSetwithout a session.On the unfixed code this fails with
LazyInitializationException(the listener tries to loadit). With the fix, the set stays unloaded and has no key in the entry.
testLoadedCollectionIsIncludedAsUids: a loaded set is still written as UIDs.testLoadedEmptyCollectionIsIncludedAsEmptyList: a loaded empty set is written as[](fails before thereview fix: the key was missing).
Jira: DHIS2-22235
🤖 AI Assisted