Dexie.js icon indicating copy to clipboard operation
Dexie.js copied to clipboard

Why does deleting not take into effect with cached liveQuery()?

Open rayzhao99 opened this issue 1 year ago • 4 comments

After upgrading to the latest Dexie of v4.0.1-alpha.25, the deleting operations does not take into effect, while all other kinds of operations (e.g. creating, retrieving & updating) work fine.

Our codes are like below:

function deleteByPageIds (pageIds: string[]) {
    return db.transaction('rw', db.pages, db.sessions, () => Promise.all([
        db.sessions.where('pageId').anyOf(pageIds).delete(),
        db.pages.bulkDelete(pageIds),
    ]));
}

We debug into this function to make sure it is invoked with correct pageIds. There are NO any errors thrown. However after the transaction finished, the corresponding records, that relate to the specified pageIds in both pages & sessions, still exists.

It is also noticed that Devtools indicated the tables were modified. But even after reloaded the tables, the records are still there.

We have no ideas on why it happens and howto avoid. Could you please give us any suggestions or advices on this problem?

rayzhao99 avatar Aug 28 '23 15:08 rayzhao99

One assumption by now is due to the interoperation between standard Promise and Dexie.Promise. According to https://dexie.org/docs/Promise/Promise#interopability, and quoted as below

Another reason is that only Dexie.Promise has the capability to keep track of the currently executing transaction between calls (See Promise.PSD).

window.Promise is always safe to use within transactions, as Dexie will patch the global Promise within the transaction zone, but leave it untouched outside the zone.

A more detailed version about our codes is like:

// a facility function to fulfill deleting with a specified callback of `deleter`
function deleteWith (tables: Table[], deleter: () => Promisable<void>) {
    // a transaction is launched with an expectation of possibly cascading 
    // with an outer transaction
    return db.transaction('rw', tables, deleter);
}

class Session {
    deleteByPageIds (pageIds: string[]) {
        // delete records
        return deleteWith([db.sessions], () =>
            db.sessions.where('pageId').anyOf(pageIds).delete());
    }
}

// a singleton instance of `Session`
const sessionDb = new Session();

class Page {
    deleteIds (pageIds: string[]) {
        // deleting recursively with a callback to delete internal/descendant objects  
        return deleteWith([db.pages, db.sessions], () => Promise.all([
            // **PROBLEM: after executing the line below, the call stack lost the track to `Page.deleteIds()`**
            sessionDb.deleteByPageIds(pageIds),
            db.pages.bulkDelete(pageIds),
        ]));
    }
}

The key problem, as noted within Page.deleteIds() and above the line sessionDb.deleteByPageIds(pageIds), is that the call stack lost the track to previous asynchronous invocation. The problem does exist, though the accurate code might be different with the code snippet provided.

So, how could we fix this, e.g. how about sourrounding the returned promise with Dexie.Promise like below

function deleteWith (tables: Table[], deleter: () => Promisable<void>) {
    // a transaction is launched with an expectation of possibly cascading 
    // with an outer transaction
    return Dexie.Promise.resolve(db.transaction('rw', tables, deleter));
}

rayzhao99 avatar Aug 29 '23 04:08 rayzhao99

It seems the problem occurs when invoking some APIs of the standard Promise, according to https://dexie.org/docs/Tutorial/Best-Practices#not-ok. The code is like below

// NOT OK, since `.then()` is invoked within the transaction

db.transaction('rw', db.pages, db.sessions, () => Promise.all([
    db.sessions.where('pageId').anyOf(pageIds).delete(),
    db.pages.bulkDelete(pageIds),
]).then());

// OK, after `.then()` move outside of the transaction
db.transaction('rw', db.pages, db.sessions, () => Promise.all([
    db.sessions.where('pageId').anyOf(pageIds).delete(),
    db.pages.bulkDelete(pageIds),
])).then();

So, is this assumption and solution correct? Please give us some suggestions if possible.

Thanks!

rayzhao99 avatar Aug 29 '23 05:08 rayzhao99

There shouldn't be any issues of this kind what I know of. If you'd have a chance to create a stand-alone reproduction of the issue, I'd be happy to debug it and I'm sure we'll get to the bottom of the problem.

dfahlander avatar Aug 29 '23 06:08 dfahlander

ran into this as well. removing Promise.all fixed the problem. will try to find time this weekend to spin up a test repo

frankleng avatar Sep 13 '23 10:09 frankleng