Skip to content

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

@MarkDuckworth

Description

@MarkDuckworth

[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();

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions