Sửa lỗi MongoDB 'Transaction numbers are only allowed on a replica set member or mongos'

intermediate🍃 MongoDB2026-03-21| MongoDB 4.0+, Node.js / Python / bất kỳ driver nào, instance mongod standalone (không phải replica set)

Error Message

MongoServerError: Transaction numbers are only allowed on a replica set member or mongos
#mongodb#transaction#replica-set#standalone#session

Chuyện gì đã xảy ra

Bạn tích hợp hỗ trợ transaction đa tài liệu, chạy ứng dụng trên máy local, và nhận ngay cái tát này:

MongoServerError: Transaction numbers are only allowed on a replica set member or mongos

Chín lần trên mười lỗi này xuất hiện trong môi trường dev local. MongoDB đang chạy ở chế độ standalone mongod đơn thuần — mặc định khi bạn cài qua package manager hoặc khởi động mà không có file config. Transaction yêu cầu phải có replica set hoặc sharded cluster (mongos). Chế độ standalone đơn giản là không hỗ trợ chúng, không có ngoại lệ.

Xác nhận nguyên nhân gốc rễ

Hãy kiểm tra instance đang chạy ở chế độ nào trước khi làm bất cứ điều gì:

mongosh --eval "db.adminCommand({ isMaster: 1 })"

Không có trường "setName" trong output? Bạn đang chạy standalone node. Đó là thủ phạm.

{
  "ismaster": true,
  "maxBsonObjectSize": 16777216,
  ...
  // Không có trường "setName" = chế độ standalone
}

Thành viên của replica set trông khác hẳn — nó luôn mang tên set:

{
  "setName": "rs0",
  "ismaster": true,
  ...
}

Lưu ý: Trên MongoDB 5.0+, isMaster đã bị deprecated. Hãy dùng db.adminCommand({ hello: 1 }) thay thế — cấu trúc output tương tự, chỉ là lệnh được khuyến nghị về sau.

Cách khắc phục: chuyển standalone sang single-node replica set

Bạn không cần ba node cho dev local. Một single-node replica set mở khóa đầy đủ hỗ trợ transaction với gần như không tốn overhead. Có ba cách để thực hiện tùy theo setup của bạn.

Tùy chọn 1 — Chỉnh sửa mongod.conf (khuyến nghị)

Tìm file config của bạn — thường là /etc/mongod.conf trên Linux hoặc /usr/local/etc/mongod.conf trên macOS với Homebrew. Thêm block replication:

# /etc/mongod.conf
replication:
  replSetName: "rs0"

Khởi động lại service:

# Linux (systemd)
sudo systemctl restart mongod

# macOS (Homebrew)
brew services restart mongodb-community

Sau đó khởi tạo replica set — đây là bước thực hiện một lần duy nhất:

mongosh --eval "rs.initiate()"

Bạn sẽ thấy:

{ "ok": 1 }

Tùy chọn 2 — Command-line flag (kiểm tra nhanh)

Khởi động mongod thủ công mà không có file config? Truyền flag trực tiếp:

mongod --replSet rs0 --dbpath /data/db

Mở terminal thứ hai và khởi tạo set một lần:

mongosh --eval "rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] })"

Tùy chọn 3 — Docker Compose

Phần phức tạp với container là rs.initiate() phải chạy sau khi MongoDB đã healthy. Một init container xử lý việc này rất gọn gàng:

services:
  mongo:
    image: mongo:7
    command: ["--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "27017:27017"
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

  mongo-init:
    image: mongo:7
    depends_on:
      mongo:
        condition: service_healthy
    restart: "no"
    entrypoint: >
      mongosh --host mongo:27017 --eval
      "rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'mongo:27017' }] })"

docker compose up -d

Xác minh kết quả

Kiểm tra trạng thái replica set:

mongosh --eval "rs.status()"

Tìm "stateStr" : "PRIMARY" trong mảng members. Tốt. Bây giờ chạy một transaction trực tiếp để xác nhận hoạt động end-to-end:

// Kiểm tra nhanh trong mongosh
const session = db.getMongo().startSession();
session.startTransaction();
try {
  session.getDatabase('test').orders.insertOne({ item: 'test' }, { session });
  session.commitTransaction();
  print('Transaction committed OK');
} catch (e) {
  session.abortTransaction();
  print('Transaction failed:', e.message);
} finally {
  session.endSession();
}

Thấy Transaction committed OK? Bạn đã xong.

Cập nhật connection string

Sau khi chuyển sang chế độ replica set, hãy thêm tên replica set vào connection string. Bỏ qua bước này và một số driver sẽ không kích hoạt hỗ trợ transaction ngay cả khi kết nối tới replica set — đây là một cái bẫy rất bực bội.

# Trước
mongodb://localhost:27017/mydb

# Sau
mongodb://localhost:27017/mydb?replicaSet=rs0

Node.js với Mongoose:

await mongoose.connect('mongodb://localhost:27017/mydb?replicaSet=rs0');

Python với PyMongo:

from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/mydb?replicaSet=rs0')

Còn môi trường production thì sao?

Gặp lỗi này trên production có nghĩa là deployment của bạn đang chạy MongoDB standalone — không có automatic failover, không có data redundancy. Hãy khắc phục điều đó bất kể transaction hay không. Một replica set 3 node đúng chuẩn là nền tảng tối thiểu cho bất kỳ workload production nào. Hầu hết các managed service (Atlas, MongoDB on DigitalOcean, v.v.) đều cung cấp replica set theo mặc định, vì vậy đây thực sự là vấn đề của self-hosted. Hướng dẫn triển khai replica set của MongoDB trình bày đầy đủ cách thiết lập.

Bài học rút ra

  • MongoDB transaction yêu cầu replica set từ phiên bản 4.0 — đây là ràng buộc kiến trúc cứng, không phải tùy chọn config.
  • Chạy single-node replica set trong môi trường dev không tốn gì và ngăn chặn đúng cái bất ngờ này khi code lên staging.
  • Luôn thêm ?replicaSet=rs0 vào connection string local sau khi thực hiện thay đổi này — nó tránh những hành vi khó hiểu của driver liên quan đến transaction discovery.
  • Trong CI với mongod bare, pattern init-container của Docker Compose là cách đáng tin cậy nhất để xử lý lệnh rs.initiate() một lần duy nhất mà không gặp race condition.

Related Error Notes