parse-server
parse-server copied to clipboard
Increment does not appear to be atomic
New Issue Checklist
- [x] I am not disclosing a vulnerability.
- [x] I am not just asking a question.
- [x] I have searched through existing issues.
- [x] I can reproduce the issue with the latest version of Parse Server.
Issue Description
obj.increment does not appear to be atomic.
Steps to reproduce
I have the following code in the beforeSave hook of "childClass" and child has a pointer "parent" to "parentClass".
await obj.get("parent").increment("number_children").save(null, {
useMasterKey: true,
});
Actual Outcome
Creating two child objects simultaneously results in "number_children" increasing from 0 to 1. This does not happen with:
await obj1.save();
await obj2.save();
but only with
await Promise.all(obj1.save(), obj2.save());
Expected Outcome
I expected the count to increase from 0 to 2.
Environment
Server
- Parse Server version:
6.2.2
- Operating system:
MacOS Ventura
- Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc):
local
Database
- System (MongoDB or Postgres):
MongoDB
- Database version:
6.0.10
- Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc):
MongoDB Atlas
Client
- SDK (iOS, Android, JavaScript, PHP, Unity, etc):
js
- SDK version:
4.2.0
Logs
Thanks for opening this issue!
- 🚀 You can help us to fix this issue faster by opening a pull request with a failing test. See our Contribution Guide for how to make a pull request, or read our New Contributor's Guide if this is your first time contributing.
Could you add a test case with that example that you already posted?
I will have to examinate if I am doing anything special on my side. The following test works:
fit('regression test for #8772 (increment should be atomic))', async () => {
Parse.Object.disableSingleInstance();
Parse.Cloud.beforeSave('Parent', (req) => {});
Parse.Cloud.beforeSave('Child', async (req) => {
await req.object.get("parent").increment("num_child").save(null, {useMasterKey:true})
});
let parent = await new Parse.Object('Parent').save(null, {useMasterKey:true});
const child = () => new Parse.Object('Child').set("parent",parent).save();
// add synchronously
await child()
await child()
parent = await parent.fetch();
expect(parent.get('num_child')).toBe(2);
// add asynchronously
await Promise.all(Array.from({length: 40}, () => child()))
parent = await parent.fetch();
expect(parent.get('num_child')).toBe(42);
I's be surprised if no atomic test already exists. If there really is none we can add your test in any case. Could you open a PR?
Actually I can't find any. I'll open a PR
Yea even for me the updates are not atomic
Yea even for me the updates are not atomic
Could you elaborate on this? The test passes. Can you provide an example?
Yea even for me the updates are not atomic
Could you elaborate on this? The test passes. Can you provide an example?
Yea i tried as well the test you added is passing. I am using multiple instances of parse server and there are cases where I am updating the object from all the servers at sa,e time using increment op. But it seems the increment isn't atomic when using multiple instances.
@RahulLanjewar93 Why could it be working in parse-server tests but not in our own code?
Did you run the test on your own infrastructure?
The increment operation should be sent directly to mongo so therefore it should always be atomic independent of how many instances you are using? I am only using one instance
Yea, increment ops in mongo are atomic. I had tested it before in my own infrastructure where i was using multiple instances. Currently I am occupied with something else, i'll have a test again and update on the same.
But it seems the increment isn't atomic when using multiple instances.
Although I haven't looked, I doubt that there is anything instance specific in the request that is sent to the DB. Maybe it's a DB issue? Or maybe it's an infrastructure issue where requests are lost, time out, or similar?
Or maybe it is a more complex Parse JS SDK issue where multiple increments / decrements, and object re-fetches in between cause issues with caching? You could add a more complex test to the https://github.com/parse-community/parse-server/pull/8789 where you randomly increment / decrement / re-fetch an object in a loop and check whether the end result is still correct.
@RahulLanjewar93 Do you have any update on the issue? You mentioned you observed the same issue, but the test in #8789 passed, so I wonder if this issue here can be closed?