Odoo supports a set of POSIX signals that enable administrators and developers to manage and interact with Odoo processes efficiently. These signals facilitate smooth server shutdowns, worker management, real-time diagnostics, and debugging capabilities. Warning: It is crucial to avoid testing these signals on a production server unless you have thoroughly tested them in a safe environment. Additionally, avoid using them on Odoo.sh, as it is a PaaS environment that may already employ some of these signals for internal operations.
Key POSIX Signals Used in Odoo
These are the POSIX signals that Odoo handles:
| ______________ | ____________________________________________ |
|---|---|
| SIGINT | Force a graceful shutdown of the Odoo server |
| SIGTERM | Force a shutdown of the Odoo server |
| SIGCLH | Internal mechanism for process reaping |
| SIGHUP | Restart the Odoo server |
| SIGXCPU | Trigger when worker CPU time limit is exceeded |
| SIGQUIT | Dump stack traces for debugging purposes |
| SIGUSR1 | Log ORM (Object-Relational Mapping) statistics |
By using these signals, administrators can perform essential tasks like restarting the server safely, running diagnostics, and addressing performance bottlenecks. Signal-based management is a vital part of ensuring the reliability, scalability, and maintainability of an Odoo instance.
Practical Examples of Using Signals in Odoo
Identifying Odoo Processes
Let's find the Odoo processes running on your server
ps -ef | grep odoo
You will see something similar to this:
dd@pro-dd:~$ ps -ef | grep odoo
dd 14753 11704 4 19:07 pts/0 00:00:02 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14777 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14778 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14779 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14780 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14782 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14783 14753 4 19:07 pts/0 00:00:02 ~/envs/18.0/bin/python3 ~/git/odoo/18.0/odoo-bin gevent --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14785 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14788 14753 0 19:07 pts/0 00:00:00 python3 ~/git/odoo/18.0/odoo-bin --addons-path ~/git/oe/18.0,~/git/odoo/18.0/addons,~/git/odoo-themes/18.0 -d v18_demo1 --workers 5
dd 14829 11985 0 19:08 pts/1 00:00:00 grep --color=auto odoo
As we can see, we are running one main process (14753) plus 5 http workers (WorkerHTTP), plus a gevent worker for websockets and finally 2 cron workers (WorkerCron). When Odoo starts he also explains that for you:
(18.0) dd@pro-dd:~$ runodoo18e -d v18_demo1 --workers 5
2024-12-15 19:07:20,778 14753 INFO ? odoo: Odoo version 18.0
2024-12-15 19:07:20,778 14753 INFO ? odoo: addons paths: ['~/git/odoo/18.0/odoo/addons', '~/.local/share/Odoo/addons/18.0', '~/git/oe/18.0', '~/git/odoo/18.0/addons', '~/git/odoo-themes/18.0']
2024-12-15 19:07:20,778 14753 INFO ? odoo: database: default@default:default
2024-12-15 19:07:20,832 14753 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltopdf to print a pdf version of the reports.
2024-12-15 19:07:20,832 14753 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltoimage to generate images from html.
2024-12-15 19:07:20,897 14753 INFO ? odoo.service.server: HTTP service (werkzeug) running on 0.0.0.0:8069
2024-12-15 19:07:20,905 14753 INFO v18_demo1 odoo.modules.loading: loading 1 modules...
2024-12-15 19:07:20,927 14753 INFO v18_demo1 odoo.modules.loading: 1 modules loaded in 0.02s, 0 queries (+0 extra)
2024-12-15 19:07:20,938 14753 INFO v18_demo1 odoo.modules.loading: loading 57 modules...
2024-12-15 19:07:21,150 14753 INFO v18_demo1 odoo.modules.loading: 57 modules loaded in 0.21s, 0 queries (+0 extra)
2024-12-15 19:07:21,189 14753 INFO v18_demo1 odoo.modules.loading: Modules loaded.
2024-12-15 19:07:21,191 14753 INFO v18_demo1 odoo.modules.registry: Registry loaded in 0.294s
2024-12-15 19:07:21,191 14753 INFO v18_demo1 odoo.sql_db: ConnectionPool(read/write;used=0/count=0/max=64): Closed 1 connections
2024-12-15 19:07:21,195 14777 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14777) alive
2024-12-15 19:07:21,197 14778 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14778) alive
2024-12-15 19:07:21,198 14779 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14779) alive
2024-12-15 19:07:21,199 14780 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14780) alive
2024-12-15 19:07:21,200 14782 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14782) alive
2024-12-15 19:07:21,202 14785 INFO v18_demo1 odoo.service.server: Worker WorkerCron (14785) alive
2024-12-15 19:07:21,202 14788 INFO v18_demo1 odoo.service.server: Worker WorkerCron (14788) alive
2024-12-15 19:07:21,541 14783 INFO ? odoo: Odoo version 18.0
2024-12-15 19:07:21,541 14783 INFO ? odoo: addons paths: ['~/git/odoo/18.0/odoo/addons', '~/.local/share/Odoo/addons/18.0', '~/git/oe/18.0', '~/git/odoo/18.0/addons', '~/git/odoo-themes/18.0']
2024-12-15 19:07:21,541 14783 INFO ? odoo: database: default@default:default
2024-12-15 19:07:21,596 14783 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltopdf to print a pdf version of the reports.
2024-12-15 19:07:21,596 14783 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltoimage to generate images from html.
2024-12-15 19:07:21,678 14783 INFO ? odoo.service.server: Evented Service (longpolling) running on 0.0.0.0:8072
Restarting the Odoo Server (SIGHUP)
To restart the Odoo server, use the following command:
kill -HUP 14753
This sends the SIGHUP signal to the main process (PID 14753). As a result, Odoo stops all child processes and restarts them with new process IDs.
Example log output after issuing this command
2024-12-15 19:10:04,860 14753 INFO v18_demo1 odoo.service.server: Stopping gracefully
2024-12-15 19:10:04,861 14779 INFO v18_demo1 odoo.service.server: Worker (14779) exiting. request_count: 0, registry count: 1.
2024-12-15 19:10:04,861 14780 INFO v18_demo1 odoo.service.server: Worker (14780) exiting. request_count: 0, registry count: 1.
2024-12-15 19:10:04,861 14777 INFO v18_demo1 odoo.service.server: Worker (14777) exiting. request_count: 0, registry count: 1.
2024-12-15 19:10:04,861 14778 INFO v18_demo1 odoo.service.server: Worker (14778) exiting. request_count: 0, registry count: 1.
2024-12-15 19:10:04,861 14782 INFO v18_demo1 odoo.service.server: Worker (14782) exiting. request_count: 0, registry count: 1.
2024-12-15 19:10:04,911 14785 INFO v18_demo1 odoo.service.server: Worker (14785) exiting. request_count: 2, registry count: 1.
2024-12-15 19:10:04,942 14788 INFO v18_demo1 odoo.service.server: Worker (14788) exiting. request_count: 2, registry count: 1.
2024-12-15 19:10:05,634 14753 INFO ? odoo: Odoo version 18.0
2024-12-15 19:10:05,634 14753 INFO ? odoo: addons paths: ['~/git/odoo/18.0/odoo/addons', '~/.local/share/Odoo/addons/18.0', '~/git/oe/18.0', '~/git/odoo/18.0/addons', '~/git/odoo-themes/18.0']
2024-12-15 19:10:05,634 14753 INFO ? odoo: database: default@default:default
2024-12-15 19:10:05,686 14753 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltopdf to print a pdf version of the reports.
2024-12-15 19:10:05,686 14753 INFO ? odoo.addons.base.models.ir_actions_report: You need Wkhtmltoimage to generate images from html.
2024-12-15 19:10:05,749 14753 INFO ? odoo.service.server: HTTP service (werkzeug) running on 0.0.0.0:8069
2024-12-15 19:10:05,757 14753 INFO v18_demo1 odoo.modules.loading: loading 1 modules...
2024-12-15 19:10:05,783 14753 INFO v18_demo1 odoo.modules.loading: 1 modules loaded in 0.03s, 0 queries (+0 extra)
2024-12-15 19:10:05,800 14753 INFO v18_demo1 odoo.modules.loading: loading 57 modules...
2024-12-15 19:10:06,020 14753 INFO v18_demo1 odoo.modules.loading: 57 modules loaded in 0.22s, 0 queries (+0 extra)
2024-12-15 19:10:06,071 14753 INFO v18_demo1 odoo.modules.loading: Modules loaded.
2024-12-15 19:10:06,074 14753 INFO v18_demo1 odoo.modules.registry: Registry loaded in 0.324s
2024-12-15 19:10:06,074 14753 INFO v18_demo1 odoo.sql_db: ConnectionPool(read/write;used=0/count=0/max=64): Closed 1 connections
2024-12-15 19:10:06,078 14873 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14873) alive
2024-12-15 19:10:06,079 14874 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14874) alive
2024-12-15 19:10:06,081 14876 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14876) alive
2024-12-15 19:10:06,081 14875 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14875) alive
2024-12-15 19:10:06,082 14878 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14878) alive
2024-12-15 19:10:06,084 14883 INFO v18_demo1 odoo.service.server: Worker WorkerCron (14883) alive
2024-12-15 19:10:06,084 14881 INFO v18_demo1 odoo.service.server: Worker WorkerCron (14881) alive
2024-12-15 19:10:06,438 14879 INFO ? odoo: Odoo version 18.0
2024-12-15 19:10:06,438 14879 INFO ? odoo: addons paths: ['~/git/odoo/18.0/odoo/addons', '~/.local/share/Odoo/addons/18.0', '~/git/oe/18.0', '~/git/odoo/18.0/addons', '~/git/odoo-themes/18.0']
2024-12-15 19:10:06,438 14879 INFO ? odoo: database: default@default:default
Force Worker CPU Time Limit (SIGXCPU)
If you want to simulate what happens when a worker exceeds its CPU time limit, you can send a SIGXCPU signal to a specific worker process:
We see process 14779 to 14788 being recycled into pid 14873 to 14881 but main process keeps the same pid 14753. Let's try forcing a CPU time limit to on WorkerHTTP and see what happens.
dd@pro-dd:~$ kill -XCPU 14878
The log output will display the following:
2024-12-15 19:14:47,149 14878 INFO v18_demo1 odoo.service.server: Worker (14878) CPU time limit (60) reached.
2024-12-15 19:14:47,150 14878 ERROR v18_demo1 odoo.service.server: Worker (14878) Exception occurred, exiting...
Traceback (most recent call last):
File "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1093, in run
t.join()
File "/usr/lib/python3.12/threading.py", line 1147, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.12/threading.py", line 1167, in _wait_for_tstate_lock
if lock.acquire(block, timeout):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1023, in signal_time_expired_handler
raise Exception('CPU time limit exceeded.')
Exception: CPU time limit exceeded.
2024-12-15 19:14:47,327 14976 INFO v18_demo1 odoo.service.server: Worker WorkerHTTP (14976) alive
Meaning that we force the termination of 14878 and Odoo triggered the creation of pid 14976. Let's now get creating and force a timeout on the main pid and see what happens.
dd@pro-dd:~$ kill -XCPU 14753
Odoo will take care of killing the child processes and Odoo server will forcefully stop.
2024-12-15 19:19:27,633 14976 INFO ? odoo.service.server: Worker (14976) Parent changed
2024-12-15 19:19:30,677 14874 INFO ? odoo.service.server: Worker (14874) Parent changed
2024-12-15 19:19:30,677 14875 INFO ? odoo.service.server: Worker (14875) Parent changed
2024-12-15 19:19:30,677 14873 INFO ? odoo.service.server: Worker (14873) Parent changed
2024-12-15 19:19:30,696 14876 INFO ? odoo.service.server: Worker (14876) Parent changed
2024-12-15 19:19:31,149 14879 WARNING ? odoo.service.server: Gevent Parent changed: 14879
2024-12-15 19:19:31,639 14976 INFO v18_demo1 odoo.service.server: Worker (14976) exiting. request_count: 0, registry count: 1.
2024-12-15 19:19:34,119 14883 INFO ? odoo.service.server: Worker (14883) Parent changed
2024-12-15 19:19:34,683 14875 INFO v18_demo1 odoo.service.server: Worker (14875) exiting. request_count: 0, registry count: 1.
2024-12-15 19:19:34,683 14873 INFO v18_demo1 odoo.service.server: Worker (14873) exiting. request_count: 0, registry count: 1.
2024-12-15 19:19:34,683 14874 INFO v18_demo1 odoo.service.server: Worker (14874) exiting. request_count: 0, registry count: 1.
2024-12-15 19:19:34,701 14876 INFO v18_demo1 odoo.service.server: Worker (14876) exiting. request_count: 0, registry count: 1.
This is not the correct way to stop the server, the best way would just be a SIGNT as follows.
Shutting Down the Odoo Server (SIGINT)
To properly shut down the Odoo server, the best practice is to send the SIGINT signal to the main process:
kill -INT 14753
This ensures that all child processes (workers) are gracefully stopped before the main Odoo process is shut down. Unlike SIGKILL, which forces termination, SIGINT ensures no data corruption or incomplete processing.
Dumping the Stack Trace (SIGQUIT)
To diagnose deadlocks or identify which threads are waiting on locks, you can trigger a stack dump. This is done by sending a SIGQUIT signal to a worker process:
dd@pro-dd:~$ kill -QUIT 17172
Dumping the stack might point to multiple threads are all waiting for the same lock, it might be a sign of a deadlock.The log will display the stack trace, which may reveal information like:
2024-12-15 20:27:20,902 17172 INFO v18_demo1 odoo.tools.misc:
# Thread: <Thread(Worker WorkerHTTP (17172) workthread, started daemon 130846535190208)> (db:v18_demo1) (uid:2) (url:http://odoo/mail/thread/data) (qc:n/a qt:0.028 pt:13.732)
File: "/usr/lib/python3.12/threading.py", line 1030, in _bootstrap
self._bootstrap_inner()
File: "/usr/lib/python3.12/threading.py", line 1073, in _bootstrap_inner
self.run()
File: "/usr/lib/python3.12/threading.py", line 1010, in run
self._target(*self._args, **self._kwargs)
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1112, in _runloop
self.sleep()
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1027, in sleep
select.select([self.multi.socket, self.wakeup_fd_r], [], [], self.multi.beat)
# Thread: <_MainThread(MainThread, started 130847655657600)> (db:v18_demo1) (uid:n/a) (url:n/a) (qc:n/a qt:n/a pt:n/a)
File: "/home/dd/git/odoo/18.0/odoo-bin", line 8, in <module>
odoo.cli.main()
File: "/home/dd/git/odoo/18.0/odoo/cli/command.py", line 66, in main
o.run(args)
File: "/home/dd/git/odoo/18.0/odoo/cli/server.py", line 180, in run
main(args)
File: "/home/dd/git/odoo/18.0/odoo/cli/server.py", line 173, in main
rc = odoo.service.server.start(preload=preload, stop=stop)
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1402, in start
rc = server.run(preload, stop)
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 978, in run
self.process_spawn()
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 884, in process_spawn
self.worker_spawn(WorkerHTTP, self.workers_http)
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 801, in worker_spawn
worker.run()
File: "/home/dd/git/odoo/18.0/odoo/service/server.py", line 1093, in run
t.join()
File: "/usr/lib/python3.12/threading.py", line 1147, in join
self._wait_for_tstate_lock()
File: "/usr/lib/python3.12/threading.py", line 1167, in _wait_for_tstate_lock
if lock.acquire(block, timeout):
File: "/home/dd/git/odoo/18.0/odoo/tools/misc.py", line 918, in dumpstacks
for line in extract_stack(stack):
Taking a closer look into this beginning line:
... (db:v18_demo1) (uid:2) (url:http://odoo/mail/thread/data) (qc:n/a qt:0.028 pt:13.732)
Where:
- qc = Query Count
- qt = Query Time
- pt = Python Time
Summary of Best Practices
- Never test signals on a production server without testing them in a development or QA environment first.
- Avoid testing signals on Odoo.sh, as it already uses signals for internal operations.
- Use SIGINT for clean server shutdowns.
- Use SIGHUP to restart the server and reload configurations.
- Use SIGQUIT to generate stack dumps for debugging.
- Use SIGXCPU to simulate worker CPU time limit events and observe the system’s behavior.
Conclusion
POSIX signals provide a powerful way to manage and debug an Odoo server. They enable real-time process control, from restarting the server to debugging performance issues. Proper use of these signals enhances the maintainability and reliability of your Odoo environment. Remember to exercise caution when using signals on production servers and always test them in a controlled environment beforehand.
If you want to know more about Odoo, Contact us
Follow Us on Social Media
Stay connected with ERPGAP and follow us on this journey. You can view updates on LinkedIn and Twitter.