WC_Product_Variable::get_available_variation() is too slow during ajax request
The support doesn work on Saturdays and Sundays, so some Friday requests can be answered on Monday. If you have problems with registration ask help on contact us page pleaseIf you not got email within 24~36 business hours, firstly check your spam box, and if no any email from the support there - back to the forum and read answer here. DO NOT ANSWER ON EMAILS [noreply@pluginus.net] FROM THE FORUM!! Emails are just for your info, all answers should be published only here.
The support doesn work on Saturdays and Sundays, so some Friday requests can be answered on Monday.
Quote from sbnc.eu on January 7, 2021, 21:15Hi!
I have an AJAX request used from the front end which is supposed to return some details for a product variation. It is very similar to the built in feature of WooCommerce ( WC_AJAX::get_variation() function, which serves the ?wc-ajax=get_variation requests for variable product pages).
My problem is that this request can respond under 2 sec normally, but when I enable the WOOCS plugin, the response times are above 30s.
I've did some measurements about the culprit, and the problem happens when I call get_available_variation() of the WC_Product_Variable object. This call normally returns in around 0.3 sec, but when WOOCS is enabled, it takes thrity-something seconds.
I've tried setting the Currency storage to Transient and PHP Session, also turned on and off the I am using cache plugin on my site option (even thought I have no cache plugin), but neither of these helped.
Let me know if I can provide any additional details!
Thank you for your help!
BR,
Bence Szalai
Hi!
I have an AJAX request used from the front end which is supposed to return some details for a product variation. It is very similar to the built in feature of WooCommerce ( WC_AJAX::get_variation() function, which serves the ?wc-ajax=get_variation requests for variable product pages).
My problem is that this request can respond under 2 sec normally, but when I enable the WOOCS plugin, the response times are above 30s.
I've did some measurements about the culprit, and the problem happens when I call get_available_variation() of the WC_Product_Variable object. This call normally returns in around 0.3 sec, but when WOOCS is enabled, it takes thrity-something seconds.
I've tried setting the Currency storage to Transient and PHP Session, also turned on and off the I am using cache plugin on my site option (even thought I have no cache plugin), but neither of these helped.
Let me know if I can provide any additional details!
Thank you for your help!
BR,
Bence Szalai
Quote from sbnc.eu on January 8, 2021, 01:22Meanwhile I've managed to further clarify the issue. Actually it is caused by the woocs_calc_tax_price() call in line 1616 of wp-content/plugins/woocommerce-currency-switcher/classes/woocs_after_33.php.
The call stack is something like this:
WC_Product_Variable::get_available_variation() calls:WC_Product_Variable::get_variation_sale_price() && WC_Product_Variable::get_variation_regular_price calls:WC_Product_Variable::get_variation_prices() calls:WC_Data_Store::read_price_data() magic method calls:WC_Product_Variable_Data_Store_CPT::read_price_data() applies filter 'woocommerce_variation_prices' which calls:WOOCS::woocommerce_variation_prices() which (if variation prices and WC tax are enabled) calls the next for each price of each variation:WOOCS::woocs_calc_tax_price() calls:WC_Product_Factory::get_product()I have some products with 50+ variations (combinations of size, color and style), each of them have a sales price, a regular price and an active price, which means for one product `WC_Product_Factory::get_product()` is called 150+ times, and it is not a fast function at all. It potentially queries the db for all details of the product variation and possibly the parent product as well, but even if the datastore does caching it can easily take between 0.1-0.5 sec. Multiply that with 150 and it is a recipe for disaster. And that's only for one product. If a request processes more of them, it'll time-out for sure.
On one hand the loop order should be changed. Since `WOOCS::woocs_calc_tax_price()` processes all prices of all products, if the two nested loops were the other way around, i.e. not looping all products 3 times for the 3 kind of prices, but looping the products once and handling the 3 prices in the inner loop, the required number of calls to `WC_Product_Factory::get_product()` could be reduced by a factor of 3.
Also I don't have 'wc_memberships', so this is not an issue for me, but under `if (function_exists("wc_memberships"))` the `WC_Product_Factory::get_product()` method is called again for the same variation, possibly duplicating the delay for those who have 'wc_memberships' active. These two calls should be replaced with one as well.
But all together while these changes would make the calculation faster for sure, the real issue here is that `WOOCS::woocommerce_variation_prices()` is re-creating `WC_Product_Variation` instances which are already available above in the call chain. For example in `WC_Data_Store::read_price_data()` all of these variations are already instantiated and available. Probably the best solution would be to refactor `WOOCS::woocommerce_variation_prices()` and for example use the `woocommerce_variation_prices_array` filter instead (few lines above `woocommerce_variation_prices` in `wp-content/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php`, which gets the `WC_Product_Variation` object passed instead of the `WC_Product`, so the variation prices could be processed directly, without re-instantiating the variation objects at all.
Meanwhile I'll just disable tax calculation in WooCommerce, since luckily I'm not using that and it should not be enabled anyway, but for anyone using tax calculation and/or 'wc_memberships', the fixes I've described above can be crucial! For anyone facing the same issue, add this filter to disable tax calculation:
add_filter( 'wc_tax_enabled', '__return_false');Please take my suggestions into consideration, and thank you for the plugin anyway indeed, as it is great help with multi-currency shops!
Meanwhile I've managed to further clarify the issue. Actually it is caused by the woocs_calc_tax_price() call in line 1616 of wp-content/plugins/woocommerce-currency-switcher/classes/woocs_after_33.php.
The call stack is something like this:
WC_Product_Variable::get_available_variation() calls:
WC_Product_Variable::get_variation_sale_price() && WC_Product_Variable::get_variation_regular_price calls:
WC_Product_Variable::get_variation_prices() calls:
WC_Data_Store::read_price_data() magic method calls:
WC_Product_Variable_Data_Store_CPT::read_price_data() applies filter 'woocommerce_variation_prices' which calls:
WOOCS::woocommerce_variation_prices() which (if variation prices and WC tax are enabled) calls the next for each price of each variation:
WOOCS::woocs_calc_tax_price() calls:
WC_Product_Factory::get_product()
I have some products with 50+ variations (combinations of size, color and style), each of them have a sales price, a regular price and an active price, which means for one product `WC_Product_Factory::get_product()` is called 150+ times, and it is not a fast function at all. It potentially queries the db for all details of the product variation and possibly the parent product as well, but even if the datastore does caching it can easily take between 0.1-0.5 sec. Multiply that with 150 and it is a recipe for disaster. And that's only for one product. If a request processes more of them, it'll time-out for sure.
On one hand the loop order should be changed. Since `WOOCS::woocs_calc_tax_price()` processes all prices of all products, if the two nested loops were the other way around, i.e. not looping all products 3 times for the 3 kind of prices, but looping the products once and handling the 3 prices in the inner loop, the required number of calls to `WC_Product_Factory::get_product()` could be reduced by a factor of 3.
Also I don't have 'wc_memberships', so this is not an issue for me, but under `if (function_exists("wc_memberships"))` the `WC_Product_Factory::get_product()` method is called again for the same variation, possibly duplicating the delay for those who have 'wc_memberships' active. These two calls should be replaced with one as well.
But all together while these changes would make the calculation faster for sure, the real issue here is that `WOOCS::woocommerce_variation_prices()` is re-creating `WC_Product_Variation` instances which are already available above in the call chain. For example in `WC_Data_Store::read_price_data()` all of these variations are already instantiated and available. Probably the best solution would be to refactor `WOOCS::woocommerce_variation_prices()` and for example use the `woocommerce_variation_prices_array` filter instead (few lines above `woocommerce_variation_prices` in `wp-content/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php`, which gets the `WC_Product_Variation` object passed instead of the `WC_Product`, so the variation prices could be processed directly, without re-instantiating the variation objects at all.
Meanwhile I'll just disable tax calculation in WooCommerce, since luckily I'm not using that and it should not be enabled anyway, but for anyone using tax calculation and/or 'wc_memberships', the fixes I've described above can be crucial! For anyone facing the same issue, add this filter to disable tax calculation:
add_filter( 'wc_tax_enabled', '__return_false');
Please take my suggestions into consideration, and thank you for the plugin anyway indeed, as it is great help with multi-currency shops!
Quote from Pablo Borysenco on January 8, 2021, 11:03Hello
Please disable - https://c2n.me/4atPBZI.png - and do a test
Hello
Please disable - https://c2n.me/4atPBZI.png - and do a test
Quote from sbnc.eu on January 8, 2021, 14:41As I said I've already tried that. The issue is not related to that.
As I said I've already tried that. The issue is not related to that.
Quote from Pablo Borysenco on January 11, 2021, 11:35Hello
Ok! I'll pass it to the developers
Hello
Ok! I'll pass it to the developers
Quote from sbnc.eu on January 12, 2021, 10:46Just FYI, meanwhile I've managed to drastically reduce the number of calls to `WC_Product_Variable::get_available_variation()` in other parts of the affected website (not related to WOOCS), so the site is okay now, but objectively speaking the plugin should still rather use `woocommerce_variation_prices_array` than `woocommerce_variation_prices` to avoid the necessary re-instantiation of all `WC_Product_Variation` objects, which would be readily available in the other filter just few lines above.
I've also made a custom replacement code, which unhooks the built in `WOOCS::woocommerce_variation_prices()` from `woocommerce_variation_prices` and performs the same operations using `woocommerce_variation_prices_array` instead, providing better performance. Let me know if you are interested, I'd share it happily with "no strings attached".
Just FYI, meanwhile I've managed to drastically reduce the number of calls to `WC_Product_Variable::get_available_variation()` in other parts of the affected website (not related to WOOCS), so the site is okay now, but objectively speaking the plugin should still rather use `woocommerce_variation_prices_array` than `woocommerce_variation_prices` to avoid the necessary re-instantiation of all `WC_Product_Variation` objects, which would be readily available in the other filter just few lines above.
I've also made a custom replacement code, which unhooks the built in `WOOCS::woocommerce_variation_prices()` from `woocommerce_variation_prices` and performs the same operations using `woocommerce_variation_prices_array` instead, providing better performance. Let me know if you are interested, I'd share it happily with"no strings attached".
Quote from Pablo Borysenco on January 12, 2021, 11:34Hello
Thank you for cooperation!
You can email this to developers(I added email in private data)
what version number of the plugin are you using?
Hello
Thank you for cooperation!
You can email this to developers(I added email in private data)
what version number of the plugin are you using?