11 thg 6, 2025·8 phút đọc

Khóa lạc quan cho công cụ quản trị: ngăn ghi đè im lặng

Tìm hiểu khóa lạc quan cho công cụ quản trị bằng cột version và kiểm tra updated_at, cùng các mẫu UI đơn giản để xử lý xung đột chỉnh sửa mà không ghi đè im lặng.

Khóa lạc quan cho công cụ quản trị: ngăn ghi đè im lặng

Vấn đề: ghi đè im lặng khi nhiều người cùng chỉnh sửa

Một “ghi đè im lặng” xảy ra khi hai người mở cùng một bản ghi, cả hai đều sửa, và người cuối cùng nhấn Lưu thắng — các thay đổi của người đầu tiên biến mất mà không có cảnh báo và thường không có cách dễ dàng để khôi phục.

Trong một bảng điều khiển admin bận rộn, chuyện này có thể xảy ra suốt ngày mà không ai nhận ra. Mọi người mở nhiều tab, nhảy giữa các ticket và quay lại một form đã nằm đó 20 phút. Khi họ lưu, họ không cập nhật phiên bản mới nhất của bản ghi. Họ đang ghi đè lên nó.

Hiện tượng này xuất hiện nhiều hơn trong công cụ hậu trường so với ứng dụng công khai vì công việc mang tính cộng tác và dựa trên bản ghi. Các nhóm nội bộ chỉnh sửa cùng một khách hàng, đơn hàng, sản phẩm và yêu cầu nhiều lần, thường trong các khoảng ngắn. Ứng dụng công khai thường là “một người dùng chỉnh sửa dữ liệu của họ”, còn công cụ admin là “nhiều người chỉnh sửa dữ liệu chung”.

Tổn hại hiếm khi lớn ngay lập tức, nhưng tích tụ rất nhanh:

  • Giá sản phẩm bị đổi về giá cũ ngay sau khi cập nhật khuyến mãi.
  • Ghi chú nội bộ của nhân viên hỗ trợ biến mất, khiến nhân viên tiếp theo lặp lại cùng bước xử lý.
  • Trạng thái đơn hàng bị lùi lại (ví dụ: từ “Shipped” về “Packed”), kích hoạt theo dõi sai.
  • Số điện thoại hoặc địa chỉ khách hàng bị thay bằng thông tin lỗi thời.

Ghi đè im lặng đau đầu vì ai cũng nghĩ hệ thống đã lưu đúng. Không có khoảnh khắc rõ ràng “có lỗi”; chỉ có sự bối rối sau này khi báo cáo sai hoặc đồng đội hỏi: “Ai đã thay đổi cái này?”

Xung đột kiểu này là bình thường. Chúng cho thấy công cụ được chia sẻ và hữu ích, chứ không phải đội bạn làm sai. Mục tiêu không phải cấm hai người cùng chỉnh sửa. Mục tiêu là phát hiện khi bản ghi thay đổi trong thời gian ai đó đang chỉnh sửa, rồi xử lý tình huống đó an toàn.

Nếu bạn đang xây dựng công cụ nội bộ trên nền tảng no-code như AppMaster, đáng để lên kế hoạch cho việc này sớm. Công cụ admin thường phát triển nhanh, và một khi đội phụ thuộc vào chúng, mất dữ liệu “thỉnh thoảng” sẽ trở thành nguồn mất lòng tin liên tục.

Khóa lạc quan (optimistic locking) nói theo ngôn ngữ đơn giản

Khi hai người mở cùng bản ghi và đều nhấn Lưu, bạn gặp tình huống đồng thời. Mỗi người bắt đầu từ một ảnh chụp cũ, nhưng chỉ một người có thể là “mới nhất” khi các lưu xảy ra.

Nếu không có bảo vệ, lần lưu sau cùng thắng. Đó là cách bạn có ghi đè im lặng: lần lưu thứ hai thay thế lặng lẽ các thay đổi của người đầu.

Khóa lạc quan là một quy tắc đơn giản: “Tôi sẽ chỉ lưu thay đổi nếu bản ghi vẫn ở trạng thái như lúc tôi bắt đầu chỉnh sửa.” Nếu bản ghi thay đổi trong lúc đó, thao tác lưu bị từ chối và người dùng thấy xung đột.

Điều này khác với khóa bi quan (pessimistic locking), kiểu “tôi đang chỉnh sửa cái này, nên không ai khác được phép.” Khóa bi quan thường đồng nghĩa với khóa cứng, timeout và người dùng bị chặn. Nó có thể hữu ích trong vài trường hợp hiếm (ví dụ chuyển tiền giữa các tài khoản), nhưng thường gây bực bội trong công cụ admin bận rộn nơi có nhiều sửa nhỏ diễn ra cả ngày.

Khóa lạc quan thường là mặc định tốt hơn vì nó giữ công việc chảy. Mọi người có thể chỉnh sửa song song, và hệ thống chỉ can thiệp khi có va chạm thực sự.

Nó phù hợp nhất khi:

  • Xung đột có thể xảy ra nhưng không liên tục.
  • Các chỉnh sửa nhanh (vài trường, form ngắn).
  • Chặn người khác sẽ làm chậm đội.
  • Bạn có thể hiển thị thông báo rõ ràng “ai đó đã cập nhật cái này”.
  • API của bạn có thể kiểm tra một version (hoặc timestamp) trên mọi cập nhật.

Nó ngăn được vấn đề “ghi đè lặng lẽ”. Thay vì mất dữ liệu, bạn có một ngắt sạch: “Bản ghi này đã thay đổi kể từ khi bạn mở.”

Những gì nó không làm cũng quan trọng. Nó không ngăn hai người đưa ra quyết định khác nhau (nhưng đều hợp lệ) dựa trên cùng thông tin cũ, và nó không tự động gộp thay đổi cho bạn. Và nếu bạn bỏ qua kiểm tra ở phía server, bạn chưa thực sự giải quyết gì.

Hạn chế phổ biến cần nhớ:

  • Nó không tự giải quyết xung đột (bạn vẫn cần một lựa chọn).
  • Không giúp nếu người dùng chỉnh sửa offline rồi đồng bộ sau mà không kiểm tra.
  • Không sửa lỗi phân quyền (ai đó vẫn có thể chỉnh sửa thứ họ không được phép).
  • Không bắt xung đột nếu bạn chỉ kiểm tra ở client.

Thực tế, khóa lạc quan chỉ là một giá trị bổ sung mang theo khi chỉnh sửa, kèm một quy tắc “chỉ cập nhật nếu trùng khớp” phía server. Nếu bạn xây dựng admin panel trong AppMaster, kiểm tra này thường nằm trong Business Process ngay nơi các cập nhật được thực thi.

Hai cách phổ biến: cột version vs updated_at

Để phát hiện bản ghi đã thay đổi trong khi ai đó đang chỉnh sửa, bạn thường chọn một trong hai dấu hiệu: số phiên bản (version) hoặc timestamp updated_at.

Cách 1: Cột version (số nguyên tăng dần)

Thêm trường version (thường là số nguyên). Khi load form chỉnh sửa, bạn cũng lấy version hiện tại. Khi lưu, bạn gửi giá trị đó về.

Cập nhật chỉ thành công nếu phiên bản lưu trữ vẫn trùng với giá trị người dùng bắt đầu. Nếu trùng, bạn cập nhật bản ghi và tăng version lên 1. Nếu không trùng, trả về xung đột thay vì ghi đè.

Cách này dễ lý giải: version 12 nghĩa là “đây là lần thay đổi thứ 12.” Nó cũng tránh các vấn đề liên quan thời gian.

Cách 2: updated_at (so sánh timestamp)

Hầu hết bảng đã có updated_at. Ý tưởng giống nhau: đọc updated_at khi mở form, sau đó gửi nó kèm khi lưu. Server chỉ cập nhật nếu updated_at không đổi.

Nó có thể hoạt động tốt, nhưng timestamp có bẫy. Các cơ sở dữ liệu khác nhau lưu độ chính xác khác nhau. Một số làm tròn tới giây, có thể bỏ sót các chỉnh sửa nhanh. Nếu nhiều hệ thống viết vào cùng một DB, trôi đồng hồ và xử lý timezone cũng tạo ra các trường hợp méo mó.

Một cách so sánh đơn giản:

  • Cột version: hành vi rõ ràng, di động giữa DB, không gặp vấn đề đồng hồ.
  • updated_at: thường “miễn phí” vì đã tồn tại, nhưng độ chính xác và xử lý đồng hồ có thể gây rắc rối.

Với hầu hết đội, cột version là tín hiệu chính tốt hơn. Nó rõ ràng, dễ dự đoán và dễ tham chiếu trong log và ticket hỗ trợ.

Nếu bạn xây dựng trong AppMaster, điều này thường có nghĩa là thêm trường version kiểu integer trong Data Designer và đảm bảo logic update kiểm tra nó trước khi lưu. Bạn vẫn có thể giữ updated_at để audit, nhưng để version quyết định xem chỉnh sửa có an toàn để áp dụng hay không.

Cần lưu gì và gửi gì mỗi lần chỉnh sửa

Khóa lạc quan chỉ hoạt động nếu mỗi lần chỉnh sửa mang theo một “dấu đã thấy lần cuối” từ thời điểm người dùng mở form. Dấu này có thể là số version hoặc timestamp updated_at. Nếu không có, server không thể biết bản ghi có thay đổi khi người dùng đang gõ hay không.

Trên bản ghi, giữ các trường nghiệp vụ bình thường, cộng thêm một trường đồng thời do server kiểm soát. Bộ tối thiểu như sau:

  • id (định danh ổn định)
  • các trường nghiệp vụ (name, status, price, notes, v.v.)
  • version (số nguyên tăng lên mỗi lần cập nhật thành công) hoặc updated_at (timestamp do server ghi)

Khi màn hình chỉnh sửa mở, form phải lưu giá trị đã thấy cuối cùng của trường đồng thời đó. Người dùng không nên chỉnh sửa nó, nên giữ dưới dạng field ẩn hoặc trong state của form. Ví dụ: API trả về version: 12, và form giữ 12 cho tới khi Lưu.

Khi người dùng nhấn Lưu, gửi hai thứ: các thay đổi và dấu đã thấy. Hình thức đơn giản nhất là gồm id, các trường thay đổi, và expected_version (hoặc expected_updated_at). Nếu bạn xây UI trong AppMaster, xử lý nó như bất kỳ giá trị ràng buộc nào khác: load cùng record, giữ nguyên và submit lại khi update.

Ở phía server, cập nhật phải có điều kiện. Bạn chỉ update nếu marker mong đợi khớp với dữ liệu hiện tại trong DB. Nếu không khớp, đừng “gộp” lặng lẽ.

Phản hồi xung đột nên rõ ràng và dễ xử lý ở UI. Phản hồi xung đột thiết thực thường bao gồm:

  • HTTP status 409 Conflict
  • một thông điệp ngắn như “This record was updated by someone else.”
  • giá trị hiện tại của server (current_version hoặc current_updated_at)
  • tùy chọn: record hiện tại từ server (để UI hiển thị thay đổi)

Ví dụ: Sam mở record Customer ở version 12. Priya lưu thay đổi, thành version 13. Sam sau đó nhấn Lưu với expected_version: 12. Server trả về 409 kèm record hiện tại version 13. Lúc này UI có thể gợi Sam xem các giá trị mới nhất thay vì ghi đè lên chỉnh sửa của Priya.

Thực hiện bước bằng bước: triển khai khóa lạc quan từ đầu đến cuối

Thêm khóa lạc quan nhanh
Thêm trường version trong Data Designer và giữ mọi cập nhật an toàn.
Bắt đầu xây dựng

Khóa lạc quan chủ yếu dựa trên một quy tắc: mỗi chỉnh sửa phải chứng minh rằng nó dựa trên phiên bản được lưu mới nhất của bản ghi.

1) Thêm trường đồng thời

Chọn một trường sẽ thay đổi ở mỗi lần ghi.

Một version số nguyên riêng là dễ hiểu nhất. Bắt đầu từ 1 và tăng lên mỗi lần update. Nếu bạn đã có updated_at đáng tin cậy luôn thay đổi, bạn có thể dùng nó, nhưng đảm bảo nó cập nhật ở mọi lần ghi (bao gồm job nền).

2) Gửi giá trị đó tới client khi đọc

Khi UI mở màn hình chỉnh sửa, bao gồm version (hoặc updated_at) hiện tại trong phản hồi. Lưu nó trong state form như một giá trị ẩn.

Hãy coi đó như một hóa đơn nói: “Tôi đang chỉnh sửa dựa trên cái tôi vừa đọc.”

3) Yêu cầu giá trị đó khi cập nhật

Khi lưu, client gửi các trường đã chỉnh sửa cộng giá trị đồng thời đã thấy.

Phía server, hãy làm cập nhật có điều kiện. Ở dạng SQL, là:

UPDATE tickets
SET status = $1,
    version = version + 1
WHERE id = $2
  AND version = $3;

Nếu câu lệnh ảnh hưởng 1 hàng, lưu thành công. Nếu 0 hàng, có người khác đã thay đổi record.

4) Trả về giá trị mới sau khi thành công

Sau khi lưu thành công, trả về record đã cập nhật với version mới (hoặc updated_at mới). Client nên thay thế state form bằng dữ liệu server trả về. Điều này tránh “lưu kép” bằng phiên bản cũ.

5) Xử lý xung đột như một kết quả bình thường

Khi cập nhật có điều kiện thất bại, trả về phản hồi xung đột rõ ràng (thường HTTP 409) bao gồm:

  • record hiện tại như tồn tại bây giờ
  • các thay đổi mà client đã cố lưu (hoặc đủ để tái tạo chúng)
  • những trường khác nhau (nếu bạn có thể tính được)

Trong AppMaster, điều này phù hợp với một trường model PostgreSQL trong Data Designer, một endpoint đọc trả version, và một Business Process thực hiện cập nhật có điều kiện rồi rẽ nhánh thành success hoặc conflict.

Các mẫu UI giúp xử lý xung đột mà không làm người dùng khó chịu

Một công cụ cho web và mobile
Sinh ứng dụng web và mobile native giữ tính nhất quán cho đội.
Dùng thử ngay

Khóa lạc quan chỉ là một nửa công việc. Nửa còn lại là trải nghiệm người dùng khi lưu bị từ chối vì ai đó đã thay đổi bản ghi.

UI xung đột tốt có hai mục tiêu: ngăn ghi đè im lặng, và giúp người dùng hoàn thành nhiệm vụ nhanh. Làm tốt, nó giống như một lan can hữu ích chứ không phải rào cản.

Mẫu 1: Hộp thoại chặn đơn giản (nhanh nhất)

Dùng khi chỉnh sửa nhỏ và người dùng có thể dễ dàng áp lại chỉnh sửa sau khi reload.

Giữ thông điệp ngắn và cụ thể: “This record changed while you were editing. Reload to see the latest version.” Rồi cho hai hành động rõ ràng:

  • Reload and continue (chính)
  • Copy my changes (tùy chọn nhưng hữu ích)

“Copy my changes” có thể đưa giá trị chưa lưu vào clipboard hoặc giữ chúng trong form sau khi reload, giúp người dùng không phải nhớ những gì đã gõ.

Cách này phù hợp với cập nhật một trường, toggle, thay đổi trạng thái hoặc ghi chú ngắn. Cũng dễ triển khai trong các công cụ builder, bao gồm màn hình AppMaster.

Mẫu 2: “Xem lại thay đổi” (tốt nhất cho bản ghi giá trị cao)

Dùng khi bản ghi quan trọng (giá, quyền, thanh toán) hoặc form dài. Thay vì lỗi dead-end, dẫn người dùng tới màn hình xung đột so sánh:

  • “Your edits” (những gì họ cố lưu)
  • “Current values” (giá trị mới nhất từ DB)
  • “What changed since you opened it” (các trường xung đột)

Giữ tập trung. Đừng hiển thị mọi trường nếu chỉ có vài trường xung đột.

Với mỗi trường xung đột, đưa các lựa chọn:

  • Giữ của tôi
  • Lấy của họ
  • Gộp (chỉ khi hợp lý, ví dụ tags hoặc notes)

Sau khi người dùng giải quyết xung đột, lưu lại với giá trị version mới nhất. Nếu hỗ trợ rich text hoặc ghi chú dài, hiển thị diff nhỏ (thêm/bớt) để người dùng quyết định nhanh.

Khi nào cho phép ghi đè bắt buộc (và ai được phép)

Đôi khi cần cho phép ghi đè bắt buộc, nhưng nên hiếm và có kiểm soát. Nếu thêm, hãy làm rõ ràng: yêu cầu lý do ngắn, ghi log ai làm, và giới hạn cho vai trò như admin hoặc supervisor.

Với người dùng thường, mặc định là “Review changes.” Ghi đè bắt buộc hợp lý nhất khi người dùng là chủ sở hữu bản ghi, bản ghi rủi ro thấp, hoặc hệ thống sửa lỗi dữ liệu có giám sát.

Tình huống minh họa: hai đồng nghiệp cùng chỉnh sửa một bản ghi

Hai nhân viên hỗ trợ, Maya và Jordan, làm việc trong cùng một công cụ admin. Cả hai mở cùng hồ sơ khách hàng để cập nhật trạng thái và thêm ghi chú sau các cuộc gọi riêng.

Dòng thời gian (với khóa lạc quan bật, dùng version hoặc updated_at):

  • 10:02 - Maya mở Customer #4821. Form tải Status = "Needs follow-up", Notes = "Called yesterday" và Version = 7.
  • 10:03 - Jordan mở cùng khách hàng, cũng thấy Version = 7.
  • 10:05 - Maya đổi Status thành "Resolved" và thêm ghi chú: "Issue fixed, confirmed by customer." Cô ấy nhấn Save.
  • 10:05 - Server cập nhật record, tăng Version lên 8 (hoặc cập nhật updated_at), và lưu audit: ai thay đổi gì khi nào.
  • 10:09 - Jordan gõ ghi chú khác: "Customer asked for a receipt" và nhấn Save.

Không có kiểm tra đồng thời, lần lưu của Jordan có thể ghi đè im lặng ghi chú và trạng thái của Maya, tùy cách update được xây dựng. Với khóa lạc quan, server từ chối cập nhật của Jordan vì anh ấy cố lưu Version = 7 trong khi record đã ở Version = 8.

Jordan thấy thông báo xung đột rõ ràng. UI hiển thị chuyện gì đã xảy ra và cho anh những bước an toàn:

  • Reload record mới nhất (huỷ edits của tôi)
  • Áp lại thay đổi của tôi lên record mới nhất (khuyến nghị khi có thể)
  • Xem khác biệt ("Mine" vs "Latest") và chọn giữ gì

Một màn hình đơn giản có thể hiển thị:

  • “This customer was updated by Maya at 10:05”
  • Các trường đã thay đổi (Status và Notes)
  • Xem trước ghi chú chưa lưu của Jordan, để anh có thể sao chép hoặc áp lại

Jordan chọn “Review differences”, giữ Status = "Resolved" của Maya, rồi ghép ghi chú của anh vào phần notes hiện có. Anh lưu lại, lần này dùng Version = 8, cập nhật thành công (bây giờ Version = 9).

Trạng thái cuối: không mất dữ liệu, không đoán ai đã ghi đè ai, và có audit trail sạch cho thấy thay đổi của Maya và cả hai ghi chú là các chỉnh sửa riêng biệt, có thể truy vết. Trong công cụ xây bằng AppMaster, điều này ánh xạ gọn vào một kiểm tra trên update cộng hộp thoại giải quyết xung đột nhỏ trong UI admin.

Lỗi phổ biến vẫn gây mất dữ liệu

Xây dựng bảng điều khiển quản trị chung
Thiết kế mô hình PostgreSQL và sinh backend production mà không cần viết Go.
Tạo ứng dụng

Hầu hết bug về “khóa lạc quan” không phải ở ý tưởng mà là ở chỗ chuyển giao giữa UI, API và DB. Nếu một lớp nào đó quên quy tắc, bạn vẫn có thể gặp ghi đè im lặng.

Lỗi kinh điển là lấy phiên bản (hoặc timestamp) khi mở màn hình, nhưng không gửi lại khi lưu. Điều này thường xảy ra khi form tái sử dụng giữa các trang và field ẩn bị loại ra, hoặc khi client API chỉ gửi “các trường đã thay đổi”.

Cạm bẫy khác là chỉ làm kiểm tra xung đột trên trình duyệt. Người dùng có thể thấy cảnh báo, nhưng nếu server vẫn chấp nhận update, client khác (hoặc retry) vẫn ghi đè dữ liệu. Server phải là người gác cổng cuối cùng.

Các pattern dễ gây mất dữ liệu nhất:

  • Thiếu token đồng thời trong request lưu (version, updated_at hoặc ETag), nên server không có gì để so sánh.
  • Chấp nhận cập nhật mà không có điều kiện nguyên tử, ví dụ update chỉ theo id thay vì “id + version.”
  • Dùng updated_at với độ chính xác thấp (ví dụ theo giây). Hai chỉnh sửa trong cùng một giây có thể trùng nhau.
  • Thay thế các trường lớn (notes, descriptions) hoặc cả mảng (tags, line items) mà không hiển thị khác biệt.
  • Xử lý mọi xung đột bằng “thử lại” vô điều kiện, có thể áp lại giá trị cũ lên dữ liệu mới.

Ví dụ cụ thể: hai lead hỗ trợ mở cùng hồ sơ khách hàng. Một người thêm ghi chú dài, người kia thay đổi trạng thái và lưu. Nếu save của bạn ghi đè toàn bộ payload, thay đổi trạng thái có thể vô tình xóa ghi chú.

Khi xung đột xảy ra, đội vẫn mất dữ liệu nếu phản hồi API quá sơ sài. Đừng chỉ trả “409 Conflict.” Trả đủ thông tin để con người có thể sửa:

  • Phiên bản server hiện tại (hoặc updated_at)
  • Giá trị server mới nhất cho các trường liên quan
  • Danh sách trường khác nhau (ít nhất tên các trường)
  • Ai thay đổi và khi nào (nếu bạn theo dõi)

Nếu bạn triển khai trong AppMaster, duy trì kỷ luật: giữ version trong state UI, gửi nó với update, và bắt buộc kiểm tra trong backend trước khi ghi lên PostgreSQL.

Kiểm tra nhanh trước khi phát hành

Làm cho các chỉnh sửa dữ liệu đáng tin cậy
Xây dựng công cụ nội bộ đáng tin cậy, ngay cả khi nhiều người cùng chỉnh sửa và hàng đợi bận rộn.
Bắt đầu bằng AppMaster

Trước khi triển khai, rà soát nhanh các failure mode khiến “đã lưu thành công” trong khi lặng lẽ ghi đè công việc người khác.

Kiểm tra dữ liệu và API

