การจัดการข้อผิดพลาดและตรรกะการลองใหม่
ผู้เรียนจะดักจับ TransientTransactionError และ UnknownTransactionCommitResult และสร้างลูปการลองใหม่ตามคำแนะนำเพื่อความน่าเชื่อถือในการใช้งานจริง
การจัดการข้อผิดพลาดและตรรกะการลองใหม่ เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Why Transactions Need Retry Logic
MongoDB transactions can fail with transient errors—temporary conditions like network blips, primary failovers, or write conflicts—that do not indicate a logical problem with your code. These errors are safe to retry: the transaction was rolled back cleanly, and retrying it will produce correct results. Without retry logic, your application will surface unnecessary errors to users for conditions that a simple retry would resolve.
Two Error Labels to Understand
MongoDB classifies transaction errors with two important error labels: TransientTransactionError and UnknownTransactionCommitResult. TransientTransactionError means the entire transaction failed and it is safe to retry from the beginning. UnknownTransactionCommitResult means the commit was sent but the client does not know if it succeeded—the commit must be retried (not the whole transaction). Each requires a different retry strategy.
// Checking error labels
if (error.hasErrorLabel('TransientTransactionError')) {
// Retry the whole transaction from scratch
console.log('Transient error — retrying transaction');
} else if (error.hasErrorLabel('UnknownTransactionCommitResult')) {
// Retry only the commit, not the whole transaction
console.log('Unknown commit result — retrying commit only');
} else {
// Application error (e.g., InsufficientFunds) — do not retry
throw error;
}Causes of TransientTransactionError
TransientTransactionError occurs when a transaction cannot proceed due to temporary conditions: write conflicts (another transaction modified the same document first), lock acquisition timeouts, replica set election failovers, or network connectivity issues. MongoDB rolls back the transaction completely before returning this error. The safe response is to start over with a fresh session and re-execute all operations.
Causes of UnknownTransactionCommitResult
UnknownTransactionCommitResult happens when a network timeout or failover occurs after MongoDB has committed the transaction but before the acknowledgment reaches the client. The transaction may or may not have been committed. The correct response is to retry only commitTransaction()—not the whole transaction—because MongoDB detects duplicate commit calls on the same session and returns successfully if the transaction is already committed.
The Recommended Retry Pattern
MongoDB's documentation specifies a two-loop retry pattern: an outer loop that retries the entire transaction on TransientTransactionError, and an inner loop that retries only the commit on UnknownTransactionCommitResult. This pattern handles all transient failure modes correctly and is what the withTransaction() helper implements internally.
async function runTransactionWithRetry(txnFunc, client, session) {
while (true) {
try {
await txnFunc(client, session); // execute transaction
break; // success — exit loop
} catch (error) {
if (error.hasErrorLabel('TransientTransactionError')) {
console.log('Retrying transaction due to TransientTransactionError');
continue; // retry the whole transaction
} else {
throw error; // non-transient error — surface to caller
}
}
}
}Retrying the Commit Separately
The commit retry loop handles UnknownTransactionCommitResult. It continuously calls commitTransaction() until either the commit is confirmed or a non-retryable error occurs. MongoDB idempotently handles duplicate commit calls on the same transaction—if the transaction was already committed, the retry returns success without re-applying the writes.
async function commitWithRetry(session) {
while (true) {
try {
await session.commitTransaction();
console.log('Transaction committed');
break;
} catch (error) {
if (error.hasErrorLabel('UnknownTransactionCommitResult')) {
console.log('Retrying commit...');
continue; // retry commit
} else {
throw error;
}
}
}
}Full Two-Loop Implementation
Combining both retry loops gives the complete production-grade transaction function. The business logic is extracted into a separate function passed as a parameter, keeping the retry scaffolding reusable. Notice that the session is reused across retries—you do not create a new session for each retry, but you must call startTransaction() again at the beginning of each attempt.
async function runTransaction(client, txnBody) {
const session = client.startSession();
try {
let committed = false;
while (!committed) {
session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } });
try {
await txnBody(session);
// Commit with retry
while (true) {
try { await session.commitTransaction(); committed = true; break; }
catch (e) {
if (e.hasErrorLabel('UnknownTransactionCommitResult')) continue;
else throw e;
}
}
} catch (e) {
await session.abortTransaction();
if (!e.hasErrorLabel('TransientTransactionError')) throw e;
// else retry
}
}
} finally {
await session.endSession();
}
}Why withTransaction() Is Simpler
The session.withTransaction(callback, options) helper implements the two-loop retry pattern automatically. You provide only the business logic callback and transaction options. Using withTransaction dramatically reduces boilerplate, minimizes the risk of getting the retry logic wrong, and is the recommended approach for all production MongoDB transaction code in the Node.js driver.
// withTransaction handles all retry logic internally
const session = client.startSession();
try {
await session.withTransaction(
async (session) => {
// Pure business logic — no retry code needed here
await debitAccount(session, fromId, amount);
await creditAccount(session, toId, amount);
await insertAuditLog(session, fromId, toId, amount);
},
{ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } }
);
} finally {
await session.endSession();
}Application-Level vs Transient Errors
Not every error inside a transaction is transient and retryable. Application-level errors—like insufficient funds, duplicate username, or invalid input—should abort the transaction and return an error to the user, not retry. Only errors with the TransientTransactionError or UnknownTransactionCommitResult labels should trigger retries. Mix both types of error handling in your transaction callback: throw domain errors that the outer layer can check before retrying.
async function transfer(session, fromId, toId, amount) {
const account = await db.collection('accounts').findOne({ _id: fromId }, { session });
// Domain error — should NOT be retried
if (account.balance < amount) {
const err = new Error('Insufficient funds');
err.isDomainError = true;
throw err;
}
await db.collection('accounts').updateOne({ _id: fromId }, { $inc: { balance: -amount } }, { session });
await db.collection('accounts').updateOne({ _id: toId }, { $inc: { balance: amount } }, { session });
}Write Conflicts: The Most Common Cause
Write conflicts occur when two transactions try to modify the same document simultaneously. MongoDB uses optimistic locking—it allows both transactions to proceed until commit time, then aborts the one that conflicts. The aborted transaction receives a TransientTransactionError with code WriteConflict. This is expected behavior—not a bug—and the retry loop handles it correctly. Frequent write conflicts indicate a hot document that may need schema redesign.
// Detection: WriteConflict is a TransientTransactionError subtype
try {
await session.commitTransaction();
} catch (error) {
if (error.hasErrorLabel('TransientTransactionError')) {
// This includes WriteConflict (code 112)
console.log('Code:', error.code); // 112 = WriteConflict
// Retry the whole transaction
}
}Max Retry Attempts: Preventing Infinite Loops
In a production retry loop, always enforce a maximum number of retries to prevent infinite loops in pathological scenarios (e.g., perpetual write conflicts on a very hot document). After the maximum attempts, throw the last error so the application can handle it gracefully. Include exponential backoff with jitter between retries to reduce contention from multiple clients simultaneously retrying the same transaction.
async function runWithMaxRetries(session, txnFn, maxRetries = 5) {
let attempts = 0;
while (attempts < maxRetries) {
try {
await session.withTransaction(txnFn);
return;
} catch (error) {
attempts++;
if (error.hasErrorLabel('TransientTransactionError') && attempts < maxRetries) {
const delay = Math.min(100 * Math.pow(2, attempts) + Math.random() * 100, 5000);
await new Promise(res => setTimeout(res, delay));
continue;
}
throw error; // exceeded retries or non-transient
}
}
}Quick Check
Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.
Lesson Recap
In this lesson you learned: TransientTransactionError means retry the entire transaction from the start, UnknownTransactionCommitResult means retry only the commit call, and withTransaction() implements this two-loop retry pattern automatically and is the recommended production approach. Next up we explore transaction performance considerations and how to minimize their overhead.
คำถามที่พบบ่อย
บทเรียน “การจัดการข้อผิดพลาดและตรรกะการลองใหม่” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การจัดการข้อผิดพลาดและตรรกะการลองใหม่” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การจัดการข้อผิดพลาดและตรรกะการลองใหม่”
ผู้เรียนจะดักจับ TransientTransactionError และ UnknownTransactionCommitResult และสร้างลูปการลองใหม่ตามคำแนะนำเพื่อความน่าเชื่อถือในการใช้งานจริง คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การจัดการข้อผิดพลาดและตรรกะการลองใหม่” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม
ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การรับประกัน ACID ในคลังเอกสารแบบกระจาย
- การเริ่มเซสชันและธุรกรรมหลายเอกสาร
- การจัดการข้อผิดพลาดและตรรกะการลองใหม่
- ข้อพิจารณาด้านประสิทธิภาพของธุรกรรม