How to fix a race condition in node js + redis + mongodb web application

ぃ、小莉子 提交于 2019-12-31 05:23:06

问题


I am building a web application that will process many transactions a second. I am using an Express Server with Node Js. On the database side, I am using Redis to store attributes of a user which will fluctuate continuously based on stock prices. I am using MongoDB to store semi-permanent attributes like Order configuration, User configuration, etc.,

I am hitting a race condition when multiple orders placed by a user are being processed at the same time, but only one would have been eligible as a check on the Redis attribute which stores the margin would not have allowed both the transactions.

The other issue is my application logic interleaves Redis and MongoDB read + write calls. So how would I go about solving race condition across both the DBs

I am thinking of trying to WATCH and MULTI + EXEC on Redis in order to make sure only one transaction happens at a time for a given user. Or I can set up a Queue on Node / Redis which will process Orders one by one. I am not sure which is the right approach. Or how to go about implementing it.

This is all pseudocode. Application logic is a lot more complex with multiple conditions. I feel like my entire application logic is a critical section ( Which I think is a bad thing )


//The server receives a request from Client to place an Order
getAvailableMargin(user.username).then((margin) => { // REDIS call to fetch margin of user. This fluctuates a lot, so I store it in REDIS
            if (margin > 0) {
                const o = {  // Prepare an order
                    user: user.username,
                    price: orderPrice,
                    symbol: symbol
                }

                const order = new Order(o);
                order.save((err, o) => { // Create new Order in MongoDB
                        if (err) {
                            return next(err);
                        }
                        User.findByIdAndUpdate(user._id, {
                            $inc: {
                                balance: pl
                            }
                        }) // Update balance in MongoDB
                        decreaseMargin(user.username) //  decrease margin of User in REDIS
                    );

                }

            });



Consider margin is 1 and with each new order margin decreases by 1.

Now if two requests are received simultaneously, then the margin in Redis will be 1 for both the requests thus causing a race condition. Also, two orders will now be open in MongoDB as a result of this. When in fact at the end of the first order, the margin should have become 0 and the second order should have been rejected. Another issue is that we have now gone ahead and updated the balance for the User in MongoDB twice, one for each order.

The expectation is that one of the orders should not execute and a retry should happen by checking the new margin in Redis. And the balance of the user should also have updated only once.

Basically, would I need to implement a watch on both Redis and MongoDB and somehow retry a transaction if any of the watched fields/docs change? Is that even possible? Or is there a much simpler solution that I might be missing?

来源:https://stackoverflow.com/questions/55469691/how-to-fix-a-race-condition-in-node-js-redis-mongodb-web-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!