@Starker3 opened this Issue on July 15th 2021

Current Behavior

Currently when adding the "Goal Overview" widget to the dashboard or loading the Goal Overview report it will make at least 2 GET requests for each goal present in the Overview (At least one request for each sparkline per goal)
A Matomo server with just 7 goals for example makes around 25 requests just for sparklines on the Goals Overview.

On websites that have a large number of goals (For example >50) this can cause a spike in the number of PHP processes that can result in the maximum number of PHP child processes to be created, this results in the following warning:
"WARNING: [pool www] server reached pm.max_children setting (50), consider raising it"

This in turn results in unexpected performance issues that may be difficult to resolve (Other than simply removing the Goal Overview widget from the dashboard, but this doesn't solve the issue when loading the Goal Overview report)

Possible Solution

Not sure if there is a quick fix for this sort of thing, other than somehow batching the requests for the sparklines so that it doesn't cause the child processes to be maxed?

Context

For users running servers with a single endpoint (Tracking and UI on the same server) this can potentially result in the entire Matomo server being slow to respond to requests (Since PHP can't handle new requests coming in this could potentially lead to slow tracker performance or other issues)

@tsteur commented on September 1st 2021 Member

I'll reopen this one as it's not fully fixed.

It's still sending a request for every goal on the goals overview page like https://demo.matomo.cloud/index.php?forceView=1&viewDataTable=sparklines&module=Goals&action=get&idGoal=6&allow_multiple=0&only_summary=1&idSite=1&period=day&date=yesterday&segment=&showtitle=1&random=7404

to fetch this data
image

In the referenced PR only the sparkline image itself is loaded when in viewport.

Ideally we load these widgets async only when in viewport as well or maybe better we create one widget that loads the data for all sparklines. Meaning have only one widget that loads all the individual sparklines (without changing the look)
image.

This way it be only one request instead of one request per goal (which could all launch archiving etc)

@tsteur commented on September 1st 2021 Member

Something like this could fix this issue

diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index 1254260eaa..cfeb426f8f 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -70,6 +70,13 @@ class Controller extends \Piwik\Plugin\Controller
         Piwik::checkUserHasSomeViewAccess();
         $this->checkSitePermission();

+        // RENDER ALL THE WIDGETS HERE at once instead of requesting the rendering of these widgets using JS
+        $out = '';
+        foreach (widgets as $widget) {
+            $out .= FrontController::getInstance()->dispatch('CoreHome', 'renderWidget');;.
+        }
+        return $out;
+
         $view = new View('<a class='mention' href='https://github.com/CoreHome'>@CoreHome</a>/widgetContainer');
         $view->isWidgetized = (bool) Common::getRequestVar('widget', 0, 'int');
         $view->containerId  = Common::getRequestVar('containerId', null, 'string');
diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php
index 6b105bfb9a..c677d317ed 100644
--- a/plugins/Goals/Pages.php
+++ b/plugins/Goals/Pages.php
@@ -57,6 +57,13 @@ class Pages
         $config->setIsNotWidgetizable();
         $widgets[] = $config;

+        $container = $this->factory->createContainerWidget('goals_overview_goals');
+        $container->setSubcategoryId($subcategory);
+        $container->setName('');
+        $container->setOrder(15);
+        $container->setIsNotWidgetizable();
+        $widgets[] = $container;
+
         foreach ($goals as $goal) {
             $name = Common::sanitizeInputValue($goal['name']);
             $goalTranslated = Piwik::translate('Goals_GoalX', array($name));
@@ -69,7 +76,8 @@ class Pages
             $config->setOrder(25);
             $config->setIsNotWidgetizable();
             $config->addParameters(array('allow_multiple' => (int) $goal['allow_multiple'], 'only_summary' => '1'));
-            $widgets[] = $config;
+            $container->addWidgetConfig($config);
+            //$widgets[] = $config;
         }

         $container = $this->createWidgetizableWidgetContainer('GoalsOverview', $subcategory, $widgets);

We would then render all the widgets within a container server side at once. In the past we loaded them individually thinking it's better to load the widgets in parallel but for some pages like goals this might not be the best way (or in general when widgets within a container use the same report data)

Powered by GitHub Issue Mirror