From 471a9eb8b2a0041df7fffd5b6d4025ae7b81e75a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 21 Jul 2021 13:59:30 +0100 Subject: [PATCH 1/8] Use relaxed durability for chrome --- src/classes/transaction/transaction.ts | 4 ++-- src/public/types/dbcore.d.ts | 8 +++++++- src/public/types/global.d.ts | 9 +++++++++ test/tests-transaction.js | 8 ++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/public/types/global.d.ts diff --git a/src/classes/transaction/transaction.ts b/src/classes/transaction/transaction.ts index 59ee5fde6..5865b8569 100644 --- a/src/classes/transaction/transaction.ts +++ b/src/classes/transaction/transaction.ts @@ -115,8 +115,8 @@ export class Transaction implements ITransaction { idbtrans = this.idbtrans = idbtrans || (this.db.core - ? this.db.core.transaction(this.storeNames, this.mode as 'readwrite' | 'readonly') - : idbdb.transaction(this.storeNames, this.mode) + ? this.db.core.transaction(this.storeNames, this.mode as 'readwrite' | 'readonly', { durability: 'relaxed' }) + : idbdb.transaction(this.storeNames, this.mode, { durability: 'relaxed' }) ) as IDBTransaction; idbtrans.onerror = wrap(ev => { diff --git a/src/public/types/dbcore.d.ts b/src/public/types/dbcore.d.ts index 92f756947..50bfcc8fe 100644 --- a/src/public/types/dbcore.d.ts +++ b/src/public/types/dbcore.d.ts @@ -20,6 +20,12 @@ export interface DBCoreTransaction { abort(): void; } +type DbCoreTransactionDurability = 'default' | 'strict' | 'relaxed' + +interface DbCoreTransactionOptions { + durability: DbCoreTransactionDurability +} + export type DBCoreMutateRequest = DBCoreAddRequest | DBCorePutRequest | DBCoreDeleteRequest | DBCoreDeleteRangeRequest; export interface DBCoreMutateResponse { @@ -158,7 +164,7 @@ export interface DBCoreIndex { export interface DBCore { stack: "dbcore"; // Transaction and Object Store - transaction(stores: string[], mode: 'readonly' | 'readwrite'): DBCoreTransaction; + transaction(stores: string[], mode: 'readonly' | 'readwrite', options?: DbCoreTransactionOptions): DBCoreTransaction; // Utility methods cmp(a: any, b: any) : number; diff --git a/src/public/types/global.d.ts b/src/public/types/global.d.ts new file mode 100644 index 000000000..af2ad6ea8 --- /dev/null +++ b/src/public/types/global.d.ts @@ -0,0 +1,9 @@ +type IDBTransactionDurability = 'default' | 'strict' | 'relaxed' + +interface IDBTransactionOptions { + durability: IDBTransactionDurability +} + +interface IDBDatabase { + transaction(storeNames: string | string[], mode?: IDBTransactionMode, options?: IDBTransactionOptions): IDBTransaction +} \ No newline at end of file diff --git a/test/tests-transaction.js b/test/tests-transaction.js index 0a0b0fef4..3f6bb40d0 100644 --- a/test/tests-transaction.js +++ b/test/tests-transaction.js @@ -48,6 +48,14 @@ asyncTest("Transaction should work when returning native Promise in transaction }).finally(start); }); +asyncTest("Transaction should specify durability as relaxed", function() { + db.transaction('rw', db.users, db.pets, trans => { + ok(trans.idbtrans.durability === 'relaxed', "Transaction has relaxed durability"); + }).catch(function (err) { + ok(false, err); + }).finally(start); +}); + asyncTest("empty transaction block", function () { db.transaction('rw', db.users, db.pets, function () { ok(true, "Entering transaction block but dont start any transaction"); From 3705db51bade3bc7c6c2541dd2b113a7bc28c998 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 10:59:25 +0100 Subject: [PATCH 2/8] add comment explaining global type additions --- src/public/types/global.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/public/types/global.d.ts b/src/public/types/global.d.ts index af2ad6ea8..7ed798439 100644 --- a/src/public/types/global.d.ts +++ b/src/public/types/global.d.ts @@ -1,3 +1,7 @@ +/** + * Patching in types for Chrome's extended transaction API + * Corresponds to this change: https://chromium.googlesource.com/chromium/src/+/d762124e2b4090a7985cddc5438678de7900fcc4/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl + */ type IDBTransactionDurability = 'default' | 'strict' | 'relaxed' interface IDBTransactionOptions { From 12e04659b7707f567f95a29908906c6e9451011e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:15:25 +0100 Subject: [PATCH 3/8] plumb chromeTransactionDurability as a Dexie option. extract out separate test suite for the durability stuff --- src/classes/dexie/dexie.ts | 5 +- .../transaction/transaction-constructor.ts | 3 + src/classes/transaction/transaction.ts | 5 +- src/functions/temp-transaction.ts | 2 +- src/public/types/dbcore.d.ts | 6 +- src/public/types/dexie-constructor.d.ts | 7 ++- src/public/types/global.d.ts | 4 +- test/tests-all.js | 1 + test/tests-chrome-transaction-durability.js | 61 +++++++++++++++++++ test/tests-transaction.js | 8 --- 10 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 test/tests-chrome-transaction-durability.js diff --git a/src/classes/dexie/dexie.ts b/src/classes/dexie/dexie.ts index b8c11b446..4b18a7850 100644 --- a/src/classes/dexie/dexie.ts +++ b/src/classes/dexie/dexie.ts @@ -96,7 +96,8 @@ export class Dexie implements IDexie { // Default DOM dependency implementations from static prop. indexedDB: deps.indexedDB, // Backend IndexedDB api. Default to browser env. IDBKeyRange: deps.IDBKeyRange, // Backend IDBKeyRange api. Default to browser env. - ...options + chromeTransactionDurability: options?.chromeTransactionDurability || 'default', // Fall back on 'default' if not passed + ...options, }; this._deps = { indexedDB: options.indexedDB as IDBFactory, @@ -198,7 +199,7 @@ export class Dexie implements IDexie { mode: IDBTransactionMode, storeNames: string[], dbschema: DbSchema, - parentTransaction?: Transaction) => new this.Transaction(mode, storeNames, dbschema, parentTransaction); + parentTransaction?: Transaction) => new this.Transaction(mode, storeNames, dbschema, this._options.chromeTransactionDurability, parentTransaction); this._fireOnBlocked = ev => { this.on("blocked").fire(ev); diff --git a/src/classes/transaction/transaction-constructor.ts b/src/classes/transaction/transaction-constructor.ts index 426efbba4..0472fb3dd 100644 --- a/src/classes/transaction/transaction-constructor.ts +++ b/src/classes/transaction/transaction-constructor.ts @@ -10,6 +10,7 @@ export interface TransactionConstructor { mode: IDBTransactionMode, storeNames: string[], dbschema: DbSchema, + chromeTransactionDurability: ChromeTransactionDurability, parent?: Transaction) : T; prototype: T; } @@ -28,12 +29,14 @@ export function createTransactionConstructor(db: Dexie) { mode: IDBTransactionMode, storeNames: string[], dbschema: DbSchema, + chromeTransactionDurability: ChromeTransactionDurability, parent?: Transaction) { this.db = db; this.mode = mode; this.storeNames = storeNames; this.schema = dbschema; + this.chromeTransactionDurability = chromeTransactionDurability; this.idbtrans = null; this.on = Events(this, "complete", "error", "abort"); this.parent = parent || null; diff --git a/src/classes/transaction/transaction.ts b/src/classes/transaction/transaction.ts index 5865b8569..9f9e01804 100644 --- a/src/classes/transaction/transaction.ts +++ b/src/classes/transaction/transaction.ts @@ -21,6 +21,7 @@ export class Transaction implements ITransaction { db: Dexie; active: boolean; mode: IDBTransactionMode; + chromeTransactionDurability: ChromeTransactionDurability; idbtrans: IDBTransaction; storeNames: string[]; on: any; @@ -115,8 +116,8 @@ export class Transaction implements ITransaction { idbtrans = this.idbtrans = idbtrans || (this.db.core - ? this.db.core.transaction(this.storeNames, this.mode as 'readwrite' | 'readonly', { durability: 'relaxed' }) - : idbdb.transaction(this.storeNames, this.mode, { durability: 'relaxed' }) + ? this.db.core.transaction(this.storeNames, this.mode as 'readwrite' | 'readonly', { durability: this.chromeTransactionDurability }) + : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }) ) as IDBTransaction; idbtrans.onerror = wrap(ev => { diff --git a/src/functions/temp-transaction.ts b/src/functions/temp-transaction.ts index f8866cfc2..d61948656 100644 --- a/src/functions/temp-transaction.ts +++ b/src/functions/temp-transaction.ts @@ -14,7 +14,7 @@ export function tempTransaction ( fn: (resolve, reject, trans: Transaction) => any) // Last argument is "writeLocked". But this doesnt apply to oneshot direct db operations, so we ignore it. { - if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { + if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { if (db._state.openComplete) { // db.idbdb is falsy but openComplete is true. Must have been an exception durin open. // Don't wait for openComplete as it would lead to infinite loop. diff --git a/src/public/types/dbcore.d.ts b/src/public/types/dbcore.d.ts index 50bfcc8fe..1d623e0f4 100644 --- a/src/public/types/dbcore.d.ts +++ b/src/public/types/dbcore.d.ts @@ -1,5 +1,7 @@ // For public interface +import {ChromeTransactionDurability} from "./dexie-constructor"; + export const enum DBCoreRangeType { Equal = 1, Range = 2, @@ -20,10 +22,8 @@ export interface DBCoreTransaction { abort(): void; } -type DbCoreTransactionDurability = 'default' | 'strict' | 'relaxed' - interface DbCoreTransactionOptions { - durability: DbCoreTransactionDurability + durability: ChromeTransactionDurability } export type DBCoreMutateRequest = DBCoreAddRequest | DBCorePutRequest | DBCoreDeleteRequest | DBCoreDeleteRangeRequest; diff --git a/src/public/types/dexie-constructor.d.ts b/src/public/types/dexie-constructor.d.ts index 9fcd5729d..e5aa8bbf6 100644 --- a/src/public/types/dexie-constructor.d.ts +++ b/src/public/types/dexie-constructor.d.ts @@ -10,13 +10,16 @@ import { DexieDOMDependencies } from "./dexie-dom-dependencies"; import { GlobalDexieEvents, ObservabilitySet } from "./db-events"; import { Observable } from "./observable"; +export type ChromeTransactionDurability = 'default' | 'strict' | 'relaxed' + export interface DexieOptions { addons?: Array<(db: Dexie) => void>, autoOpen?: boolean, indexedDB?: {open: Function}, IDBKeyRange?: {bound: Function, lowerBound: Function, upperBound: Function}, - allowEmptyDB?: boolean; - modifyChunkSize?: number + allowEmptyDB?: boolean, + modifyChunkSize?: number, + chromeTransactionDurability?: ChromeTransactionDurability } export interface DexieConstructor extends DexieExceptionClasses { diff --git a/src/public/types/global.d.ts b/src/public/types/global.d.ts index 7ed798439..b35d839af 100644 --- a/src/public/types/global.d.ts +++ b/src/public/types/global.d.ts @@ -2,10 +2,10 @@ * Patching in types for Chrome's extended transaction API * Corresponds to this change: https://chromium.googlesource.com/chromium/src/+/d762124e2b4090a7985cddc5438678de7900fcc4/third_party/blink/renderer/modules/indexeddb/idb_transaction_options.idl */ -type IDBTransactionDurability = 'default' | 'strict' | 'relaxed' +type ChromeTransactionDurability = 'default' | 'strict' | 'relaxed' interface IDBTransactionOptions { - durability: IDBTransactionDurability + durability: ChromeTransactionDurability } interface IDBDatabase { diff --git a/test/tests-all.js b/test/tests-all.js index e0f0d3301..e565965b6 100644 --- a/test/tests-all.js +++ b/test/tests-all.js @@ -1,6 +1,7 @@ import Dexie from 'dexie'; Dexie.test = true; // Improve code coverage import "./tests-table.js"; +import "./tests-chrome-transaction-durability.js"; import "./tests-collection.js"; import "./tests-whereclause.js"; import "./tests-transaction.js"; diff --git a/test/tests-chrome-transaction-durability.js b/test/tests-chrome-transaction-durability.js new file mode 100644 index 000000000..e0f35c966 --- /dev/null +++ b/test/tests-chrome-transaction-durability.js @@ -0,0 +1,61 @@ +import Dexie from 'dexie'; +import {start, asyncTest, ok} from 'QUnit'; +import {resetDatabase} from './dexie-unittest-utils'; + +"use strict"; + +module("chrome-transaction-durability", { + setup: function () { + }, + teardown: function () { + } +}); + +asyncTest("Transaction should use relaxed durability if specified", function() { + const db = setupDb('relaxed') + db.transaction('rw', db.users, trans => { + ok(trans.idbtrans.durability === 'relaxed', "Transaction has relaxed durability"); + }).catch(function (err) { + ok(false, err); + }).finally(function () { + resetDatabase(db).catch(function (e) { + ok(false, "Error resetting database: " + e.stack); + }).finally(start); + }); +}); + + +asyncTest("Transaction should use strict durability if specified", function() { + const db = setupDb('strict') + db.transaction('rw', db.users, trans => { + ok(trans.idbtrans.durability === 'strict', "Transaction has strict durability"); + }).catch(function (err) { + ok(false, err); + }).finally(function () { + resetDatabase(db).catch(function (e) { + ok(false, "Error resetting database: " + e.stack); + }).finally(start); + }); +}); + + +asyncTest("Transaction should use default durability if not specified", function() { + const db = setupDb() + db.transaction('rw', db.users, trans => { + ok(trans.idbtrans.durability === 'default', "Transaction has default durability"); + }).catch(function (err) { + ok(false, err); + }).finally(function () { + resetDatabase(db).catch(function (e) { + ok(false, "Error resetting database: " + e.stack); + }).finally(start); + }); +}); + +const setupDb = (chromeTransactionDurability) => { + const db = new Dexie("TestDBTrans", { chromeTransactionDurability }); + db.version(1).stores({ + users: "username", + }); + return db; +} \ No newline at end of file diff --git a/test/tests-transaction.js b/test/tests-transaction.js index 3f6bb40d0..0a0b0fef4 100644 --- a/test/tests-transaction.js +++ b/test/tests-transaction.js @@ -48,14 +48,6 @@ asyncTest("Transaction should work when returning native Promise in transaction }).finally(start); }); -asyncTest("Transaction should specify durability as relaxed", function() { - db.transaction('rw', db.users, db.pets, trans => { - ok(trans.idbtrans.durability === 'relaxed', "Transaction has relaxed durability"); - }).catch(function (err) { - ok(false, err); - }).finally(start); -}); - asyncTest("empty transaction block", function () { db.transaction('rw', db.users, db.pets, function () { ok(true, "Entering transaction block but dont start any transaction"); From 9a2d15908a5960e3daf7eb6dc22fa7cb9aec81d3 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 14:19:53 +0100 Subject: [PATCH 4/8] rm unnecessary default - test still passes --- src/classes/dexie/dexie.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/classes/dexie/dexie.ts b/src/classes/dexie/dexie.ts index 4b18a7850..406b33d57 100644 --- a/src/classes/dexie/dexie.ts +++ b/src/classes/dexie/dexie.ts @@ -96,7 +96,6 @@ export class Dexie implements IDexie { // Default DOM dependency implementations from static prop. indexedDB: deps.indexedDB, // Backend IndexedDB api. Default to browser env. IDBKeyRange: deps.IDBKeyRange, // Backend IDBKeyRange api. Default to browser env. - chromeTransactionDurability: options?.chromeTransactionDurability || 'default', // Fall back on 'default' if not passed ...options, }; this._deps = { From 8987ab8178bbe949014cf8155c118dac74fec934 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Jul 2021 17:03:19 +0100 Subject: [PATCH 5/8] wrap tests with isChrome --- test/dexie-unittest-utils.js | 3 +++ test/tests-chrome-transaction-durability.js | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/test/dexie-unittest-utils.js b/test/dexie-unittest-utils.js index 3723ce5ac..40ff0aa81 100644 --- a/test/dexie-unittest-utils.js +++ b/test/dexie-unittest-utils.js @@ -126,6 +126,7 @@ export function deleteDatabase(db) { export const isIE = !(window.ActiveXObject) && "ActiveXObject" in window; export const isEdge = /Edge\/\d+/.test(navigator.userAgent); +export const isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime); var hasPolyfillIE = [].slice.call(document.getElementsByTagName("script")).some( s => s.src.indexOf("idb-iegap") !== -1); @@ -149,6 +150,8 @@ export function supports (features) { } case "domevents": return typeof window === 'object' && window.addEventListener; + case "transactiondurability": + return IDBTransaction.prototype.durability === 'default'; default: throw new Error ("Unknown feature: " + feature); diff --git a/test/tests-chrome-transaction-durability.js b/test/tests-chrome-transaction-durability.js index e0f35c966..221bba7de 100644 --- a/test/tests-chrome-transaction-durability.js +++ b/test/tests-chrome-transaction-durability.js @@ -1,6 +1,6 @@ import Dexie from 'dexie'; import {start, asyncTest, ok} from 'QUnit'; -import {resetDatabase} from './dexie-unittest-utils'; +import {isChrome, resetDatabase} from './dexie-unittest-utils'; "use strict"; @@ -12,6 +12,12 @@ module("chrome-transaction-durability", { }); asyncTest("Transaction should use relaxed durability if specified", function() { + if (!isChrome) { + ok(true, "This browser does not Chrome transaction durability"); + start(); + return; + } + const db = setupDb('relaxed') db.transaction('rw', db.users, trans => { ok(trans.idbtrans.durability === 'relaxed', "Transaction has relaxed durability"); @@ -26,6 +32,12 @@ asyncTest("Transaction should use relaxed durability if specified", function() { asyncTest("Transaction should use strict durability if specified", function() { + if (!isChrome) { + ok(true, "This browser does not Chrome transaction durability"); + start(); + return; + } + const db = setupDb('strict') db.transaction('rw', db.users, trans => { ok(trans.idbtrans.durability === 'strict', "Transaction has strict durability"); @@ -40,6 +52,12 @@ asyncTest("Transaction should use strict durability if specified", function() { asyncTest("Transaction should use default durability if not specified", function() { + if (!isChrome) { + ok(true, "This browser does not Chrome transaction durability"); + start(); + return; + } + const db = setupDb() db.transaction('rw', db.users, trans => { ok(trans.idbtrans.durability === 'default', "Transaction has default durability"); From 8f05c067a63115813615f47820751da621335e1c Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Aug 2021 09:07:21 +0100 Subject: [PATCH 6/8] simplify isChrome check --- test/dexie-unittest-utils.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/dexie-unittest-utils.js b/test/dexie-unittest-utils.js index 40ff0aa81..f9f37de2c 100644 --- a/test/dexie-unittest-utils.js +++ b/test/dexie-unittest-utils.js @@ -126,7 +126,7 @@ export function deleteDatabase(db) { export const isIE = !(window.ActiveXObject) && "ActiveXObject" in window; export const isEdge = /Edge\/\d+/.test(navigator.userAgent); -export const isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime); +export const isChrome = !!window.chrome; var hasPolyfillIE = [].slice.call(document.getElementsByTagName("script")).some( s => s.src.indexOf("idb-iegap") !== -1); @@ -150,8 +150,6 @@ export function supports (features) { } case "domevents": return typeof window === 'object' && window.addEventListener; - case "transactiondurability": - return IDBTransaction.prototype.durability === 'default'; default: throw new Error ("Unknown feature: " + feature); From c421f26270c1fd06296a8669ddcbc3a34a0ab052 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Aug 2021 09:08:15 +0100 Subject: [PATCH 7/8] fix typos --- test/tests-chrome-transaction-durability.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/tests-chrome-transaction-durability.js b/test/tests-chrome-transaction-durability.js index 221bba7de..3c53d56d9 100644 --- a/test/tests-chrome-transaction-durability.js +++ b/test/tests-chrome-transaction-durability.js @@ -13,7 +13,7 @@ module("chrome-transaction-durability", { asyncTest("Transaction should use relaxed durability if specified", function() { if (!isChrome) { - ok(true, "This browser does not Chrome transaction durability"); + ok(true, "This browser does not support Chrome transaction durability"); start(); return; } @@ -33,7 +33,7 @@ asyncTest("Transaction should use relaxed durability if specified", function() { asyncTest("Transaction should use strict durability if specified", function() { if (!isChrome) { - ok(true, "This browser does not Chrome transaction durability"); + ok(true, "This browser does not support Chrome transaction durability"); start(); return; } @@ -53,7 +53,7 @@ asyncTest("Transaction should use strict durability if specified", function() { asyncTest("Transaction should use default durability if not specified", function() { if (!isChrome) { - ok(true, "This browser does not Chrome transaction durability"); + ok(true, "This browser does not support Chrome transaction durability"); start(); return; } From c0dee79c4b7fa17f4ddd38c65df93a06ec8dca5a Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Aug 2021 10:03:35 +0100 Subject: [PATCH 8/8] get another build going --- src/classes/dexie/dexie.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/dexie/dexie.ts b/src/classes/dexie/dexie.ts index 406b33d57..0abacfab7 100644 --- a/src/classes/dexie/dexie.ts +++ b/src/classes/dexie/dexie.ts @@ -96,7 +96,7 @@ export class Dexie implements IDexie { // Default DOM dependency implementations from static prop. indexedDB: deps.indexedDB, // Backend IndexedDB api. Default to browser env. IDBKeyRange: deps.IDBKeyRange, // Backend IDBKeyRange api. Default to browser env. - ...options, + ...options }; this._deps = { indexedDB: options.indexedDB as IDBFactory,