Skip to content

[ADD] speedup sale migration by prepopulating computed fields - #710

Closed
hbrunn wants to merge 6 commits into
OCA:9.0from
hbrunn:9.0-sale_speedup
Closed

hbrunn wants to merge 6 commits into
OCA:9.0from
hbrunn:9.0-sale_speedup

Conversation

@hbrunn

@hbrunn hbrunn commented Dec 26, 2016

Copy link
Copy Markdown
Member

Description of the issue/feature this PR addresses:

Current behavior before PR:

Desired behavior after PR is merged:

--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr

@hbrunn hbrunn added this to the 9.0 milestone Dec 26, 2016

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be ids[x[0] for x in ...]?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, look closely for the comma

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, a bit weird syntax, but OK, you're right

@hbrunn hbrunn Dec 26, 2016

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the weird syntax is actually slightly faster - compare

python -m timeit '[x for x, _ in [(y, y) for y in range(100000)]]'

and

python -m timeit '[x[0] for x in [(y, y) for y in range(100000)]]'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted for future list comprehension that I make.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can also be helpful in a normal loop when you iterate over a huge result set, but do quite little per iteration. Then

for a, b, c, d in cr.fetchall()

performs better than

for row in cr.fetchall()

if within the loop, all values of row are used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info. The problem is if the list if very huge or you don't know all possible values (in a select * statement), but obviously that cases can be always contained.

@NL66278

NL66278 commented Dec 29, 2016

Copy link
Copy Markdown

It might speed things up even more if you first add all the fields to a table and then fill the values with one statement. Now you have to go several times to all or most of the sale orders and lines.

@hbrunn

hbrunn commented Dec 29, 2016

Copy link
Copy Markdown
Member Author

yes, good point.

But at the moment I'm busy with a general speedup of recomputations of which I promise myself much more performance gains, because something is fishy here. It runs currently for testing purposes, afterwards I'll propose this to upstream, OCB and here hbrunn/OCB@25e5f95

There are two problems with current recomputation: The time used grows nonlinear, that's a problem with my 300k sale order lines. Additionally, what makes this hard to debug is that there's some nondeterminism going on, two times recomputing the same field on the same rows yields a different execution path (or the same, that's up to chance).

@NL66278

NL66278 commented Jan 13, 2017

Copy link
Copy Markdown

Apart from the general rewrite of recomputation you might also look at this PR: #735
Allows to just stop the expensive recomputations, while having Odoo create the fields themselves. Consequence is that the actual data_migration must move to the post_migration

cr.execute(
"""\
update sale_order_line
set qty_to_invoice=product_uom_qty - qty_invoiced

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hbrunn This sql sentence is not correct. It does not replicate the same conditions as https://github.com/odoo/odoo/blob/9.0/addons/sale/sale.py#L516

For example, in products where the invoice policy is set based on delivered quantities the quantity to invoice is to be based on the quantity delivered - quantity invoiced.

"""\
update sale_order_line
set invoice_status=(
case when qty_to_invoice<=0 then 'invoiced'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with case when coalesce(qty_to_invoice,0)<=0 then 'invoiced'

"""\
update sale_order_line
set qty_to_invoice=product_uom_qty - qty_invoiced
where qty_invoiced is not null

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This where condition is not correct. Instead use:

update sale_order_line
set qty_to_invoice=product_uom_qty - coalesce(qty_invoiced,0)

This issue has been giving me a hard time.

@hbrunn

hbrunn commented Apr 4, 2017

Copy link
Copy Markdown
Member Author

@jbeficent thanks for you input, done. Next time, just create a PR

@pedrobaeza

Copy link
Copy Markdown
Member

What is the status of this?

@hbrunn

hbrunn commented May 8, 2017

Copy link
Copy Markdown
Member Author

I'm waiting for @jbeficent to test the last commit where I integrated his points

@pedrobaeza

Copy link
Copy Markdown
Member

@jbeficent can you review? Travis is red anyway.

"""\
update sale_order_line
set qty_to_invoice=case
when pt.invoice_policy = 'order' then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invoice_policy field in product.template doesn't exists in pre-migration.py because it's a new field in 9.0 version If you move this query to post-migrations it works

@pedrobaeza

Copy link
Copy Markdown
Member

@jbeficent please feedback about this PR.

@hbrunn check last comment and red status

@hbrunn

hbrunn commented Aug 8, 2017

Copy link
Copy Markdown
Member Author

@pedrobaeza last comment defeats the purpose of the pr, the code is fixed but travis is acting up

@pedrobaeza pedrobaeza closed this Aug 8, 2017
@pedrobaeza pedrobaeza reopened this Aug 8, 2017
@pedrobaeza

Copy link
Copy Markdown
Member

Again the TRAVIS_COMMIT error, so I close and reopen the PR for forcing a correct build.

def prepopulate_fields(cr):
""" Recomputing a fields will be very expensive via the ORM, so do it
here for fields where the computation is trivial"""
cr.execute('alter table sale_order_line add column qty_invoiced numeric')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ADD set default=0

set qty_invoiced=line2qty_invoiced.qty_invoiced
from line2qty_invoiced where line2qty_invoiced.id=sale_order_line.id
""")
cr.execute('alter table sale_order_line add column qty_to_invoice numeric')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ADD set default=product_uom_qty

@mvaled mvaled Sep 29, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About this PG says: cannot use column references in default expression

@mvaled mvaled Sep 29, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From PG doc:

DEFAULT default_expr

The DEFAULT clause assigns a default data value for the column whose column definition it appears within. The value is any variable-free expression (subqueries and cross-references to other columns in the current table are not allowed). The data type of the default expression must match the data type of the column.

The default expression will be used in any insert operation that does not specify a value for the column. If there is no default for a column, then the default is null.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I didn't know. Then we should still keep here 2 sentences

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working on hbrunn#5. I hope next week this can be merged.

where l.order_id=o.id
""")
cr.execute('alter table sale_order_line add column invoice_status varchar')
cr.execute("update sale_order_line set invoice_status='no'")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, just add in the previous statement set default='no'

where state in ('sale', 'done')
""")
cr.execute('alter table sale_order add column invoice_status varchar')
cr.execute("update sale_order set invoice_status='no'")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same (set default='no')

@mvaled

mvaled commented Sep 27, 2017

Copy link
Copy Markdown

Should we merge this?

@mvaled

mvaled commented Sep 27, 2017

Copy link
Copy Markdown

I'm testing this today. I'll be reporting later (tomorrow perhaps)

@mvaled

mvaled commented Sep 27, 2017

Copy link
Copy Markdown

TL;DR It seems this is incorrect.

Experiment

I took a DB with Odoo 8, and clone it twice:

createdb -T o8 o9base
createdb -T o8 o9speedy

Then I migrated the o9base without this patch (https://github.com/merchise-autrement/openupgrade/tree/merchise-develop-9.0) and the o9speedy with the patch (https://github.com/merchise-autrement/OpenUpgrade/tree/merchise-9.0-sale-speedup).

However comparing the resulting sale_order_line and sale_order gives a lot of differences:

For instance, the field sale.order.line.qty_invoiced is different in almost all lines:

$ diff -u <(psql -d o9speedy -Ac 'select id, qty_invoiced from sale_order_line where order by id;') <(psql -d o9base -Ac 'select id, qty_invoiced from sale_order_line order by id;') | wc -l
191365

The first thing I notice is that where the o9base get 0, o9speedy gets NULL. Maybe this is not a problem. Because we can always do UPDATE sale_order_line SET qty_invoiced=0 WHERE qty_invoiced IS NULL. Let's do it:

$ psql -d o9speedy -c "UPDATE sale_order_line SET qty_invoiced=0 WHERE qty_invoiced IS NULL"
UPDATE 62646

However, we still have a lot of differences:

$ diff -u <(psql -d o9speedy -Ac 'select id, qty_invoiced from sale_order_line order by id;') <(psql -d o9base -Ac 'select id, qty_invoiced from sale_order_line order by id;') | wc -l
82575

Let's see some records that differ:

-38|15.0000000000000000
-39|1.00000000000000000000
+38|0
+39|0

For the record 38, the speedier version computes 15 while the normal version computes 0. Other fields show as much differences as well. I'll try to look at these differences and see if I can spot the issue.

The Python implementation tests for the invoice's type being other than
'cancel'.
@mvaled

mvaled commented Sep 28, 2017

Copy link
Copy Markdown

I pushed 8625c10 to fix the qty_invoiced amount.

@hbrunn

hbrunn commented Sep 28, 2017

Copy link
Copy Markdown
Member Author

can you PR this to this PR's branch? Then I'll merge this in this PR. If you want to speed up getting this merged, you can check @pedrobaeza's comments to. I won't have time to address them any time soon.

@mvaled

mvaled commented Sep 28, 2017

Copy link
Copy Markdown

I already did: hbrunn#4

[FIX] SQL computation for sale.order.line.qty_invoice.
@mvaled

mvaled commented Sep 28, 2017

Copy link
Copy Markdown

I'll be addressing @pedrobaeza's comments soon.

@mvaled

mvaled commented Sep 28, 2017

Copy link
Copy Markdown

See PR hbrunn#5

@mvaled

mvaled commented Sep 29, 2017 via email

Copy link
Copy Markdown

@mvaled

mvaled commented Sep 29, 2017

Copy link
Copy Markdown

Still working on this. The field qty_to_invoice shows differences.

@pedrobaeza

Copy link
Copy Markdown
Member

Any news on this?

@mvaled

mvaled commented Oct 23, 2017

Copy link
Copy Markdown

I've been unable to keep working on this for the past weeks. We're heavily busy migrating ~50 addons to Odoo 9 and then to Odoo 10. After they all migrated to 9, I could try another round of DB migration and see if I could work on this.

@pedrobaeza

Copy link
Copy Markdown
Member

OK, let me know. I don't see this as critical because times are more or less manageable in my cases, but maybe with a lot of sale orders it's not the same.

@pedrobaeza

Copy link
Copy Markdown
Member

Closing this due to errors on the migration. If anyone wants to continue this experiment, can take this code and go on with the needed changes, proposing the new alternative.

@pedrobaeza pedrobaeza closed this Jun 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants