Skip to content

Commit

Permalink
Support for using other db within a transaction
Browse files Browse the repository at this point in the history
This commit makes it easy to have multiple Dexie instances and be able
to use one within the transaction scope of the other without having to
work around this with Dexie.spawn() or transaction scopes marked with
"!".

The simple scenario is like this:

// Application database
var db = new Dexie("app-database");

// Log database
var logDb = new Dexie("log");

You want to be able to use logDb within transactions of db. But in
previous version this was only possible if surrounding calls to logDb in
a transaction marked as "!", or by using Dexie.spawn().

In previous version, you would have to encapsulate all calls to logDb
like this:

// Code needed before:
function log(message) {
logDb.transaction('rw!', logDb.log, function () {
logDb.log.add(logEntry);
});
}

After this commit, this is no longer needed since it could be told by
the ongoing transaction whether it belongs to the same db or not:

// Now you don't need to care:
function log(message) {
logDb.log.add(logEntry);
}

Also: Corrected some lazy coded unit tests that utilized its own Dexie
instances (testDB etc) but lazily used just 'db' where testDB was
intended to use. These tests didnt fail before but started to fail now.

Removed a unit test that should verify that mixing database instances
should not be possible within a transaction. Needed to remove the test
because that is possible now.
  • Loading branch information
dfahlander committed Feb 27, 2015
1 parent 04568ee commit 8d9c062
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
12 changes: 5 additions & 7 deletions src/Dexie.js
Expand Up @@ -692,7 +692,8 @@
// Let scopeFunc be the last argument
scopeFunc = arguments[arguments.length - 1];
var parentTransaction = Promise.PSD && Promise.PSD.trans;
if (mode.indexOf('!') !== -1) parentTransaction = null; // Caller dont want to reuse existing transaction
// Check if parent transactions is bound to this db instance, and if caller wants to reuse it
if (!parentTransaction || parentTransaction.db !== db || mode.indexOf('!') !== -1) parentTransaction = null;
var onlyIfCompatible = mode.indexOf('?') !== -1;
mode = mode.replace('!', '').replace('?', '');
//
Expand Down Expand Up @@ -722,10 +723,6 @@
if (parentTransaction) {
// Basic checks
if (!error) {
if (parentTransaction.db !== db) {
if (onlyIfCompatible) parentTransaction = null; // Spawn new transaction instead.
else error = new Error("Current transaction bound to different database instance");
}
if (parentTransaction && parentTransaction.mode === READONLY && mode === READWRITE) {
if (onlyIfCompatible) parentTransaction = null; // Spawn new transaction instead.
else error = error || new Error("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY");
Expand Down Expand Up @@ -2032,8 +2029,9 @@
configurable: true,
enumerable: true,
get: function () {
if (Promise.PSD && Promise.PSD.trans) {
return Promise.PSD.trans.tables[tableName];
var currentTrans = Promise.PSD && Promise.PSD.trans;
if (currentTrans && currentTrans.db === db) {
return currentTrans.tables[tableName];
}
return tableInstance;
}
Expand Down
14 changes: 7 additions & 7 deletions test/tests-exception-handling.js
Expand Up @@ -214,12 +214,12 @@
var ourDB = new Dexie("TestDB2");
ourDB.version(1).stores({ users: "++id,first,last,&username,&*email,*pets" });
ourDB.on("populate", function () {
db.users.add({ first: "David", last: "Fahlander", username: "dfahlander", email: ["david@awarica.com", "daw@thridi.com"], pets: ["dog"] });
db.users.add({ first: "Karl", last: "Cedersköld", username: "kceder", email: ["karl@ceder.what"], pets: [] });
ourDB.users.add({ first: "Daniel", last: "Fahlenius", username: "dfahlenius", email: ["david@awarica.com", "daw@thridi.com"], pets: ["dog"] });
ourDB.users.add({ first: "Carl", last: "Cedersköld", username: "cceder", email: ["karl@ceder.what"], pets: [] });
});
var errorCount = 0;
ourDB.on("error", function (e) {
ok(errorCount < 3, "Uncatched error successfully bubbled to db.on('error'): " + e);
ok(errorCount < 3, "Uncatched error successfully bubbled to ourDB.on('error'): " + e);
if (++errorCount == 3) {
ourDB.delete().then(start);
}
Expand All @@ -228,21 +228,21 @@
ourDB.open();

ourDB.transaction("rw", ourDB.users, function () {
db.users.add({ username: "dfahlander" }).then(function () {
ourDB.users.add({ username: "dfahlenius" }).then(function () {
ok(false, "Should not be able to add two users with same username");
});
}).then(function () {
ok(false, "Transaction should not complete since errors wasnt catched");
});
ourDB.transaction("rw", ourDB.users, function () {
db.users.add({ username: "dfahlander" }).then(function () {
ourDB.users.add({ username: "dfahlenius" }).then(function () {
ok(false, "Should not be able to add two users with same username");
});
}).then(function () {
ok(false, "Transaction should not complete since errors wasnt catched");
});
ourDB.transaction("rw", ourDB.users, function () {
db.users.add({ username: "dfahlander" }).then(function () {
ourDB.users.add({ username: "dfahlenius" }).then(function () {
ok(false, "Should not be able to add two users with same username");
});
}).then(function () {
Expand Down Expand Up @@ -286,7 +286,7 @@
var popufail = new Dexie("PopufailDB");
popufail.version(1).stores({ users: "++id,first,last,&username,&*email,*pets" });
popufail.on('populate', function () {
db.users.add({ first: NaN, last: undefined, username: function () { } }).catch(function (e) {
popufail.users.add({ first: NaN, last: undefined, username: function () { } }).catch(function (e) {
ok(true, "Got error when catching add() operation: " + e);
return Dexie.Promise.reject(e);
});
Expand Down
60 changes: 32 additions & 28 deletions test/tests-transaction.js
Expand Up @@ -258,34 +258,6 @@
}).finally(start);
});

asyncTest("Transaction bound to different db instance", function () {

var counter = 0;
var db2 = new Dexie("TestDB2");
db2.version(1).stores({
users: "username",
pets: "++id,kind",
petsPerUser: "++,user,pet"
});
db2.open();
db.transaction('rw', "users", "pets", function () {
ok(true, "Entered outer transaction scope");
db2.transaction('rw', "users", "pets", function () {
ok(false, "Should not enter transaction scope when incompatible database instances");
}).catch(function (err) {
ok(true, "Got error: " + err);
}).finally(function () {
if (++counter == 2) db2.delete().then(start);
});
}).then(function () {
ok(false, "Main transaction should not resolve due to error in sub transaction");
}).catch(function (err) {
ok(true, "Got error: " + err);
}).finally(function() {
if (++counter == 2) db2.delete().then(start);
});
});

//
// Testing the "!" mode
//
Expand Down Expand Up @@ -452,5 +424,37 @@
}).finally(start);
});

asyncTest("Transactions in multiple databases", function () {
var logDb = new Dexie("logger");
logDb.version(1).stores({
log: "++,time,type,message"
});
logDb.open();
var lastLogAddPromise;
db.transaction('rw', db.pets, function () {
// Test that a non-transactional add in the other DB can coexist with
// the current transaction on db:
logDb.log.add({time: new Date(), type: "info", message: "Now adding a dog"});
db.pets.add({kind: "dog"}).then(function(petId){
// Test that a transactional add in the other DB can coexist with
// the current transaction on db:
lastLogAddPromise = logDb.transaction('rw!', logDb.log, function (){
logDb.log.add({time: new Date(), type: "info", message: "Added dog got key " + petId});
});
});
}).then(function() {
return lastLogAddPromise; // Need to wait for the transaction of the other database to complete as well.
}).then(function(){
return logDb.log.toArray();
}).then(function (logItems) {
equal(logItems.length, 2, "Log has two items");
equal(logItems[0].message, "Now adding a dog", "First message in log is: " + logItems[0].message);
equal(logItems[1].message, "Added dog got key 1", "Second message in log is: " + logItems[1].message);
}).catch(function (err) {
ok(false, err);
}).finally(function(){
return logDb.delete();
}).finally(start);
});
})();
//debugger;

0 comments on commit 8d9c062

Please sign in to comment.