A Simple Explanation of Cache

If this is the first article you read about Cache, congratulations, you are lucky you find this simple and straightforward information about it, more specifically about Web caching.

Cache can be implemented by Software or Hardware, it doesn’t matter, it is an optimized storage for delivering fast content between you PC and the Internet content you are browsing. 

When you visit your favorite page, most of the content is not coming from the server, it is coming from the cache, because the cache is already stored from the remote server the first time you visit the page.  

If the page loads from the server every time you visit your page, it will cause bottlenecks somewhere in the “road”, especially if the page is visited at the same time by many people.  This is why it is more likely you will have the impression that your favorite page loads faster. Nobody likes slow websites, and this is why cache is necessary.

During every connection between a device and a server more than one cache can be used during the connectivity, even your browser implements a cache, sometimes called proxy.

But how does a basic cache work? Assuming the most basic scenario, a cache might works as follows:

When you visit a page for the first time, the response time will always be the slowest one, because the response is being served directly by the server; however a copy of the response data is stored in the cache.

After the first time the page is visited the client request will be served primarily from the cache. If there is data needed to be updated it will be brought directly from the server and a copy of it will be stored in cache. This approach will provide a faster response and reduce server traffic.

However, you might be asking yourself some questions: how does the cache determine when to refresh a specific content?  How to assure that the data display is always the most accurate data?

The above is being determined by cache policies; that are basically rules-algorithms that manage a cache; usually being a combination of several rules as for example: last written time, last access time, less access data, etc. Only the browser itself utilices a combination of very complex policies, aiming to provide faster page loads.  

As a general conclusion, it can be assumed that your devices accessing and requesting info from the Internet utilize a cache mechanism. Most of the time you don’t need to take it, it is being implemented out of the box; however in some complex scenarios this cache implementation and/or policies would require to be adjusted in order to provide a better customer experience; being this task more complex every day since technology trends are evolving constantly.

Configuring Cron Job Groups for Magento

There are many articles on the web regarding how to configure cron job groups in Magento; however, it is very hard to express the concepts by using just words; leading to confusion among developers when dealing with such an important task on any merchant website, for this reason this article is aiming to explain in a more graphical way the key concepts of setting a cron job in Magento

Assuming that the one more cron jobs are already defined under a cron job group, the next step is to create the cron_groups.xml file for that specific cron group. 

We might take the following configuration as example:

<group id=”astralweb”>

   <schedule_generate_every>10</schedule_generate_every>

   <schedule_ahead_for>40</schedule_ahead_for>

   <schedule_lifetime>2</schedule_lifetime>

   <history_cleanup_every>10</history_cleanup_every>

   <history_success_lifetime>60</history_success_lifetime>

   <history_failure_lifetime>600</history_failure_lifetime>

   <use_separate_process>1</use_separate_process>

</group>

The group id is the name of the group to be configures, “astralweb” in this case; the following couple of lines are the most confusing for most of developers.

<schedule_generate_every>10</schedule_generate_every>

<schedule_ahead_for>40</schedule_ahead_for>

Schedule generate every: In minutes, specifying how often the cron jobs will be triggered. Meaning in this case that every 10 minutes a new job will be executed.

Schedule ahead for: In minutes, specifying the max range in time to run/schedule the max amount of cron jobs.

What does all of these mean? The above description can be considered as a division as follows:

By following the same example we can check how many consecutive jobs (of a single job) we can run:

In the timeline, this batch can be represented as follows:

What happened here?

  • The first cron job is executed immediately.
  • Jobs 2,3,4 are scheduled to be run.
  • 1 executed + 3 scheduled = 4 jobs in total.
  • This will be the same behavior for all jobs running in the same group.

However, Magento will wait for the last job of this batch (job 4) before creating another batch, so in timeline the behavior would be something like this:

After executing the last job (job 4), a new batch will be created as follows:

After watching the timelines above you must be sure that “schedule ahead for” is always greater than “schedule generate every”, otherwise, the cron job will run only once, since the ratio would not be enough for scheduling more than one job.   

Finally, after explaining the relationship between “schedule generate every” and “schedule ahead for”, the the rest of the settings are self explanatory:

<schedule_lifetime>2</schedule_lifetime>

<history_cleanup_every>10</history_cleanup_every>

<history_success_lifetime>60</history_success_lifetime>

<history_failure_lifetime>600</history_failure_lifetime>

<use_separate_process>1</use_separate_process>

Schedule Lifetime: In minutes. This is the time for the job to start running. Meaning in this case that our cron jobs will have 1:59 minutes to start running when the time reaches. If after this time the job has not started to run it will be considered as missed.

History Cleanup Every: In minutes; configuring the time the cron job will be recorded in the database.

History Success Lifetime: In minutes; configuring the time the cron job will be recorded in the database after a successful execution.

History Failure Lifetime: In minutes; configuring the time the cron job will be recorded in the database after a failed execution.

Use separate process: 1 for running in a separate PHP process; 0 otherwise. In theory, running in separate processes would avoid the need to accidentally consume unresponsive or idle processes.

As we can see the key for running a successful cron group is the correct relationship between “schedule generate every” and “schedule ahead for”; by fully understanding how affects the cron behavior the developers would reduce any issue caused by misunderstandings on setting any particular cron group.

Magento – Bypassing Fastly cache on specific page

The most important feature of any CDN/Cache server/provider is to reduce loading speed by delivering the requested content to a particular user, in a closer location; instead of serving the content from the original server, at a larger distance.

This is exactly one of the main goals of Fastly, that is specially optimized to be used with Magento; however there might be certain requirements, on a certain page, in which you are forced to bring all the newest data from the original server (remote server), for ensuring only the new data is served. Unfortunately, disabling Fastly Cache/CDN is not an option, because the server speed will be downgraded; so how to serve non-cacheable data only without affecting performance? 

Luckily, Magento and Fastly provide a way for bypassing Fastly cache for only specific pages. This means that Fastly will still work normally for the whole site except for the pages you decide not to cache.

The above is easily configured in

Stores->Configuration->Advanced->System->Full Page Cache

If Fastly is configured in your server you should see “Fastly CDN” after expanding “Full Page Cache”. Then on Fastly Configuration->Custom VCL Snippets, click on Create Custom Snippet and create a similar snippet as follows:

Set the Name, Type and Priority as your own discretion. The key field is the VCL, in which in this example we set it as follows:

if (req.url ~ “redeximp/import/redimport”) {  return (pass);}

The above snippet means that in case the user is accessing the web path redeximp/import/redimport Fastly cache should be bypassed (skipped). 

Finally, after saving, in Automatic Upload & Service Activation click on Upload VCL to Fastly for applying the changes.

And that will be all; you will have more control on how Faslty behaves in your store, providing more tools for providing a better developer/user experience.

Magento Page Builder – Fix Warning on Save

Since version 2.4.3, Magento includes the Page Builder module in both Commerce and Community Editions, empowering merchants to create rich content by themselves or allowing them to quickly design quick mockups for testing before escalating to any development team.

By making Page Builder open to all editions Magento is encouraging developers to extend the Page Builder functions, sometimes forcing developers to explore deeper into the code when the extended function or behavior differs from the original.

It might be possible that after working with Page Builder the following message is displayed after saving some element that contains a Page Builder content:

This message of “Temporarily allowed to save HTML value that contains restricted elements. Allowed HTML tags are:…” represents a warning message, not an error. The Page Builder is working normally, but the validator is telling us to be careful with content that is not expected by the validator, located in Magento\Cms\Model\Wysiwyg\Validator in the following line

$this->config->isSetFlag(self::CONFIG_PATH_THROW_EXCEPTION);

