-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix exception when saving and autosave trigger at the same time #6694
Conversation
I wouldn't remove auto-saving. Actually, I feel this should be the standard behavior: why do I need to remember as a user to save the file? |
I will try if I can find a way to lock the saving to one thread |
That might lead to performance problems: saving might take a few seconds so in the worst case the save requests add up. The same with "Save all" etc. I think the underlying problem is really that the tmp file is reused between save operations. This is really not a good idea (also for data integrity). |
I'm open for ideas: If you have autosave running and manually hit saving you run into problems. Even if you would write to another temp file first that doesn't help. You will get an exception nonetheless as the file is locked by another thread when it is moved. |
Which file is locked? My idea was:
The first step should only lock the temp file, and write to it. There is no problem there since every write process has its own temp file, and there is no interaction. The second step is atomic, so there is no locking or inter-thread problem. In addition to these technicalities, I think it be worthwhile to improve the user experience a bit. I guess it would be good to hard-cancel every running auto-save and normal save (and delete the temporary file), as soon as the user runs a new save for the same file. This reduces overhead. |
@tobiasdiez Even though you have atomic moving, the other thread will throw an IllegalAccessException. Following situation:
A writes to temp files and now calls the Atomic File Move => Performs Atomic Moves temp file A to target file To reproduce: the only way one could solve this would be to cancel the running? save operation |
But this exception is coming from this lock, isn't it? (Which comes from the fact that we are writing to the same tmp file)
Since |
The temporary file lock is never set, its always null because the Files... Returns directly a File Channel. (see the comment in the code in my changes). I will update the code to show the problem |
Good! Can you also please add a comment at the place where the exception is thrown (I don't really have time right now to play with this in detail). |
08a7b16
to
5a05db0
Compare
I tested the idea to create the file in the System's TEMP Folder, but as I expected an atomic move is not possible if the TEMP folder is on a different disk than the library file. (My Temp folder is on a SSD, while my jabref bib is on a different HDD, "Atomic move between providers is not supported) I tested you idea with a unique file name in the same folder which results in an exception when multipe threads try to acess the file. I commented in the code where to put the breakpoint. Locking the section could solve this, but the problem is that when the saving is done manually, it's the FX thread which executes the save operation. So that should also be a background task
|
Yeah, seems like we need a wrapper that controls which save operations are in progress, and if there is already one for the target file, then stops the old one and only then starts the new one... damn. Thanks for your time investigating this! |
@tobiasdiez's one works, because we use temp files. Worker-Queue of Guava? An ADR is required ^^. |
try to combine autosave logic and normal save actions TODO: how to handle return values of saves?
Idea: put the logic into save database action |
In #5669 (comment) - I put |
This somehow relates to #5967, but I don't know, how. |
Synchronizing won't help here (always a new instance created) and I already have an idea how to fix this |
What's the status here? |
Work in progress. Have an idea to cancel the running or queue the new save actions |
* upstream/master: (120 commits) Follow up fix for copy paste (#6820) Add CSS Customisation (#6725) Separate signing and notarizing (#6822) Remove checkstyle hack. 8.36 got released (#6816) Feature/enable lucene query parsing (#6799) New release cycle Bump WyriHaximus/github-action-wait-for-status from v1.1.2 to v1.2 (#6814) Bump mockito-core from 3.5.5 to 3.5.7 (#6813) Bump classgraph from 4.8.87 to 4.8.89 (#6812) Bump me.champeau.gradle.jmh from 0.5.0 to 0.5.1 (#6811) Bump checkstyle from 8.35 to 8.36 (#6810) Improve Changelog Refactor edit action (#6808) Fixed typo in BuildInfo (#6807) disable checkstyle for generated fix checkstyle Simplify check-links.yaml (markdown-link-check) (#6720) Rename /gen to /generate (#6800) Disable CSL refresh on push (#6803) New Crowdin updates (#6804) ... # Conflicts: # src/main/java/org/jabref/gui/JabRefFrame.java
The JabRef maintainers will add the following name to the AUTHORS file. In case you want to use a different one, please comment here and adjust your name in your git configuration for future commits.
|
prepare save action executor TODO: How to handle return value Wat for future?
Hmmm. Then I think my queue is already sufficient. The key problem is however that we can't create a lock on a file and that there is no atomic move on all systems. So this will create problems with Dropbox or whatever? |
Yes, I think the queue would already be a huge improvement. The only problem I can remember with our "atomic write" has been the changed file security meta data. But that was fixed as well. Do we really have reports for issues with Dropbox? |
I think, AtomicWrite was introduced to catch for errors during file writing - and have the previous version available. Since there are no reports of corrupted data bases (at least I am not aware of), I would use the "usual" file update behavior. Reason: Sharepoint, Google Drive, Dropbox and other file sharing services IMHO rely on that (especially when it comes to proper file verisoning) |
Maybe we can turn the action a bit inside out: before updating create a backup copy of the file and then update the current file? |
Preventing corrupted data is exactly the point of the atomic writer, so having no such reports is proofing that everything is working fine.
Can you elaborate how the atomic writer leads to problems? Since we essentially overwrite the content very quickly (delete & copy), I don't think file sharing services confuse these changes for two files - but I might be wrong.
That may still leave the main library file in a corrupted state. In the best case, this means just more work for the user (copy backup over current file) and in the worse case means the user looses part of her db (when the corruption is noticed to late). But the atomic writer works in a very similar way, just interchanging the roles of "backup" and "current file": write contents to a new file, and when this succeeds replace the "current file" with this new file (which on some os is even an atomic operation). |
* upstream/main: Main instead of master Custom DOI base address fix (#7569) Change export to save (#7518) Bump unoloader from 7.1.1 to 7.1.2 (#7609) Bump org.beryx.jlink from 2.23.5 to 2.23.6 (#7610) Bump com.adarshr.test-logger from 2.1.1 to 3.0.0 (#7611) Bump libreoffice from 7.1.1 to 7.1.2 (#7612) Squashed 'buildres/csl/csl-styles/' changes from e1acabe..bfa3b6d (#7603) Rename master to main
Fuck this shit. Now I'm creating a block when I wait for the save to complete. Argh. |
* upstream/main: (71 commits) [Bot] Update CSL styles (#7735) Fix for issue 6966: open all files of multiple entries (#7709) Add simple unit tests (#7696) Add simple unit tests (#7543) Update check-outdated-dependencies.yml Added preset for new entry keybindings and reintroduced defaults (#7705) Select the entry which has smaller dictonary order when merge (#7708) Update CHANGELOG.md fix: make more fields, fomatters, ids and languages sorted by alphabetical order (#7717) Bump libreoffice from 7.1.2 to 7.1.3 (#7721) Bump unoloader from 7.1.2 to 7.1.3 (#7724) Bump org.beryx.jlink from 2.23.7 to 2.23.8 (#7723) Bump org.openjfx.javafxplugin from 0.0.9 to 0.0.10 (#7725) fix: make fields sorted by lexicographical order (#7711) Fix tests Refactoring existing unit tests (#7687) Refactoring and addition of unit tests (#7581) Refactor simple Unit Tests (#7571) Add simple unit tests (#7544) add and extend unit tests (#7685) ...
Possibly, this has already been discussed. But git seems to solve parallel write operations to a repository from multiple git processes with a lock file. Before the process starts, it checks, whether the lock file is there. If not, it creates it, performs the actions and removes the lock file. If the lock is there, it shows a warning. For JabRef, one might not even need such a lock file, but just a variable that gets set when operations are performed (and finished). Do I miss something important? |
Now, JabRef has issues on the edge case WSL shares: #9547 |
Fixes #6684
Fixes #6644
Fixes #6102
Fixes #6000
Summary:
Manual save + autosave produce an exception/race condition on saving, because both want to access the same file for moving the temp file to the target file.
Solution: Throttle multiple saving operations in a kind of queue