Use UnorderedOrdering for PatientFolder to avoid ZODB conflicts#135
Merged
Conversation
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.
Description
Switch the
IOrderingadapter forPatientFolderfromplone.folder.default.DefaultOrderingtoplone.folder.unordered.UnorderedOrdering.PatientFolderis folderish. With the default adapter, Plone keeps aplone.folder.ordered.orderannotation — a flatpersistent.list.PersistentListof every child id, mutated on every_setObjectvianotifyAdded(obj_id)(order.append(...)).PersistentListdoes not implement_p_resolveConflict(), so two concurrent registrations on the same folder always conflict on this single annotation OID, and the conflict propagates all the way to the publisher's 3-retry loop. The cost of each retry grows linearly with the size of the list, becausePersistentListrewrites the whole pickle on every mutation.For clinical-lab installations the
PatientFolderaccumulates onePatientper registered sample, so its child count grows on the same trajectory as the sample volume — into the tens or hundreds of thousands. Concurrent sample registration is exactly the workload that maximises contention on this single annotation: every new Patient created during sample-add hits the sameorder.append.With
UnorderedOrderingthe adapter'snotifyAdded/notifyRemovedbecome no-ops;idsInOrder()falls back toaq_base(folder).objectIds(ordered=False), which reads the BTreeFolder2 internal tree (scales, has built-in conflict resolution).The change is interface-scoped to
IPatientFolderonly.Patientcontent (which is itself folderish) and any other folderish type provided by senaite.patient are unaffected — they continue to useDefaultOrdering.Patient listings never depend on the parent folder's manual ordering — they sort catalog brains. An audit of the package found only upgrade-step iterations over
portal.patients.objectValues(), none of which assume insertion order.Migration
Existing
PatientFolderinstances still carry theplone.folder.ordered.orderPersistentListandplone.folder.ordered.posOIBTreeannotations from prior writes. They are no longer maintained but remain on disk. The new upgrade stepdrop_patientfolder_ordering_annotations(profile version1602 → 1603) pops both annotations fromportal.patients.The adapter override is what stops new writes from touching the annotations; the upgrade step is hygiene to free their storage.
Related
This follows the same pattern proposed upstream in senaite/senaite.core (PR for
IClient→UnorderedOrdering): Use UnorderedOrdering for Clients to avoid ZODB conflicts #2897. Both folders share the same growth profile under high concurrent sample-registration load.--
I confirm I have tested this PR thoroughly and coded it according to PEP8
and Plone's Python styleguide standards.