MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Мангуста | Мидълуер | Операции за връщане назад, извършвани от пре/след кукички, когато се изведе грешка

TLDR; Мидълуерът Mongoose не е проектиран за това.

Този метод за вмъкване на транзакции всъщност коригира функционалността на междинния софтуер и вие по същество създавате api, напълно отделен от mongoose междинен софтуер.

Това, което би било по-добре, е да обърнете логиката за вашата заявка за премахване в отделна функция.

Просто и предвидено решение

Позволете на метод за обработка на транзакция да направи своята магия и създайте отделен метод за премахване за вашия родителски модел. Mongoose обвива mongodb.ClientSession.prototype.withTransaction с mongoose.Connection.prototype.transaction и ние дори не трябва да създаваме или управляваме сесия! Вижте разликата между дължината на това и онова по-долу. И спестявате психическото главоболие от запомнянето на вътрешността на този междинен софтуер на цената на една отделна функция.


const parentSchema = new mongoose.Schema({
    name: String,
    children: [{ type: mongoose.Schema.Types.ObjectId, ref: "Child" }],
});

const childSchema = new mongoose.Schema({
    name: String,
    parent: { type: mongoose.Schema.Types.ObjectId, ref: "Parent" },
});

// Assume `parent` is a parent document here
async function fullRemoveParent(parent) {
    // The document's connection
    const db = parent.db;

    // This handles everything with the transaction for us, including retries
    // session, commits, aborts, etc.
    await db.transaction(async function (session) {
        // Make sure to associate all actions with the session
        await parent.remove({ session });
        await db
            .model("Child")
            .deleteMany({ _id: { $in: parent.children } })
            .session(session);
    });

    // And done!
}

Малко разширение

Друг начин да направите това лесно е да регистрирате междинен софтуер, който просто наследява сесия iff _ заявката има регистрирана. Може да изведе грешка, ако транзакцията не е стартирана.

const parentSchema = new mongoose.Schema({
    name: String,
    children: [{ type: mongoose.Schema.Types.ObjectId, ref: "Child" }],
});

const childSchema = new mongoose.Schema({
    name: String,
    parent: { type: mongoose.Schema.Types.ObjectId, ref: "Parent" },
});

parentSchema.pre("remove", async function () {
    // Look how easy!! Just make sure to pass a transactional 
    // session to the removal
    await this.db
        .model("Child")
        .deleteMany({ _id: { $in: parent.children } })
        .session(this.$session());

    // // If you want to: throw an error/warning if you forgot to add a session
    // // and transaction
    // if(!this.$session() || !this.$session().inTransaction()) {
    //    throw new Error("HEY YOU FORGOT A TRANSACTION.");
    // }
});

// Assume `parent` is a parent document here
async function fullRemoveParent(parent) {
    db.transaction(async function(session) {
        await parent.remove({ session });
    });
}

Рисковано и сложно решение

Това работи и е напълно, ужасно сложно. Не се препоръчва. Вероятно ще се повреди някой ден, защото разчита на тънкостите на мангустовия API. Не знам защо кодирах това, моля, не го включвайте в проектите си .

import mongoose from "mongoose";
import mongodb from "mongodb";

const parentSchema = new mongoose.Schema({
    name: String,
    children: [{ type: mongoose.Schema.Types.ObjectId, ref: "Child" }],
});

const childSchema = new mongoose.Schema({
    name: String,
    parent: { type: mongoose.Schema.Types.ObjectId, ref: "Parent" },
});

// Choose a transaction timeout
const TRANSACTION_TIMEOUT = 120000; // milliseconds

// No need for next() callback if using an async function.
parentSchema.pre("remove", async function () {
    // `this` refers to the document, not the query
    let session = this.$session();

    // Check if this op is already part of a session, and start one if not.
    if (!session) {
        // `this.db` refers to the documents's connection.
        session = await this.db.startSession();

        // Set the document's associated session.
        this.$session(session);

        // Note if you created the session, so post can clean it up.
        this.$locals.localSession = true;

        //
    }

    // Check if already in transaction.
    if (!session.inTransaction()) {
        await session.startTransaction();

        // Note if you created transaction.
        this.$locals.localTransaction = true;

        // If you want a timeout
        this.$locals.startTime = new Date();
    }

    // Let's assume that we need to remove all parent references in the
    // children. (just add session-associated ops to extend this)
    await this.db
        .model("Child") // Child model of this connection
        .updateMany(
            { _id: { $in: this.children } },
            { $unset: { parent: true } }
        )
        .session(session);
});

parentSchema.post("remove", async function (parent) {
    if (this.$locals.localTransaction) {
        // Here, there may be an error when we commit, so we need to check if it
        // is a 'retryable' error, then retry if so.
        try {
            await this.$session().commitTransaction();
        } catch (err) {
            if (
                err instanceof mongodb.MongoError &&
                err.hasErrorLabel("TransientTransactionError") &&
                new Date() - this.$locals.startTime < TRANSACTION_TIMEOUT
            ) {
                await parent.remove({ session: this.$session() });
            } else {
                throw err;
            }
        }
    }

    if (this.$locals.localSession) {
        await this.$session().endSession();
        this.$session(null);
    }
});

// Specific error handling middleware if its really time to abort (clean up
// the injections)
parentSchema.post("remove", async function (err, doc, next) {
    if (this.$locals.localTransaction) {
        await this.$session().abortTransaction();
    }

    if (this.$locals.localSession) {
        await this.$session().endSession();
        this.$session(null);
    }

    next(err);
});




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. модел mongoose за различни типове потребители

  2. MongoDB:безопасно ли е да използвате идентификатора на документа публично?

  3. грешка при изчакване на връзката с mongodb

  4. Запитване, вложено в mongoDB

  5. Статус 500 Вътрешна сървърна грешка в IE-11 с Angular Js приложение