почему insertmany не работает, используя mongoos с транзакциями? - PullRequest
0 голосов
/ 28 сентября 2019

Я пытаюсь вставить данные, используя inertMany. Но я не могу вставить данные, почему? Я использую mongoose session, если произошла какая-либо ошибка, я откат изменений

https://codesandbox.io/s/dreamy-bell-9u0bz

app.get("/saveData", async (req, res, next) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  try {
    const data = [
      {
        empid: "Ad",
        id: 4,
        date: "19-Jul-2019"
      },
      {
        empid: "Bc",
        id: 56,
        date: "18-Jul-2019"
      },

      {
        empid: "C",
        id: 6,
        date: "11-Jul-2019"
      }
    ];
    console.log("before save");
    let saveBlog = await BlogPostModel.insertMany(data, { session }); //when fail its goes to catch
    await session.commitTransaction();
    return res.send(saveBlog);
  } catch (error) {
    console.log(error);
    await session.abortTransaction();
    return res.status(400).send(error);
  }
});

1 Ответ

1 голос
/ 28 сентября 2019

Поскольку вы, кажется, не поняли помеченный дубликат или комментарий к своему последнему вопросу , вот прямая демонстрация:

const { Schema } = mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true, useUnifiedTopology: true };

mongoose.Promise = global.Promise;

mongoose.set('debug', true);
mongoose.set('useCreateIndex', true);
mongoose.set('useFindAndModify', false);

const blogPostSchema = new Schema({
  id: { type: Number, unique: true },
  empid: String,
  date: Date
});

const BlogPost = mongoose.model('BlogPost', blogPostSchema);

const sampleData = [
  { empid: "test13", id: 6, date: '11-Jul-2019' },
  { empid: "test123", id: 4, date: '19-Jul-2019' },
  { empid: "test13", id: 4, date: '18-Jul-2019' }
];

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {
    const conn = await mongoose.connect(uri, opts);

    // Clean data
    await Promise.all(
      Object.values(conn.models).map(m => m.deleteMany())
    );

    // Collections must existi in transactions
    await Promise.all(
      Object.values(conn.models).map(m => m.createCollection())
    );


    // With Transaction
    log("With Transaction");
    let session = await conn.startSession();
    session.startTransaction();

    try {
      await BlogPost.insertMany(sampleData, { session });
      await session.commitTransaction();
    } catch(e) {
      // Show the error and abort
      log({ err: e.errmsg, result: e.result.result.writeErrors });
      await session.abortTransaction();
    }

    log({ results: (await BlogPost.find()) });

    // No transaction
    log("Without Transaction");
    try {
      await BlogPost.insertMany(sampleData);
    } catch(e) {
      // Show the error
      log({ err: e.errmsg, result: e.result.result.writeErrors });
    }

    log({ results: (await BlogPost.find()) });


  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }


})();

Ивывод:

Mongoose: blogposts.createIndex({ id: 1 }, { unique: true, background: true })
Mongoose: blogposts.deleteMany({}, {})
"With Transaction"
Mongoose: blogposts.insertMany([ { _id: 5d8f28ac462a1e1a8c6838a2, empid: 'test13', id: 6, date: 2019-07-10T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a3, empid: 'test123', id: 4, date: 2019-07-18T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a4, empid: 'test13', id: 4, date: 2019-07-17T14:00:00.000Z, __v: 0 } ], { session: ClientSession("650da06d23544ef8bc1d345d93331d1e") })
{
  "err": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
  "result": [
    {
      "code": 11000,
      "index": 2,
      "errmsg": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
      "op": {
        "_id": "5d8f28ac462a1e1a8c6838a4",
        "empid": "test13",
        "id": 4,
        "date": "2019-07-17T14:00:00.000Z",
        "__v": 0
      }
    }
  ]
}
Mongoose: blogposts.find({}, { projection: {} })
{
  "results": []
}
"Without Transaction"
Mongoose: blogposts.insertMany([ { _id: 5d8f28ac462a1e1a8c6838a5, empid: 'test13', id: 6, date: 2019-07-10T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a6, empid: 'test123', id: 4, date: 2019-07-18T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a7, empid: 'test13', id: 4, date: 2019-07-17T14:00:00.000Z, __v: 0 } ], {})
{
  "err": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
  "result": [
    {
      "code": 11000,
      "index": 2,
      "errmsg": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
      "op": {
        "_id": "5d8f28ac462a1e1a8c6838a7",
        "empid": "test13",
        "id": 4,
        "date": "2019-07-17T14:00:00.000Z",
        "__v": 0
      }
    }
  ]
}
Mongoose: blogposts.find({}, { projection: {} })
{
  "results": [
    {
      "_id": "5d8f28ac462a1e1a8c6838a5",
      "empid": "test13",
      "id": 6,
      "date": "2019-07-10T14:00:00.000Z",
      "__v": 0
    },
    {
      "_id": "5d8f28ac462a1e1a8c6838a6",
      "empid": "test123",
      "id": 4,
      "date": "2019-07-18T14:00:00.000Z",
      "__v": 0
    }
  ]
}

Обратите внимание, что при использовании транзакции в коллекцию не вставляются элементы.Использование insertMany() с поведением по умолчанию ordered: true будет вставлять все пакетные элементы до момента, когда возникнет какая-либо ошибка.

Обратите также внимание, как указано, поскольку вы действительно ожидаете ошибка вы должны включить такое утверждение в свой собственный try..catch или аналогичный обработчик ошибок.В противном случае любая ошибка (которая ожидается в этом примере) просто упадет до external catch, что, конечно, при демонстрации просто завершает работу программы.


Не совсемв самом вопросе, но что-то, на самом деле не упомянутое в демонстрациях Как использовать транзакцию MongoDB с использованием Mongoose? действительно означает, что вы должны знать, что список транзакций активен вы должен также включать атрибут session в любых последующих чтениях, чтобы увидеть изменения, внесенные в этой транзакции.

Например, следующее не будет показывать содержимое в коллекции:

let session = await conn.startSession();
session.startTransaction();

try {
  await BlogPost.insertMany(sampleData, { session });
  let documents = await BlogPost.find(); // This would return nothing
  await session.commitTransaction();
} catch(e) {
  // Show the error and abort
  log({ err: e.errmsg, result: e.result.result.writeErrors });
  await session.abortTransaction();
}

Однако включение session в find() на самом деле покажет, что вставлено:

try {
  await BlogPost.insertMany(sampleData, { session });
  // Actually includes the session and therefore the state
  let documents = await BlogPost.find({},{ session });

  await session.commitTransaction();
} catch(e) {
  // Show the error and abort
  log({ err: e.errmsg, result: e.result.result.writeErrors });
  await session.abortTransaction();
}

И, конечно, чтение будет вэтот случай будет зависеть от сбоя insertMany() , а не по любой причине, поскольку любая ошибка приведет к выходу на catch до того, как будет сделан следующий запрос.

Когда транзакция зафиксирована, она, конечно, доступна для глобального состояния соединения.Но пока в процессе только операции, включающие ту же самую информацию session, с которой была запущена транзакция, будут видеть все изменения, внесенные в эту транзакцию.

...