Declarative javascript library for RethinkDB supporting Atomic transactions
This Promised based Micro Library can run multiple DML operations in ATOMIC mode keeping the data in READ COMMITED isolation level. This is not be mistaken with default READ UNCOMMITTED isolation in which ReThinkDB runs. It doesnt override ReThinkDB read isolation, but does promise READ COMMITED isolation just for the data set particapting in the transaction context. Its semantics are inspired by sequelize, a javascript based SQL ORM with transaction support.
All the DML operations INSERT, UPDATE AND DELETE return promisable objects, so they are thenable and chainable with other promisable objects including other DML transactional operations or anything promisable. It expects all your transactional operations to be and include PRIMARY KEY (it looks for primary key with property "id"). This restriction may be relaxed during in the subsequent releases
//Insert New User into Profile Collection
ThinkTransaction.begin({timeout: 30})
.then(function(trans) {
trans.op.insert({
Profile : { // Profile is name of your collection in rethinkdb database
id : uuid.v4(), //REQUIRED : You will need to explicity pass id value along with other attributes of the document
type: "User",
firstName : "John",
lastName : "K"
}
})
.then(function(results){ //On success, commit the transaction
console.log('insert commit handler');
trans.commit().then(function(result){
console.log('commit complete');
console.log(result);
})
})
.catch(function(err){ //On failure, rollback the transaction
console.log('insert catch handler');
console.log(err);
trans.rollback().then(function(result){
console.log('rollback complete');
console.log(result);
});
});
});
//returns a promise
trans.op.insert({
Profile : { // Profile is name of your collection in rethinkdb database
id : uuid.v4(), //REQUIRED : You will need to explicity pass id value along with other attributes of the document
type: "User",
firstName : "John",
lastName : "K"
}
})
//returns a promise
trans.op.update({
Profile : { //Profile is name of your collection in rethinkdb database
set :{lastName: "Kennedy"}, //By design, You could set any property of the document, including properties with deep structures.
//But, for now we have tested it only with simple flat properties
where : {id: "7307a001-5051-41be-857d-fbe64a98cb5c", type : "User"} //This could be any js object you may pass to rethinkdb
// .filter() command. However, this library expects id (primary key on Profile collection) to be part of the filter.
// This limitation is to make determinstic selection on what you are trying to update, as other properties are bound to change
// from updates from other concurrent transactions
}
})
//returns a promise
trans.op.delete({
Profile : {
where : {firstName : "John", lastName: "Kennedy", type : "User"} //This could be any js object you may pass to rethinkdb
// .filter() command. However, this library expects id (primary key on Profile collection) to be part of the filter.
// This limitation is to make determinstic selection on what you are trying to delete, as other properties are bound to change
// from updates from other concurrent transactions
}
})
Promise.all([
//delete User Raja from Profile collection
trans.op.delete({
Profile : {
where :{id : rajasUId}
}
}),
//Remove all the connections for User Raja from ProfileNetwork
//This may not be the best of the examples to illustrate efficient DB operation, as we are deleting documents by id (primary key) in a loop.
//Currently this is a limtation with this transaction library with all DML operations expecting primary key.
r.table('ProfileNetwork').getAll(rajasUId, {index: 'profileId'}).run()
.then(function(results){
// In this scenario, Deletions on the ProfileNetwork involve multiple delete commands, but the commands execute in parallel
return Promise.map(results, function(document){
return trans.op.delete({
ProfileNetwork : {
where :{id : document.id, profileId : rajasUId}
}
});
});
})
])
for the complete working code and additional examples. These examples are using bluebird and thinky libraries. You may also choose to use rethinkdbdash, but make sure you change the references in the library to allow this library to reuse the same connection pool.
thinktrans makes some opinions on how it should respond to any INSERT, UPDATE and DELETE operations, LIKE
- Checking for duplicates while INSERT
- Checking the state of the document after its acquires write lock for UPDATE or DELETE, with the state the document had been when the transaction kicked-off. Any changes to the document affecting its WHERE criteria or SET criteria will cause the transaction to fail. Find more on its execution theory here
You are welcome to do a pull request. It would greatly help this module if it could find more contributors to build it and test it.
- Implement backend job that monitors the ReThinkDB database connection, and fails the transactions immediately when connection is lost. r.getPoolMaster().on('healthy', function(healthy) { });
- Side-by-side plug and play with sequelize SQL ORM library to support transactions cross living between SQL database like MySQL and RethinkDB.
- Support user friendly errors, currently it rethrows upstream errors AS IS
- And, ofcourse Issues/feedback coming from GitHub Issues https://github.com/jaladankisuresh/thinktrans/issues
open sourced with MIT license
NOTE : THIS LIBRARY MAY NOT BE PRODUCTION READY YET. SOFTWARE IS PROVIDED 'AS IS', WITHOUT ANY WARRANTY.