Prevent broken transactions when autonumbering#7671
Prevent broken transactions when autonumbering#7671melton-jason merged 27 commits intov7.11.4-prereleasefrom
Conversation
|
There previously was an issue with the This issue has been fixed! |
kwhuber
left a comment
There was a problem hiding this comment.
General Concurrency with WorkBench
- Save the Data Entry record and ensure the record saves successfully
- Reload the page and ensure Specify loads and still remains accessible
Concurrent Autonumbering with the WorkBench
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
Concurrent Autonumbering with Non Collection Scoping
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
emenslin
left a comment
There was a problem hiding this comment.
- Save the Data Entry record and ensure the record saves successfully
- Reload the page and ensure Specify loads and still remains accessible
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
Everything looks good except I'm running into some issues with accession autonumbering and I'm not sure if the scoping is working properly. I'm not entirely sure if I am testing it right though so if there could be some clarification on the expected behavior and some specific testing instructions I would appreciate it.
bhumikaguptaa
left a comment
There was a problem hiding this comment.
- Save the Data Entry record and ensure the record saves successfully
- Reload the page and ensure Specify loads and still remains accessible
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
Everything works as expected.
|
Hi everyone! I've updated the sample database to include more than one Division (the new Mammals collection is in a different Division then the other Collections) and some Accession Data Sets; specifically, I've provided two databases: one with Accessions scoped to Division and one with Accessions that are globally scoped. I've also updated the testing instructions to include testing for Accession autonumbering. During my own testing, I came across two Issues that are also present in main:
These will likely not be addressed in this PR: I'll write up an Issue for each of these Footnotes
|
emenslin
left a comment
There was a problem hiding this comment.
- Save the Data Entry record and ensure the record saves successfully
- Reload the page and ensure Specify loads and still remains accessible
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
- Save the Data Entry record and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results.
- Save the Accession and ensure the record saves successfully
- Ensure that the Accessions in different Divisions are independently auto-numbered (e.g., Accessions in different Divisions should not share the same autonumbering)
- Save the Accession and ensure the record saves successfully
- Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results in the other Division
- Generally ensure that autonumbering respects the scoping of Accessions (e.g, using DataEntry, create Accessions in different scopes and ensure they're autonumbered as expected)
Autonumbering behavior looks good! The only issue I ran into was when editing the preparation count of an existing CO record and pressing save it gets stuck loading. I checked this on multiple different CO and it seems to be happening consistently in the mammals collection but not in other collections. I checked in 7.11.3 and I can recreate it so it is probably not related to this PR but I wanted to mention it since I discovered it when testing.
This Issue is actually what happens when the CollectionObject doesn't have a CollectionObjectType (COT). The business rules (provided below) expect the COT to always be present, and don't handle cases when there is not a COT. This results in errors behind-the-scenes, errors that are reported in the console in a production environment. Regardless, these errors make is so that the request to Create or Update the resource are never actually sent to the backend when the resource is saved, meaning the frontend will be waiting indefinitely for a response to a request that was never sent! Screen.Recording.2026-02-03.at.1.04.28.PM.movThis functionality was not modified in any way in this PR. I've updated the sample databases in the testing instructions to add the Specify 7 resources to the Division, Discipline, and Collection for Mammology, so you shouldn't encounter the same issue when using the sample databases! |
|
This pull request has been mentioned on Specify Community Forum. There might be relevant details there: https://discourse.specifysoftware.org/t/specify-7-11-4-release-announcement/3340/1 |
Replaces #5404
Fixes #4148, #7560, #4894
Addresses part of #5337
Checklist
self-explanatory (or properly documented)
This branch should functionally be the equivalent of #7455, based on
v7.11.3instead ofmain.The Pull Request seeks to address an issue related to the prior autonumbering code, where database transactions could become broken due to a
LOCK TABLESstatement within transactions:See #6490 (comment)
Currently in
main, this autonumbering behavior with the WorkBench can result in a functionally complete lockout of the database, preventing other connections from reading tables like Discipline and Collection until the WorkBench operation finishes.Below is a video of the issue taken in
v7.11.3:v7_11_3_wb_issue.mov
Below is with the changes in this branch:
v7__11_3_wb_fix.mov
(Developer-Focused) New Internal Autonumbering Design
Specify now uses transaction-independent User Advisory Locks to determine which session is currently using autonumbering for a particular table. If no other sessions currently have the autonumbering lock for a particular table, a connection will acquire the lock.
If a record attempts to autonumber a field that is being autonumbered by another session (e.g. another session holds the autonumbering lock), Specify will wait up to 10 seconds for the prior connection to release its lock. If the lock is not released by that time, then Specify will error out of the operation.
To maintain autonumbering behavior for concurrent actions where there is a long-running transaction also doing autonumbering (such as when a long-running WorkBench operation is ongoing), Specify uses Redis (for IPC) to store the currently highest autonumbering behavior for a particular formatter.
When a sessions holds an AutoNumbering lock for a particular table, it checks this store for the highest automumber and compares it to the highest value the session can see in the database: using the higher of the two to determine the new highest value.
Thus, even with transactions that have a high Isolation Level, Specify is still internally committing the AutoNumbering values to the store that can be accessed by other sessions.
Roughly, this means that AutoNumbering acts at a similar isolation level to READ UNCOMMITTED within the application.
The core part of the implementation of this design is through the new
LockDispatcherclass:specify7/specifyweb/specify/models_utils/lock_tables.py
Lines 202 to 258 in 4de5c06
This class can be used as a context manager and handles acquiring and releasing User Advisory Locks:
The
AutonumberingLockDispatcherbuilds the IPC (Redis) integration required for AutoNumbering on top of the baseLockDispatcherclass, which handles the database locks.specify7/specifyweb/specify/utils/autonumbering.py
Lines 39 to 84 in 4de5c06
(User-Focused) New Autonumbering Behavior
Below is graph which simply describes the new behavior
flowchart TD A[WorkBench 001] --> B[WorkBench 002] B l1@-- waiting for DataEntry--> C(.) B -- concurrent record saved --> D(DataEntry 003) C l2@-- DataEntry finished--> E(WorkBench 004) D --> E classDef wb stroke:#f00 classDef de stroke:#00f classDef dashed stroke-dasharray: 5 5 class A,B,C,E wb; class D de; class l1,l2 dashedAll AutoNumbering operations are processed serially. This means if there's an ongoing Workbench operation that creates a record that is numbered
01, the next autonumbering value for any other session will be02.If the Workbench were to create another record, it would be numbered
03.Testing instructions
Sample Database
To speed up setup for testing this Pull Request, i've set up a testing database that contains all resources required for testing:
Feel free to use the database!
Where Accessions are scoped to Division:
issue-6490_scoped_accession_testing.sql.zip
Where Accessions are globally scoped:
issue-6490_global_accession_testing.sql.zip
General Concurrency with WorkBench
Concurrent Autonumbering with the WorkBench
Concurrent Autonumbering with Non Collection Scoping
Review Table Scoping Hiearchy for an overview on table scopes
With the Sample Database, this would be using the Locality Data Set in the Plants Collection with creating a new Locality in the Vascular Plants Collection
Autonumbering with Variable Table Scoping
In a database with more than one Division where Accessions are scoped to Division
In a database with more than one Division where Accessions are globally scoped
Start a WorkBench Upload operation on a sufficiently large Accession Data Set (the operation needs to be in process while some of the below steps of Concurrent Autonumbering with Non Collection Scoping are completed) where a field on Accession is being autonumbered
Open Specify in a new tab, window, or browser
Switch to a different Collection which is in a different Division as the records being uploaded
Open an Accession DataEntry form
Save the Accession and ensure the record saves successfully
Wait for the WorkBench Upload operation to complete
Ensure that the value for the autonumbered field from Data Entry is skipped in the WorkBench upload results in the other Division
Generally ensure that autonumbering respects the scoping of Accessions (e.g, using DataEntry, create Accessions in different scopes and ensure they're autonumbered as expected)
General