10. For Python profiling details, check my last year talk1
Reproducing is the key2
Doing it manually is not accurate3
11. Let's work on a use case
Have the description of the issue1
Have the customer database/code running on an isolated server2
Reproduce the issue using a script3
Profile python with odoo.tools.misc.profile4
Log PostgreSQL queries5
Analyse and solve6
Do it again and again7
14. M POS
Issue is a concurrency issue, which means we should call multiple workers at the same time, in a repeatable
way, to create pos orders or close pos sessions.
I've set the password as computable from user to ease the reproduction_protocol.
mport odoolib
mport sys
mport random
mport threading
rom datetime import datetime, date
ef selling(username, password, size):
[...]
ef closing(username, password):
connection = odoolib.get_connection(hostname="my_server", database="mydb
config_model = connection.get_model('pos.config')
session_model = connection.get_model('pos.session')
order_model = connection.get_model('pos.order')
# Search config
config_id = config_model.search([('name', 'ilike', pos_id)])
if len(config_id) > 1:
config_id = config_id[0]
# Open session
config_model.open_session_cb(config_id)
pos_config = config_model.read(config_id, ['current_session_id', 'journa
session_id = pos_config['current_session_id'][0]
order_ids = order_model.search([['session_id', '=', session_id]])
if __name__ == "__
if len(sys.arg
print("Usa
print("")
print("Ex
print("
print(" To
print("
else:
concurrenc
lst_thd =
for i in r
pos_id
pos_id
userna
passwo
if sys
si
if
th
else:
th
thd.st
16. S
On various workers, in various chunks.
import threading
#How many workers will I load?
max_connections = 48
semaphore = threading.BoundedSemaphore(max_connections)
lst_thd = []
def process_company(company_id, date_from, date_to):
try:
connection = odoolib.get_connection(hostname="load", database="odoo", login="adm
[...]
chunk_size = 2000
chunks_rent_ids = [rent_ids[x : x + chunk_size] for x in range(0, len(rent_ids),
[...]
finally:
semaphore.release()
if __name__ == "__main__":
connection = odoolib.get_connection(hostname="load", database="odoo", login="admin",
[...]
# Split the work
thd_to_launch = []
for p in to_process:
thd_to_launch.append((p[0], d_start.strftime("%Y-%m-%d"), d_end.strftime("%Y-%m-%
for ttl in thd_to_launch:
# Semaphore
semaphore.acquire()
thd = threading.Thread(target=process_company, args=ttl)
thd.start()
21. ~/.psqlrc
set QUIET 1
pset null '¤'
set PROMPT1 '%[%033[1m%][%/] # '
-- SELECT * FROM<enter>. %R shows what type of input it expects.
set PROMPT2 '... > '
timing
x auto
set VERBOSITY verbose
set HISTFILE ~/.psql_history- :DBNAME
set HISTCONTROL ignoredups
set COMP_KEYWORD_CASE upper
setenv EDITOR 'vim'
unset QUIET
22. Parallel workers
Workers used for query
processing is based on a
geometric progression
with 3 as common ratio
and
min_parallel_relation_size
as scale factor.
Considering the 8MB of
default parameter:
Size Worker
<8 MB 0
<24 MB 1
<72 MB 2
<216 MB 3
<684 MB 4
<1944 MB 5
<5822 MB 6
... ...
Improve query speed @ CPU cost1
max_worker_processes2
max_parallel_workers3
system wide workers for parallel processing
<= max_worker_processes
max_parallel_workers_per_gather4
query wide workers for parallel processing
<= max_parallel_workers
Max 4 per our recommendation