MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Mongoose | Phần mềm trung gian | Các hoạt động khôi phục được thực hiện bởi các hook trước / sau khi có lỗi

TLDR; Phần mềm trung gian Mongoose không được thiết kế cho việc này.

Phương pháp chèn giao dịch này thực sự là vá chức năng phần mềm trung gian và về cơ bản bạn đang tạo một api hoàn toàn tách biệt với mongoose phần mềm trung gian.

Điều tốt hơn là đảo ngược logic cho truy vấn xóa của bạn trong một hàm riêng biệt.

Giải pháp đơn giản &có mục đích

Cho phép một phương thức xử lý giao dịch thực hiện điều kỳ diệu của nó và tạo một phương thức loại bỏ riêng cho mô hình mẹ của bạn. Mongoose kết thúc mongodb.ClientSession.prototype.withTransaction với mongoose.Connection.prototype.transaction và chúng tôi thậm chí không phải khởi tạo hoặc quản lý một phiên! Nhìn vào sự khác biệt giữa độ dài của cái này và cái kia bên dưới. Và bạn đỡ phải đau đầu khi nhớ nội dung bên trong của phần mềm trung gian đó với chi phí của một chức năng riêng biệt.


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!
}

Phần mở rộng Nhỏ

Một cách khác để thực hiện việc này dễ dàng là đăng ký một phần mềm trung gian chỉ đơn giản là kế thừa một phiên iff _ truy vấn đã được đăng ký. Có thể gặp lỗi nếu giao dịch chưa được bắt đầu.

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 });
    });
}

Giải pháp phức tạp &rủi ro

Điều này hoạt động, và hoàn toàn, phức tạp kinh khủng. Không được khuyến khích. Có thể sẽ bị hỏng vào một ngày nào đó vì nó dựa vào sự phức tạp của API mongoose. Tôi không biết tại sao tôi lại viết mã này, vui lòng không đưa nó vào các dự án của bạn .

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. Tìm đối tượng dựa trên phần tử mảng, chỉ trả về phần tử mảng phù hợp?

  2. Làm thế nào để chèn hình ảnh trong mongoDB bằng java?

  3. Không tìm thấy nguồn dữ liệu:com.mongodb.spark.sql.DefaultSource

  4. Mongodb:Tác động đến hiệu suất của $ HINT

  5. Xóa mọi ký hiệu không phải utf-8 khỏi chuỗi