odoo-activity đưa các worker của một hệ thống Odoo và các backend PostgreSQL của nó về chung một màn hình, đổ ngăn xếp mà không phải đào log, và mở cùng dữ liệu đó cho agent qua MCP.

Suốt nhiều năm, pg_activity trả lời rất tốt đúng một câu hỏi: postgres đang làm gì ngay lúc này. Mở nó ra là thấy các truy vấn, cái chạy lâu nhất nằm trên cùng.

Odoo thì không có thứ tương đương. Khi một hệ thống đang nghẽn cứng và có người hỏi nó đang bận với cái gì, bạn phải xoay xở tạm bợ:

  • ps cho biết một worker đã sống sáu ngày. Nó chẳng nói gì về cái request mà worker ấy đang kẹt trong bốn mươi giây vừa rồi.
  • kill -3 <pid> khiến Odoo in ngăn xếp của mọi thread vào log. Rồi bạn phải đào bới trong log để tìm chúng, theo từng worker, và nếu worker đang bị chặn ở postgres hay ở IO thì traceback tuy chính xác nhưng vô dụng.
  • py-spy dump cho kết quả tốt hơn, nhưng nó cần quyền root trên một máy mà nhiều khả năng bạn chỉ có user odoo.
  • Vá Odoo để ghi lời gọi hiện tại vào tiêu đề tiến trình bằng setproctitle, thứ chúng tôi từng thử từ lâu. Nó đọc rất tiện trong ps, đổi lại là một bản vá vào bản lõi phải duy trì cho từng dự án.

Mỗi cách chỉ trả lời một mảnh của câu hỏi, qua một công cụ khác nhau, với kết quả ở một hình dạng khác nhau. Ghép chúng lại là việc làm tay dưới áp lực thời gian, mà đó lại đúng là lúc một bước chẩn đoán bị bỏ qua.

odoo-activity (oa) là nỗ lực của chúng tôi cho cái màn hình còn thiếu đó.

Một màn hình

Màn hình chính của odoo-activity: CPU, bộ nhớ, swap và tải của máy chủ ở phần đầu, các hệ thống Odoo với database lồng bên dưới ở khung thứ hai, và một khung chi tiết có tab ở khung thứ ba

CPU, bộ nhớ, swap và tải của máy chủ nằm ở phần đầu. Khung thứ hai hiển thị mọi hệ thống Odoo trên máy, với các database lồng bên dưới. Khung thứ ba là khung chi tiết, và các tab của nó đổi theo thứ đang được chọn:

Đang chọnCác tab
một hệ thốngTop, Processes, Stacks, Logs, Config
một database của nóQueries, Users, Locks, Jobs, Crons, Modules

Bộ tab này chưa chốt và sẽ còn đổi theo cách chúng tôi dùng công cụ; nguyên tắc chúng tôi giữ là thứ gì xứng đáng có một tab thì phải đáng đọc trong lúc chẩn đoán, chứ không chỉ là có sẵn.

Các hệ thống được tự phát hiện chứ không phải khai báo: unit của systemd --user, chương trình của supervisor, các bản build trên odoo.sh, và các hệ thống chạy cục bộ ngay từ terminal, gộp chung vào một danh sách. Từ đó, mọi thứ về một hệ thống đều phân giải ra từ đúng một file, là file cấu hình của nó dưới <workdir>/config/: odoo.conf hoặc server.conf, và odooNN.conf cho một node của hệ thống nhiều node, với tên mang hậu tố -NN tương ứng (foo-01). Đó là nơi lấy ra danh sách database, file log, cổng database, tất cả.

Máy ở xa cũng hoạt động theo đúng cách đó:

oa                       # this machine
oa odoo@somehost         # over ssh
oa odoo@somehost -p 10113

Kết nối ssh được ghép kênh, nên lần thăm dò đầu tiên mở phiên và mọi thứ sau đó dùng lại phiên ấy. Với máy ở xa, chu kỳ làm mới chạy chậm hơn, và phím R buộc tab đang mở làm mới ngay.

Odoo và postgres trong cùng một bảng

Tab Top liệt kê các worker của hệ thống (đi xuống theo cây PPID từ PID của tiến trình master mà trình quản lý báo về) và, trong cùng bảng đó, các backend postgres đang phục vụ các database của hệ thống này.

Cặp đôi ấy là phần chúng tôi dùng nhiều nhất. Một backend đang nhai dữ liệu sẽ có cổng TCP phía client nằm trong tiêu đề ps của nó; chạy lsof trên cổng đó sẽ trả về PID ở đầu bên kia, chính là worker Odoo đã mở kết nối. “Truy vấn nào chậm” và “worker nào đang kẹt” thôi còn là hai cuộc điều tra tách rời.

