diff --git a/src/classes/dexie/dexie.ts b/src/classes/dexie/dexie.ts index b8c11b446..0abacfab7 100644 --- a/src/classes/dexie/dexie.ts +++ b/src/classes/dexie/dexie.ts @@ -198,7 +198,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 59ee5fde6..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') - : idbdb.transaction(this.storeNames, this.mode) + ? 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 92f756947..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,6 +22,10 @@ export interface DBCoreTransaction { abort(): void; } +interface DbCoreTransactionOptions { + durability: ChromeTransactionDurability +} + 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/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 new file mode 100644 index 000000000..b35d839af --- /dev/null +++ b/src/public/types/global.d.ts @@ -0,0 +1,13 @@ +/** + * 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 ChromeTransactionDurability = 'default' | 'strict' | 'relaxed' + +interface IDBTransactionOptions { + durability: ChromeTransactionDurability +} + +interface IDBDatabase { + transaction(storeNames: string | string[], mode?: IDBTransactionMode, options?: IDBTransactionOptions): IDBTransaction +} \ No newline at end of file diff --git a/test/dexie-unittest-utils.js b/test/dexie-unittest-utils.js index 3723ce5ac..f9f37de2c 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; var hasPolyfillIE = [].slice.call(document.getElementsByTagName("script")).some( s => s.src.indexOf("idb-iegap") !== -1); 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..3c53d56d9 --- /dev/null +++ b/test/tests-chrome-transaction-durability.js @@ -0,0 +1,79 @@ +import Dexie from 'dexie'; +import {start, asyncTest, ok} from 'QUnit'; +import {isChrome, resetDatabase} from './dexie-unittest-utils'; + +"use strict"; + +module("chrome-transaction-durability", { + setup: function () { + }, + teardown: function () { + } +}); + +asyncTest("Transaction should use relaxed durability if specified", function() { + if (!isChrome) { + ok(true, "This browser does not support Chrome transaction durability"); + start(); + return; + } + + 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() { + if (!isChrome) { + ok(true, "This browser does not support Chrome transaction durability"); + start(); + return; + } + + 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() { + if (!isChrome) { + ok(true, "This browser does not support Chrome transaction durability"); + start(); + return; + } + + 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