Note: In Bitrix Framework caching time is recorded in seconds.
What is the use of caching in own components
Making direct database queries through API, obtaining information, formatting it in the component template, and displaying it to the user may seem to be the simplest solution.
However, the issue is the performance of the web project in case many users work with it at the same time. If it takes the component 0.1 sec. to respond without cache and executing, say, 100 database queries, then if 100 users work simultaneously the database server load will increase, and so will the component response time up to, for example, 5-10 sec.
An equally important point to keep in mind is the speed of response of a component when receiving data from cache. If it takes the component 2 sec. to respond to each user without cache, then, with cache, it will take the component 2 sec. to respond to one user and 0.1 sec. – to the remaining 100 users over the next, say, 30 minutes.
When using cache in own components 2.0:
- Web project performance and load tolerance drastically increase because the database load drops to a minimum, and the web solution will be able to serve, for example, not just 50,000 users per day but 1,000,000 and more.
- Web pages are loaded to the user’s browser much faster (in tenths of a second) because their structural information is saved on the server and is not taken from the database.
Caching Time
The time required for caching depends on the type of caching. If the caching Auto+Management is used, the information will be supplied from cache until it is changed in the database and cache resets automatically. The caching time for this mode must be long, e.g., 1 year.
If Auto caching is used, it is recommended to set up the longest cache interval permitted with due regard to business logic. The caching time for this mode depends on the frequency of the information update. For some components, the time must be set to 24 hours. For frequently updated components, a controlled cache is recommended; alternatively, a value of, for example, 10 minutes shall be set.
Embedded Cache Support
2.0 components come with an embedded support of a typical cache algorithm. The component structure using embedded cache support will be similar to:
// Verification and initialization of input parameters
if ($arParams["ID"] <= 0)
$arParams["ID"] = 10;
// If no valid cache is available (i.e. data must be requested
// and valid cache must be created)
if ($this->StartResultCache())
{
// Data query and completion of $arResult
$arResult = array(
"ID" => rand(1, 100)
);
for ($i = 0; $i < 5; $i++)
$arResult["FIELDS"][] = rand(1, 100);
// If any condition is met, there is no need
// to cache data
if ($arParams["ID"] < 10)
$this->AbortResultCache();
// Connect output template
$this->IncludeComponentTemplate();
}
// Set up page header using deferred
// function
$APPLICATION->SetTitle($arResult["ID"]);
Comments to the Code
The method CBitrixComponent::StartResultCache()has the following description: bool $this->StartResultCache($cacheTime = False, $additionalCacheID = False, $cachePath = False)
where:
- $cacheTime - caching time (if False -
IntVal($arParams["CACHE_TIME"])
is used for substitution);
- $additionalCacheID - additional parameters on which cache depends, apart from the current website’s SITE_ID,component name, file path, and input parameters;
- $cachePath - path to cache file (if False -
"/".SITE_ID.
is used for substitution).
If a valid cache is available, the method displays its contents, completes $arResult, and returns False. If there is no valid cache, the method returns True.
If cache depends not only on the website, input parameters, component name, and a path to the current website, but also on other parameters, these parameters must be sent to the method as a second parameter in the form of a string. For example, if cache also depends on the user groups to which a current visitor belongs, the condition should be written as follows:
if ($this->StartResultCache(false, $USER->GetGroups()))
{
// There is no valid cache. We have to choose data from the base to $arResult
}
If following data selection (if no valid cache is available), it becomes evident that there is no need to cache data, the method
$this->AbortResultCache();
must be invoked. E.g., if it turns out that there is no news with such ID, caching should be interrupted by displaying a message that there is no such news. If caching is not interrupted, some intruders may clog up with cache all disc space allocated to the website by invoking the page with arbitrary (including non-existent) IDs.
The method $this->IncludeComponentTemplate();
connects the component template and saves to the cache file the output and array of results $arResult. All changes to $arResult and output will not be saved to cache after the template connection method is invoked.
If during the component code execution we have not entered into the body of the condition if ($this->StartResultCache())
, it means that there is a valid cache for this component, page, and input parameters. After this method is invoked, HTML from cache is displayed and we have a completed array $arResult. Here, we can do something. For example, set up the page header using deferred functions.
If during the execution of certain conditions the component cache must be cleared (e.g., the component “knows” that the data have been changed), the method $this->ClearResultCache($additionalCacheID = False, $cachePath = False)
can be used. Parameters of this method are consistent with the same-name parameters of the StartResultCache method.
Complex Caching
If the component requires any special caching that cannot be executed using embedded cache support, the standard class CPHPCache can be used. The component structure using the class CPHPCache will be more or less as follows:
// Verification and initialization of the input parameters
if ($arParams["ID"] <= 0)
$arParams["ID"] = 10;
$arParams["CACHE_TIME"] = IntVal($arParams["CACHE_TIME"]);
$CACHE_ID = SITE_ID."|".$APPLICATION->GetCurPage()."|";
// Cache only depends on prepared parameters without "~"
foreach ($this->arParams as $k => $v)
if (strncmp("~", $k, 1))
$CACHE_ID .= ",".$k."=".$v;
$CACHE_ID .= "|".$USER->GetGroups();
$cache = new CPageCache;
if ($cache->StartDataCache($arParams["CACHE_TIME"], $CACHE_ID, "/".SITE_ID.$this->GetRelativePath()))
{
// Request of data and formation of the array $arResult
$arResult = array("a" => 1, "b" => 2);
// Component template connection
$this->IncludeComponentTemplate();
$templateCachedData = $this->GetTemplateCachedData();
$cache->EndDataCache(
array(
"arResult" => $arResult,
"templateCachedData" => $templateCachedData
)
);
}
else
{
extract($cache->GetVars());
$this->SetTemplateCachedData($templateCachedData);
}
Comments to the Code
Cache must only depend on the prepared parameters, i.e. on the parameters that are properly initialized and reduced to a required type (e.g., using IntVal()) etc. The array $arParams contains both prepared parameters and initial parameters (with the same key but with the prefix "~"). If the cache depends on unprepared parameters, intruders will be able to clog up all the disk space allocated to the website with cache by calling a page with IDs equal to "8a", "8b", ... (which give 8 after IntVal()).
The method $this->IncludeComponentTemplate()
does not request data from the database. However, it is better to also include it into the cached area because this method performs certain disk operations.
Before calling the method for cache completion and cache saving (the method EndDataCache), it is necessary to request parameters from the template. These parameters must be used even if the template itself is not connected and data are taken from cache. In the current version, such parameters include css styles of the template that are connected by deferred functions and thus do not go to cache. The structure of the data returned by the template is not documented and has no meaning for the component. These are just data that must be placed to cache and then taken from cache and returned to the template.
In order to return to the template, the data previously saved in cache according to the template’s “wish”, the methods $this->SetTemplateCachedData($templateCachedData);
or CBitrixComponentTemplate::ApplyCachedData($templateCachedData);
can be used. One of these methods must be invoked in the component area which is executed in case there is a valid cache available. It must receive (in parameters) the data that the template “asked” to save.
Some Recommendations
If the component uses standard caching but no template is connected (because it is not necessary), the following shall be used:
$this->EndResultCache();
A possible solution where the component template is removed from the cached area. Other components can be connected in the template itself.
$cache_id = serialize(array($arParams, ($arParams['CACHE_GROUPS']==='N'? false: $USER->GetGroups())));
$obCache = new CPHPCache;
if ($obCache->InitCache($arParams['CACHE_TIME'], $cache_id, '/'))
{
$vars = $obCache->GetVars();
$arResult = $vars['arResult'];
}
elseif ($obCache->StartDataCache())
{
// code
$obCache->EndDataCache(array(
'arResult' => $arResult,
));
}
If the code is written properly and template.php нcontains no “heavy” code, this option might work well enough.