parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.71k stars 4.76k forks source link

Increment does not appear to be atomic #8772

Open theolundqvist opened 9 months ago

theolundqvist commented 9 months ago

New Issue Checklist

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". ```js 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

Database

Client

Logs

parse-github-assistant[bot] commented 9 months ago

Thanks for opening this issue!

mtrezza commented 9 months ago

Could you add a test case with that example that you already posted?

theolundqvist commented 8 months ago

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);
mtrezza commented 8 months ago

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?

theolundqvist commented 8 months ago

Actually I can't find any. I'll open a PR

RahulLanjewar93 commented 8 months ago

Yea even for me the updates are not atomic

theolundqvist commented 7 months ago

Yea even for me the updates are not atomic

Could you elaborate on this? The test passes. Can you provide an example?

RahulLanjewar93 commented 7 months ago

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.

theolundqvist commented 7 months ago

@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

RahulLanjewar93 commented 7 months ago

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.

mtrezza commented 3 months ago

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.

mtrezza commented 1 week ago

@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?