-
Type: Question
-
Resolution: Done
-
Priority: Major - P3
-
None
-
Affects Version/s: 4.0.2
-
Component/s: JavaScript, Replication
-
None
First of all, sorry for my english.
I'm trying to implement a simple example of ACID transaction in MongoDB, using NodeJS driver. I also use Mongoose. I'm just experimenting with transaction to understand how it works and to foresee and prevent all possible errors.
I have a collection 'testcollection', and i have a single document in it, like:
var testcollection = new mongoose.Schema({ id: Number, name: String, number: Number });
This is code:
var mongoDB = require('./libs/db'); var _ = require('underscore'); mongoDB.connect("mongodb://testuser:name32@localhost:27018/testbase"); var testcoll = require('./libs/db/models/testcollection'); var _session = mongoose.startSession({readPreference: {mode: "primary"}}); (async () => _session = await _session)(); // wait for ClientSession async function transactionOperations(id, session){ async function transactionOperations(id, session){ await testcoll.updateOne({name: "ilya", id: 1}, {$inc: {number: 2}}).session(session); // change document as transaction !!! await testcoll.updateOne({name: "ilya", id: 1}, {$inc: {number: 3}}); // this is not transaction!!! } var performTransaction = function(id){ async function commit() { try { await _session.commitTransaction(); } catch (error) { if (error.errorLabels && error.errorLabels.indexOf('UnknownTransactionCommitResult') >= 0) { console.warn('UnknownTransactionCommitResult, retrying commit operation.', error); await commit(); } else { console.error('Transaction aborted. Caught exception during transaction.', error); } } } async function runTransactionWithRetry(txnFunc, _transactionOperations) { try { await txnFunc(_transactionOperations); } catch (error) { if (error.errorLabels && error.errorLabels.indexOf('TransientTransactionError') >= 0) { console.warn('TransientTransactionError, retrying transaction.', error); await _session.abortTransaction(); await runTransactionWithRetry(txnFunc, _transactionOperations); } else { if(_session.inTransaction()){ await runTransactionWithRetry(txnFunc, _transactionOperations); } else { await _session.abortTransaction(); console.error('Transaction aborted. Caught exception during transaction.', error); } } } } async function createTransaction(_transactionOperations) { // wait for ClientSession _session = await _session; console.log("START " + id + " TRANSACTION"); _session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority',} }); // ----- all transaction operations here ----- await _transactionOperations(id, _session); // ----- all transaction operations here ----- try { await commit(); } catch (error) { await _session.abortTransaction(); console.error('Transaction aborted. Caught exception during transaction.', error); } } runTransactionWithRetry(createTransaction, transactionOperations); }
In my transaction i'm trying to simulate the situation, when at first i change document as a transaction (increment field 'number' +2), and than change it not as a transaction (increment same field +3 in same doc, but i don't pass session parameter in query function). So it must cuase some write conflict, and throw some error.
When i running this transaction, it waits for a 60 seconds (as i understood, max txn time), and than throw an error:
errorLabels: [ 'TransientTransactionError' ], operationTime: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1540719109 }, ok: 0, errmsg: 'Transaction 1 has been aborted.', code: 251, codeName: 'NoSuchTransaction'
It seems, that transaction runs, can't perform update operation because of write conflict and than just waiting for txn timeout. If i'm not mistaking, this is a specific write-conflict error, so why it wait 60 seconds and throws 'NoSuchTransaction' MongoError?
BUT!!! Somitimes (for example, when i add some console.log-s in transaction body) it throws another error, such as:
errorLabels: [ 'TransientTransactionError' ], operationTime: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1540719759 }, ok: 0, errmsg: 'Unable to acquire lock \'{8613801417341912551: Database, 1696272389700830695}\' within a max lock request timeout of \'5ms\' milliseconds.', code: 24, codeName: 'LockTimeout'
Here transaction don't have enough time (5ms) to lock the document. As i understood, transactions in mongo locks document not in the time, when calls session.startTransaction(), but when the operations, that are assosiated with this session and transaction, are executing.
So, what i should do with this problem? How can i handle correctly this write conflicts? Why in one situation i catch one MongoError, and in other situation - another, but both errors is caused by single reason?
Thanks a lot.