简体   繁体   English

MongoDB 事务有很多 WriteConflict 错误

[英]A lot of WriteConflict errors with MongoDB transactions

I'm currently playing with transactions in latest available docker image of MongoDB 4.1.4 (using Node 8.12.0 and Mongoose 5.3.8 as client).我目前正在玩 MongoDB 4.1.4 的最新可用 docker 图像中的事务(使用 Node 8.12.0 和 Mongoose 5.3.8 作为客户端)。 I've made a simple replica set with 3 mongo instances, everything works fine and all until I do a lot of WriteConflict errors during short time.我用 3 个 mongo 实例制作了一个简单的副本集,一切正常,直到我在短时间内出现很多WriteConflict错误。

My code looks like this:我的代码如下所示:

// name, value are strings
// date is current time

const session = await createAnalyticsTransaction(); // returns 'session'

// _id is pregenerated
var stat = await Logger.findById(_id).session(session);

if (stat) {
    // do nothing if it already exists
    return true;
} 

await Logger.update({
    _id
}, {
    $setOnInsert: {
        _id,
        name,
        created: date.toDate(),
        modified: date.toDate()
    }
}, { 
    session, 
    upsert: true 
});

/*
    var period = 'month';
    var time = '2018-11'; 
    await Analytics.update({
        _id
    }, { 
        $setOnInsert: {
            _id,
            name,
            period,
            time,
            created: date.toDate()
        },
        $inc: inc
    }, {
        upsert: true,
        session: session 
    });
*/

await session.commitTransaction();
await session.endSession();

Everything works here so far until I uncomment an upsert into Analytics collection with $inc and $setOnInsert and run about 1000 simultaneous operations.到目前为止一切正常,直到我使用$inc$setOnInsert取消对 Analytics 集合的更新插入的注释并同时运行大约 1000 个操作。 The idea is that Analytics collection should be created if it wasn't created yet.这个想法是,如果尚未创建 Analytics 集合,则应创建它。 And then I start getting a lot of MongoError: WriteConflict , with error's property errorLabels having TransientTransactionError .然后我开始收到很多MongoError: WriteConflict错误的属性errorLabelsTransientTransactionError

在此处输入图像描述

I assume it's because of $inc or upsert: true ?我认为这是因为$incupsert: true Did anyone experience this?有没有人经历过这个? What's the best solution in this case?在这种情况下最好的解决方案是什么?

{ MongoError: WriteConflict
   at /Users/akuzmenok/Zend/MeteorLingua/lingua-analytics/node_modules/mongodb-core/lib/connection/pool.js:581:63
   at authenticateStragglers (/Users/akuzmenok/Zend/MeteorLingua/lingua-analytics/node_modules/mongodb-core/lib/connection/pool.js:504:16)
   at Connection.messageHandler (/Users/akuzmenok/Zend/MeteorLingua/lingua-analytics/node_modules/mongodb-core/lib/connection/pool.js:540:5)
   at emitMessageHandler (/Users/akuzmenok/Zend/MeteorLingua/lingua-analytics/node_modules/mongodb-core/lib/connection/connection.js:310:10)
   at Socket.<anonymous> (/Users/akuzmenok/Zend/MeteorLingua/lingua-analytics/node_modules/mongodb-core/lib/connection/connection.js:453:17)
   at emitOne (events.js:116:13)
   at Socket.emit (events.js:211:7)
   at addChunk (_stream_readable.js:263:12)
   at readableAddChunk (_stream_readable.js:250:11)
   at Socket.Readable.push (_stream_readable.js:208:10)
   at TCP.onread (net.js:597:20)
=> awaited here:
   at Function.Promise.await (/Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
   at Promise.asyncApply (imports/lib/analytics.js:97:9)
   at /Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
   at Function.Promise.await (/Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
   at Promise.asyncApply (imports/lib/analytics.js:139:5)
   at /Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
   at Function.Promise.await (/Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
   at Promise.asyncApply (imports/lib/analytics.js:158:5)
   at /Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
   at Function.Promise.await (/Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
   at Promise.asyncApply (imports/lib/analytics.js:49:23)
   at /Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
=> awaited here:
   at Function.Promise.await (/Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
   at Promise.asyncApply (imports/lib/analytics.js:14:23)
   at /Users/akuzmenok/.meteor/packages/promise/.0.11.1.1ugu6ow.mjjhg++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
 errorLabels: [ 'TransientTransactionError' ],
 operationTime: Timestamp { _bsontype: 'Timestamp', low_: 12, high_: 1541424838 },
 ok: 0,
 errmsg: 'WriteConflict',
 code: 112,
 codeName: 'WriteConflict',
 '$clusterTime':
  { clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 12, high_: 1541424838 },
    signature: { hash: [Object], keyId: 0 } },
 name: 'MongoError',
 [Symbol(mongoErrorContextSymbol)]: {} }

Another note, I'm starting a transaction like this:另一个注意事项,我正在开始这样的交易:

const session = await MongoAnalytics.startSession({ causalConsistency: true });
session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } });

You read data from database and then update it.您从数据库读取数据,然后更新它。 It look like :它看起来像:

DATA (state 0) <---
UPDATED DATA (state 1) --->

When you perform two asynchronous call :当您执行两个异步调用时:

DATA (state 0) <---
DATA (state 0) <---
UPDATED DATA (state 1) --->
UPDATED DATA (state 1') ---> ERROR

It returns an error because the state of the data changed.它返回一个错误,因为数据的状态改变了。 This is how transactions are supposed to work.这就是交易应该如何工作。



To avoid the access conflict you can implement a custom queue system.为了避免访问冲突,您可以实现自定义队列系统。 Or catch the error and re-run the transaction with a setTimeout with a maximum number of try.或者捕获错误并使用 setTimeout 和最大尝试次数重新运行事务。

Queue system :排队系统

DATA (state 0) <---
UPDATED DATA (state 1) --->
DATA (state 1) <---
UPDATED DATA (state 2) --->

Re-run system重新运行系统

DATA (state 0) <---
DATA (state 0) <---
UPDATED DATA (state 1) --->
UPDATED DATA (state 1') ---> ERROR
DATA (state 1) <---
UPDATED DATA (state 2) --->

Do this in success and failure在成功和失败中这样做

session.endSession()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM