forked from mystiq/hydrogen-web
detect the webkit bug, and await a bogus request when opening a txn
This commit is contained in:
parent
e0d14207ac
commit
932d26ed8c
3 changed files with 60 additions and 2 deletions
|
@ -16,10 +16,14 @@ limitations under the License.
|
||||||
|
|
||||||
import {Transaction} from "./Transaction.js";
|
import {Transaction} from "./Transaction.js";
|
||||||
import { STORE_NAMES, StorageError } from "../common.js";
|
import { STORE_NAMES, StorageError } from "../common.js";
|
||||||
|
import { reqAsPromise } from "./utils.js";
|
||||||
|
|
||||||
|
const WEBKITEARLYCLOSETXNBUG_BOGUS_KEY = "782rh281re38-boguskey";
|
||||||
|
|
||||||
export class Storage {
|
export class Storage {
|
||||||
constructor(idbDatabase) {
|
constructor(idbDatabase, hasWebkitEarlyCloseTxnBug) {
|
||||||
this._db = idbDatabase;
|
this._db = idbDatabase;
|
||||||
|
this._hasWebkitEarlyCloseTxnBug = hasWebkitEarlyCloseTxnBug;
|
||||||
const nameMap = STORE_NAMES.reduce((nameMap, name) => {
|
const nameMap = STORE_NAMES.reduce((nameMap, name) => {
|
||||||
nameMap[name] = name;
|
nameMap[name] = name;
|
||||||
return nameMap;
|
return nameMap;
|
||||||
|
@ -38,6 +42,11 @@ export class Storage {
|
||||||
this._validateStoreNames(storeNames);
|
this._validateStoreNames(storeNames);
|
||||||
try {
|
try {
|
||||||
const txn = this._db.transaction(storeNames, "readonly");
|
const txn = this._db.transaction(storeNames, "readonly");
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=222746 workaround,
|
||||||
|
// await a bogus idb request on the new txn so it doesn't close early if we await a microtask first
|
||||||
|
if (this._hasWebkitEarlyCloseTxnBug) {
|
||||||
|
await reqAsPromise(txn.objectStore(storeNames[0]).get(WEBKITEARLYCLOSETXNBUG_BOGUS_KEY));
|
||||||
|
}
|
||||||
return new Transaction(txn, storeNames);
|
return new Transaction(txn, storeNames);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
throw new StorageError("readTxn failed", err);
|
throw new StorageError("readTxn failed", err);
|
||||||
|
@ -48,6 +57,11 @@ export class Storage {
|
||||||
this._validateStoreNames(storeNames);
|
this._validateStoreNames(storeNames);
|
||||||
try {
|
try {
|
||||||
const txn = this._db.transaction(storeNames, "readwrite");
|
const txn = this._db.transaction(storeNames, "readwrite");
|
||||||
|
// https://bugs.webkit.org/show_bug.cgi?id=222746 workaround,
|
||||||
|
// await a bogus idb request on the new txn so it doesn't close early if we await a microtask first
|
||||||
|
if (this._hasWebkitEarlyCloseTxnBug) {
|
||||||
|
await reqAsPromise(txn.objectStore(storeNames[0]).get(WEBKITEARLYCLOSETXNBUG_BOGUS_KEY));
|
||||||
|
}
|
||||||
return new Transaction(txn, storeNames);
|
return new Transaction(txn, storeNames);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
throw new StorageError("readWriteTxn failed", err);
|
throw new StorageError("readWriteTxn failed", err);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {Storage} from "./Storage.js";
|
||||||
import { openDatabase, reqAsPromise } from "./utils.js";
|
import { openDatabase, reqAsPromise } from "./utils.js";
|
||||||
import { exportSession, importSession } from "./export.js";
|
import { exportSession, importSession } from "./export.js";
|
||||||
import { schema } from "./schema.js";
|
import { schema } from "./schema.js";
|
||||||
|
import { detectWebkitEarlyCloseTxnBug } from "./quirks.js";
|
||||||
|
|
||||||
const sessionName = sessionId => `hydrogen_session_${sessionId}`;
|
const sessionName = sessionId => `hydrogen_session_${sessionId}`;
|
||||||
const openDatabaseWithSessionId = sessionId => openDatabase(sessionName(sessionId), createStores, schema.length);
|
const openDatabaseWithSessionId = sessionId => openDatabase(sessionName(sessionId), createStores, schema.length);
|
||||||
|
@ -50,8 +51,10 @@ export class StorageFactory {
|
||||||
console.warn("no persisted storage, database can be evicted by browser");
|
console.warn("no persisted storage, database can be evicted by browser");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasWebkitEarlyCloseTxnBug = await detectWebkitEarlyCloseTxnBug();
|
||||||
const db = await openDatabaseWithSessionId(sessionId);
|
const db = await openDatabaseWithSessionId(sessionId);
|
||||||
return new Storage(db);
|
return new Storage(db, hasWebkitEarlyCloseTxnBug);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(sessionId) {
|
delete(sessionId) {
|
||||||
|
|
41
src/matrix/storage/idb/quirks.js
Normal file
41
src/matrix/storage/idb/quirks.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import {openDatabase, txnAsPromise, reqAsPromise} from "./utils.js";
|
||||||
|
|
||||||
|
// filed as https://bugs.webkit.org/show_bug.cgi?id=222746
|
||||||
|
export async function detectWebkitEarlyCloseTxnBug() {
|
||||||
|
const dbName = "hydrogen_webkit_test_inactive_txn_bug";
|
||||||
|
try {
|
||||||
|
const db = await openDatabase(dbName, db => {
|
||||||
|
db.createObjectStore("test", {keyPath: "key"});
|
||||||
|
}, 1);
|
||||||
|
const readTxn = db.transaction(["test"], "readonly");
|
||||||
|
await reqAsPromise(readTxn.objectStore("test").get("somekey"));
|
||||||
|
// schedule a macro task in between the two txns
|
||||||
|
await new Promise(r => setTimeout(r, 0));
|
||||||
|
const writeTxn = db.transaction(["test"], "readwrite");
|
||||||
|
await Promise.resolve();
|
||||||
|
writeTxn.objectStore("test").add({key: "somekey", value: "foo"});
|
||||||
|
await txnAsPromise(writeTxn);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.name === "TransactionInactiveError") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
Loading…
Reference in a new issue