浅谈 Odoo 的 @ormcache 装饰器。

1. 缓存的背景

在实际开发中,某些计算可能会消耗大量时间和资源。例如,在计算字段的值时,可能需要执行复杂的逻辑或查询大量数据。为了避免在相同参数下重复执行这些耗时的计算,我们可以使用缓存机制。

2. 使用 @ormcache 装饰器

在 Odoo 中,@ormcache 装饰器是一个强大的工具,它可以应用在方法上,用于缓存方法的结果。通过将它与计算字段结合使用,我们可以实现对计算结果的高效缓存。在修改的方法中清除缓存,保证数据的统一性,下次访问的时候就会从orm里面重新读取

3. 示例代码

以下是一个简单的示例代码,演示了如何在 Odoo 中使用 @ormcache 装饰器结合计算字段:

# my_ormcache_test.py
import time

from odoo import models, api
from odoo.tools import ormcache

class ResPartner(models.Model):
    _inherit = 'res.partner'

    def write(self, vals):
        self.clear_cache()
        return super(ResPartner, self).write(vals)

    @api.model
    @ormcache('partner_name')
    def compute_something(self, partner_name):
        # 模拟耗时操作
        time.sleep(5)
        print('从ORM中读取------')
        return f"{partner_name}"

    def button_compute(self):
        print('从缓存中中读取------')
        ret = self.compute_something(self.name)

    def clear_cache(self):
        self.compute_something.clear_cache(self)

odoo界面截图

优化 Odoo 性能:方法级别缓存的魔力_缓存

在这个例子中,我们定义了一个计算字段 compute_something,并通过 @ormcache 装饰器告诉 Odoo 对这个方法的结果进行缓存。在 button_compute 方法中演示了从缓存中读取数据,而不重新计算。


4. 源码剖析

    def lookup(self, method, *args, **kwargs):
        d, key0, counter = self.lru(args[0])
        key = key0 + self.key(*args, **kwargs)
        try:
            r = d[key]
            counter.hit += 1
            return r
        except KeyError:
            counter.miss += 1
            value = d[key] = self.method(*args, **kwargs)
            return value
        except TypeError:
            _logger.warning("cache lookup error on %r", key, exc_info=True)
            counter.err += 1
            return self.method(*args, **kwargs)
  1. self.lru(args[0]) 返回了一个字典 d、一个缓存键的基础部分 key0 以及一个计数器 counter。这里假设 args[0] 是用于生成缓存键的参数。
  2. key = key0 + self.key(*args, **kwargs) 通过将基础部分和方法的参数计算得到最终的缓存键 key
  3. 接下来,尝试从字典 d 中获取缓存结果:
  • 如果成功命中缓存,则返回缓存的值,并增加计数器的命中次数。
  • 如果在缓存中未找到,捕获 KeyError 异常,表示缓存未命中,增加计数器的未命中次数。然后执行原始方法,并将结果存入缓存。
  1. 如果捕获到 TypeError 异常,表示在缓存查找过程中出现了错误。此时,增加计数器的错误次数,并执行原始方法。

总体而言,这段代码实现了缓存查找的逻辑,如果能够从缓存中找到结果就直接返回,否则执行原始方法,将结果存入缓存。异常处理部分确保即使在缓存查找的过程中出现错误,也能够执行原始方法以确保不影响程序的正常运行。