Caching

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.


1. Static Query Cache (APCu)

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.


2. Dataset Cache (Enum)

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.


3. Module Base Cache

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:

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 require or include functions 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, or MELBIS()->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();

4. Trick Cache (Smart Fallback Cache)

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:

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.


5. Smart Cache

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.


Cache Clearing

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 CacheTrickClear to work, modules must have the “Max cache age” parameter set — this is what determines which Trick files are considered stale.


Batch Data Mode and Work Organization

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.


Summary Table

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

Recommendations

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.