Thêm một node Odoo thứ hai là người dùng bắt đầu bị đăng xuất. session_db, session_redis và auth_session_timeout làm gì, và hệ thống của bạn thật sự cần cái nào.
Bạn thêm một node Odoo thứ hai vào cụm máy chủ. Chỉ vài phút sau, người dùng bắt đầu báo bị đăng xuất ngẫu nhiên. Session tồn tại trên node A, còn node B thì chưa từng thấy chúng.
Bài này điểm qua các module cộng đồng mở rộng hoặc thay thế session store mặc định trên filesystem của Odoo, cùng những công cụ có sẵn để kiểm soát vòng đời session trên môi trường production. Với ai bỏ qua phần 1: session gốc là các file JSON trên đĩa, nằm dưới {data-dir}/sessions/, mỗi session một file.
Khi nào session gốc trở thành vấn đề
FilesystemSessionStore mặc định của Odoo chạy tốt với hệ thống một máy chủ. Nhiều worker trên cùng một máy dùng chung một filesystem, nên session luôn truy cập được bất kể worker nào xử lý request [8, Odoo 19.0].
Vấn đề nảy sinh trong hai tình huống khác nhau.
Thứ nhất là cụm nhiều node. Khi Odoo chạy trên hai máy trở lên mà không có filesystem dùng chung, file session ghi trên node A là vô hình với node B. Bất kỳ request nào rơi vào node khác với node đã tạo session đều không tìm thấy session và đẩy người dùng về trang đăng nhập. Mount NFS có thể chữa cháy, nhưng nó kéo theo các kiểu hỏng và chi phí hiệu năng riêng.
Thứ hai là độ trễ do GC, vấn đề chỉ ảnh hưởng tới Odoo 15.0 trở về trước. Ở các phiên bản đó, việc dọn rác session kích hoạt ở khoảng 1 trên 100 request thông qua http.session_gc(): trên một máy chủ đã tích tụ hàng chục nghìn file session, lần quét thư mục ấy làm tăng độ trễ thấy rõ cho những request xui xẻo kích hoạt nó. Từ Odoo 16.0, việc dọn rác chạy trong cron “Base: Auto-vacuum internal data” hằng ngày [10], hoàn toàn nằm ngoài đường đi của request, nên trên các phiên bản còn được hỗ trợ thì không còn hiện tượng vọt trễ này nữa.
Với hệ thống một máy chủ, session gốc là lựa chọn mặc định đúng đắn. Phần còn lại của bài viết dành cho mọi trường hợp khác.
Session lưu trong PostgreSQL (session_db)
session_db là module thuộc OCA/server-tools, có cho Odoo 13.0-19.0 [1][2]. Nó thay FilesystemSessionStore bằng PGSessionStore, lưu session vào một bảng http_sessions riêng trong PostgreSQL.
Kích hoạt chỉ cần một biến môi trường:
SESSION_DB_URI=postgresql://user:password@host/dbname
Không phải sửa code. Module monkey-patch http.root.session_store lúc khởi động, thay thế store một cách trong suốt trong toàn bộ vòng đời tiến trình [1].
Phần cài đặt dùng một kết nối PostgreSQL riêng, tách khỏi connection pool của ORM, kèm cơ chế thử lại tự động. Mỗi lần lưu là một upsert:
INSERT INTO http_sessions(sid, write_date, payload)
VALUES (%(sid)s, now() at time zone 'UTC', %(payload)s)
ON CONFLICT (sid)
DO UPDATE SET payload = %(payload)s,
write_date = now() at time zone 'UTC'
ON CONFLICT DO UPDATE khiến thao tác ghi trở nên idempotent và an toàn khi nhiều worker trên nhiều node cùng ghi [1]. Dữ liệu session được tuần tự hóa dưới dạng JSON, đúng định dạng của filesystem store, nên việc chuyển đổi diễn ra trong suốt.
Lý do chính để chọn session_db thay vì session_redis là sự đơn giản trong vận hành: nếu PostgreSQL đã nằm sẵn trong hệ thống, bạn không phải thêm hạ tầng mới. Mọi node Odoo đều đã có sẵn kết nối PostgreSQL được cấu hình; session_db tận dụng lại phụ thuộc đó thay vì tạo thêm một cái mới.
Session lưu trong Redis (session_redis)
session_redis là module của Camptocamp trong repository odoo-cloud-platform, có từ Odoo 8.0 [3][4][5]. Nó thay session store bằng RedisSessionStore, đưa toàn bộ thao tác đọc ghi session qua Redis.
Cấu hình tối thiểu để kích hoạt:
ODOO_SESSION_REDIS=1
ODOO_SESSION_REDIS_HOST=redis-host
ODOO_SESSION_REDIS_PORT=6379
Cũng có dạng URL: ODOO_SESSION_REDIS_URL=redis://.... Các biến tùy chọn gồm ODOO_SESSION_REDIS_PASSWORD và ODOO_SESSION_REDIS_PREFIX, hữu ích khi nhiều instance Odoo dùng chung một máy chủ Redis.
TTL của session
Một tính năng đáng chú ý là kiểm soát TTL riêng cho session đã xác thực và session ẩn danh [3]:
ODOO_SESSION_REDIS_EXPIRATION=86400 # đã xác thực, tính bằng giây (1 ngày)
ODOO_SESSION_REDIS_EXPIRATION_ANONYMOUS=3600 # ẩn danh, tính bằng giây (1 giờ)
Giá trị phù hợp tùy vào cách hệ thống được dùng. Trên một hệ thống back-office thông thường, session ẩn danh do các probe giám sát, health check và lời gọi API chưa xác thực tạo ra. Giữ chúng khoảng một giờ là đủ và tránh tích tụ hàng nghìn session ngắn hạn trong Redis. Người dùng đã xác thực đăng nhập một lần mỗi ngày và mong session sống hết ngày làm việc, nên 24 giờ là mặc định hợp lý.
Với một site thương mại điện tử, cán cân đổi khác. Session ẩn danh mang theo giỏ hàng và danh sách yêu thích của khách. Cho chúng hết hạn sau một giờ nghĩa là một khách quay lại mà chưa đăng nhập sẽ mất giỏ hàng. Trong bối cảnh đó, đặt ODOO_SESSION_REDIS_EXPIRATION_ANONYMOUS dài hơn (24 giờ trở lên) sẽ tránh được phiền toái này.
Redis Sentinel
Redis Sentinel được hỗ trợ cho các hệ thống Redis có tính sẵn sàng cao. Khi dùng cần thêm ba biến: ODOO_SESSION_REDIS_SENTINEL_HOST, ODOO_SESSION_REDIS_SENTINEL_PORT và ODOO_SESSION_REDIS_SENTINEL_MASTER_NAME. Cả ba phải được đặt cùng nhau [4].
Redis hay PostgreSQL
So với session_db, Redis có độ trễ đọc thấp hơn (nằm trong bộ nhớ, không phải đi vòng qua SQL) và tự xử lý việc hết hạn key mà không cần cron dọn dẹp. Đây là lựa chọn hợp hơn khi lưu lượng request rất lớn và mỗi request đều đọc session, hoặc khi hạ tầng đã có sẵn một cụm Redis. Nếu không thuộc hai trường hợp đó, session_db vận hành đơn giản hơn.
Quản lý vòng đời session
Đăng xuất theo thời gian không hoạt động
auth_session_timeout là module thuộc OCA/server-auth, có cho Odoo 12.0-19.0 [7]. Nó tự động chấm dứt các session không hoạt động quá một ngưỡng cấu hình được. Ngưỡng này đặt qua một tham số hệ thống trong phần thiết lập của Odoo, mặc định 2 giờ.
Module này độc lập với backend lưu trữ: nó hoạt động dù session nằm trên filesystem, trong PostgreSQL hay trong Redis. Nó đáng giá nhất với các hệ thống nhạy cảm về bảo mật: môi trường chịu quản lý chặt, máy trạm dùng chung, hoặc các instance Odoo mở ra Internet, nơi một phiên trình duyệt mở vô thời hạn là rủi ro.
Từ Odoo 16.0, bản thân Odoo đã phủ được một phần việc này: tham số hệ thống sessions.max_inactivity_seconds rút ngắn thời hạn session (và ở các phiên bản gần đây là cả thời hạn cookie) mà không cần module nào. Khác biệt nằm ở thời điểm thực thi: tham số gốc chỉ có hiệu lực khi cron GC hằng ngày dọn file session, còn auth_session_timeout kiểm tra thời gian không hoạt động ở mỗi request và đăng xuất người dùng ngay. Với yêu cầu timeout nghiêm ngặt, module vẫn là công cụ đúng.
API buộc đăng xuất
Với những trường hợp mà hết hạn theo thời gian không hoạt động là chưa đủ, Trobz đã đóng góp auth_session_logout_api cho OCA/server-auth [9]. Module mở ra một API endpoint có bảo mật, cho phép buộc đăng xuất từ xa một session cụ thể của một người dùng. Tính đến tháng 5/2026, PR nhắm tới Odoo 16.0 và đang chờ merge — xem [9] để biết trạng thái hiện tại.
GC tất định: từng là module, nay đã là tính năng gốc
Vấn đề độ trễ GC ngẫu nhiên mô tả ở phần đầu từng được giải quyết bằng base_deterministic_session_gc, một module do Trobz viết và đóng góp cho OCA/server-tools, dành cho Odoo 12.0-14.0 [6]. Module vô hiệu hóa http.session_gc() và thay bằng một cron action theo lịch, khiến việc dọn dẹp trở nên đoán trước được và đưa nó hẳn ra khỏi đường đi của request. Module cần cấu hình server_wide_modules để nạp lúc khởi động.
Từ Odoo 16.0, thiết kế này đã là tính năng gốc: GC session chạy trong _gc_sessions(), thuộc cron “Base: Auto-vacuum internal data” hằng ngày, và http.session_gc() theo từng request đã biến mất [10]. Ngưỡng không hoạt động cấu hình được qua tham số hệ thống sessions.max_inactivity_seconds. Không cần module hay cấu hình thêm.
Biến môi trường ODOO_SKIP_GC_SESSIONS vẫn còn từ 16.0 trở đi, nhưng ý nghĩa của nó ngược với những gì lịch sử cái tên gợi ra: nó bỏ qua hoàn toàn phần GC bằng cron có sẵn [10]. Chỉ đặt biến này khi việc dọn session đã được lo ở nơi khác, ví dụ bằng cơ chế TTL của session_redis, một job dọn dẹp tùy chỉnh trên bảng http_sessions với session_db, hoặc công cụ ở tầng nền tảng.
Chọn hướng nào cho hệ thống của bạn
Một máy chủ, bao nhiêu worker cũng được: session gốc trên filesystem là đủ. Thêm auth_session_timeout nếu yêu cầu bảo mật đòi hỏi đăng xuất ngay khi hết thời gian không hoạt động; nếu chỉ cần hết hạn nhẹ nhàng hơn, rút ngắn sessions.max_inactivity_seconds (16.0 trở đi).
Cụm nhiều node: bắt buộc phải có session_db hoặc session_redis. Chọn cái nào tùy hạ tầng sẵn có. PostgreSQL đã có trong hệ thống và lưu lượng đọc session không quá lớn: dùng session_db. Đã có sẵn Redis, hoặc việc đọc session nằm trên đường găng với lưu lượng cao: dùng session_redis.
Hệ thống nhạy cảm về bảo mật: thêm auth_session_timeout bất kể backend lưu trữ là gì. Nó độc lập với nơi session được lưu. Với hệ thống còn cần đăng xuất từ xa tức thì, hãy theo dõi auth_session_logout_api cho tới khi được merge vào OCA [9].
Những điểm chính
Session gốc trên filesystem của Odoo đủ dùng cho production với hệ thống một máy chủ. Với cụm nhiều node, session_db và session_redis đều cho phép dùng chung session giữa các node với cấu hình tối thiểu: một biến môi trường để kích hoạt, không phải sửa code. auth_session_timeout là phần bổ sung lắp vào là chạy để đăng xuất theo thời gian không hoạt động, bất kể backend lưu trữ. Vấn đề độ trễ GC ngẫu nhiên mà base_deterministic_session_gc từng giải quyết đã được xử lý ngay trong Odoo từ 16.0, khi GC session chạy trong cron auto-vacuum hằng ngày thay vì trên đường đi của request.
Quay lại loạt bài: Phần 1 mô tả chi tiết đối tượng session, cách lưu trên filesystem và mô hình bảo mật dựa trên HMAC. Phần 3 là phần hỏi đáp cho những câu hỏi về session chúng tôi nghe nhiều nhất trong thực tế.
Nguồn
[1] server-tools/18.0/session_db/pg_session_store.py:62-167 (OCA/server-tools)
[2] server-tools/18.0/session_db/README.rst (OCA/server-tools)
[3] session_redis/session.py:20-127 (Camptocamp/odoo-cloud-platform)
[4] session_redis/http.py:24-90 (Camptocamp/odoo-cloud-platform)
[5] session_redis/README.rst (Camptocamp/odoo-cloud-platform)
[6] server-tools/14.0/base_deterministic_session_gc/http.py:15-42 (OCA/server-tools)
[7] server-auth/18.0/auth_session_timeout/__manifest__.py:1-21 (OCA/server-auth)
[8] odoo/http.py:968-1048 (FilesystemSessionStore gốc của Odoo 19.0)
[9] OCA/server-auth PR #891: auth_session_logout_api (Trobz, nhắm tới 16.0, đang chờ merge)
[10] odoo/addons/base/models/ir_http.py:406-410 (Odoo 19.0, cron autovacuum _gc_sessions(), ODOO_SKIP_GC_SESSIONS)