Khi một hệ thống bốc cháy

Một trình tự thao tác đại khái, từ rẻ nhất tới xâm lấn nhất:

  1. Đọc phần đầu màn hình trước. Swap đang leo và tải cao nghĩa là vấn đề bộ nhớ, không phải mã nguồn, và có đổ bao nhiêu ngăn xếp cũng không cho bạn biết điều đó.

  2. Top (p). Có bao nhiêu worker, mỗi cái tốn bao nhiêu bộ nhớ, và chúng đang giữ mở bao nhiêu backend postgres. Một worker chiếm bộ nhớ cao là một vấn đề khác hẳn với việc tất cả chúng đều đang bận.

  3. Đổ ngăn xếp (D). Gửi SIGQUIT tới master và mọi worker, rồi đọc lại các bản đổ ngăn xếp mới từ log và phân tích chúng. Mất chừng hai giây. Các worker trả về đã sắp xếp theo mức bận giảm dần, thread bận đứng trước thread rảnh, và mỗi traceback được đảo ngược để khung trong cùng (dòng thật sự đang chạy) là thứ bạn đọc đầu tiên, theo phong cách py-spy.

    Đây chính là mẹo kill -3, trừ đi phần khảo cổ trong log. SIGQUIT ở đây không giết gì cả: Odoo cài sẵn một trình xử lý cho tín hiệu này và nó chỉ in ngăn xếp ra.

  4. Các khung trỏ vào postgres? Chuyển xuống dòng database rồi sang tab Queries bằng [ / ]. Các truy vấn không ở trạng thái nghỉ lấy từ pg_stat_activity, cái chạy lâu nhất nằm trên cùng.

  5. Kẹt chứ không phải chậm? Xem Locks (l) trên database. Đang chờ một cron hay một job trong hàng đợi? Xem Crons (c) và Jobs (j), cũng ở đó.

  6. Rồi mới ra tay. K giết đúng cái worker hỏng bằng -9. L gửi -3 rồi đưa bạn sang tab Logs nếu muốn ghi lại traceback trước khi giết nó. r khởi động lại cả hệ thống, s bật tắt start/stop. Tất cả đều nằm sau một hộp thoại xác nhận.

Các bước 1 tới 5 không thay đổi gì trên máy đích. Điều đó có ý nghĩa khi cái máy đang nói tới là production và bạn mới ở phút thứ ba của một sự cố.

Cũng dữ liệu đó, dành cho một agent

Mọi thứ ở trên đều được phơi ra dưới dạng một máy chủ MCP:

oa-mcp odoo@somehost

Các tool đều chỉ đọc, một cách có chủ đích: list_instances, host_stats, instance_top, instance_databases, instance_version, instance_config, instance_log_tail, instance_dump_stacks, long_queries, và db_query cho một tập con có giới hạn các lệnh chẩn đoán của odoo-db (modules, crons, jobs, users, locks). Không start, không stop, không restart, không kill.

oa-mcp bị ghim vào đúng một máy, y như oa <host>. Một lời gọi tool nêu tên máy khác sẽ bị từ chối chứ không bị âm thầm chuyển đích. oa-mcp-multi thì để đích được chỉ định theo từng lời gọi, giới hạn bởi một regex --host-filter và một --host-file chứa các bí danh ssh.

Hình dung mong muốn là hai người cùng xử lý một sự cố, mà một trong hai là một agent: bạn ở oa somehost, nó ở oa-mcp somehost, cả hai cùng nhìn vào một hệ thống. Bạn hỏi vì sao cái này chậm, nó đổ ngăn xếp và đọc pg_stat_activity, còn bạn thì đã đang nhìn vào đúng cái bảng nó đang mô tả.

Các tool hành động sẽ do bạn chạy, không phải agent. Bắt đầu từ chế độ chỉ đọc là nhắm vào đúng nút thắt thật: khâu chẩn đoán.

Nó cần những gì

  • Hệ thống được quản lý bằng systemd --user, supervisor, chạy trên odoo.sh, hoặc chạy cục bộ ngay từ terminal.
  • odoo-db nằm trong PATH cho các tab database, odoo-config và odoo-addons-path cho tab Config.
  • psql để phát hiện database và cho tab Queries, lsof cho phần nối backend với worker.
  • Với một máy đích ở xa: đúng những công cụ đó phải có trên máy ở xa, không phải trên máy của bạn.
uv tool install odoo-activity