[IMP] test_new_api: add performance test for modified.

Part-of: odoo/odoo#99274
This commit is contained in:
Rémy Voet (ryv)
2022-09-02 23:16:06 +02:00
parent 52ef83e006
commit 54d75a28cd
3 changed files with 157 additions and 0 deletions
@@ -1658,3 +1658,42 @@ class Prefetch(models.Model):
ron = fields.Float('Ron Weasley', prefetch='Harry Potter')
hansel = fields.Integer('Hansel', prefetch="Hansel and Gretel")
gretel = fields.Char('Gretel', prefetch="Hansel and Gretel")
class Modified(models.Model):
_name = 'test_new_api.modified'
_description = 'A model to check modified trigger'
name = fields.Char('Name')
line_ids = fields.One2many('test_new_api.modified.line', 'modified_id')
total_quantity = fields.Integer(compute='_compute_total_quantity')
@api.depends('line_ids.quantity')
def _compute_total_quantity(self):
for rec in self:
rec.total_quantity = sum(rec.line_ids.mapped('quantity'))
class ModifiedLine(models.Model):
_name = 'test_new_api.modified.line'
_description = 'A model to check modified trigger'
modified_id = fields.Many2one('test_new_api.modified')
modified_name = fields.Char(related="modified_id.name")
quantity = fields.Integer()
price = fields.Float()
total_price = fields.Float(compute='_compute_total_quantity', recursive=True)
total_price_quantity = fields.Float(compute='_compute_total_price_quantity')
parent_id = fields.Many2one('test_new_api.modified.line')
child_ids = fields.One2many('test_new_api.modified.line', 'parent_id')
@api.depends('price', 'child_ids.total_price', 'child_ids.price')
def _compute_total_quantity(self):
for rec in self:
rec.total_price = sum(rec.child_ids.mapped('total_price')) + rec.price
@api.depends('total_price', 'quantity')
def _compute_total_price_quantity(self):
for rec in self:
rec.total_price_quantity = rec.total_price * rec.quantity
@@ -94,3 +94,5 @@ access_test_new_api_precompute_readonly,access_test_new_api_precompute_readonly,
access_test_new_api_precompute_required,access_test_new_api_precompute_required,model_test_new_api_precompute_required,,1,0,0,0
access_test_new_api_precompute_monetary,access_test_new_api_precompute_monetary,model_test_new_api_precompute_monetary,,1,0,0,0
access_test_new_api_prefetch,access_test_new_api_prefetch,model_test_new_api_prefetch,,1,0,0,0
access_test_new_api_modified,access_test_new_api_modified,model_test_new_api_modified,base.group_user,1,1,1,1
access_test_new_api_modified_line,access_test_new_api_modified_line,model_test_new_api_modified_line,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
94 access_test_new_api_precompute_required access_test_new_api_precompute_required model_test_new_api_precompute_required 1 0 0 0
95 access_test_new_api_precompute_monetary access_test_new_api_precompute_monetary model_test_new_api_precompute_monetary 1 0 0 0
96 access_test_new_api_prefetch access_test_new_api_prefetch model_test_new_api_prefetch 1 0 0 0
97 access_test_new_api_modified access_test_new_api_modified model_test_new_api_modified base.group_user 1 1 1 1
98 access_test_new_api_modified_line access_test_new_api_modified_line model_test_new_api_modified_line base.group_user 1 1 1 1
@@ -4085,3 +4085,119 @@ class TestPrecompute(common.TransactionCase):
]
with self.assertQueries(QUERIES):
model.create({})
class TestModifiedPerformance(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.Modified = cls.env['test_new_api.modified']
cls.ModifiedLine = cls.env['test_new_api.modified.line']
cls.modified_a = cls.Modified.create({
'name': 'Test',
})
cls.modified_line_a = cls.ModifiedLine.create({
'modified_id': cls.modified_a.id,
'quantity': 5,
'price': 1,
})
cls.modified_line_a_child = cls.ModifiedLine.create({
'modified_id': cls.modified_a.id,
'quantity': 5,
'price': 2,
'parent_id': cls.modified_line_a.id,
})
cls.modified_line_a_child_child = cls.ModifiedLine.create({
'modified_id': cls.modified_a.id,
'quantity': 5,
'price': 3,
'parent_id': cls.modified_line_a_child.id,
})
cls.env.invalidate_all() # Clean the cache
def test_modified_trigger_related(self):
with self.assertQueries(["""
SELECT "test_new_api_modified_line".id
FROM "test_new_api_modified_line"
WHERE ("test_new_api_modified_line"."modified_id" IN %s)
ORDER BY "test_new_api_modified_line"."id"
""",
"""
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id"
FROM "test_new_api_modified_line"
WHERE "test_new_api_modified_line".id IN %s
"""], flush=False):
# Two queries to fetch the `line_ids` in order to invalidate the cache for `modified_name`
# (even if the cache is empty for this field)
self.modified_a.name = "Other"
self.assertEqual(self.modified_line_a.modified_name, 'Other') # check
def test_modified_trigger_no_store_compute(self):
with self.assertQueries(["""
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
"test_new_api_modified_line"."write_date" AS "write_date"
FROM "test_new_api_modified_line"
WHERE "test_new_api_modified_line".id IN %s
"""], flush=False):
# One query to fetch `modified_id` in order to invalidate `total_quantity`
self.modified_line_a.quantity = 8
self.assertEqual(self.modified_a.total_quantity, 18)
def test_modified_trigger_recursive_empty_cache(self):
with self.assertQueries(["""
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
"test_new_api_modified_line"."write_date" AS "write_date"
FROM "test_new_api_modified_line"
WHERE "test_new_api_modified_line".id IN %s
"""], flush=False):
# One query to fetch `parent_id` in order to invalidate `total_price` recursively
# but because no record has already recompute `total_price` no need to invalidate recursivly
self.modified_line_a_child_child.price = 4
self.assertEqual(self.modified_line_a.total_price, 7)
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
self.assertEqual(self.modified_line_a_child.total_price, 6)
self.assertEqual(self.modified_line_a_child.total_price_quantity, 30)
def test_modified_trigger_recursive_fill_cache(self):
self.assertEqual(self.modified_line_a.total_price, 6)
self.assertEqual(self.modified_line_a.total_price_quantity, 30)
with self.assertQueryCount(0, flush=False):
# No query because the `modified_line_a.total_price` has fetch every data needed
self.modified_line_a_child_child.price = 4
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
self.assertEqual(self.modified_line_a.total_price, 7)
def test_modified_trigger_recursive_partial_invalidate(self):
self.assertEqual(self.modified_line_a_child.total_price_quantity, 25)
self.modified_line_a_child_child.invalidate_recordset()
self.modified_line_a_child.price
with self.assertQueries(["""
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
"test_new_api_modified_line"."write_date" AS "write_date"
FROM "test_new_api_modified_line"
WHERE "test_new_api_modified_line".id IN %s
"""] * 2, flush=False):
# Two requests:
# - one for fetch modified_line_a_child_child data (invalidate just before)
# - one because modified_line_a_child.parent_id (invalidate just before because we invalidate inverse in `_invalidate_cache`,
# see TODO) -> We should change that
self.modified_line_a_child_child.price = 4
self.assertEqual(self.modified_line_a_child_child.total_price_quantity, 20)
self.assertEqual(self.modified_line_a_child.total_price_quantity, 30)
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
self.assertEqual(self.modified_line_a.total_price, 7)