idb
idb copied to clipboard
[Question] How to prevent errors from failing transaction
Hi, I want to only add items to the db and ignore those that are already present in db. ID is unique, I did
async storeUsers(users: StoredUserPreview[]) {
const tx = await this.db.transaction('users', 'readwrite');
return Promise.all(
users.map((user) => tx.store.add(user)).concat(tx.done as any),
);
}
but when at least one key is already in the db whole transaction is rejected.
I found this issue that is build in native indexeddb and seems to be resolved. Is this possible to adapt?
I know that there is an option to first search if the key exists using count. What is the difference of those two methods (count vs catching error) performance-wise ?
Here's how I'd do it with the current library:
function preventTransationCloseOnError(promise) {
const request = unwrap(promise);
request.addEventListener('error', (event) => {
event.preventDefault();
event.stopPropagation();
});
return promise;
}
const tx = db.transaction('users', 'readwrite');
return Promise.all(
...users.map((user) => preventTransationCloseOnError(tx.store.add(user))),
tx.done,
);
However, this seems like a rough edge with the library, so I'll think about a better way to do this in a future version.
unwrap
is a function exported by the library.
Thanks! I will test it later today. Is this approach even sensible? I feel like cheating a bit.
Yesterday I managed to use count to get to the same result. Maybe for reference if anyone else needs it:
async storeUsers(users: StoredUser[]) {
const usersToAdd = []
for (const user of users) {
const isUserNotInDb = await this.db.countFromIndex('users', 'userId', user.id) === 0;
if(isUserNotInDb) {
usersToAdd.push(user);
}
}
const tx = await this.db.transaction('users', 'readwrite');
return Promise.all(
usersToAdd.map(user => tx.store.add(user)).concat(tx.done as any)
);
}
My gut feeling is that the preventTransationCloseOnError
approach will be faster.
I think the simplest way to put this into the library would be a ignoreConstraints
method, that takes a promise wrapping a Request
, and:
- Prevents defaults on requests where the error is a
ConstraintError
- Returns a promise similar to the input, but resolves with undefined if the request errors with a
ConstraintError
In addition, I need to ensure I'm only reacting to events with a matching target.
It would be great to have an option to have add
not fail if the key already exists or perhaps to replace the data at the key.
Isn't that exactly what set
does?
Isn't that exactly what
set
does?
I'm not sure what you mean. I used store.put
for my use case because I want the values to update each time, but store.add
errors if the key already exists. It might be nice to have an option for it to not error.