Skip to content

transaction.set(...) fails the transaction without retry when document created by another app #6659

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

Closed
MarkDuckworth opened this issue Oct 6, 2022 · 3 comments
Assignees

Comments

@MarkDuckworth
Copy link
Contributor

[REQUIRED] Describe your environment

  • Firebase SDK version: 9.10.0
  • Firebase Product: firestore

[REQUIRED] Describe the problem

transaction.get(ref) followed by transaction.set(ref, {...}) is causing the transaction to fail with error 'Document already exists' if the document was created by another app between the get and set.

Expected behavior is for the transaction to fail the first attempt because the document changed (was created) between the read and write. But it's also expected that the transaction will automatically retry.

I see the expected behavior in v9.6.11, and the error in v9.10.0. There appears to be a regression somewhere in between these versions.

Steps to reproduce:

  1. Before repro, execute clean() to delete the document.
  2. Execute method transact().
  3. During the sleep in transact(), execute method write(). This might be done by a second browser.
  4. Observe that the transaction from step 2 fails without retry. But the transaction retries and is successful in 9.6.11.

Relevant Code:

async function sleep(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

async function clean() {
    console.log("starting clean")
    await deleteDoc(ref);
    console.log("finished clean")
}

async function write() {
    await setDoc(ref, {created: (new Date()).toUTCString()})
    console.log("Doc created")
}

async function transact() {
    await runTransaction(db, async (tx) => {
        const snapshot = await tx.get(ref);
        console.log("doc read")

        if (snapshot.exists()) {
            console.log("document exists - updating last write")
            tx.update(ref, {lastWrite: (new Date()).toUTCString()})
            return;
        }

        console.log("sleeping")
        await sleep(10000);
        console.log("wake from sleep")

        tx.set(ref, {created: (new Date()).toUTCString()});
    });

    console.log("Transaction successful");
}

async function reproduce()
{
    await clean();
    setTimeout(write, 2000);
    transact();
}

reproduce();
@tom-andersen
Copy link
Contributor

Thank you very much for the example code! This made reproduction of problem very easy.

We have a fix planned. I will let you know when it becomes available.

@milaGGL
Copy link
Contributor

milaGGL commented Oct 28, 2022

Hi @MarkDuckworth, the bug fix has been merged and will be included in the next release.

@dconeybe
Copy link
Contributor

FYI v9.14.0 released on November 10, 2022 contains the fix
https://firebase.google.com/support/release-notes/js#version_9140_-_november_10_2022

@firebase firebase locked and limited conversation to collaborators Dec 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants