Hệ thống đã production-ready. Câu hỏi cuối, và là câu quan trọng nhất: khi ổ cứng hỏng, bạn phục hồi được không? Nhiều người pg_dump cơ sở dữ liệu, thấy file backup nằm đó, rồi yên tâm. Đó là một cái bẫy chết người — vì một nửa dữ liệu của Odoo không nằm trong CSDL. Bài này chỉ ra chỗ nửa còn lại nằm, cách backup/restore đúng, và khung migration script để nâng cấp schema an toàn.

Hai phần của một backup Odoo

Dữ liệu Odoo chia làm hai nơi:

  • Cơ sở dữ liệu (PostgreSQL): bảng, bản ghi, cấu hình. Backup bằng pg_dump.
  • Filestore: một thư mục chứa attachment và ảnh (ir.attachment lớn được lưu ra file thay vì nhét vào CSDL để bảng không phình). Nằm ở /var/lib/odoo/filestore/<db>.

Thiếu một trong hai là phục hồi hỏng: có CSDL mà mất filestore thì mọi ảnh/tệp thành liên kết chết. Đây không phải lý thuyết — đo thật trên blog19:

Ảnh chụp terminal nền tối backup thật của blog19 gồm CSDL và filestore. Lệnh pg_dump U odoo Fc blog19 ra blog19.dump rồi ls la. Kết quả file blog19.dump 10991944 byte chú thích khoảng 10.5 MB đã nén. Lệnh pg_restore list blog19.dump đếm dòng cho 17189 chú thích mục lục 17189 dòng. Lệnh pg_restore list grep TABLE DATA public quan cho 15556 TABLE DATA public quan_the_thanh_vien odoo, 15559 quan_lich_su_diem, 15561 quan_uu_dai chú thích module của ta nằm trong dump. Lệnh du sh var lib odoo filestore blog19 cho 65M chú thích attachment ảnh và 542 tệp chú thích ngoài CSDL. Chú thích dump 10.5MB nhưng filestore 65MB gấp 6 lần quên nó là ảnh vỡ hết

Hình 1: Thật. pg_dump -Fc cho file 10.5 MB (đã nén), mục lục 17.189 dòng — đọc bằng pg_restore --list thấy đủ bảng module quan_the_thanh_vien, quan_lich_su_diem, quan_uu_dai. Nhưng filestore là 65 MB, 542 tệp — gấp 6 lần dump CSDL. Đây là toàn bộ ảnh/attachment, nằm ngoài CSDL. Backup mà quên nó thì phục hồi lên là ảnh vỡ hết.

Script backup và restore

Backup đúng là hai lệnh, restore là ba:

Ảnh chụp mã script backup và migration nền tối. Phần 1 script backup phải có cả hai CSDL và filestore. Bash DB blog19 DAY ngày. Chú thích 1 CSDL pg_dump định dạng custom Fc đã nén. pg_dump U odoo Fc DB ra backup DB DAY dump. Chú thích 2 FILESTORE attachment ảnh nằm ngoài CSDL nên tar riêng. tar czf backup DB DAY filestore tgz C var lib odoo filestore DB. Chú thích thiếu 1 trong 2 phục hồi lên là ảnh vỡ tệp mất. Phần 2 restore. createdb U odoo blog19_moi. pg_restore U odoo d blog19_moi dump. tar xzf filestore tgz C var lib odoo filestore đặt lại filestore đúng chỗ. Phần 3 migration script migrations version post-migrate.py. def migrate cr version chạy khi u module và version cũ nhỏ hơn version mới. if not version return. Ví dụ đổi giá trị enum cũ sang mới bằng SQL trực tiếp cr execute UPDATE quan_the_thanh_vien SET hang_the bac WHERE hang_the IS NULL

Hình 2: Backup gồm pg_dump -Fc (định dạng custom, nén sẵn, restore chọn lọc được) và tar filestore. Restore: createdb + pg_restore + giải nén filestore về đúng chỗ. Nếu chỉ cần một lệnh, giao diện /web/database/backup của Odoo xuất một file zip gồm cả hai — tiện cho backup thủ công, nhưng script pg_dump hợp cho lịch tự động vì không giữ nguyên cả DB trong RAM.

-Fc hay -Fp

pg_dump có hai định dạng hay dùng:

  • -Fc (custom): nhị phân, nén sẵn, và pg_restore cho phép chọn lọc (khôi phục riêng vài bảng, đổi thứ tự, chạy song song). Đây là lựa chọn mặc định cho production.
  • -Fp (plain): file SQL text thuần, đọc được bằng mắt, restore bằng psql. Hợp khi cần xem/sửa nội dung dump, nhưng không nén và không chọn lọc được.

Migration: khi schema đổi

Backup lo dữ liệu; migration lo việc đổi cấu trúc giữa các phiên bản module mà không mất dữ liệu cũ. Khi bạn -u một module có version mới hơn, Odoo tự tìm thư mục migrations/<version>/ và chạy các script trong đó:

  • pre-migrate.py: chạy trước khi Odoo cập nhật schema — dùng khi cần giữ dữ liệu trước lúc cột bị đổi/xoá.
  • post-migrate.py: chạy sau khi schema đã cập nhật — dùng để điền dữ liệu cho cột mới, chuyển đổi giá trị.

Mỗi script có hàm migrate(cr, version): cr là con trỏ CSDL để chạy SQL trực tiếp, version là phiên bản cũ (rỗng nếu cài mới — thường return ngay). Thư viện openupgradelib cung cấp nhiều hàm tiện ích (đổi tên cột, gộp bản ghi) cho migration phức tạp.

# migrations/19.0.2.0.0/post-migrate.py
def migrate(cr, version):
    if not version:      # cài mới, không cần migrate
        return
    cr.execute("UPDATE quan_the_thanh_vien SET hang_the='bac' "
               "WHERE hang_the IS NULL")   # điền mặc định cho cột cũ

Neutralize khi copy prod sang test

Một cái bẫy vận hành: khi bê backup production về máy test, đừng chạy nguyên. Bản sao vẫn giữ cron (có thể gửi email thật cho khách!), API key, cấu hình thanh toán thật. Odoo có bước neutralize (/web/database hoặc --load=...) tắt cron, chuyển mail sang chế độ không gửi, vô hiệu API key — chạy nó ngay sau khi restore lên môi trường không phải production.

Ba ý mang về

  1. Backup Odoo = CSDL + filestore, thiếu một là hỏng. Đo thật: dump CSDL 10.5 MB nhưng filestore 65 MB (542 tệp) — gấp 6 lần. Filestore chứa attachment/ảnh nằm ngoài CSDL.
  2. pg_dump -Fc (nén, restore chọn lọc) cho production; restore là createdb + pg_restore + đặt lại filestore. Giao diện /web/database/backup xuất zip gồm cả hai cho backup thủ công.
  3. Migration đổi schema an toàn qua migrations/<version>/pre|post-migrate.py với hàm migrate(cr, version); và neutralize bản sao trước khi chạy ngoài production để không gửi mail/gọi API thật.

Ta vừa lướt qua migration script — nhưng đây là chủ đề đủ sâu để mổ xẻ riêng. Phần sau đi vào chi tiết pre/post/end script khi nâng cấp: thứ tự chạy chính xác, khi nào dùng cái nào, và các hàm openupgradelib hay dùng nhất.