I am using Node.js and Mongoose to interact with MongoDB. But I encountered a strange error: sometimes (not all the time) duplicate documents get inserted into the collection, even though I just use insertOne
in bulkWrite
within a transaction, and the createdAt
field value is the same for both documents. Please, tell me why?
I couldn’t get the whole situation. In your insertOne
or bulkWrite
, do you insert the same document more than once? If you want to enforce unique documents in a collection, you could use a index with the unique option
My query is something like that:
const session = await mongooseConnection.startSession()
await session.withTransaction(async () => {
return TransactionHistory.bulkWrite([
{
insertOne: {
document: {...}
}
}
], { session })
})
} catch (err) {
if (session.inTransaction()) {
await session.abortTransaction()
}
} finally {
await session.endSession()
}
If I set a unique index, I will receive an error: ‘duplicate key’ even if the collection does not contain that key, and the transaction will be rolled back.
Please tell me why and how I can fix it?
Can you provide a more detailed example? With the documents that are duplicated and the index created in the collection?
This is an example of duplicate documents:
[{
"transactionHash": "0x81c656af8401cd8e36c9b9e69e700bab294931382c2b1174790e9dead94fe085",
"tokenId": "11000000089572",
"orderId": 132794,
"name": "Envia Blade",
"sellerAddress": "0x5dbf960c5ca99fd92f42b7bc54d2985ce96bff72",
"buyerAddress": "0xfacf10ed56a87c14a9066a45a5b44abda30a7817",
"nftType": 1,
"price": "666.0",
"floatPrice": 666,
"currencyCode": "USDT",
"image": "https://genso.game/nft_images/10000000895.png",
"nftItemId": "10000000895",
"type": "equipment",
"timestamp": 1703725395,
"createdAt": {
"$date": "2023-12-28T01:03:43.156Z"
},
"updatedAt": {
"$date": "2023-12-28T01:03:43.156Z"
}
},
{
"transactionHash": "0x81c656af8401cd8e36c9b9e69e700bab294931382c2b1174790e9dead94fe085",
"tokenId": "11000000089572",
"orderId": 132794,
"name": "Envia Blade",
"sellerAddress": "0x5dbf960c5ca99fd92f42b7bc54d2985ce96bff72",
"buyerAddress": "0xfacf10ed56a87c14a9066a45a5b44abda30a7817",
"nftType": 1,
"price": "666.0",
"floatPrice": 666,
"currencyCode": "USDT",
"image": "https://genso.game/nft_images/10000000895.png",
"nftItemId": "10000000895",
"type": "equipment",
"timestamp": 1703725395,
"createdAt": {
"$date": "2023-12-28T01:03:43.160Z"
},
"updatedAt": {
"$date": "2023-12-28T01:03:43.160Z"
}
}]
If I set unique for ‘transactionHash’, I will receive a ‘duplicate key’ error, even if the collection does not already contain that key, and the transaction will be rolled back. If I do not set a unique index, I may encounter duplicate documents
I tried these two documents with a unique index on transactionHash
, and it worked as expected. The first document was inserted with no issues, and for the second one, I got an E11000 duplicate key error collection ...
. I’m using MongoDB Enterprise 7.0, and the command that I used to create the index was db.test.createIndex({transactionHash: 1}, {unique: true})
I am attempting to fix this bug by adding logic to prevent a reentrant call in session.withTransaction()
. Here is my code for executing the transaction:
const executeTransaction = async (callback) => {
const session = await mongooseConnection.startSession()
try {
logger.info('Start transaction')
let isRunning = false
await session.withTransaction(async () => {
if (!isRunning) {
isRunning = true
logger.info('Transaction is running')
const callbackResult = await callback(session)
return callbackResult
}
logger.info('Transaction has already ran')
return null
})
await session.endSession()
logger.info('End transaction')
} catch (err) {
logger.error(`Transaction error: ${err}`)
if (session.inTransaction()) {
await session.abortTransaction()
}
await session.endSession()
throw err
}
}
In a normal case, I receive these logs:
[INFO] 2024-01-16 03:22:21 - Start transaction
[INFO] 2024-01-16 03:22:21 - Transaction is running
[INFO] 2024-01-16 03:22:21 - End transaction
But when I try again, again and again, I get these logs and a MongooseError:
[INFO] 2024-01-16 03:22:10 - Start transaction
[INFO] 2024-01-16 03:22:10 - Transaction is running
[INFO] 2024-01-16 03:22:10 - Transaction has already ran
[INFO] 2024-01-16 03:22:10 - End transaction
MongooseError: Cannot set a document’s session to a session that has ended. Make sure you haven’t called endSession()
on the session you are passing to $session()
.
I checked the database, and the data was inserted without duplicate documents. However, why am I encountering this MongooseError error, and why is the callback function in session.withTransaction()
being called multiple times?