'Node MySQL second query gets executed faster than first query
I have a problem. In my NodeJS API. I use the Node-Binance-API package to use the API from Binance. In this API I can subscribe to account changes using a websocket. The function to subscribe to my account looks like the following:
exports.subscribeAccount = async (agentId) => {
binance.websockets.userData((response) => {
if (eventType === "outboundAccountPosition") {
console.log("outboundAccountPosition")
}
else if (eventType === "executionReport") {
const order = Order.executionReportToOrder(response, agentId);
order.save();
}
})
}
The order.save() method looks like this:
save() {
let sql = `
INSERT INTO \`Order\` (
orderId, agentId, symbol, clientOrderId, side, orderType, timeInForce, orderQuantity, orderPrice, stopPrice, icebergQuantity, originalClientOrderId, currentExecutionType, currentOrderStatus, orderRejectReason, lastExecutedQuantity, cumulativeFilledQuantity, lastExecutedPrice, commissionAmount, commissionAsset, transactionTime, tradeId, isOrderOnBook, isTradeMakerSide, creationTime, cumulativeQuoteAssetTransactedQuantity, lastQuoteAssetTransactedQuantity, quoteOrderQuantity
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE currentExecutionType=VALUES(currentExecutionType), currentOrderStatus=VALUES(currentOrderStatus), orderRejectReason=VALUES(orderRejectReason), lastExecutedQuantity=VALUES(lastExecutedQuantity), cumulativeFilledQuantity=VALUES(cumulativeFilledQuantity), lastExecutedPrice=VALUES(lastExecutedPrice), commissionAmount=VALUES(commissionAmount), commissionAsset=VALUES(commissionAsset), transactionTime=VALUES(transactionTime), tradeId=VALUES(tradeId), isOrderOnBook=VALUES(isOrderOnBook), isTradeMakerSide=VALUES(isTradeMakerSide), creationTime=VALUES(creationTime), cumulativeQuoteAssetTransactedQuantity=VALUES(cumulativeQuoteAssetTransactedQuantity), lastQuoteAssetTransactedQuantity=VALUES(lastQuoteAssetTransactedQuantity), quoteOrderQuantity=VALUES(quoteOrderQuantity);`;
return db.execute(sql, [
this.orderId,
this.agentId,
this.symbol,
this.clientOrderId,
this.side,
this.orderType,
this.timeInForce,
this.orderQuantity,
this.orderPrice,
this.stopPrice,
this.icebergQuantity,
this.originalClientOrderId,
this.currentExecutionType,
this.currentOrderStatus,
this.orderRejectReason,
this.lastExecutedQuantity,
this.cumulativeFilledQuantity,
this.lastExecutedPrice,
this.commissionAmount,
this.commissionAsset,
this.transactionTime,
this.tradeId,
this.isOrderOnBook,
this.isTradeMakerSide,
this.creationTime,
this. cumulativeQuoteAssetTransactedQuantity,
this.lastQuoteAssetTransactedQuantity,
this.quoteOrderQuantity
]);
}
But now I have the following problem. The websocket works great and it pushes updates about orders. When I enter an order that gets executed instantly, this method gets called twice, right after each other. The first call is the NEW order and the second one is the FILLED order. the order.save() function writes the object to the database using the db.execute() function of the mysql2 package, but I can see that sometimes the second query gets executed faster than the first, so the final state of the order in my database is NEW. How can I prevent this from happening? Can I cancel the first query when I see the second one coming in or let them execute after each other?
TABLE
The SHOW CREATE TABLE Order results in:
CREATE TABLE `Order` (
`orderId` bigint(20) NOT NULL,
`agentId` int(11) NOT NULL,
`symbol` varchar(25) NOT NULL,
`clientOrderId` varchar(255) NOT NULL,
`side` enum('BUY','SELL') NOT NULL,
`orderType` enum('MARKET','LIMIT','STOP_LOSS','STOP_LOSS_LIMIT','TAKE_PROFIT','TAKE_PROFIT_LIMIT') NOT NULL,
`timeInForce` enum('GTC','IOC','FOK') NOT NULL,
`orderQuantity` decimal(16,8) NOT NULL,
`orderPrice` decimal(16,8) NOT NULL,
`stopPrice` decimal(16,8) NOT NULL,
`icebergQuantity` decimal(16,8) NOT NULL,
`originalClientOrderId` varchar(255) NOT NULL,
`currentExecutionType` enum('NEW','CANCELED','REPLACED','REJECTED','TRADE','EXPIRED') NOT NULL,
`currentOrderStatus` enum('NEW','FILLED','CANCELED','EXPIRED','PENDING_CANCEL','PARTIALLY_FILLED') NOT NULL,
`orderRejectReason` varchar(255) NOT NULL,
`lastExecutedQuantity` decimal(16,8) NOT NULL,
`cumulativeFilledQuantity` decimal(16,8) NOT NULL,
`lastExecutedPrice` decimal(16,8) NOT NULL,
`commissionAmount` decimal(16,8) NOT NULL,
`commissionAsset` varchar(15) DEFAULT NULL,
`transactionTime` bigint(20) NOT NULL,
`tradeId` bigint(20) NOT NULL,
`isOrderOnBook` tinyint(1) NOT NULL,
`isTradeMakerSide` tinyint(1) NOT NULL,
`creationTime` bigint(20) NOT NULL,
`cumulativeQuoteAssetTransactedQuantity` decimal(16,8) NOT NULL,
`lastQuoteAssetTransactedQuantity` decimal(16,8) NOT NULL,
`quoteOrderQuantity` decimal(16,8) NOT NULL,
PRIMARY KEY (`orderId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
Solution 1:[1]
If this is not a binance bug, it is pretty sure that the websocket callback events come in the right order as explained here. As a first step you should double-check this with console.log() in the beginning of the callback function. Further I assume that the Order.executionReportToOrder is simple synchronous glue code.
If all this is true the overtaking must appear later in the following asychronous processing. Then you can try to use a mutex-lib like async-mutex to preserve the order of the callback execution like this:
exports.subscribeAccount = async (agentId) => {
binance.websockets.userData((response) => {
console.log("Add some meaningful debug information here!")
mutex.runExclusive(() => {
console.log("Add some meaningful debug information here!")
if (eventType === "outboundAccountPosition") {
console.log("outboundAccountPosition")
}
else if (eventType === "executionReport") {
const order = Order.executionReportToOrder(response, agentId);
order.save();
}
})
})
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Markus |
