Skip to content

donhat-dev/oh-my-odoo-web

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

oh-my-odoo-web

POC workspace cho mô hình Odoo ecommerce high-traffic với:

  • sản phẩm có biến thể (ví dụ size, màu)
  • giá và visibility theo nhóm người dùng
  • edge cache / local CDN trong một docker-compose.yml
  • Redis làm lớp cache dễ chuyển sang production

Đặt vấn đề

Odoo là một nền tảng ERP mã nguồn mở phổ biến với module ecommerce tích hợp, nhưng kiến trúc monolith và cách xử lý cache mặc định của nó có thể gặp khó khăn khi phục vụ các cửa hàng trực tuyến có lưu lượng truy cập cao và yêu cầu cá nhân hóa. Vấn đề này xuất phát từ các nguyên nhân cốt lõi sau, liên quan đến cách Odoo xử lý rendering, cache và logic kinh doanh:

1. Server-Side Rendering (SSR) nặng nề

Odoo tính toán và render lại toàn bộ mã HTML (QWeb) cho mỗi HTTP request. Dưới áp lực traffic lớn, điều này làm cạn kiệt tài nguyên CPU (worker pool) nhanh chóng và đẩy độ trễ TTFB lên rất cao.

Cụ thể, luồng xử lý một request /shop đi qua:

  1. Controller website_sale gọi request.render() → delegate sang Response object (http.py#L1767)
  2. Khi response được finalize, Response.render() gọi ir.ui.view._render_template() (http.py#L1392)
  3. QWeb engine compile template thành Python function (bước này được cache qua @ormcachebase/models/ir_qweb.py#L617)
  4. Tuy nhiên, việc thực thi function đó vẫn chạy fresh mỗi request với context riêng (ir_qweb.py#L605: render_template(irQweb, values))
  5. Không có bất kỳ HTTP-level output cache nào (không ETag, không Cache-Control cho rendered HTML)

Kết luận: Template compilation được cache, nhưng template execution và HTML output thì không. Mỗi request tạo ra một chuỗi ''.join(rendering) mới hoàn toàn.

2. Bottleneck ở Database & ORM

Tính toán giá (Pricelist) nhiều cấp độ, kiểm tra tồn kho realtime và quản lý biến thể tạo ra chi phí truy vấn SQL đắt đỏ. Khi traffic tăng đột biến (flash-sale), dễ gây khóa dòng (Row-level Locking) làm quá tải cơ sở dữ liệu.

Pricelist — độ phức tạp O(n × m):

  • Controller /shop load mặc định 20 sản phẩm/trang (ppg = website.shop_ppg or 20)
  • Gọi products._get_sales_prices(website) → trigger pricelist._compute_price_rule() (product_pricelist.py#L160)
  • Hàm này duyệt qua mỗi sản phẩm × mỗi pricelist rule để tìm rule phù hợp (_is_applicable_for()), kèm thêm truy vấn fiscal position và tax mapping

Stock — 3 query GROUP BY trên mỗi batch:

  • free_qty là computed field, gọi _compute_quantities_dict() (stock/models/product.py#L125) thực hiện 3 câu _read_group() lên stock.quantstock.move
  • Nếu free_qty được truy xuất từng sản phẩm riêng lẻ (không batched), mỗi sản phẩm sinh riêng 3 query

Row-level Locking khi ghi tồn kho:

  • stock.quant._update_available_quantity() sử dụng SELECT ... FOR NO KEY UPDATE SKIP LOCKED (stock_quant.py#L1088) để serialize các giao dịch trừ kho đồng thời — đảm bảo tính nhất quán nhưng đánh đổi throughput dưới tải cao

3. Mâu thuẫn giữa Full-page Caching và Cá nhân hóa

Rất khó áp dụng Full-page cache ở mức Edge/Nginx vì HTML được render chứa dữ liệu theo từng user session. Nếu cache bừa bãi, user B sẽ thấy giỏ hàng hoặc giá của user A.

Bằng chứng từ source code Odoo 18:

  • Cart badge: Template website_sale.header_cart_link đọc request.session['website_sale_cart_quantity'] và render trực tiếp vào HTML. Chính Odoo đã đánh dấu đoạn này bằng t-nocache="The number of products is dynamic, this rendering cannot be cached." (website_sale/views/templates.xml#L3)
  • User avatar & name: Template portal.frontend_layout nhúng user_id.nameimage_data_uri(user_id.avatar_256) trực tiếp vào thẻ <img><span> của header (portal/views/portal_templates.xml#L110)
  • Pricelist per session: Controller /shop lưu website_sale_current_pl (pricelist ID) vào request.session và dùng nó để tính giá — mỗi user/nhóm user có thể nhận pricelist khác nhau (website_sale/controllers/main.py#L291)
  • Permission flags: Với user đã đăng nhập, website_templates.xml nhúng các data attribute riêng (data-can-publish, data-editable-in-backend) vào thẻ <html>, khiến HTML khác biệt theo quyền

4. Session State / Disk IOPS

Giỏ hàng e-commerce là stateful session. Mặc định Odoo ghi session xuống filesystem, khi lượng truy cập lên hàng chục nghìn, IOPS ổ cứng sẽ trở thành nút thắt vật lý.

  • Odoo sử dụng class FilesystemSessionStore kế thừa từ Werkzeug (http.py#L897)
  • Session files được phân tán vào 4096 thư mục con (hash 2 ký tự đầu của sid) tại đường dẫn {data_dir}/sessions/ (tools/config.py#L800)
  • Property session_store được khởi tạo duy nhất một lần và luôn trỏ đến filesystem (http.py#L2303)
  • Không có backend Redis/Memcached built-in — cần module bên ngoài để thay đổi session store

Trạng thái hiện tại

Repo hiện đang ở giai đoạn planning-first. Chưa có scaffold ứng dụng hoặc docker-compose.yml; trước mắt repo tập trung vào quyết định kiến trúc và kế hoạch triển khai POC. see docs/poc-plan.md để biết chi tiết.

Tài liệu chính

  • docs/poc-plan.md — kế hoạch triển khai POC, stack service, cache strategy, sync workflow, và danh sách module custom đề xuất.
  • docs/benchmark-seeding.md — cách seed nhanh 100–200+ sản phẩm benchmark có variant và ảnh để load test / stress test storefront.
  • docs/k6-native-vs-edge.md — kịch bản k6 để benchmark tuần tự native Odoo origin so với edge storefront trong cùng stack.
  • .github/copilot-instructions.md — hướng dẫn ngắn cho agent khi làm việc trong repo này.

Hướng kiến trúc được chọn cho POC

  • Giữ website_sale + QWeb cho page shell.
  • Cache ở lớp edge cho HTML public an toàn và static assets.
  • Tách giá, tồn kho, cart badge, và visibility theo persona sang batch JSON API.
  • Dùng Redis cho cache key/tag/invalidation.
  • Dùng worker/job bất đồng bộ để purge và warm cache.

Local endpoints

  • Native Odoo: http://localhost:8069
  • Edge HTTP: http://shop.local.test:8080
  • Edge HTTPS + HTTP/2: https://shop.local.test:8443
  • CDN HTTPS + HTTP/2: https://cdn.local.test:8443

OpenResty sẽ tự sinh self-signed certificate local vào docker/openresty/certs/ nếu chưa có sẵn. Đây là HTTPS dev-only để kiểm tra browser waterfall, Lighthouse và HTTP/2; k6 nội bộ vẫn có thể tiếp tục dùng cổng HTTP hiện tại.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors