diff --git a/src/matrix/storage/idb/Store.js b/src/matrix/storage/idb/Store.js index dbe85b2b..cffd03e8 100644 --- a/src/matrix/storage/idb/Store.js +++ b/src/matrix/storage/idb/Store.js @@ -106,8 +106,9 @@ class QueryTargetWrapper { } export class Store extends QueryTarget { - constructor(idbStore) { + constructor(idbStore, transaction) { super(new QueryTargetWrapper(idbStore)); + this._transaction = transaction; } get _idbStore() { @@ -118,33 +119,15 @@ export class Store extends QueryTarget { return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); } - async put(value) { - try { - return await reqAsPromise(this._idbStore.put(value)); - } catch(err) { - const originalErr = err.cause; - throw new StorageError(`put on ${err.databaseName}.${err.storeName} failed`, originalErr, value); - } + put(value) { + this._transaction._addWriteRequest(this._idbStore.put(value)); } - async add(value) { - try { - // this will catch both the sync error already mapped - // in the QueryTargetWrapper above, and also the async request errors, which are still DOMException's - return await reqAsPromise(this._idbStore.add(value)); - } catch(err) { - const originalErr = err.cause; - throw new StorageError(`add on ${err.databaseName}.${err.storeName} failed`, originalErr, value); - } + add(value) { + this._transaction._addWriteRequest(this._idbStore.add(value)); } - async delete(keyOrKeyRange) { - try { - return await reqAsPromise(this._idbStore.delete(keyOrKeyRange)); - } catch(err) { - const originalErr = err.cause; - throw new StorageError(`delete on ${err.databaseName}.${err.storeName} failed`, originalErr, keyOrKeyRange); - } - + delete(keyOrKeyRange) { + this._transaction._addWriteRequest(this._idbStore.delete(keyOrKeyRange)); } } diff --git a/src/matrix/storage/idb/Transaction.js b/src/matrix/storage/idb/Transaction.js index 08eacb34..1b98515a 100644 --- a/src/matrix/storage/idb/Transaction.js +++ b/src/matrix/storage/idb/Transaction.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import {IDBRequestError} from "./error.js"; import {txnAsPromise} from "./utils.js"; import {StorageError} from "../common.js"; import {Store} from "./Store.js"; @@ -38,6 +39,14 @@ export class Transaction { this._txn = txn; this._allowedStoreNames = allowedStoreNames; this._stores = {}; + this._writeRequests = null; + } + + _addWriteRequest(request) { + if (!this._writeRequests) { + this._writeRequests = []; + } + this._writeRequests.push(request); } _idbStore(name) { @@ -45,7 +54,7 @@ export class Transaction { // more specific error? this is a bug, so maybe not ... throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`); } - return new Store(this._txn.objectStore(name)); + return new Store(this._txn.objectStore(name), this); } _store(name, mapStore) { @@ -116,8 +125,28 @@ export class Transaction { return this._store("accountData", idbStore => new AccountDataStore(idbStore)); } - complete() { - return txnAsPromise(this._txn); + async complete() { + // check the write requests if we haven't failed yet + if (this._writeRequests) { + for (const request of this._writeRequests) { + if (request.error) { + try { + this.abort(); + } catch (err) {/* ignore abort error, although it would be useful to know if the other stuff got committed or not... */} + return Promise.reject(new IDBRequestError(request, "Write request failed")); + } + } + } + const result = await txnAsPromise(this._txn); + // check the write requests if we haven't failed yet + if (this._writeRequests) { + for (const request of this._writeRequests) { + if (request.readyState !== "done") { + return Promise.reject(new IDBRequestError(request, "Request is still pending after transaction finished")); + } + } + } + return result; } abort() {