e-oj / Fawn

Transactions for MongoDB (See the README)
https://www.npmjs.com/package/fawn
MIT License
485 stars 54 forks source link

Fawn Dropping out of task without hitting catch. #14

Closed thedanielfactor closed 7 years ago

thedanielfactor commented 7 years ago

I have a schema that works under normal mongoose CRUD. This schema and session_friend.js work with Fawn when I use it under a simple Express project setup. But when I am moving this code to a larger API it seems to be falling down. I assume it is related to where the require happens but here is a simple version of what I am doing.

Structure Snippet:

server.js  - Main node js file.
      '------- /config/config.js
      '------- /custom_modules/util.js - Main anchor for include objects (mongoose, fawn, twilio, etc...)
      '------- /api/** - Custom api routes:
      '------- /api/session/model/Session.js - Session Schema - Contains fawn tasks
      '------- /api/session/session.js - Route code.

util.js

NOTE: Promise is a global for BlueBird promises.

...
var mongoose = require('mongoose');
var uri = Config.get("SESSION.mongodburi");

mongoose.Promise = Promise;
mongoose.connect(uri);
var Fawn = require('fawn');
Fawn.init(mongoose, "fawn_task_cache", {promiseLibrary: Promise});
...
module.exports = {
    ...
    mongoose: mongoose,
    fawn: Fawn
}

/api/session/session_friend.js

var build = require('../presence/build_functions');

util.mongoose.Promise = Promise;

// Model Objects
var UserPresence = require('../presence/model/UserPresence');
var GroupSession = require('./model/GroupSession');

// ROUTE '/session/friend/:friendId'
module.exports = {
    post: function (req, res) {
        var userPrincipal = build.getUserPrincipal(req.headers['x-access-token']);

        console.log("*** UserPrincipal " + userPrincipal.userId);
        console.log("*** req.params.friendId : " + req.params.friendId);

        if (userPrincipal.userId && req.params.friendId) {
            UserPresence.findById(req.params.friendId).then(function (friendPresence) {
                if (friendPresence && friendPresence.sessionId) {
                    console.log("**** We Got Presence! ****");
                    var task = util.fawn.Task();
                    console.log("*** Lets use some tasks time...");
                    // Add the session to the user and add the user to the session.  Do it in a "transaction".
                    task.update("UserPresence", {_id: userPrincipal.userId}, {sessionId: friendPresence.sessionId})
                        .update("GroupSession", {_id: friendPresence.sessionId}, {$push: {userList: userPrincipal.userId}})
                        .run({useMongoose: true})
                        .then(function () {
                            GroupSession.findById(friendPresence.sessionId)
                                        .then(function (session) {
                                            session.userList = [];
                                            res.send(session);
                                        });
                        }).catch(function (err) {
                            res.send(err);
                        });
                } else if (!friendPresence.sessionId) {
                    console.log("**** We Not Gottem! ****");

                    var groupSession = new GroupSession();

                    groupSession.userList = [req.params.friendId, userPrincipal.userId];
                    groupSession.hosted = false;

                    console.log("**** Group Session: " + JSON.stringify(groupSession));
                    var task = util.fawn.Task();
                    // Create the session, add both users get updated.
                    task.save(GroupSession, groupSession)
                        .update("UserPresence", {_id: userPrincipal.userId}, {sessionId: {$ojFuture: "0._id"}})
                        .update("UserPresence", {_id: req.params.friendId}, {sessionId: {$ojFuture: "0._id"}})
                        .run({useMongoose: true})
                        .then(function (results) {
                            console.log("**** After Save Group Session: " + JSON.stringify(results[0]));
                            // Update of users sessions complete.
                            var gsess = results[0];
                            console.log("Saved Group Session " + gsess.userList);
                            gsess.userlist = [];
                            res.send(gsess);
                        })
                        .catch(function (err) {
                            console.log("****** " + err);
                            res.send(err);
                        });
                }
            });
        }
    }
}

When I get to this code all three steps are created in the fawn cache table but then the api hangs because nothing is returned. On the console I get the following warning.


(node:2996) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html```
and the state of step 1 is set to "1", the others are still "0".  I do not get any error.  It just appears to exit the process abruptly.```