try {

   $this->validator->validate($content);

} catch (ValidationException $exception) {

How to fix the warning message?

Step 1:

Log the $content variable, check what kind of html content is saving and take note of all the div and attributes elements being used.

Step 2:

On your module vendor/module/etc/di.xml create a Virtual Type called DefaultWYSIWYGValidator and provide all the allowed tags and attributes used by your Magento app:

<virtualType name=”DefaultWYSIWYGValidator” type=”Magento\Framework\Validator\HTML\ConfigurableWYSIWYGValidator”>

   <arguments>

       <argument name=”allowedTags” xsi:type=”array”>

           <item name=”div” xsi:type=”string”>div</item>

           <item name=”a” xsi:type=”string”>a</item>

           …

           <item name=”b” xsi:type=”string”>b</item>

       </argument>

       <argument name=”allowedAttributes” xsi:type=”array”>

           <item name=”class” xsi:type=”string”>class</item>

           …

           <item name=”id” xsi:type=”string”>id</item>

       </argument>

       <argument name=“attributesAllowedByTags” xsi:type=”array”>

           <item name=”div” xsi:type=”array”>

               <item name=”data-content-type” xsi:type=”string”>data-content-type</item>

              …

               <item name=”data-content” xsi:type=”string”>data-content</item>

           </item>

           <item name=”ul” xsi:type=”array”>

               …

       <argument name=”attributeValidators” xsi:type=”array”>

           <item name=”style” xsi:type=”object”>Magento\Framework\Validator\HTML\StyleAttributeValidator</item>

       </argument>

   </arguments>

</virtualType>

Step 3:

Refresh cache

bin/magento cache:flush

After this the warning message will not be displayed anymore because the validator is already recognizing all the possible html elements introduced by the new changes. Improving the general experience of using Page Builder.

Magento 2 Types of Profiling (for optimization of site)

Benchmarking and performance analysis is a very common task when developing a web site, especially if the website is ment to sell services or products. A fast website will add a powerful value to customer experience in general; increasing also the chances of building loyalty and trust.

Magento provides two tools for helping with benchmarking a particular site: MAGE_PROFILER for code and database profiler.

MAGE_PROFILER

This profiling will display the processes and class resources involved with the specific url path being analyzed; listed as a dependency list at the bottom of the screen.

This profiling will allow developers to determine what classes or functions are taking too much time to execute, helping in filtering out the sections or coding that requires some improvement.

For enabling MAGE_PROFILER using type html it is required to execute the following commands:    

bin/magento dev:profiler:enable html

bin/magento cache:flush

For disabling MAGE_PROFILER:

bin/magento dev:profiler:disable

 

Database profiler

The database profiler is similar to the MAGE_PROFILER but is focused on database queries performance. 

For enabling this profiling the following steps are required:

Step 1: Add a reference class to env.php

On  <magento_root>/app/etc/env.php to add the following class reference:

‘db’ =>

  …

      ‘default’ =>

      array (

        ‘host’ => ‘localhost’,

        ‘dbname’ => ‘magento’,

        ‘…

        ‘active’ => ‘1’,

        ‘profiler’ => [

            ‘class’ => ‘\Magento\Framework\DB\Profiler’,

            ‘enabled’ => true,

        ],

      ),

    ),

  ),

Step 2 Modify the index.php file

On <magento_root>/index.php add the following after the $bootstrap->run($app); line in your bootstrap file:

/** @var \Magento\Framework\App\ResourceConnection $res */

$res = \Magento\Framework\App\ObjectManager::getInstance()->get(‘Magento\Framework\App\ResourceConnection’);

/** @var Magento\Framework\DB\Profiler $profiler */

$profiler = $res->getConnection(‘read’)->getProfiler();

echo “<table cellpadding=’0′ cellspacing=’0′ border=’1′>”;

echo “<tr>”;

echo “<th>Time <br/>[Total Time: “.$profiler->getTotalElapsedSecs().” secs]</th>”;

echo “<th>SQL [Total: “.$profiler->getTotalNumQueries().” queries]</th>”;

echo “<th>Query Params</th>”;

echo “</tr>”;

foreach ($profiler->getQueryProfiles() as $query) {

    /** @var Zend_Db_Profiler_Query $query*/

    echo ‘<tr>’;

    echo ‘<td>’, number_format(1000 * $query->getElapsedSecs(), 2), ‘ms’, ‘</td>’;

    echo ‘<td>’, $query->getQuery(), ‘</td>’;

    echo ‘<td>’, json_encode($query->getQueryParams()), ‘</td>’;

    echo ‘</tr>’;

}

echo “</table>”;

Step 3: Clean cache

bin/magento cache:flush

Finally, removing the database profiler is just as simple as rolling back the changes above.

There is no need to say that these profiling tools are beneficial for developers that want to understand a little more better the general user experience and to fix any hidden performance issue that is only noticeable  at code or system level.