Very poor performance on window Generate Invoice (manual) with RMA
Description
Environment
Attachments
Activity
Michael Powacht August 5, 2021 at 12:25 PM
Thanks for your info .
Carlos Ruiz August 5, 2021 at 10:24 AM
Hi
Thanks for your information, I got curious why that worked without the index and did some tests.
Firstly filled the table M_RMALine with 200.000 records and checked the execution of both queries:
The old query shows an execution plan with this cost:
Sort (cost=20109.92..20109.93 rows=1 width=546)
The new query shows this cost:
Sort (cost=94.74..94.74 rows=1 width=546)
I tried created an index on M_RMALine.M_RMA_ID but postgres didn't make any use of this new index, so I was wrongly assuming that would help.
Just to be on the safe side I conducted the same test in oracle, and oracle makes the same execution plan for both cases - and answer a lot faster than postgresql.
Regards,
Carlos Ruiz
Michael Powacht August 4, 2021 at 2:28 AM
Thank you Carlos!
I hope you and your family are safe and healthy!
I'm currently running on PG 12.5, but the SQL statement change in InvoiceGen.java was made when still running on PG 10.x with similar performance gains.
I just simulated a full table scan with both query versions, comparing the performance of IN vs. EXISTS using 12.5/PGAdmin and the difference is huge. Here is the query:
IN Statement:
Successfully run. Total query runtime: 12 min 58 secs. 16 rows affected.
EXISTS Statement:
Successfully run. Total query runtime: 1 secs 166 msec. 16 rows affected.
I agree, we should defintiley index foreign keys in line-tables to speed up the performance of parent-child relationships.
To make sure. I've checked all indexes currently in my database for all the tables accessed in the above query using:
tablename | indexname | indexdef |
---|---|---|
c_bpartner | c_bpartner_bporg | CREATE INDEX c_bpartner_bporg ON adempiere.c_bpartner USING btree (ad_orgbp_id) |
c_bpartner | c_bpartner_name | CREATE INDEX c_bpartner_name ON adempiere.c_bpartner USING btree (name) |
c_bpartner | c_bpartner_uu_idx | CREATE UNIQUE INDEX c_bpartner_uu_idx ON adempiere.c_bpartner USING btree (c_bpartner_uu) |
c_bpartner | c_bpartner_pkey | CREATE UNIQUE INDEX c_bpartner_pkey ON adempiere.c_bpartner USING btree (c_bpartner_id) |
c_bpartner | c_bpartner_value | CREATE UNIQUE INDEX c_bpartner_value ON adempiere.c_bpartner USING btree (ad_client_id, value) |
c_bpartner | zi_c_bpartner_taxid | CREATE UNIQUE INDEX zi_c_bpartner_taxid ON adempiere.c_bpartner USING btree (ad_org_id, iscustomer, c_bp_group_id, taxid) |
c_doctype | c_doctype_pkey | CREATE UNIQUE INDEX c_doctype_pkey ON adempiere.c_doctype USING btree (c_doctype_id) |
c_doctype | c_doctype_name | CREATE UNIQUE INDEX c_doctype_name ON adempiere.c_doctype USING btree (ad_client_id, name) |
c_doctype | c_doctype_uu_idx | CREATE UNIQUE INDEX c_doctype_uu_idx ON adempiere.c_doctype USING btree (c_doctype_uu) |
m_inout | m_inout_pkey | CREATE UNIQUE INDEX m_inout_pkey ON adempiere.m_inout USING btree (m_inout_id) |
m_inout | m_inout_bpartner | CREATE INDEX m_inout_bpartner ON adempiere.m_inout USING btree (c_bpartner_id) |
m_inout | m_inout_documentno | CREATE INDEX m_inout_documentno ON adempiere.m_inout USING btree (documentno) |
m_inout | m_inout_order | CREATE INDEX m_inout_order ON adempiere.m_inout USING btree (c_order_id) |
m_inout | m_inout_uu_idx | CREATE UNIQUE INDEX m_inout_uu_idx ON adempiere.m_inout USING btree (m_inout_uu) |
m_inout | idxm_inout_proc_on | CREATE INDEX idxm_inout_proc_on ON adempiere.m_inout USING btree (posted, processed, processedon, ad_client_id) |
m_inoutline | m_inoutline_uu_idx | CREATE UNIQUE INDEX m_inoutline_uu_idx ON adempiere.m_inoutline USING btree (m_inoutline_uu) |
m_inoutline | m_inoutline_pkey | CREATE UNIQUE INDEX m_inoutline_pkey ON adempiere.m_inoutline USING btree (m_inoutline_id) |
m_inoutline | m_inoutline_inout | CREATE INDEX m_inoutline_inout ON adempiere.m_inoutline USING btree (m_inout_id) |
m_inoutline | m_inoutline_product | CREATE INDEX m_inoutline_product ON adempiere.m_inoutline USING btree (m_product_id) |
m_inoutline | m_inoutline_rmaline_id | CREATE INDEX m_inoutline_rmaline_id ON adempiere.m_inoutline USING btree (m_rmaline_id) |
m_rma | m_rma_pkey | CREATE UNIQUE INDEX m_rma_pkey ON adempiere.m_rma USING btree (m_rma_id) |
m_rma | m_rma_uu_idx | CREATE UNIQUE INDEX m_rma_uu_idx ON adempiere.m_rma USING btree (m_rma_uu) |
m_rmaline | m_rmaline_pkey | CREATE UNIQUE INDEX m_rmaline_pkey ON adempiere.m_rmaline USING btree (m_rmaline_id) |
m_rmaline | m_rmaline_uu_idx | CREATE UNIQUE INDEX m_rmaline_uu_idx ON adempiere.m_rmaline USING btree (m_rmaline_uu) |
There are a few custom indexes we created, but it seems none of them affecting above query or the exists-statement.
Index m_inoutline_rmaline_id I only created a few days ago to improve performance for another report and also doesn't affect above query.
Thanks also for your advice regarding the feature branch, that's very helpful.
Cheers,
Michael
Carlos Ruiz August 3, 2021 at 2:13 PM
Hi - thanks for the pull request 809
in the pull request wrote:
please provide the PostgreSQL version you have tested this and the corresponding query analysis that shows the new version of the SQL is more efficient.
I also took a look, first generic thing: is better to do the commit in a branch feature - as you did it in master it will become complex for you to manage keeping the pull request up to date when the master start changing
Now, about the change, it is changing this statement:
with this:
now - analyzing the new EXISTS clause it is basically the same as the IN clause - but I think it’s not really relevant to improve the performance of the query. What is really needed there is an index on M_RMALine at least for the column M_RMA_ID. I think probably you created this index on your database, and the patch would be incomplete and probably EXISTS performance can be worst without such index.
So, I think instead of this proposed change, the only thing needed is probably the index, and by the way, there must be a lot of “line” tables where an index to resolve the foreign key on the parent document is worthy, maybe we can do something generic to add those indexes to the most common tables: C_OrderLine, C_InvoiceLine, M_*Line, etc
Can you please check your DB, I think you must have an index created to make the change work.
Regards,
Carlos Ruiz
Michael Powacht July 31, 2021 at 3:54 AM
Thanks Carlos, I submitted a pull request to idempiere/master.
If there is a large number of Customer RMA in the system (in our case +500), the performance in Form 'Generate Invoices (manual)' with Document Type=RMA - upon retrieving customer RMA documents for the creation of credit memos - becomes unacceptable. Also database activity spikes for an extended period of time slowing down the entire system.
The root cause is the SQL statement under getRMASql() in org.adempiere.ui/src/org/compiere/apps/form/InvoiceGen.java.
We were able to optimize the performance of this SQL statement as per attachment.
Please consider pushing the SQL statement change to the trunk.