[MONGODB] 어떻게 몽구스를 사용하여 MongoDB의 트랜잭션을 사용 하는가?
MONGODB어떻게 몽구스를 사용하여 MongoDB의 트랜잭션을 사용 하는가?
나는 MongoDB의 아틀라스 클라우드 (https://cloud.mongodb.com/)와 몽구스 라이브러리를 사용하고 있습니다.
나는 거래 개념을 사용하여 여러 문서를 만들려고했는데, 그것은 작동하지 않습니다. 나는 어떤 오류가 발생하고 있지 않다. 하지만, 제대로 작동하지 않는 롤백 보인다.
app.js
//*** more code here
var app = express();
require('./models/db');
//*** more code here
모델 / db.js
var mongoose = require( 'mongoose' );
// Build the connection string
var dbURI = 'mongodb+srv://mydb:pass@cluster0-****.mongodb.net/mydb?retryWrites=true';
// Create the database connection
mongoose.connect(dbURI, {
useCreateIndex: true,
useNewUrlParser: true,
});
// Get Mongoose to use the global promise library
mongoose.Promise = global.Promise;
모델 /의 user.js
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
userName: {
type: String,
required: true
},
pass: {
type: String,
select: false
}
});
module.exports = mongoose.model("User", UserSchema, "user");
myroute.js
const db = require("mongoose");
const User = require("./models/user");
router.post("/addusers", async (req, res, next) => {
const SESSION = await db.startSession();
await SESSION.startTransaction();
try {
const newUser = new User({
//*** data for user ***
});
await newUser.save();
//*** for test purpose, trigger some error ***
throw new Error("some error");
await SESSION.commitTransaction();
//*** return data
} catch (error) {
await SESSION.abortTransaction();
} finally {
SESSION.endSession();
}
});
위의 코드 오류없이 작동하지만, 여전히 DB에 사용자를 만듭니다. 그것은 생성 된 사용자를 롤백 가정 및 컬렉션이 비어 있어야합니다.
나는 내가 여기에 놓친 모르겠어요. 사람이 오해 여기 무슨 일을 알려 주시기 바랍니다 수 있습니까?
응용 프로그램, 모델, 스키마 및 라우터는 다른 파일에 있습니다.
해결법
-
==============================
1.당신은 트랜잭션 동안 활성화 된 모든 읽기 / 쓰기 작업에 대한 옵션 내에서 세션을 포함해야합니다. 그런 다음에야 그들은 실제로 당신이 그들을 롤백 할 수있는 트랜잭션 범위에 적용됩니다.
당신은 트랜잭션 동안 활성화 된 모든 읽기 / 쓰기 작업에 대한 옵션 내에서 세션을 포함해야합니다. 그런 다음에야 그들은 실제로 당신이 그들을 롤백 할 수있는 트랜잭션 범위에 적용됩니다.
좀 더 전체 목록, 그리고 그냥 관계형 거래 경험이있는 대부분의 사람들에게 매우 잘 알고 있어야 더 고전 주문 / OrderItems에 모델링을 사용하는 등 :
const { Schema } = mongoose = require('mongoose'); // URI including the name of the replicaSet connecting to const uri = 'mongodb://localhost:27017/trandemo?replicaSet=fresh'; const opts = { useNewUrlParser: true }; // sensible defaults mongoose.Promise = global.Promise; mongoose.set('debug', true); mongoose.set('useFindAndModify', false); mongoose.set('useCreateIndex', true); // schema defs const orderSchema = new Schema({ name: String }); const orderItemsSchema = new Schema({ order: { type: Schema.Types.ObjectId, ref: 'Order' }, itemName: String, price: Number }); const Order = mongoose.model('Order', orderSchema); const OrderItems = mongoose.model('OrderItems', orderItemsSchema); // log helper const log = data => console.log(JSON.stringify(data, undefined, 2)); // main (async function() { try { const conn = await mongoose.connect(uri, opts); // clean models await Promise.all( Object.entries(conn.models).map(([k,m]) => m.deleteMany()) ) let session = await conn.startSession(); session.startTransaction(); // Collections must exist in transactions await Promise.all( Object.entries(conn.models).map(([k,m]) => m.createCollection()) ); let [order, other] = await Order.insertMany([ { name: 'Bill' }, { name: 'Ted' } ], { session }); let fred = new Order({ name: 'Fred' }); await fred.save({ session }); let items = await OrderItems.insertMany( [ { order: order._id, itemName: 'Cheese', price: 1 }, { order: order._id, itemName: 'Bread', price: 2 }, { order: order._id, itemName: 'Milk', price: 3 } ], { session } ); // update an item let result1 = await OrderItems.updateOne( { order: order._id, itemName: 'Milk' }, { $inc: { price: 1 } }, { session } ); log(result1); // commit await session.commitTransaction(); // start another session.startTransaction(); // Update and abort let result2 = await OrderItems.findOneAndUpdate( { order: order._id, itemName: 'Milk' }, { $inc: { price: 1 } }, { 'new': true, session } ); log(result2); await session.abortTransaction(); /* * $lookup join - expect Milk to be price: 4 * */ let joined = await Order.aggregate([ { '$match': { _id: order._id } }, { '$lookup': { 'from': OrderItems.collection.name, 'foreignField': 'order', 'localField': '_id', 'as': 'orderitems' }} ]); log(joined); } catch(e) { console.error(e) } finally { mongoose.disconnect() } })()
나는 일반적으로는이 모든 작업에 필요한 "옵션"개체의 키의 이름이기 때문에, 소문자 변수 세션을 호출 추천 할 것 그래서. 소문자 규칙이를 유지하는 것은 물론 ES6 개체 할당 같은 것들을 사용 가능 :
const conn = await mongoose.connect(uri, opts); ... let session = await conn.startSession(); session.startTransaction();
또한 거래 몽구스 문서는 약간 오해의 소지가 있거나 적어도 더 설명이 될 수있다. 무엇이 예에서 DB 실제로 몽구스 연결 인스턴스, 그리고 기본 DB 또는 일부가이 문제를 잘못 해석 할 수 있습니다로도 몽구스 글로벌 수입 그대로를 의미합니다. 이 목록 및 발췌 위의 참고 mongoose.connect에서 ()를 획득하고 공유 수입에서 액세스 할 수있는 무언가로 코드 내에서 유지되어야한다.
연결이 설정 한 후 다른 방법으로 당신도 언제라도 mongoose.connection 속성을 통해 모듈의 코드에서이 잡아 수 있습니다. 이것은 서버 경로 처리기로 일 안에 일반적으로 안전하고 코드가 호출되는 시점하여 데이터베이스 연결이되기 때문에 같은.
코드는 또한 다른 모델 방법에서 세션 사용을 보여줍니다
let [order, other] = await Order.insertMany([ { name: 'Bill' }, { name: 'Ted' } ], { session }); let fred = new Order({ name: 'Fred' }); await fred.save({ session });
이 세션 키와 값이 예상되는 경우 모든 찾기 () 기반의 방식과 방법은 모든 최종있는 기반 업데이트 () 또는 삽입 () 및 삭제 () "옵션은 차단". 저장 () 메소드의 유일한 인수는이 옵션 블록입니다. 이것은 그 참조 세션에 현재의 트랜잭션 (transaction)에 이러한 작업을 적용 할 MongoDB를 알 것입니다.
거의 같은 방법으로 전에 트랜잭션은 트랜잭션이 진행되는 동안 데이터의 상태를 표시하지 않는 세션 옵션을 지정하지 않는 발견 () 또는 이와 유사한에 대한 모든 요청을 위해 최선을 다하고 있습니다. 수정 된 데이터 상태는 트랜잭션 완료되면 다른 작업 만 사용할 수 있습니다. 문서에 포함으로이 쓰기에 효과가 있습니다.
때 "중단"이 발행 :
// Update and abort let result2 = await OrderItems.findOneAndUpdate( { order: order._id, itemName: 'Milk' }, { $inc: { price: 1 } }, { 'new': true, session } ); log(result2); await session.abortTransaction();
활성 트랜잭션에 대한 모든 작업은 상태에서 제거되고 적용되지 않습니다. 따라서 그들은 나중에 작업을 결과에 표시되지 않습니다. 여기 예에서 문서의 값이 증가하고 현재 세션 5의 검색 값을 보여줍니다. session.abortTransaction () 문서의 이전 상태로 되돌릴한다 그러나 후. 노력하지 않는 한 동일한 세션에서 데이터를 읽는되지 않은 전역 문맥, 그 상태 변화가 표시되지 않습니다.
즉, 일반적인 개요를 제공해야합니다. 이 쓰기 오류 및 재시도 다양한 수준을 처리하기 위해 추가 할 수 있습니다 더 복잡하지만, 그것은 이미 광범위하게 문서와 많은 샘플에 덮여, 또는보다 구체적인 질문에 대한 답변을 얻을 수 있습니다.
참고로 포함되는 리스팅의 출력은 여기에 도시되어있다 :
Mongoose: orders.deleteMany({}, {}) Mongoose: orderitems.deleteMany({}, {}) Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") }) Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") }) Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") }) Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") }) { "n": 1, "nModified": 1, "opTime": { "ts": "6626894672394452998", "t": 139 }, "electionId": "7fffffff000000000000008b", "ok": 1, "operationTime": "6626894672394452998", "$clusterTime": { "clusterTime": "6626894672394452998", "signature": { "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "keyId": 0 } } } Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false }) { "_id": "5bf775986c7c1a61d12137e2", "order": "5bf775986c7c1a61d12137dd", "itemName": "Milk", "price": 5, "__v": 0 } Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {}) [ { "_id": "5bf775986c7c1a61d12137dd", "name": "Bill", "__v": 0, "orderitems": [ { "_id": "5bf775986c7c1a61d12137e0", "order": "5bf775986c7c1a61d12137dd", "itemName": "Cheese", "price": 1, "__v": 0 }, { "_id": "5bf775986c7c1a61d12137e1", "order": "5bf775986c7c1a61d12137dd", "itemName": "Bread", "price": 2, "__v": 0 }, { "_id": "5bf775986c7c1a61d12137e2", "order": "5bf775986c7c1a61d12137dd", "itemName": "Milk", "price": 4, "__v": 0 } ] } ]
from https://stackoverflow.com/questions/53435616/how-to-use-mongodb-transaction-using-mongoose by cc-by-sa and MIT license
'MONGODB' 카테고리의 다른 글
[MONGODB] MongoDB를 역 정규식 (0) | 2019.12.06 |
---|---|
[MONGODB] 어떻게 특정 값에 MongoDB의 문서에있는 모든 배열 요소를 변경하려면? [복제] (0) | 2019.12.06 |
[MONGODB] 몽고 컬렉션에서 특수 문자 작업 (0) | 2019.12.06 |
[MONGODB] 어떻게 몽구스의 DB 문서에 _id 설정하는 방법? (0) | 2019.12.06 |
[MONGODB] upsert 배열 데이터를 몽고 수 있습니까? (0) | 2019.12.06 |