Odoo — It Depends

A Use Case For Odoo Compute Functions and Performance

Holden Rehg
3 min readJun 4, 2016

Update — Mar 1st, 2021

Hello! If you’ve seen my articles pop up in your searches before, I really appreciate the time people have taken to read my articles. At this point, there’s been over 75k views across the 25 or so articles I’ve written here on Medium. That’s amazing to me since writing is a hobby I started doing just to try to get a bit better at writing and communicating.

I wanted to let everyone who lands on these articles that I have migrated everything over to a self hosted blog (for a bunch of reasons that I won’t get into here). So please take a look at https://holdenrehg.com/blog for all of the articles or https://holdenrehg.com/blog/2016-06-04_odoo-it-depends to specifically see this article.

Thanks again! — Holden

When Odoo refactored their API and caused forums to be fill with “new api” “old api” talk, I feel that it really increased the ease of use for developers. Many of the powerful features and tools built into Odoo became even simpler to understand. It also became simpler to write poorly performing code.

It also became simpler to write poorly performing code.

In this article I’m going to break down an example specifically for Odoo’s compute functions that show how quickly a dependency chain can get out of hand and affect the entire system. Let’s take a look at defining a simple class.

This is a class for defining groups of partners. It has a name for the group and a reference to all the partners in the group. Simple enough. No performance issues yet.

Lets add a couple more classes.

Now we’ve gotten a small system for defining late invoices and for checking if any given partner has a late invoice. There are two compute functions. One on each class and each has a single dependency. We can start mapping out what our dependency chain looks like.

res.partner:has_late_bills => res.partner:invoice_ids
account.invoice:is_late => account.invoice:date_invoice

Every time an invoice is updated on a partner it computes the partner’s has_late_bills field and every time an invoice’s date_invoice field is updated it recomputes the invoice’s is_late field.

Again, this is pretty simple. Still no performance issues yet. Let’s update our partner group class to calculate if an entire group has any late bills.

Here’s where it gets interested

This includes a single mistake that can cause degraded performance and even unnecessary database queries (when using stored=True on compute functions) depending on how many concurrent users are accessing the system, the amount of invoices, the number of partners per partner group, etc.

The groups has_late_bills depends on both the partners has_late_bills field and the partners invoices ids which is not necessary. Our dependency chain get’s slightly more complicated.

res.partner:has_late_bills       => invoice_ids
account.invoice:is_late => date_invoice
res.partner.group:has_late_bills => partner_ids.has_late_bills
res.partner.group:has_late_bills => partner_ids.invoice_ids

By creating a single invoice, it fires the following compute actions:

  • Create invoice
  • Compute account.invoice:is_late because date_invoice updated
  • Compute res.partner:has_late_bills because the partner has a new invoice
  • Compute res.partner.group:has_late_bills if one of the partners has a new has_late_bills value after recomputing it in the line above
  • Compute res.partner.group:has_late_bills because the one of the partners in the group has a new invoice

The last two actions are duplicates of each other. There is no reason why this needs to fire two times and can be resolved by updating our depends decorator.

@api.depends(‘partner_ids.has_late_bills’)

This may not be a huge problem in this specific example when updating a single invoice. It performs 4 operations instead of 3, but a 25% increase in computation starts to hurt at scale. Bulk editing 100 invoices turns into 400 operations instead of 300. 5 concurrent users bulk editing 100 invoices turns into 2000 operations instead of 1500.

In Conclusion

Think about this example within larger custom modules. This has 3 classes and 3 computed fields. In modules that have dozens or hundred of classes that use unnecessary dependencies on compute functions could severely hurt user experience.

Note: Watch out for stored, computed fields that cause both a computation and database write.

Luckily these are easy to track down! Log statements are your friend, and drawing simple dependency chain diagrams can allow you to cross out paths that already exists.

Best of luck Odoods!

--

--

Holden Rehg

🧑‍💻 Software Freelancer and Consultant | Works mostly with #odoo and #python web apps | Writes at http://holdenrehg.com/blog