# Bắt đầu với odoo-activity

> 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.

**Date:** 2026-08-11
**Source:** <https://trobz.com/vi/insights/introducing-odoo-activity/>

---



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][odoo-pr-41365]. 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

{{< img src="/images/insights/introducing-odoo-activity/one-screen-tuico-int02.webp" alt="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ọn | Các tab |
| --- | --- |
| một hệ thống | Top, 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 đó:

```bash
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:

```bash
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.

```bash
uv tool install odoo-activity
```

[odoo-pr-41365]: https://github.com/odoo/odoo/pull/41365

