The caching system is one of the key components of the Melbis platform’s performance. Its philosophy is simple: a developer writes a module in whatever way makes sense from a logic and code readability standpoint, and the platform handles optimization through several levels of caching.
Melbis Shop provides five types of cache. Each solves its own problem and can be used independently or in combination with others.
Caching at the level of an individual SQL query. The result is stored in the server’s RAM via the APCu extension. On a repeated call with the same parameters, no database query is executed — the result is taken from memory.
The default retention period is one day; a longer period can be set
if needed. However, the cache will become invalid in any case if changes
appear in the involved tables: the parser automatically finds
all tables in the query text (using the
{DBNICK}_... pattern) and binds the cache to the time of
their last modification.
Useful for frequently called reference data inside modules where it makes no sense to enable the full module base cache, but repetitive similar queries create unnecessary load.
// Load the list of currencies — the same for the entire page
$currencies = MELBIS()->SqlSelectStatic(__LINE__,
"SELECT id, name, rate
FROM {DBNICK}_currency
WHERE active = 1"
// no timeout specified — the default value is used (one day)
);
// Load a single record
$settings = MELBIS()->SqlSelectStaticFlat(__LINE__,
"SELECT *
FROM {DBNICK}_key_value
WHERE key_name = :KEY",
['key' => 'delivery_free_limit']
);SqlSelectStatic returns an array of rows,
SqlSelectStaticFlat returns a single flat record.
Solves the N+1 query problem — when iterating over a list of products, a separate query is made for each item (for example, to fetch an image or attributes).
How it works. In the module that assembles the
product list, a set of identifiers is registered in advance via
EnumSet. When the nested module calls
SqlSelectEnumFlat, it retrieves the prepared list of IDs
via EnumGet for substitution into the query — and loads all
the required data in a single query. On subsequent
calls, the data is taken from memory.
// In the product list module:
$goods = MELBIS()->SqlSelect(__LINE__, "SELECT id, name, price FROM {DBNICK}_store WHERE ...");
// Register the set of IDs
MELBIS()->EnumSet('store', array_column($goods, 'id'));Nested module melbis_store_image:
$image = MELBIS()->SqlSelectEnumFlat(__LINE__,
"SELECT store_id, file_name, upload_time
FROM {DBNICK}_store_image
WHERE store_id IN (:IDS)
AND kind_key = :KIND",
'store_id',
$id,
['kind' => 'kDefault']
);The EnumGet method inside SqlSelectEnumFlat
automatically forms a safe list of identifiers for
substitution into IN (...) without using prepared
statements — values are cast to integers and joined with commas.
EnumSet and EnumGet support
multiple arrays for the same key. This is especially important
on pages where the same objects appear in different blocks. For example,
a catalog page may have three different product lists: the main section,
bestsellers, and new arrivals. A separate EnumSet is made
for each:
MELBIS()->EnumSet('store', array_column($goods_main, 'id'));
MELBIS()->EnumSet('store', array_column($goods_hits, 'id'));
MELBIS()->EnumSet('store', array_column($goods_new, 'id'));When the nested module calls SqlSelectEnumFlat with a
specific $id, the EnumGet method iterates
through all registered sets under the name 'store' and
finds the one containing that identifier. The query with the
corresponding set of IDs will already be cached — the data will be taken
from memory. If the product was not included in any set,
EnumGet returns a set containing only the current element,
and the query executes as usual.
The primary and most universal type of caching. Enabled for each module individually in the IDE on the “Parameters” tab.
After a module executes, its HTML result is saved to disk. On the next call with the same input parameters, the parser serves the ready-made HTML without re-running the module.
Base cache parameters:
0 means “no pause”: the cache is
updated immediately when data changes in the monitored tables. A
non-zero value reduces server load — the cache is not rebuilt more often
than once every N minutes, even if the data has changed.How the platform determines when the cache is stale:
Each module specifies which database tables it works with (the “Tables” tab in the IDE). When the parser starts, it loads the last modification time of all tables and tracks it. When a table is updated, the cache of modules that depend on it automatically becomes stale.
Important: Tables are added to the monitored list only if they actually exist in the database at the time the parser starts. Therefore, temporary tables (
TEMPORARY TABLE) created inside a module are automatically excluded from monitoring — there is no need to exclude them manually.
Important: For the cache to work correctly, libraries must not be included via PHP’s
requireorincludefunctions inside a module. All dependencies must be connected through the IDE (checkboxes in the library list). Only in this case will the parser automatically account for tables from connected libraries when determining the cache lifetime.
Important: If a module accesses data directly via
$_GET,$_POST, orMELBIS()->gVars(bypassing input parameters), it will not work correctly with caching enabled — the result will be cached once and will not account for different values of that data. All input data must be passed as declared module parameters.
PHP’s
header()function should not be used inside a module whose result is cached:header()outputs data directly to the stream and cannot be cached. Headers should be set in the root script.
To forcibly reset the cache from within a module (for example, if something went wrong and a re-run is needed):
MELBIS()->UnitCacheReset();A protective mechanism against peak loads. Configured on the “Trick” tab in the IDE.
The idea: under high server load or slow page compilation, instead of regenerating the page from scratch, the platform serves an older version of the cache — one that was saved earlier. This keeps the site operational even during unexpected traffic spikes or attack attempts — the server does not get overwhelmed by a queue of slowly generated pages.
Trick cache parameters:
0 — no limit.The Trick cache works in parallel with the base cache: under normal load the module operates as usual; under peak load it serves the saved Trick version.
Works in conjunction with the base cache when the update pause is non-zero. Configured on the “Smart” tab in the IDE.
The cache pause problem: when an update interval is set, all cache copies expire simultaneously, and at the moment they are invalidated the server experiences a sharp load spike — many parallel requests rebuild the cache all at once. Additionally, the data may be stale for most of the pause interval.
The solution — Smart cache: the platform analyzes the current load and compilation time and proactively updates the cache ahead of schedule during a quiet moment. As a result, the data is more up to date and the load is smoothed out.
Smart cache parameters:
Important: for the Smart cache to work, the base cache pause must be greater than zero.
Over time, stale cache files accumulate on disk and in memory. The platform provides tools for clearing them.
Automatic clearing on update is enabled by calling:
MELBIS()->CacheClearAuto();Each time the module’s base cache is updated, old files will be deleted automatically. However, this is not recommended under high load — additional filesystem operations increase page generation time.
The recommended approach is a separate cron module that runs once a day and performs scheduled cleanup:
// In the cache cleanup cron module:
function MELBIS_CRON_CACHE_CLEAR($mVars)
{
MELBIS()->CacheBaseClear(); // clear base cache
MELBIS()->CacheTrickClear(); // clear Trick cache (deletes files older than "Max cache age")
MELBIS()->CacheStaticClear(); // clear stale APCu records of the static cache
return '';
}For
CacheTrickClearto work, modules must have the “Max cache age” parameter set — this is what determines which Trick files are considered stale.
An important architectural advantage of Melbis Shop is the batch data modification mode. Unlike systems with a web interface where each click of “Save” immediately modifies a table, managers in Melbis Shop make changes in batches: they enter several products, review them, and save everything at once. As a result, tables change infrequently and in a single pass — this is the ideal mode for a caching system, since it rebuilds the cache far less often.
Nevertheless, work organization matters. If a manager saves each product one at a time every five minutes, the cache of the corresponding modules will be rebuilt just as frequently.
To manage this, Melbis Shop includes restrictions on operation execution: in the “Users” section, for each operation you can set both time-based restrictions (for example, prohibiting saves during peak hours) and restrictions based on current server load — an operation is only permitted if the server is sufficiently free.
| Cache type | Storage location | Managed by | Purpose |
|---|---|---|---|
| SqlSelectStatic | APCu (memory) | Module code | Cache for individual queries without caching the entire module |
| Enum | PHP memory (static) | Module code | Eliminating N+1 queries in loops |
| Base | Disk | IDE → Parameters | Cache of the entire module result |
| Trick | Disk | IDE → Trick | Protection against peak loads |
| Smart | APCu + disk | IDE → Smart | Load smoothing during cache pause |
Do not cache everything indiscriminately. There is no point in caching modules that have unique input parameters on every call and a short execution time.
Set a large pause and enable Smart cache. If Smart is in use, you can safely set a pause of 100 minutes or more — the Smart cache will keep data current and smooth out the load, preventing avalanche-style updates.
Separate libraries by purpose. One large
inc library connected to all modules will cause a change to
any of its tables to reset the cache for the entire site.
Use Trick for critical blocks. The site header, catalog, homepage — anything that must remain accessible under any load should have a Trick cache.
Plan cache cleanup. Set up a cron module with a
daily call to CacheBaseClear, CacheTrickClear,
and CacheStaticClear.