• Resolved Jason

    (@galapogos01)


    Hi team,

    I am finding this plugin to be extremely heavy on database queries (44 per product page, more on archives).

    An analysis shows that the plugin runs database queries in the 7 different template finders, multiplied for each hook section it uses on the PDP (eg. woocommerce_before_add_to_cart_button, woocommerce_after_add_to_cart_button etc. None of the data is reused between templates.

    These also run during Flexible_Product_Fields_Plugin->wp_enqueue_scripts() as well as for any other products, including upsells, related products, or product summary tiles on the PDP or archive / PLP.

    Due to the way caching is being done, each hook section has a different cache key, so nothing can be pulled from cache. One suggestion would be to reuse the '' section cache key for each individual section call, but really you should be sharing data between the templates without going back to the database.

    The result of this poor code is big increases in CPU/load with the plugin enabled.

    Can you please ask your development team to look at the architecture of how you’ve included templates / scripts and where you cache data to avoid multiple queries during the execution of the plugin?

    Thanks, Jason

    • This topic was modified 2 months, 2 weeks ago by Jason.
Viewing 11 replies - 1 through 11 (of 11 total)
  • Plugin Support rzepsen

    (@rzepsen)

    Hi @galapogos01

    Thanks for your detailed analysis — you’re absolutely right that the plugin generates a noticeable number of database queries, especially on product pages.

    Part of this is the price we pay for the flexibility the plugin provides. Flexible Product Fields allows fields to be assigned dynamically to products, categories, tags, and other contexts directly from the templates, and this requires checking several conditions during runtime. Because of that, some database queries are unavoidable.

    That said, we are aware of the potential load, which is why we introduced caching in this area specifically to reduce repeated database access. The cache should already help offload the database in most scenarios and, based on our testing, it works well enough in typical store environments.

    You’re also correct that the number of queries may increase further when additional product elements are rendered on the page — for example upsells, related products, or product summary tiles — since those also trigger parts of the same logic.

    In theory, the caching layer could be optimized further (for example by sharing more data between template calls as you suggested), but implementing that would be quite complex due to how the templates and hook sections are structured internally. At the moment, the existing caching approach provides a reasonable balance between performance and the flexibility the plugin offers.

    That said, we really appreciate your feedback and the suggestions regarding cache keys and template data reuse — it’s valuable insight and something we’ll keep in mind when evaluating future architectural improvements.

    Thread Starter Jason

    (@galapogos01)

    Is there somewhere I can better communicate with you? I’ve found a way to ensure the cache does not re-hit the database for each hook section.

    I’m hoping you will mainline these improvements so all customers get the benefit, plus I don’t need to maintain a fork 😉

    Plugin Support rzepsen

    (@rzepsen)

    Hi @galapogos01

    If you prefer not to publish the solution here, you can always reach out to us on our official support forum. That’s the best place to discuss implementation details directly with our team.

    Feel free to share your idea there and we’ll be happy to take a closer look. If the improvement makes sense for the plugin architecture, we can also consider incorporating it so all users can benefit from it.

    Can you please share this information with me? I am having the same issues with the plugin.

    Plugin Support rzepsen

    (@rzepsen)

    Hi @formtechnology ,

    Yes, of course. I’m just waiting for a response from @galapogos01 at the moment, as I contacted them about this issue. They haven’t replied yet.

    As soon as I hear back from them, I’ll be happy to share the information with you.

    Thread Starter Jason

    (@galapogos01)

    Hi guys,

    I did a few things in flexible-product-fields/src/Service/TemplateFinder/TemplateFinder.php ; the most effective were:

    1. Disable all templates I do not use on my site.

     	private function init_finders(): void {
    $this->finders->add( new ProductTemplateFinder() );
    - $this->finders->add( new CategoryTemplateFinder() );
    - $this->finders->add( new TagTemplateFinder() );
    - $this->finders->add( new AllTemplateFinder() );
    - $this->finders->add( new ProductExludedTemplateFinder() );
    - $this->finders->add( new CategoryExludedTemplateFinder() );
    - $this->finders->add( new TagExludedTemplateFinder() );
    + // Disabled unused template finders - we only assign to specific products
    + // $this->finders->add( new CategoryTemplateFinder() );
    + // $this->finders->add( new TagTemplateFinder() );
    + // $this->finders->add( new AllTemplateFinder() );
    + // $this->finders->add( new ProductExludedTemplateFinder() );
    + // $this->finders->add( new CategoryExludedTemplateFinder() );
    + // $this->finders->add( new TagExludedTemplateFinder() );
    }

    2. As your enqueue_scripts hook effectively primes the cache for all section types but it never gets used, modify the lookup to check for cached values first.

    +	private const META_KEY_SECTION = '_section';
    +
    protected FinderCollection $finders;

    protected ProductHandlerInterface $product_handler;
    @@ -45,6 +47,22 @@
    return $this->cache->get( $product, $section );
    }

    + // If requesting a specific section but we already have ALL templates cached,
    + // filter in PHP instead of running new queries
    + if ( $section !== '' && $this->cache->has( $product, '' ) ) {
    + $all_templates = $this->cache->get( $product, '' );
    + $filtered_posts = array_filter(
    + $all_templates->get_posts(),
    + function( $post ) use ( $section ) {
    + return \get_post_meta( $post->ID, self::META_KEY_SECTION, true ) === $section;
    + }
    + );
    + $templates = new TemplateCollection( array_values( $filtered_posts ) );
    + $templates->init_fields( $this->product_fields->get_field_types() );
    + $this->cache->set( $product, $section, $templates );
    + return $templates;
    + }
    +
    $this->product_handler = $this->get_product_handler( $product );
    $this->template_query->set_section_hook( $section );

    I can’t confirm these fixes work on all sites but they reduced the number of DB queries by an order of magnitude for me.

    Hopefully your devs can take this inspiration and do better than me!

    • This reply was modified 2 months, 1 week ago by Jason.
    • This reply was modified 2 months, 1 week ago by Jason.
    • This reply was modified 2 months, 1 week ago by Jason.
    Plugin Support rzepsen

    (@rzepsen)

    Thanks @galapogos01

    I’ve passed on the suggestions to the developer. We’ll see what we can do about it. As soon as the update is ready, I’ll let you know.

    thanks

    Plugin Support rzepsen

    (@rzepsen)

    Hi @galapogos01,, @formtechnology

    I just want to inform you that we have just released the Flexible Product Fields 2.4.13 update containing the proper fixes for the problem you’ve reported. So please simply do the update.

    If the WordPress updater hasn’t informed you about the newer versions available, please check for the updates manually choosing the Dashboard tab > Updates > Check Again option.

    Please let me know if you have any further questions or if there is something more I can help you with.

    Kind regards,

    Plugin Support rzepsen

    (@rzepsen)

    As we haven’t got any replies, I’m marking this topic as resolved for now.

    Thread Starter Jason

    (@galapogos01)

    @rzepsen you did not give me much time to reply!

    2.14.3 only provides a small relief.

    Your devs ignored the patch I provided for caching. This provided some big benefits.

    I have also developed another patch, this one to avoid nested/repeating get_options() calls from get_field_type_settings() which is called multiple times in init_fields on every front end request. This also calls the short_url filter for the upgrade to pro URLs among other redundant things only needed in admin.

    This patch provides a reduced set of data back on frontend calls, eliminating the get_optionscalls.

    --- wp-content/plugins/flexible-product-fields/src/Field/Type/TypeIntegration.php
    +++ wp-content/plugins/flexible-product-fields/src/Field/Type/TypeIntegration.php
    @@ -77,6 +77,35 @@
    * @return array Settings of field type.
    */
    private function get_field_type_settings(): array {
    + if ( ! is_admin() ) {
    + return [
    + 'type' => $this->type_object->get_field_type(),
    + 'value' => $this->type_object->get_field_type(),
    + 'template_file' => $this->type_object->get_template_file(),
    + 'is_available' => $this->type_object->is_available(),
    + 'has_required' => $this->type_object->has_required(),
    + 'has_max_length' => $this->type_object->has_max_length(),
    + 'has_placeholder' => $this->type_object->has_placeholder(),
    + 'has_value' => $this->type_object->has_value(),
    + 'has_css_class' => $this->type_object->has_css_class(),
    + 'has_tooltip' => $this->type_object->has_tooltip(),
    + 'has_value_min' => $this->type_object->has_value_min(),
    + 'has_value_max' => $this->type_object->has_value_max(),
    + 'has_value_step' => $this->type_object->has_value_step(),
    + 'has_options' => $this->type_object->has_options(),
    + 'has_date_format' => $this->type_object->has_date_format(),
    + 'has_days_before' => $this->type_object->has_days_before(),
    + 'has_days_after' => $this->type_object->has_days_after(),
    + 'has_dates_excluded' => $this->type_object->has_dates_excluded(),
    + 'has_days_excluded' => $this->type_object->has_days_excluded(),
    + 'has_week_start' => $this->type_object->has_week_start(),
    + 'has_max_dates' => $this->type_object->has_max_dates(),
    + 'has_today_max_hour' => $this->type_object->has_today_max_hour(),
    + 'has_price' => $this->type_object->has_price(),
    + 'has_price_in_options' => $this->type_object->has_price_in_options(),
    + 'type_object' => $this->type_object,
    + ];
    + }
    return [
    'type' => $this->type_object->get_field_type(),
    'field_group' => $this->type_object->get_field_group(),

    Please consider mainlining both this and the caching patch!

    • This reply was modified 1 month, 2 weeks ago by Jason.
Viewing 11 replies - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.