{"id":19591926,"url":"https://github.com/catalyst/moodle-tool_forcedcache","last_synced_at":"2025-04-27T14:33:31.874Z","repository":{"id":43287351,"uuid":"252603595","full_name":"catalyst/moodle-tool_forcedcache","owner":"catalyst","description":"Moodle MUC config managed deterministicly in code using rulesets","archived":false,"fork":false,"pushed_at":"2025-03-05T20:33:04.000Z","size":177,"stargazers_count":10,"open_issues_count":14,"forks_count":11,"subscribers_count":18,"default_branch":"MOODLE_405_STABLE","last_synced_at":"2025-04-05T00:51:12.702Z","etag":null,"topics":["cache","moodle","muc","ruleset"],"latest_commit_sha":null,"homepage":"https://moodle.org/plugins/tool_forcedcache","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/catalyst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-03T01:25:49.000Z","updated_at":"2025-03-05T20:33:09.000Z","dependencies_parsed_at":"2024-11-11T08:33:51.997Z","dependency_job_id":"75fc833e-a3fe-4bf6-9580-0fa9bd8435bf","html_url":"https://github.com/catalyst/moodle-tool_forcedcache","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fmoodle-tool_forcedcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fmoodle-tool_forcedcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fmoodle-tool_forcedcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fmoodle-tool_forcedcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catalyst","download_url":"https://codeload.github.com/catalyst/moodle-tool_forcedcache/tar.gz/refs/heads/MOODLE_405_STABLE","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251154624,"owners_count":21544531,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","moodle","muc","ruleset"],"created_at":"2024-11-11T08:31:55.963Z","updated_at":"2025-04-27T14:33:31.862Z","avatar_url":"https://github.com/catalyst.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![ci](https://github.com/catalyst/moodle-tool_forcedcache/actions/workflows/ci.yml/badge.svg?branch=MOODLE_405_STABLE)](https://github.com/catalyst/moodle-tool_forcedcache/actions/workflows/ci.yml?branch=MOODLE_405_STABLE)\n\n# moodle-tool_forcedcache\n\n* [What is this?](#what-is-this)\n* [Branches](#branches)\n* [Installation](#installation)\n* [Configuration](#configuration)\n* [Debugging](#debugging)\n* [Support](#support)\n\n## What is this?\n\nThis is a moodle plugin that will override Moodle's default options for caching with custom configuration.\nThis allows for deterministic configuration, based on a lightweight configuration and rules stored in code.\nThis has the advantage of making caching code-configurable before deployment, and allows for more control of the cache configurations throughout your fleet.\n\n## Moodle supported branches\n\n| Version    | Branch           | PHP  |\n|-------------------|------------------|------|\n| Moodle 4.5+       | MOODLE_405_STABLE | 7.3+ |\n| Moodle 4.0-4.4    | MOODLE_40_STABLE  | 7.3+ |\n\n## Totara supported branches\n| Version    | Branch           | PHP  |\n|-------------------|------------------|------|\n| Totara 19+ | TOTARA_19 | 8.1+ |\n| Totara 18  | MOODLE_40_STABLE  | 7.3+ |\n\n\n## Installation\n\n1. [Clone the plugin](#step-1-clone-the-plugin)\n2. [Wire up the configuration](#step-3-wire-up-the-configuration-if-required)\n3. [Update cache configurations as needed](#step-4-configure-the-cache-settings)\n4. [Apply the new cache rules](#step-5-apply-the-cache-rules)\n\nStep 1: Clone the plugin\n------------------------\n\nUsing git from the root directory of your moodle:\n\n```\ngit clone https://github.com/catalyst/moodle-tool_forcedcache.git admin/tool/forcedcache\n```\n\nThen run the Moodle install or upgrade as normal.\n\nhttps://docs.moodle.org/en/Installing_plugins\n\nStep 2: Wire up the configuration\n-----------------------------------------------\nAll configuration in this plugin is declared in code. You could do one of the following:\n- Set your configuration directly in a PHP `array` in config.php (Recommended)\n- Or Create your own configuration file (JSON), and specify the `path` to it in config.php\n- Or by updating the config.json that comes with the plugin, then moving it to an [appropriate location](#set-a-path-to-the-json-configuration).\n\n*Note: Only an `array` OR a `path` can be specified. It is not valid to declare both at once.*\n\n\n#### Defining the configuration `array` in PHP\nThe caching configuration can be set inside of `config.php`, by creating an associative PHP array with the appropriate structure.\n\nBelow is an example closely matching to our current production setup:\n\n```php\n$CFG-\u003etool_forcedcache_config_array = [\n    'stores' =\u003e [\n        // APCu is an in memory cache local to each front end.\n        // It has a limited size and doesn't like being full.\n        // See: https://docs.moodle.org/en/APC_user_cache_(APCu)\n        'APCu' =\u003e [\n            'type' =\u003e 'apcu',\n            'config' =\u003e [\n                'prefix' =\u003e 'apcu_',\n            ],\n        ],\n        // Redis is an all round great workhorse cache which\n        // we use as the main shared cache.\n        // https://docs.moodle.org/en/Redis_cache_store\n        'redis' =\u003e [\n            'type' =\u003e 'redis',\n            'config' =\u003e [\n                'server' =\u003e '127.0.0.1:6379',\n                'prefix' =\u003e 'mdl_',\n                'password' =\u003e '',\n                'serializer' =\u003e 1,\n                'compressor' =\u003e 2,\n            ],\n        ],\n        // This is a file cache local to each front end on fast SSD.\n        // It is conceptual similar to but should not be confused with\n        // $CFG-\u003elocalcachedir which is outside of MUC.\n        'local_file' =\u003e [\n            'type' =\u003e 'file',\n            'config' =\u003e [\n                'path' =\u003e '/tmp/local-cache-file',\n                'autocreate' =\u003e 1,\n            ],\n        ],\n        // This is a shared file cache inside normal $CFG-\u003edataroot.\n        'shared_file' =\u003e [\n            'type' =\u003e 'file',\n            'config' =\u003e [\n                'path' =\u003e '/mnt/path/to/shared-cache-file',\n                'autocreate' =\u003e 1,\n            ],\n        ],\n    ],\n    'rules' =\u003e [\n        'application' =\u003e [\n            // These 3 definitions are localizable, but also very small and highly\n            // requested so are great candidates for APCu.\n            [\n                'conditions' =\u003e [\n                    'name' =\u003e 'core/plugin_functions',\n                ],\n                'stores' =\u003e ['APCu', 'redis'],\n            ],\n            [\n                'conditions' =\u003e [\n                    'name' =\u003e 'core/string',\n                ],\n                'stores' =\u003e ['APCu', 'redis'],\n            ],\n            [\n                'conditions' =\u003e [\n                    'name' =\u003e 'core/langmenu',\n                ],\n                'stores' =\u003e ['APCu', 'redis'],\n            ],\n            // This is another special case similar to coursemodinfo below,\n            // this cache has a very large number items so we would put it\n            // into local and shared files, but don't due to MDL-69088.\n            // In practice this doesn't matter as rebuilding these items is\n            // relatively quick, unlike coursemodinfo which is very costly.\n            [\n                'conditions' =\u003e [\n                    'name' =\u003e 'core/htmlpurifier',\n                ],\n                'stores' =\u003e ['local_file'],\n            ],\n            // Course mod info is a special case because it is so large so we\n            // use files instead of redis for the shared stacked cache.\n            [\n                'conditions' =\u003e [\n                    'name' =\u003e 'core/coursemodinfo',\n                ],\n                'stores' =\u003e ['local_file', 'shared_file'],\n            ],\n            // Everything else which is localizable we have in both a local\n            // cache backed by a shared case to warm up the local caches faster\n            // while auto scaling in new front ends.\n            [\n                'conditions' =\u003e [\n                    'canuselocalstore' =\u003e true,\n                ],\n                'stores' =\u003e ['local_file', 'redis'],\n            ],\n            // Anything left over which cannot be localized just goes into shared\n            // redis as is.\n            [\n                'stores' =\u003e ['redis'],\n            ]\n        ],\n        'session' =\u003e [\n            [\n                'stores' =\u003e ['redis'],\n            ]\n        ],\n        'request' =\u003e [],\n    ],\n    'definitionoverrides' =\u003e [\n        'core/plugin_functions' =\u003e [\n            'canuselocalstore' =\u003e true,\n        ],\n    ],\n];\n```\n\n\n#### Set a `path` to the JSON configuration\nIf you choose to define your cache configuration in a JSON file, you will need to set this to a `$CFG` variable in `config.php` as shown below, to allow the plugin to use this as the preferred path to the configuration:\n```\n$CFG-\u003etool_forcedcache_config_path = 'path/to/config.json';\n```\nIf this is not supplied, the plugin will default to `config.json` inside of the plugin directory. The default is not a valid production path and this file should only serve as an example. Please move this file outside the [dirroot](https://moodle.org/mod/glossary/showentry.php?eid=20\u0026displayformat=dictionary) directory. Once the path is decided on, the configuration can be viewed. See [Debugging](#debugging) for more information.\n\n\nStep 4: Configure the cache settings\n-------------------------------------\nSee [Configuration](#configuration) for all options.\n\nStep 5: Apply the cache rules\n-----------------------------\n__Please ensure that you have visited `admin/tool/forcedcache/index.php` and confirmed that the \u003cins\u003econfiguration is valid\u003c/ins\u003e and would be \u003cins\u003eapplying the rules you expect\u003c/ins\u003e BEFORE updating the factory class.__\n\nOnce the plugin is installed and configured the way you want, the rules can be applied by setting a configuration variable inside `config.php`\n```php\n$CFG-\u003ealternative_cache_factory_class = 'tool_forcedcache_cache_factory';\n```\nThis will set cache configurations to be readonly, and force the configuration specified in the code.\n\nOnce this has been set, you can test whether or not the plugin is `active` by visiting `admin/tool/forcedcache/index.php`. With a clean install, this will apply the default plugin configurations defined in `admin/tool/forcedcache/config.json`. If there are issues at this stage, we recommend you check the previous steps, and the [Debugging](#Debugging) section below.\n\n\n\nConfiguration\n-------------\n\n#### Configuration Object\nWhen creating a new configuration object, it must match to a certain structure, or the plugin will not activate. The configuration object must have:\n- a list of `stores` - which holds the list of cache stores available and their configuration.\n- a list of `rules` - which defines the cache controls you want for different aspects of the system, such as caching at the application level, session level and request level.\n- a list of `definitionoverrides` - which lets you overide the configuration of a particular [cache definitions](https://docs.moodle.org/en/Caching#Known_cache_definitions).\n\n\n#### Stores\n```php\n'stores' =\u003e [\n    'apcu-example' =\u003e [\n        'type' =\u003e 'apcu',\n        'config' =\u003e [\n            'prefix' =\u003e 'mdl'\n        ]\n    ]\n]\n```\n`stores` fields:\n- should be a hashmap of `instance-name -\u003e instance-configuration`.\n\nThe example store here is an APCu store with an `instance-name` of `apcu-example`.\n\n`instance-configuration` fields:\n- `type` is the plugin name of the matching store plugin, __without__ the `cachestore_` prefix. For example, `cachestore_apcu` would just be `apcu`.\n- `config` is a hashmap containing the key and value of settings that would be mapped `1:1` to control the store's instance configuration.\n\n\n#### Rules\n```php\n'rules' =\u003e [\n    'application' =\u003e [\n        [\n            'conditions' =\u003e [ 'canuselocalstore' =\u003e true ],\n            'stores' =\u003e [ 'apcu1', 'file1' ],\n        ],\n        [\n            'stores' =\u003e [ 'file1' ],\n        ],\n    ],\n    'session' =\u003e [\n        [\n            'conditions' =\u003e [ 'canuselocalstore' =\u003e true ],\n            'stores' =\u003e [ 'apcu1', 'file1' ],\n        ],\n        [\n            'stores' =\u003e [ 'file1' ],\n        ],\n    ],\n    'request' =\u003e [],\n],\n```\n\n`rules` fields:\n- a hashmap of `cache-type -\u003e rulesets` ([Learn about cache types - sometimes referred to as mode](https://docs.moodle.org/en/Caching)).\n- The 3 required cache types, are `application`, `session` and `request`.\n- `rulesets` are checked and the first ruleset evaluating to true is applied. If the condition for that ruleset is evaluated to false, the next ruleset is checked. If the ruleset has no conditions, this is automatically considered as evaluating to true.\n    - __order matters__, the first matching set of conditions for a given ruleset will apply the stores configured.\n    - If there are no rulesets defined for a cache type, or there are no rulesets that a definition can match, the definition will fall through to the default store instance used for that cache type.\n\n`ruleset` fields:\n- `stores`: a flat array of store `instance-names` as defined in the [previous section](#Stores).\n    - __order matters__, the stores will be applied are preferred in the order defined, the first taking preference.\n- `conditions` (optional) - a list of conditions which determines whether the list of `stores` defined in the same ruleset will apply.\n    - The format for each condition is `name -\u003e value`.\n    -  Each condition is checked against the [cache definitions](https://docs.moodle.org/en/Caching#Known_cache_definitions)'s properties, which could be the `name`, `canuselocalstore`, or a combination of other cache definition properties.\n\n\n#### Definition overrides\n```php\n'definitionoverrides' =\u003e [\n    'core/plugin_functions' =\u003e [\n        'canuselocalstore' =\u003e true\n    ]\n]\n```\n`definitionoverrides` fields:\n- a hashmap of `cache-definition -\u003e properties (to be overridden)`\n\n`properties` fields:\n- a hashmap of `name -\u003e value`, which aligns with the property's name and value.\n\nYou can specify any config overrides here that should be applied to specific [cache definitions](https://docs.moodle.org/en/Caching#Known_cache_definitions). This is not always a safe operation, and the plugin makes no effort to ensure this won't cause issues.\n\n\n#### Cache Stores Examples\nBelow are a list of cache stores and configuration boilerplates for cache stores that come pre-installed with Moodle.\n\n##### APCu\n```php\n'APCu' =\u003e [\n    'type' =\u003e 'apcu',\n    'config' =\u003e [\n        'prefix' =\u003e 'apcu_'\n    ]\n],\n```\n\n##### File Cache\n```php\n'local_file' =\u003e [\n    'type' =\u003e 'file',\n    'config' =\u003e [\n        'path' =\u003e '/tmp/muc',\n        'autocreate' =\u003e 1\n    ]\n],\n```\n\n##### Memcached\n```php\n'memcached' =\u003e [\n    'type' =\u003e 'memcached',\n    'config' =\u003e [\n        'servers' =\u003e [\n            [\n                '127.0.0.1',\n                '11211',\n            ]\n        ],\n        'compression' =\u003e 1,\n        'serialiser' =\u003e 1,\n        'prefix' =\u003e 'mdl',\n        'hash' =\u003e 0,\n        'bufferwrites' =\u003e 0,\n        'clustered' =\u003e false,\n        'setservers' =\u003e [],\n        'isshared' =\u003e 0\n    ]\n],\n```\n\n##### MongoDB\n```php\n'mongodb' =\u003e [\n    'type' =\u003e 'mongodb',\n    'config' =\u003e [\n        'server' =\u003e 'mongodb://127.0.0.1:27017',\n        'database' =\u003e 'mcache',\n        'extendedmode' =\u003e false,\n        'username' =\u003e 'username',\n        'password' =\u003e 'password',\n        'usesafe' =\u003e true\n    ],\n],\n```\n\n##### Redis\n```php\n'redis' =\u003e [\n    'type' =\u003e 'redis',\n    'config' =\u003e [\n        'server' =\u003e '127.0.0.1:6379',\n        'prefix' =\u003e 'mdl_',\n        'password' =\u003e 'password',\n        'serializer' =\u003e 1,\n        'compressor' =\u003e 2,\n    ],\n],\n```\n\n## Debugging\nTo assist in debugging the configuration, `admin/tool/forcedcache/index.php` will display some information about the status of the plugin.\nIf there are any errors reported when creating configuration from the JSON file, the error message will be displayed on this page. If the JSON is able to be parsed, the rulesets configuration will be displayed for each of the caching modes.\n\nIf the plugin has been enabled, you can also visit `cache/admin.php` to view the overall configuration. If there are any store instances defined in the JSON that are not appearing in the list of configured instances, it means that a store instance was unable to be created from the supplied configuration. Check the `config` settings under the relevant store inside the defined configuration.\n\n## Support\nIf you have issues please log them in github here\n\nhttps://github.com/catalyst/moodle-tool_forcedcache/issues\n\nPlease note our time is limited, so if you need urgent support or want to\nsponsor a new feature then please contact Catalyst IT Australia:\n\nhttps://www.catalyst-au.net/contact-us\n\nThis plugin was developed by Catalyst IT Australia:\n\nhttps://www.catalyst-au.net/\n\n\u003cimg alt=\"Catalyst IT\" src=\"https://cdn.rawgit.com/CatalystIT-AU/moodle-auth_saml2/master/pix/catalyst-logo.svg\" width=\"400\"\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatalyst%2Fmoodle-tool_forcedcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatalyst%2Fmoodle-tool_forcedcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatalyst%2Fmoodle-tool_forcedcache/lists"}