This guide provides an essential deep dive into Odoo prefetching and caching, the two cornerstone technologies that power high-performance Odoo applications. Many developers struggle with slow-downs without realizing that Odoo provides a powerful, built-in solution to common performance bottlenecks. Consequently, by understanding how the framework intelligently handles data, you can write cleaner, faster, and more scalable code. This tutorial will break down these complex mechanisms into simple, step-by-step explanations, empowering you to unlock the true speed of your Odoo modules.
Understanding Odoo’s Core Speed: The ORM Pillars
First, you should know that Odoo’s remarkable speed is not an accident. Instead, it is the direct result of intentional engineering within its Object-Relational Mapping (ORM). The ORM acts as a bridge between your Python code and the SQL database. Furthermore, it includes two foundational pillars designed to minimize database interactions and accelerate data access: caching and prefetching. For any serious Odoo developer, understanding these concepts is not optional; it is fundamental. Ultimately, mastering Odoo prefetching and caching allows you to work with the framework, leveraging its full potential to build applications that are fast by default.
A Deep Dive into Odoo’s Caching System
The first pillar of Odoo’s performance is its sophisticated, multi-layered caching system. In essence, caching is a strategy for storing frequently accessed data in a temporary, high-speed memory location. This simple principle has a profound impact on application responsiveness.
The Fundamental Role of Caching in Odoo
To begin, caching in Odoo works by keeping a copy of data that has already been retrieved from the database. For example, when you first request a partner’s record, Odoo fetches it from the database and simultaneously stores it in a cache. Subsequently, if you request that same partner record again within the same transaction, Odoo instantly pulls it from the high-speed cache instead of performing another costly database query. As a result, this process dramatically reduces the load on the database and delivers near-instantaneous data access for repeated requests. This core function is a key part of Odoo prefetching and caching.
Exploring the Layered Odoo Cache Architecture
Moreover, Odoo employs a clever, layered approach to its cache. You can think of it as having multiple levels of memory, each optimized for a specific task.
- Recordset Cache (
lru_cache): This is the primary cache you will interact with. Odoo uses a Least Recently Used (LRU) cache for recordsets themselves. Therefore, whenever you browse or read a record, it is placed in this memory. This ensures that the data you are actively working with remains immediately available. - General Cache: Beyond record-specific data, Odoo also maintains a general cache for all data within a single transaction. Every piece of information read from the database is stored here. Consequently, this transactional cache guarantees data consistency and speed for the duration of a user’s operation.
A Step-by-Step Example of Caching in Action
To illustrate, let’s see how you can observe this behavior.
Step 1: Enable SQL Logging in Odoo
First, you must start your Odoo server with logging enabled to see the SQL queries. You can do this by adding --log-level=debug_sql to your startup command.
Step 2: Write a Simple Test Method
Next, create a simple method in one of your models to fetch the same record twice.
from odoo import models, api
class MyDemoModel(models.Model):
_name = 'my.demo.model'
def test_caching_behavior(self):
# Find a specific partner record to test with
# Make sure a partner with this ID exists in your database
partner = self.env['res.partner'].browse(1)
# First access: This should trigger a SELECT query
print(f"First access: {partner.name}")
# Second access: This should NOT trigger another query
# Odoo retrieves the name from the cache
print(f"Second access: {partner.name}")
Step 3: Run and Observe the Logs
Now, when you execute this method, you should check your Odoo server logs. You will notice that Odoo generates a SELECT SQL query for the first time partner.name is accessed. However, for the second access, no new SQL query appears. This silence in the logs is proof that Odoo served the data directly from its cache, demonstrating the efficiency of the system.
The “Magic” of Odoo Prefetching Unveiled
The second, and arguably more “magical,” pillar of Odoo performance is prefetching. This mechanism is Odoo’s brilliant solution to one of the most common performance killers in ORM-based applications: the N+1 query problem. Understanding this concept is critical for writing efficient code that handles large datasets.
Conquering the Dreaded N+1 Query Problem
First, let’s define the problem. The N+1 query problem occurs when your code retrieves a list of ‘N’ items and then loops through them, performing an additional database query for each item to fetch related data.
Imagine you want to display a list of 100 sales orders and the name of the customer for each order. A naive approach would look like this:
- Query 1:
SELECT * FROM sale_order LIMIT 100;(This fetches 100 orders) - Looping N times (100 times):
SELECT name FROM res_partner WHERE id = ?;(Query for customer of order 1)SELECT name FROM res_partner WHERE id = ?;(Query for customer of order 2)- …and so on for all 100 orders.
In total, this process results in 101 separate database queries. This inefficiency quickly slows the application to a crawl as the number of records grows.
Odoo’s Intelligent Prefetching Engine
Here is where Odoo shines. The framework’s ORM is smart enough to anticipate this pattern. When it detects that you are iterating over a recordset to access data on a related model (like a Many2one field), it doesn’t wait for you to ask for each record individually.
Instead, Odoo’s prefetching engine does the following:
- It collects all the IDs of the related records it will need (e.g., all the
partner_idvalues from the 100 sale orders). - It then performs a single, large
SELECTquery to fetch all the required partner records at once (e.g.,SELECT * FROM res_partner WHERE id IN (id1, id2, ..., id100);). - Finally, it loads these records into the cache.
As you loop through your sale orders in Python, the customer data is already waiting in memory, so no additional database queries are needed. Odoo effectively transforms an N+1 query disaster into just 2 highly efficient queries. This automated optimization is a core benefit of the Odoo prefetching and caching system.
A Practical Prefetching Code Example
Let’s look at how you would write code that automatically triggers this feature.
# Assuming you have a list of sale order records
sale_orders = self.env['sale.order'].search([], limit=100)
# This loop automatically triggers prefetching
for order in sale_orders:
# Odoo does NOT run a new query inside this loop.
# It prefetched all customer names after the search.
print(f"Order: {order.name}, Customer: {order.partner_id.name}")
In this example, when you call the search method, you get a recordset of 100 orders. The moment your for loop begins and accesses order.partner_id.name for the first time, the prefetching mechanism kicks in. It fetches the data for all 100 partners in one go, ensuring the rest of the loop runs without hitting the database at all.
Best Practices for Maximum Performance
To truly benefit from Odoo prefetching and caching, you must follow certain best practices. These habits ensure your code aligns with the framework’s optimization strategies.
Always Trust, But Verify, the ORM
First and foremost, you should use standard ORM methods (search, browse, mapped, field access) for your data operations. These methods are designed to be fully compatible with the caching and prefetching engines. However, you should also develop the habit of verifying the results. Always use Odoo’s SQL logging to inspect the queries your code generates. This practice helps you spot inefficiencies and confirm that prefetching is working as expected.
Supercharge Prefetching with Smart Data Loading
Additionally, you can give the prefetching engine a helping hand. While prefetching is often automatic, you can make it even more explicit and powerful by loading all the data you need before starting a complex process or loop. The .mapped() method is perfect for this.
Don’t Do This (Potentially Inefficient):
invoices = self.env['account.move'].search([('state', '=', 'posted')])
total_tax = 0
for inv in invoices:
# Accessing related data inside a loop can sometimes be less clear
for line in inv.invoice_line_ids:
for tax in line.tax_ids:
total_tax += tax.amount # Accessing a field deep in a relation
Do This (Highly Efficient):
invoices = self.env['account.move'].search([('state', '=', 'posted')])
# Use mapped() to pre-load all tax amounts in a single, optimized way
all_tax_amounts = invoices.mapped('invoice_line_ids.tax_ids.amount')
# Now, perform the calculation on data that is already in memory
total_tax = sum(all_tax_amounts)
By using .mapped(), you explicitly tell Odoo to traverse the relationships and gather all the final values (amount) at once. This single line of code can replace dozens of individual queries, resulting in a massive performance gain.
For further details on ORM methods, you can always consult the official Odoo ORM API documentation.
Remember the Transactional Scope of the Cache
Finally, it is crucial to remember that the cache is tied to the current transaction. Once the transaction concludes (e.g., a web request is finished, or a server action completes), Odoo discards the cache. This design ensures data consistency and prevents old data from being served in a new operation. Therefore, you cannot rely on the cache to persist data between separate user actions. Its purpose is to optimize performance within a single, continuous operation.
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.


Pingback: Amazing 7 Odoo Troubleshooting Hacks