process write errors on complete or abort in transaction

This commit is contained in:
Bruno Windels 2021-09-17 18:23:31 +02:00
parent 533b0f40d3
commit ad45016b87
3 changed files with 35 additions and 26 deletions

View file

@ -296,14 +296,10 @@ export class Sync {
// avoid corrupting state by only // avoid corrupting state by only
// storing the sync up till the point // storing the sync up till the point
// the exception occurred // the exception occurred
try { syncTxn.abort(log);
syncTxn.abort(); throw syncTxn.getCause(err);
} catch (abortErr) {
log.set("couldNotAbortTxn", true);
} }
throw err; await syncTxn.complete(log);
}
await syncTxn.complete();
} }
_afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) { _afterSync(sessionState, inviteStates, roomStates, archivedRoomStates, log) {

View file

@ -159,13 +159,39 @@ export class Transaction {
return this._store(StoreNames.accountData, idbStore => new AccountDataStore(idbStore)); return this._store(StoreNames.accountData, idbStore => new AccountDataStore(idbStore));
} }
complete(): Promise<void> { async complete(log?: LogItem): Promise<void> {
return txnAsPromise(this._txn); try {
await txnAsPromise(this._txn);
} catch (err) {
if (this._writeErrors.length) {
this._logWriteErrors(log);
throw this._writeErrors[0].error;
}
throw err;
}
} }
abort(): void { getCause(error: Error) {
if (error instanceof StorageError) {
if (error.errcode === "AbortError" && this._writeErrors.length) {
return this._writeErrors[0].error;
}
}
return error;
}
abort(log?: LogItem): void {
// TODO: should we wrap the exception in a StorageError? // TODO: should we wrap the exception in a StorageError?
try {
this._txn.abort(); this._txn.abort();
} catch (abortErr) {
log?.set("couldNotAbortTxn", true);
}
if (this._writeErrors.length) {
this._logWriteErrors(log);
}
}
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, key: IDBValidKey | IDBKeyRange | undefined) { addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, key: IDBValidKey | IDBKeyRange | undefined) {
// don't log subsequent `AbortError`s // don't log subsequent `AbortError`s
if (error.errcode !== "AbortError" || this._writeErrors.length === 0) { if (error.errcode !== "AbortError" || this._writeErrors.length === 0) {

View file

@ -17,6 +17,7 @@ limitations under the License.
import { IDBRequestError } from "./error"; import { IDBRequestError } from "./error";
import { StorageError } from "../common"; import { StorageError } from "../common";
import { AbortError } from "../../../utils/error.js";
let needsSyncPromise = false; let needsSyncPromise = false;
@ -112,22 +113,8 @@ export function txnAsPromise(txn): Promise<void> {
// @ts-ignore // @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });
txn.addEventListener("error", event => {
const request = event.target;
// catch first error here, but don't reject yet,
// as we don't have access to the failed request in the abort event handler
if (!error && request) {
error = new IDBRequestError(request);
}
});
txn.addEventListener("abort", event => { txn.addEventListener("abort", event => {
if (!error) { reject(new AbortError());
const txn = event.target;
const dbName = txn.db.name;
const storeNames = Array.from(txn.objectStoreNames).join(", ")
error = new StorageError(`Transaction on ${dbName} with stores ${storeNames} was aborted.`);
}
reject(error);
// @ts-ignore // @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });