Sequelize Transaction with async.parallel - asynchronous

I want to use managed sequelize transactions with my async.parallel().
Right now, I do this:
return sequelize.transaction(function (t) {
return async.parallel([
function(callback){ return func1(t, callback); },
function(callback){ return func2(t, callback); }
], function(err){
if(err){
...
}
});
}).then(function(){
...
}).catch(function(err){
...
});
If func1 or func2 fails before the other one, the transaction is rolled back. Then the next of the two functions will try to use the transaction, but then I get this error:
Error: rollback has been called on this transaction(8b7f7cbc-b617-44fb-a9dd-906397e6f7ca), you can no longer use it.
which makes sense.
What is the recommended way to work with sequelize transactions and async.parallel together?

I have simply used the unmanaged transactions rather than the managed transactions and then it worked.
return sequelize.transaction().then(function (t) {
return async.parallel([
function(callback){ return func1(t, callback); },
function(callback){ return func2(t, callback); }
], function(err){
if(err){
return t.rollback();
}
return t.commit();
});
});
Note that now we use sequelize.transaction().then(...) instead of a callback function in transaction. Also note that t.rollback() return Promises so you can use .then() on them.
More info: http://docs.sequelizejs.com/manual/tutorial/transactions.html#unmanaged-transaction-then-callback-

Related

Error: Meteor code must always run within a Fiber

I am using stripe for payments in my app, I want to create a receipt document in my own database after a succesful transaction
My code:
Meteor.methods({
makePurchase: function(tabId, token) {
check(tabId, String);
tab = Tabs.findOne(tabId);
Stripe.charges.create({
amount: tab.price,
currency: "USD",
card: token.id
}, function (error, result) {
console.log(result);
if (error) {
console.log('makePurchaseError: ' + error);
return error;
}
Purchases.insert({
sellerId: tab.userId,
tabId: tab._id,
price: tab.price
}, function(error, result) {
if (error) {
console.log('InsertionError: ' + error);
return error;
}
});
});
}
});
However this code returns an error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am not familiar with Fibers, any idea as to why this is?
The problem here is that the callback function which you pass to Stripe.charges.create is called asynchronously (of course), so it's happening outside the current Meteor's Fiber.
One way to fix that is to create your own Fiber, but the easiest thing you can do is to wrap the callback with Meteor.bindEnvironment, so basically
Stripe.charges.create({
// ...
}, Meteor.bindEnvironment(function (error, result) {
// ...
}));
Edit
As suggested in the other answer, another and probably better pattern to follow here is using Meteor.wrapAsync helper method (see docs), which basically allows you to turn any asynchronous method into a function that is fiber aware and can be used synchronously.
In your specific case an equivalent solution would be to write:
let result;
try {
result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
// ...
}
Please note the second argument passed to Meteor.wrapAsync. It is there to make sure that the original Stripe.charges.create will receive the proper this context, just in case it's needed.
You might want to take a look at the docs for http://docs.meteor.com/#/full/meteor_wrapasync.

Why is current user also getting returned after using the $ne

On the server i got this
Meteor.publish('allusersbutme', function () {
// every except the current use
if (Meteor.userId) {
return Meteor.users.find({_id: {$ne: Meteor.userId}}, {fields: {profile: 1, 'services.facebook': 1}});
}
});
on the client I have
Meteor.subscribe('allusersbutme');
Template.discover.helpers(
{
users: function () {
return Meteor.users.find({}).fetch();
}
});
The users helper is still returning the current user. This works if I do return Meteor.users.find({_id: {$ne: Meteor.userId}}).fetch(). This is abit confusing.
You can't use Meteor.userId() inside a publish function, right? I thought you had to use this.userId. Besides, Meteor.userId is a function, so your query is making sure that _id is $ne to a function... which I imagine is always true.
Meteor.publish docs for this.userId: http://docs.meteor.com/#/full/publish_userId

How do I return an error from a Meteor.call method inside another Meteor.call