![fawncache](https://user-images.githubusercontent.com/448370/28986618-5879c006-792d-11e7-9de2-c12715e0e856.png)
e-oj commented 7 years ago

Is this happening in other areas of the app? Also, do the updates in the "if" block work? It's strange that there are no errors. Try updating your version of mongoose; that might fix the deprecation warning (https://github.com/Automattic/mongoose/issues/4951#issuecomment-289629542)

e-oj commented 7 years ago

Also, try running the task with {useMongoose: false}

thedanielfactor commented 7 years ago

I am using mongoose 4.11.5 and node 6.11.1. This is the only place I use Fawn. All other places work great with Mongoose. Like I mentioned in my original statement. This works great when I am using a very simple setup. The following setup works great.

server.js - Main node js.
    '------ /routes/groupsession.js - Route file.  
    '------ /model/GroupSession.js - Schema file.

server.js

var express = require('express');
var app = express();
var Promise = require('bluebird');
...
// MongoDB
var mongoose = require('mongoose');
var uri = 'mongodb://localhost:27017/vertigo-session';
mongoose.Promise = require('bluebird');
mongoose.connect(uri);
...
app.use('/presence', presenceRouter);
app.use('/groupsession', sessionRouter);
...

groupsession.js

var Promise = require('bluebird');
var mongoose = require('mongoose');
var Fawn = require('fawn');
const uuidv4 = require('uuid/v4');

// Model Objects
var UserPresence = require('../model/UserPresence');
var GroupSession = require('../model/GroupSession');

Fawn.init(mongoose);

var sessionRouter = express.Router();
...
sessionRouter.route('/friend/:friendId')
    .post(function(req, res) {
        logger.debug("Joining friend " + req.params.userId);
        if (req.userPrincipal && req.params.friendId) {
            UserPresence.findById(req.params.friendId).then(function(friendPresence) {
                if (friendPresence && friendPresence.sessionId) {
                    var task = Fawn.Task();
                    // Add the session to the user and add the user to the session.  Do it in a "transaction".
                    task.update("UserPresence", {_id: req.userPrincipal.userId}, {sessionId: friendPresence.sessionId})
                        .update("GroupSession", {_id: friendPresence.sessionId}, {$push: {userList: req.userPrincipal.userId}})
                        .run({useMongoose: true})
                        .then(function () {
                            GroupSession.findById(friendPresence.sessionId).then(function (session) {
                                session.userList = [];
                                res.send(session);
                            });
                        }).catch(function (err) {
                            res.send(err);
                        });
                } else if (!friendPresence.sessionId) {
                    var groupSession = new GroupSession();
                    groupSession.userList = [req.params.friendId, req.userPrincipal.userId];
                    groupSession.hosted = false;

                    var task = Fawn.Task();
                    // Create the session, add both users get updated.
                    task.save(GroupSession, groupSession)
                        .update("UserPresence", {_id: req.userPrincipal.userId}, {sessionId: {$ojFuture: "0._id"}})
                        .update("UserPresence", {_id: req.params.friendId}, {sessionId: {$ojFuture: "0._id"}})
                        .run({useMongoose: true})
                        .then(function (results) {
                            // Update of users sessions complete.
                            var gsess = results[0];
                            console.log("Saved Group Session " + gsess.userList);
                            gsess.userlist = [];
                            res.send(gsess);
                        })
                        .catch(function (err) {
                            debugger;
                            res.send(err);
                        });
                }
            });
        } else {
            res.status(403).send("You do not have rights to visit this page");
        }
    });

module.exports = sessionRouter;
e-oj commented 7 years ago

I've tried to reproduce this locally but I can't.

utils.js

var mongoose = require("mongoose");
var Fawn = require("../index");

mongoose.Promise = require("bluebird");
mongoose.connect("mongodb://127.0.0.1:27017/AnimalDB", {useMongoClient: true});
Fawn.init(mongoose, "_tasks_");

exports.mongoose = mongoose;
exports.Fawn = Fawn;

db.js

var utils = require("./utils");

var Animals = utils.mongoose.model("Animals", new utils.mongoose.Schema({
  name: String
  , color: String
  , bipedal: Boolean
}));

var animal1 = new Animals({
  name: "Bob"
  , color: "magenta"
  , bipedal: true
});

var animal2 = new Animals({
  name: "Giraffe"
  , color: "yellow"
  , bipedal: false
});

animal2.save()
  .then(function(giraffe){
    utils.Fawn.Task()
      .save(Animals, animal1)
      .update("Animals", {_id: giraffe._id}, {color: {$ojFuture: "0.color"}})
      .run({useMongoose: true})
      .then(function(results){
        console.log(results);
      });
  });

This works for me so your setup is good. It's difficult to figure out what the problem is without reproducing it. Does it work if you run the task with {useMongoose: false}?

thedanielfactor commented 7 years ago

If I set{useMongoose: false} I get the following error in both versions of my app. ****** Error: No such key exists in result {"n":1,"ok":1} at index0

In the small working version and the large API I have added my work to.

thedanielfactor commented 7 years ago

I did notice one difference in what you are doing is that my util.js is setup as a global. So i do not require it.

e-oj commented 7 years ago

if you set {useMongoose: false} it returns the node-mongodb-native result. you can access the value with {sessionId: {$ojFuture: "0.ops.0._id"}}

e-oj commented 7 years ago

{"n":1,"ok":1} means the save was successful. So for some reason, saving with mongoose isn't working in your case but using the native driver works. Your setup seems fine; I made my utils global and it still worked. You said it just hangs and gives the promise warning?

thedanielfactor commented 7 years ago

Well, I figured out that it does not "hang" it just exits the fawn transaction but does not hit the catch. I added res.send({message: "End of the line."}) after the task statement and I got that back. So the "hang" comes from the promise failing somewhere but not hitting the catch.

e-oj commented 7 years ago

@thedanielfactor So I was eventually able to reproduce and fix this issue. Update your Fawn version and it should work fine. Thanks for your patience and help with this and sorry for the inconvenience. Feel free to reopen this issue if the problem persists.

thedanielfactor commented 7 years ago

Works Great!!!! Thanks for the fast response and for working through this with me!