Đảm bảo bản ghi mang token đồng thời xuyên suốt. Token có thể là version hoặc updated_at, nhưng phải được coi là một phần của bản ghi, không phải metadata tùy chọn.

  • Đọc bao gồm token (và UI lưu nó trong state form, không chỉ hiển thị trên màn hình).
  • Mọi cập nhật gửi token đã thấy, và server xác minh trước khi ghi.
  • Khi thành công, server trả token mới để UI đồng bộ.
  • Bulk edits và inline edits tuân theo cùng quy tắc, không đi đường tắt.
  • Job nền chỉnh sửa cùng hàng cũng kiểm tra token (nếu không, chúng sẽ tạo xung đột trông như ngẫu nhiên).

Nếu bạn xây bằng AppMaster, kiểm tra rằng trường trong Data Designer tồn tại (version hoặc updated_at), và Business Process update so sánh nó trước khi ghi.

Kiểm tra UI

Xung đột chỉ “an toàn” nếu bước tiếp theo rõ ràng.

Khi server từ chối update, hiện thông báo như: “This record changed since you opened it.” Rồi cung cấp một bước an toàn đầu tiên: tải lại dữ liệu mới nhất. Nếu có thể, thêm đường dẫn “tải lại và áp lại” giữ input chưa lưu để sửa nhỏ không biến thành việc gõ lại.

Nếu đội bạn thực sự cần, thêm tuỳ chọn “force save” có kiểm soát. Ràng buộc theo vai trò, confirm, và ghi log ai đã force — giữ khả năng cứu khẩn cấp mà không biến mất dữ liệu thành mặc định.

Bước tiếp theo: thêm khóa cho một workflow rồi mở rộng

Bắt đầu nhỏ. Chọn một màn hình admin nơi mọi người thường chồng lấn, và thêm khóa lạc quan trước. Các khu vực va chạm cao thường là tickets, orders, pricing và inventory. Nếu bạn làm cho xung đột an toàn trên một màn hình bận, bạn sẽ nhanh thấy mẫu để lặp lại.

Chọn hành vi xung đột mặc định trước vì nó định hình logic backend và UI:

  • Block-and-reload: dừng lưu, tải lại bản ghi mới nhất, và yêu cầu người dùng áp lại thay đổi.
  • Review-and-merge: hiển thị “thay đổi của bạn” so với “thay đổi mới nhất” và cho người dùng quyết định giữ gì.

Block-and-reload dễ xây và phù hợp khi chỉnh sửa ngắn. Review-and-merge đáng giá khi bản ghi dài hoặc rủi ro cao.

Rồi thực hiện và test một flow hoàn chỉnh trước khi nhân rộng:

  • Chọn một màn hình và liệt kê các trường người dùng chỉnh sửa nhiều nhất.
  • Thêm version (hoặc updated_at) vào payload form và bắt buộc khi lưu.
  • Làm cập nhật có điều kiện ở nơi ghi vào DB (chỉ update nếu version khớp).
  • Thiết kế thông báo xung đột và hành động tiếp theo (reload, copy my text, open compare view).
  • Test với hai trình duyệt: lưu ở tab A, sau đó thử lưu dữ liệu cũ ở tab B.

Ghi log nhẹ cho xung đột. Một event “conflict happened” đơn giản với loại record, tên màn hình và vai trò người dùng giúp bạn phát hiện điểm nóng.

Nếu bạn xây công cụ admin bằng AppMaster (appmaster.io), các phần chính khớp rõ ràng: mô hình trường version trong Data Designer, cập nhật có điều kiện trong Business Processes, và hộp thoại xung đột nhỏ trong UI builder. Khi workflow đầu ổn định, lặp lại mẫu cho từng màn hình và giữ UI xung đột nhất quán để người dùng chỉ cần học một lần và tin tưởng ở mọi nơi.

Câu hỏi thường gặp

What is a “silent overwrite” and why does it happen?

Một ghi đè im lặng xảy ra khi hai người cùng chỉnh sửa một bản ghi từ các tab hoặc phiên khác nhau, và lần lưu sau cùng đã thay thế thay đổi trước đó mà không báo trước. Phần rủi ro là cả hai người đều thấy “lưu thành công”, nên những chỉnh sửa bị mất thường chỉ được phát hiện sau đó.

What does optimistic locking do in plain terms?

Khóa lạc quan nghĩa là ứng dụng chỉ lưu thay đổi của bạn nếu bản ghi chưa bị thay đổi kể từ khi bạn mở nó. Nếu ai đó đã lưu trước bạn, thao tác của bạn sẽ bị từ chối với trạng thái xung đột để bạn xem lại dữ liệu mới nhất thay vì ghi đè.

Why not just lock the record so nobody else can edit it?

Khóa theo kiểu bi quan (pessimistic locking) chặn người khác chỉnh sửa trong khi bạn đang làm việc, điều này thường gây chờ đợi, timeout và các câu hỏi kiểu “ai đang khóa cái này?”. Đối với bảng điều khiển quản trị, khóa lạc quan phù hợp hơn vì mọi người có thể làm việc song song và hệ thống chỉ can thiệp khi xảy ra va chạm thực sự.

Should I use a version number or `updated_at` for conflict checks?

Trường version thường là lựa chọn đơn giản và dễ đoán nhất vì tránh vấn đề độ chính xác thời gian và sai số đồng hồ. Kiểm tra updated_at có thể dùng được nhưng có thể bỏ sót các chỉnh sửa nhanh nếu timestamp chỉ lưu theo giây hoặc bị xử lý không nhất quán giữa các hệ thống.

What data has to be included to make optimistic locking work?

Bạn cần một token đồng thời do server điều khiển trên bản ghi, thường là version (số nguyên) hoặc updated_at (timestamp). Client phải đọc token này khi mở form, giữ nguyên trong quá trình chỉnh sửa, và gửi lại khi lưu dưới dạng giá trị “expected”.

Why must the version check be done on the server, not just in the UI?

Bởi vì client không phải lúc nào cũng đáng tin cậy để bảo vệ dữ liệu chia sẻ. Server phải thực thi cập nhật có điều kiện như “update where id = X and version = Y” — nếu không, một client khác, retry hoặc job nền vẫn có thể ghi đè im lặng.

What should the user see when a conflict happens?

Mặc định tốt là hiện một thông báo chặn: “Bản ghi đã thay đổi kể từ khi bạn mở nó.” Sau đó cung cấp một bước an toàn: tải lại dữ liệu mới nhất. Nếu người dùng đã nhập nhiều text, giữ input chưa lưu để họ có thể áp dụng lại sau khi reload thay vì phải gõ lại.

What should the API return on a conflict to help the UI recover?

Trả về phản hồi conflict rõ ràng (thường 409) cùng đủ ngữ cảnh để UI phục hồi: phiên bản hiện tại của server và các giá trị mới nhất. Nếu có thể, cho biết ai cập nhật và khi nào để người dùng hiểu tại sao lưu bị từ chối và điều gì đã thay đổi.

What are the most common mistakes that still lead to data loss?

Hãy cảnh giác với việc thiếu token khi lưu, cập nhật chỉ lọc theo id thay vì id + version, và kiểm tra timestamp có độ chính xác thấp. Một lỗi phổ biến khác là thay thế toàn bộ payload (notes hoặc mảng) thay vì cập nhật các trường cần thiết, dễ khiến ghi đè mất dữ liệu của người khác.

How do I implement this in AppMaster without custom coding?

Trong AppMaster, thêm trường version trong Data Designer và trả nó trong record khi UI đọc vào trạng thái form. Sau đó thực thi cập nhật có điều kiện trong Business Process để ghi chỉ thành công khi version mong đợi khớp, và xử lý nhánh xung đột trong UI bằng luồng reload/review.

Dễ dàng bắt đầu
Tạo thứ gì đó tuyệt vời

Thử nghiệm với AppMaster với gói miễn phí.
Khi bạn sẵn sàng, bạn có thể chọn đăng ký phù hợp.

Bắt đầu