My meteor code goes a couple Meteor.call methods deep at some points. If I have an error in the 2nd layer and I want to throw that meteor error back to the client side how can I do that?
Currently I have something like this, but I'm getting very confusing outputs and I don't think I fully understand what is happening when I'm calling throw new Meteor.Error(500, e.category_code, e.description);
In client.js
Meteor.call('firstCall', data, function (error, result) {
if(result) {
doSomething();
}
else{
console.log(error);//just shows 500
}
});
In server.js
var Future = Meteor.npmRequire("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut.return(result);
}, function (error) {
console.log(error);
fut.throw(error);
});
return fut.wait();
}
firstCall: function (data){
try{
Meteor.call('secondCall', data, 'http://testhref.com/test', 'http://testhref2.com/test' function (error, result) {
return result;
});
}
catch(e){
throw new Meteor.Error(500, e.category_code, e.description);
}
}
secondCall: function (data, paymentHref, otherHref){
try{
var associate = extractFromPromise(balanced.get(paymentHref).associate_to_customer(otherHref).debit({
"amount": data.paymentInformation[0].total_amount * 100,
"appears_on_statement_as": "Trash Mountain"}));
}
catch(e){
Collection.update(data.id, {
$set: {
'failed.category_code': e.category_code,
'failed.description': e.description
}
});
throw new Meteor.Error(500, e.category_code, e.description);
}
}
In your case, the catch in firstCall is not going to have anything defined for e.category_code and e.description when secondCall throws. This is because in secondCall you are passing these two as arguments to Meteor.Error, which takes as its arguments error, reason, and details:
https://github.com/meteor/meteor/blob/devel/packages/meteor/errors.js
In order to pass these through, you will need to amend firstCall to use these properties:
firstCall: function (data){
try{
Meteor.call('secondCall', data, 'http://testhref.com/test', 'http://testhref2.com/test');
}
catch(e){
throw new Meteor.Error(500, e.reason, e.details);
}
}
I'm not even sure you need to split it up into two calls for modularity, as you can just use normal Javascript functions. But we can discuss that elsewhere.
I few things to mention here:
Async function don't throw exceptions (except you make them kind of sync using Meteor._wrapAsync as I will explain later), they return the error on another way (as the first argument in NodeJS callback-style). This applies both for Meteor.call and to your doSomeAsyncThing.
I can't see the benefit of using Meteor.call on the server. Meteor.call is meant to call server methods from the client. In this case you could just call YourObj.secondCall from inside of firstCall.
Returning something from inside of a callback (as you are doing inside firstCall) doesn't have any effect. You want your async code to work as sync code, so I suggest using Meteor._wrapAsync which is very well explained here.
So, I would implement server side a bit different:
firstCall: function (data){
try{
return this.secondCall(data);
}
catch(e){
throw new Meteor.Error(500, e.category_code, e.description);
}
secondCall: function (data){
try{
return Meteor._wrapAsync(doSomeAsyncThing)(data);
}
catch(e){
Collection.update(data.id, {
$set: {
'failed.category_code': e.category_code,
'failed.description': e.description
}
});
throw new Meteor.Error(500, e.category_code, e.description);
}
Hope this helps!

How to explicitly unsubscribe from a collection?

I have a MongoDB with a large "messages" collection; all messages belonging to a specific groupId. So have started with a publication like this:
Meteor.publish("messages", function(groupId) {
return Messages.find({
groupId: groupId
});
});
and a subscription like this:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
This got me into trouble because initially currentGroupId is undefined but sill mongod would use up the CPU to find messages with groupId == null (although I know there are none).
Now, I tried to rewrite the publication as follows:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return {}; // is this the way to return an empty publication!?
}
});
and/or to rewrite the subscription to:
Deps.autorun(function() {
if (Session.get("currentGroupId")) {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
} else {
// can I put a Meteor.unsubscribe("messages") here!?
}
});
which both helps initially. But as soon as currentGroupId becomes undefined again (because the user navigates to a different page), mongod is still busy requerying the database for the last subscribed groupId. So how can I unsubscribe from a publication such that the mongod is stopped being queried?
According to the documentation it must be http://docs.meteor.com/#publish_stop
this.stop()
Call inside the publish function. Stops this client's subscription;
the onError callback is not invoked on the client.
So something like
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return this.stop();
}
});
And I guess on the client side you can just remove your if/else like in your first example
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
I found it more simple and straight-forward to call the .stop() function on the handler which is returned from the .subscribe() call:
let handler = Meteor.subscribe('items');
...
handler.stop();
Simply adding a condition to the publication:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
});
and keeping the subscription:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
does the job.
There is no need to stop the publication explicitly. Eventually, the MongoDB is not queried anymore after finishing the currently running query and issuing yet another one (which seems to be queued somewhere in the system).
in your case, you should stop the autorun
there is an example in the documentation
Your autorun is actually called with a parameter that allows you to stop it:
Deps.autorun(function (c) {
if (! Session.equals("shouldAlert", true))
return;
c.stop();
alert("Oh no!");
});

How to simply change q.js-promise 'errormessage' on reject?

I'm using Q.js for promises.
I'd like to know if it's possible to quickly format/change the error-message when a Q-promise fails.
Consider the contrived example:
return Q.when(//$.ajaxpromise for instance).then(function(result){
//handle result
}).fail(function(err){
//somehow change err (returned from $.ajax) to something
//sensible (say the statuscode + responseText) and
//push it up the callstack
});
Of course I could do the following but it feels kind of cumbersome:
var deferred = Q.defer();
Q.when( //$.ajaxpromise for instance).then(function(result){
//handle result
deferred.resolve();
}).fail(function(err){
deferred.reject(new Error(err.responseText));
});
return deferred.promise;
Anyway to do this more elegantly?
The wonderful thing about Q promises (and any Promises/A+ implementation) is that you can just throw:
return Q.when(otherPromise)
.then(function (result) { /* handle result */ })
.fail(function (err) { throw new Error('A real error!'); });
You can find this information in the "Propagation" section of the Q readme.

Resources