How to build a transaction system into a .then() chain? - node.js

I have multiple chained, synchronous requests in my code. I am using the NodeJS package request-promise.
Here is some pseudocode to show how it is formatted:
initRequest.then(function(response){
return request2;
}).then(function(response2){
return request3;
}).then(function(response3){
return requestN;
}).catch(function(err){
log(error)
});
If, for example, request3 fails, what happens? Does the chain continue, or does it break out of the loop completely?
And if request2 was a POST, and request3 failed, is there a way to systematically roll back the data that request2 changed?
Thanks.

Does the chain continue, or does it break out of the loop completely?
It breaks and proceeds to catch or finally proposal, which is available in recent Node.js versions and polyfillable in older versions - similarly to how try..catch..finally works for synchronous code (this is how plain promises are translated to async functions as well).
And if request2 was a POST, and request3 failed, is there a way to systematically roll back the data that request2 changed?
This should be secured by a developer. If there's a possibility that data should be rolled back, necessary information (data entry ids) should be saved to variables and rolled back in catch.

if request3 fails, it will stop executing the rest of your request chains.
and there is no way to systematically rollback what request2 changed you would have to implement it your custom way.
to handle when request3 fails, catch request3 it self.
here is a simple/mini way to handle when request3 fails
initRequest.then(function(response){
return request2;
}).then(function(response2){
return request3.catch(function(err2){
//if something goes wrong do rollback
request2Rollback.then(rollbackRes => {
throw new Error("request3 failed! roll backed request 2!");
}).catch(function(err){
// the rollback itself has failed so do something serious here
throw err;
})
});;
}).then(function(response3){
return requestN;
}).catch(function(err){
log(error)
});

Related

Mongoose Node - Request Multiple updates to multi documents with success all or cancel all?

Good question I expect to be slane down quickly.
Sometimes we want to update many documents when an action is performed at the front end.
Example React Code
this.props.submitRecord(newRecord, (err, record) => {
if (err) actions.showSnackBar(err);
else {
actions.showSnackBar("Record Submitted Successfully ...");
this.props.validateClub(this.props.club._id, (err, message) => {
if (err) actions.showSnackBar(err);
else {
obj.setState({
player: {},
open: false
});
actions.showSnackBar(message);
}
});
}
});
As we can see I firstly submit the first request, and on success, I submit the second request. If the first fails, the second one won't happen. But, if the first one passes, and the second one fails for whatever reason, we have a data mismatch.
Ideally, we want to send them all together and they all pass or none pass. Is there a simple way at doing this with react, Node and mongoose or do I have to do it the hard way (Which is also subject to error been possible, store old values until all request are satisfied, or make some revert request on the node server, lol).
Thanks
Transactions are a part of Mongodb 4.0. There was no transaction support in Mongodb in the previous versions. The other way could be to perform rollback on failure through code, and there are some non-opinionated npm packages such as mongoose-transaction.
https://www.mongodb.com/transactions

node promise control flow

I am using promise in bluebird to send mysql queries and manage the control flow & errors. Here is what it looks like:
sendQuery1(..)
.then(sendQuery2(..))
.then(function(results from last query){
if(rain){
res.render(...)
}else{
/*
I need to send additional 2 queries here
*/
}
}).catch(errors);
promise chain is very convenient, but I found out the error handling will be a messy if there are multiple subchains inside.
For here I probably need to write the following inside the /* */
return sendQuery3(..)
.then(sendQuery4(..))
.then(function(..){
res.render(".....")
}).catch(error2);
Are there any better ways to handle this type of problems?
I don't exactly see your problem here. When you have a chain inside a chain (which you normally don't, you should check your architecture right there), you can just catch the errors like shown.
I'd advise you use a global (or local) error handler function, and pass that to the catch-function. Therefore, even when you have multiple catches, you can use the same error-handler.
The in my opinion best solution would be to create a "promise chain bypass", therefore using catch to skip certain parts based on your condition. If this is not what you are looking for, please specify your problem.
You will catch the error, there's no problem in your code. Yup it gets messy but you can go with the following approach to keep the code clean and still implement your logic:
sendQuery1(..)
.then(sendQuery2(..))
.then(function(results from last query){
if(rain){
res.render(...)
throw new Error('breakChain'); //intentionally throwing error to skip the remaining chain
}
return; //will act like 'else'
})
.then(sendQuery3(..))
.then(sendQuery4(..))
.catch(function (e) {
if(e.message != 'breakChain') //act on error if it was other than 'breakChain'
throw e;
});
What you are discussing here is branching of the chain based on some logic condition. It is generally better to actually branch the chain rather than throw an error just to abort the rest of the chain. This keeps errors as errors rather than the scheme that Shaharyar proposed of synthesizing an error that isn't really an error.
You can branch the chain by returning a new promise chain from within a .then() handler like this:
sendQuery1(..).then(function(r1) {
return sendQuery2(...);
}).then(function(r2){
if (rain){
// processing is done, so just render
res.render(...)
} else {
// return promise here to attach this new branch to the original chain
return sendQuery3(..).then(sendQuery4).then(function() {
// process last query
});
}
}).catch(errors);
FYI, since you've only posted pseudo-code, not real code and we can't see which functions need access to which prior results in order to do their work, we can't fully optimize the code. This is to show an example of how branching works inside a promise chain. That's the principle you probably want to use. If you show your real code, we can offer a much more specific and optimized answer.

Mocha/Chai: How to perform async test of something that didn't happen?

I am trying to write a short mocha/chai Node test for some async process, expecting it to ignore irrelevant input. It basically looks like this (compared to the test of relevant input). The problem is how do I write the second test? It's an async process that eventually does nothing, no error/success emits...
it('should process input', function(done) {
object
.on('success', function(result) {
expect.result.to.equal("OK");
done();
})
.asyncDoSomething('relevant input');
});
it('should ignore input', function(done) {
object.asyncDoSomething('irrelevant input');
// TODO: how do I verify the async process eventually did nothing?
});
That's a good one - the only solution that comes to mind is to wait for a timeout and assume if it didn't happen in this time, then it will not happen. But this is not good design and needlessly slows down the test suite.
Have you thought about isolating the decision logic to somewhere where it could be tested synchronously and then make a test for that?
For the moment (still awaiting possibly better solutions?), I have updated the emitter to emit some sort of an 'ignored' event for all cases where it decides to ignore the input asynchronously. For testing, I check the "cause" of the ignore using:
expect(cause).to.equal(expectedCause)

node async call return data in response

I am new to nodejs so I have a basic question and this is my scanrio
I have a javascript client which is making a http request to a node server to read a value from the database.
Once the node server receives the request it makes a simple db call and returns the data to the client in the response, and this is where the problem is.
router.get('/state', function(req, res){
var result = dbServer.makeDBCall();//Before this line executes and returns the result the next line executes
res.send(result);
}
The database call from the node server is asynchronous, therefore before the result is returned the node server has already sent a blank response to the client. What is the standard/acceptable way of getting this achieved, I know I can block the node thread using async, but then the whole purpose of node is gone right?
It depends on what kind of database node module you are using.
Other than the standard callback approach, there are also the promise way. The pg-promise library is 1 of those kind.
See sample code:
this.databaseConnection.makeDBCall('your query...')
.then(function(dbResponse) {
// Parse the response to the format you want then...
res.send(result);
})
.catch(function(error) {
// Handle error
res.send(error.message);
});
#spdev : I saw 1 of your comments about you being worried about how Node actually knows who to reply the response to, especially when there are multiple requests.
This is a very good question, and to be honest with you - I don't know much about it as well.
In short the answer is yes, Node somehow handles this by creating a corresponding ServerResponse object when a HTTP request comes through. This object seems to have some smartness to tell the Nodejs network stack how to route itself back to the caller when it gets parsed as data packets.
I tried Googling a bit for an answer but didn't got too far. I hope the ServerResponseObject documentation can provide more insight for you. Share with me if you got an answer thanks!
https://nodejs.org/api/all.html#http_class_http_serverresponse
Try below code.
router.get('/state', function(req, res){
var result = dbServer.makeDBCall(function(err,result){
if(!err) {
res.send(result);
}
});
}
Hope this Help.
The dbServer.makeDBCall(); must have a callback that runs when the statement completes executing.
Something like -
dbServer.makeDBCall({query: 'args'}, function(err, result){
if (err) // handle error
res.send(result);
})
You return the response from db from that callback function.
Learn more about callback from here-
nodeJs callbacks simple example
https://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks/

gcloud Datastore transaction issue using nodejs

I am using nodejs to contact the google datastore. This is my code:
dataset.runInTransaction(function(transaction, done,err) {
// From the `transaction` object, execute dataset methods as usual.
// Call `done` when you're ready to commit all of the changes.
transaction.save(entities, function(err) {
if(err){
console.log("ERROR TRNASCTION");
transaction.rollback(done);
return;
}else{
console.log("TRANSACTION SUCCESS!");
done();
}
});
});
If the save was not successful, I would like the transaction to rollback and if it was I want it to commit. The problem I am facing is that neither of them seem to be running, just no output on the console. Nothing is being sent to my database so I would assume at least the 'if(err)' condition would run but it doesn't. I am new to glcoud so I am not sure if I am doing something wrong here? I am following the doc at https://googlecloudplatform.github.io/gcloud-node/#/docs/v0.26.0/datastore/transaction?method=save.
In the context of a transaction, the save method doesn't actually need a callback. The things you wish to save are queued up until you call done(). Calling done will commit the transaction.
You can then handle errors from the commit operation in a second function passed to runInTransaction. See https://googlecloudplatform.github.io/gcloud-node/#/docs/v0.26.0/datastore/dataset?method=runInTransaction for examples of that.
--
Just mentioning this since it relates to the rollback part: https://github.com/GoogleCloudPlatform/gcloud-node/issues/633 -- we're waiting on an upgrade to Datastore's API before tackling that issue.

Resources