Skip to content

Commit b7117b0

Browse files
committed
Add test module perf
1 parent 93f0ea8 commit b7117b0

File tree

2 files changed

+131
-44
lines changed

2 files changed

+131
-44
lines changed

expression_performance/models/perf_models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,13 @@ class PerfLine(models.Model):
6666
def _custom_populate_factories(self, rng: Random):
6767
ids_container = self.env['perf.container'].search([])._ids
6868
generator_ab = super()._custom_populate_factories(rng)
69-
for __ in itertools.count():
69+
for i in itertools.count():
70+
parent_id = False
71+
if i > 1 and rng.random() > 0.01: # 25 % of set
72+
parent_id = rng.randint(1, i-1)
7073
yield next(generator_ab) | {
7174
'uniform_container_id': rng.choice(ids_container),
75+
'parent_id': parent_id,
7276
}
7377

7478

expression_performance/tests/test_sql.py

Lines changed: 126 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ def get_db_name():
1616
return db
1717

1818

19+
ratios_possible = [
20+
500, # 50 %
21+
50, # 5 %
22+
5, # 0.5 %
23+
0.5, # 0.05 %
24+
0.01, # 0.001 %
25+
]
26+
27+
1928
class TestPerformanceSQL(BaseCase):
2029

2130
@classmethod
@@ -31,7 +40,7 @@ def setUpClass(cls):
3140

3241
cls.env = api.Environment(cls.cr, odoo.SUPERUSER_ID, {})
3342

34-
def populate(self, nb_tag=0, nb_container=100_000, nb_line=500_000):
43+
def populate(self, nb_tag=0, nb_container=10_000, nb_line=50_000):
3544
models = {
3645
'perf.tag': nb_tag,
3746
'perf.container': nb_container,
@@ -41,23 +50,31 @@ def populate(self, nb_tag=0, nb_container=100_000, nb_line=500_000):
4150
self.env[model]._custom_populate(nb)
4251

4352
self.env.cr.execute('ANALYSE;')
53+
self.env.cr.commit()
4454

4555
def create_indexes_many2one(self):
4656
self.env.cr.execute("CREATE INDEX IF NOT EXISTS perf_line_uniform_container_id ON perf_line(uniform_container_id)")
4757
self.env.cr.execute("CREATE INDEX IF NOT EXISTS perf_line_parent_id ON perf_line(parent_id)")
4858
self.env.cr.commit()
4959

50-
def delete_all_indexes(self):
60+
def create_indexes_uniform(self):
61+
for table in ['perf_line', 'perf_container', 'perf_tag']:
62+
self.env.cr.execute(f"CREATE INDEX IF NOT EXISTS {table}_float_uniform_1000 ON {table}(float_uniform_1000)")
63+
self.env.cr.commit()
64+
65+
def delete_indexes_uniform(self):
5166
for table in ['perf_line', 'perf_container', 'perf_tag']:
5267
self.env.cr.execute(f"DROP INDEX IF EXISTS {table}_float_uniform_1000")
68+
self.env.cr.commit()
5369

70+
def delete_indexes_many2one(self):
5471
self.env.cr.execute("DROP INDEX IF EXISTS perf_line_uniform_container_id")
72+
self.env.cr.execute("DROP INDEX IF EXISTS perf_line_parent_id")
5573
self.env.cr.commit()
5674

57-
def create_indexes_uniform(self):
58-
for table in ['perf_line', 'perf_container', 'perf_tag']:
59-
self.env.cr.execute(f"CREATE INDEX IF NOT EXISTS {table}_float_uniform_1000 ON {table}(float_uniform_1000)")
60-
self.env.cr.commit()
75+
def delete_all_indexes(self):
76+
self.delete_indexes_uniform()
77+
self.delete_indexes_many2one()
6178

6279
def test_create_indexes_many2one(self):
6380
self.create_indexes_many2one()
@@ -68,19 +85,23 @@ def test_create_indexes_uniform(self):
6885
def test_delete_all_indexes(self):
6986
self.delete_all_indexes()
7087

71-
def test_one_level_condition(self):
72-
self.populate()
88+
def test_truncate_everything(self):
89+
print('Truncate everything')
90+
for table in ['perf_line', 'perf_container', 'perf_tag']:
91+
self.env.cr.execute(f'TRUNCATE "{table}" RESTART IDENTITY CASCADE')
7392
self.env.cr.commit()
7493

75-
ratios_possible = [
76-
500, # 50 %
77-
50, # 5 %
78-
5, # 0.5 %
79-
0.5, # 0.05 %
80-
0.01, # 0.001 %
81-
]
94+
def test_one_sub_level_1_and(self):
95+
self.test_truncate_everything()
96+
self.populate()
97+
98+
def domain_like(args):
99+
return [
100+
'&',
101+
('float_uniform_1000', '<', args[0]),
102+
('uniform_container_id.float_uniform_1000', '<', args[1]),
103+
]
82104

83-
# For [<condition_1> line AND <condition_2> line.parent_id] on line
84105
queries = {
85106
'join': """
86107
SELECT "perf_line"."id"
@@ -91,6 +112,7 @@ def test_one_level_condition(self):
91112
"perf_container"."float_uniform_1000" < %s
92113
ORDER BY "perf_line"."id"
93114
""",
115+
94116
'subselect': """
95117
SELECT "perf_line"."id"
96118
FROM "perf_line"
@@ -99,6 +121,7 @@ def test_one_level_condition(self):
99121
"perf_line"."uniform_container_id" IN (SELECT "perf_container"."id" FROM "perf_container" WHERE "perf_container"."float_uniform_1000" < %s)
100122
ORDER BY "perf_line"."id"
101123
""",
124+
102125
'exists': """
103126
SELECT "perf_line"."id"
104127
FROM "perf_line"
@@ -109,31 +132,24 @@ def test_one_level_condition(self):
109132
""",
110133
}
111134

112-
for line_ratio, container_ratio in combinations_with_replacement(ratios_possible, 2):
113-
arguments = [line_ratio, container_ratio]
114-
res = None
115-
for name, query in queries.items():
116-
self.env.cr.execute(query, arguments)
117-
res_new = [id_ for id_, in self.env.cr.fetchall()]
118-
if res and res != res_new:
119-
raise Exception(f'{name} not the same result than the previous one')
135+
self.launch_queries(
136+
'Level 1 - AND',
137+
queries,
138+
combinations_with_replacement(ratios_possible, 2),
139+
domain_like,
140+
)
120141

121-
explain_dict = {}
122-
for name, query in queries.items():
123-
self.env.cr.execute("EXPLAIN " + query, arguments)
124-
explain_dict[name] = "\n".join(s for s, in self.env.cr.fetchall())
142+
def test_one_sub_level_1_or(self):
143+
self.test_truncate_everything()
144+
self.populate()
125145

126-
if len(set(explain_dict.values())) == 1:
127-
print("AND - For arguments - All the same", arguments)
128-
continue
129-
130-
print()
131-
print('AND - For arguments', arguments)
132-
for name, explain in explain_dict.items():
133-
print(f'=> {name} : ')
134-
print(explain)
146+
def domain_like(args):
147+
return [
148+
'|',
149+
('float_uniform_1000', '<', args[0]),
150+
('uniform_container_id.float_uniform_1000', '<', args[1]),
151+
]
135152

136-
# For [<condition_1> line OR <condition_2> line.parent_id] on line
137153
queries = {
138154
'join': """
139155
SELECT "perf_line"."id"
@@ -144,6 +160,7 @@ def test_one_level_condition(self):
144160
"perf_container"."float_uniform_1000" < %s
145161
ORDER BY "perf_line"."id"
146162
""",
163+
147164
'subselect': """
148165
SELECT "perf_line"."id"
149166
FROM "perf_line"
@@ -152,6 +169,7 @@ def test_one_level_condition(self):
152169
"perf_line"."uniform_container_id" IN (SELECT "perf_container"."id" FROM "perf_container" WHERE "perf_container"."float_uniform_1000" < %s)
153170
ORDER BY "perf_line"."id"
154171
""",
172+
155173
'exists': """
156174
SELECT "perf_line"."id"
157175
FROM "perf_line"
@@ -162,25 +180,90 @@ def test_one_level_condition(self):
162180
""",
163181
}
164182

165-
for line_ratio, container_ratio in combinations_with_replacement(ratios_possible, 2):
166-
arguments = [line_ratio, container_ratio]
183+
self.launch_queries(
184+
'Level 1 - OR',
185+
queries,
186+
combinations_with_replacement(ratios_possible, 2),
187+
domain_like,
188+
)
189+
190+
def test_two_level_condition(self):
191+
self.test_truncate_everything()
192+
self.populate()
193+
194+
def domain_like(args):
195+
return [
196+
('parent_id.uniform_container_id.float_uniform_1000', '<', args[0])
197+
]
198+
199+
queries = {
200+
'join': """
201+
SELECT "perf_line"."id"
202+
FROM "perf_line"
203+
LEFT JOIN "perf_line" AS "perf_line__parent_id" ON "perf_line__parent_id"."id" = "perf_line"."parent_id"
204+
LEFT JOIN "perf_container" ON "perf_line__parent_id"."uniform_container_id" = "perf_container"."id"
205+
WHERE
206+
"perf_container"."float_uniform_1000" < %s
207+
ORDER BY "perf_line"."id"
208+
""",
209+
210+
'subselect': """
211+
SELECT "perf_line"."id"
212+
FROM "perf_line"
213+
WHERE
214+
"perf_line"."parent_id" IN (
215+
SELECT "perf_line"."id" FROM "perf_line"
216+
WHERE "perf_line"."uniform_container_id" IN (
217+
SELECT "perf_container"."id" FROM "perf_container"
218+
WHERE "perf_container"."float_uniform_1000" < %s
219+
)
220+
)
221+
ORDER BY "perf_line"."id"
222+
""",
223+
224+
'exists': """
225+
SELECT "perf_line"."id"
226+
FROM "perf_line"
227+
WHERE
228+
EXISTS (
229+
SELECT FROM "perf_line" AS "perf_line__parent_id"
230+
WHERE EXISTS (
231+
SELECT FROM "perf_container"
232+
WHERE "perf_container"."float_uniform_1000" < %s AND "perf_line__parent_id"."uniform_container_id" = "perf_container"."id"
233+
) AND "perf_line"."parent_id" = "perf_line__parent_id"."id"
234+
)
235+
ORDER BY "perf_line"."id"
236+
""",
237+
}
238+
239+
self.launch_queries(
240+
'Level 2 - For arguments',
241+
queries,
242+
combinations_with_replacement(ratios_possible, 1),
243+
domain_like,
244+
)
245+
246+
def launch_queries(self, name_test, queries, args_combinaison, domain_like: callable):
247+
for arguments in args_combinaison:
167248
res = None
168249
for name, query in queries.items():
169250
self.env.cr.execute(query, arguments)
170251
res_new = [id_ for id_, in self.env.cr.fetchall()]
171252
if res and res != res_new:
172-
raise Exception(f'{name} not the same result than the previous one')
253+
raise Exception(f'{name} not the same result than the previous one: {name_test}')
254+
res = res_new
173255

174256
explain_dict = {}
175257
for name, query in queries.items():
176258
self.env.cr.execute("EXPLAIN " + query, arguments)
177259
explain_dict[name] = "\n".join(s for s, in self.env.cr.fetchall())
178260

179-
if set(explain_dict.values()) == 1:
261+
if len(set(explain_dict.values())) == 1:
262+
print(f"{name_test}: {domain_like(arguments)} : ALL the same")
180263
continue
181264

182265
print()
183-
print('OR - For arguments', arguments)
266+
print(f"{name_test}: {domain_like(arguments)}")
184267
for name, explain in explain_dict.items():
185268
print(f'=> {name} : ')
186269
print(explain)

0 commit comments

Comments
 (0)