Code Monkey home page Code Monkey logo

moodle-tool_forcedcache's Introduction

moodle-tool_forcedcache

GitHub Workflow Status (branch)

What is this?

This is a moodle plugin that will override Moodle's default options for caching with custom configuration. This allows for deterministic configuration, based on a lightweight configuration and rules stored in code. This has the advantage of making caching code-configurable before deployment, and allows for more control of the cache configurations throughout your fleet.

Branches

Moodle version Branch PHP
Moodle 4.0+ MOODLE_40_STABLE 7.3+
Moodle 3.5 - 3.11 master 7.1+

Installation

Requirements:

  • If you are on Moodle < 3.9, you must have the changes in MDL-41492, applied in your project as this plugin uses those interface points created.

Recommendations:

  • We recommended to have MDL-70233, installed to prevent default cache creation during CACHING_DISABLED conditions such as system upgrade.
  1. Clone the plugin
  2. Apply core patches (if required)
  3. Wire up the configuration
  4. Update cache configurations as needed
  5. Apply the new cache rules

Step 1: Clone the plugin

Using git from the root directory of your moodle:

git clone https://github.com/catalyst/moodle-tool_forcedcache.git admin/tool/forcedcache

Then run the Moodle install or upgrade as normal.

https://docs.moodle.org/en/Installing_plugins

Step 2: Apply core patches (if required)

This plugin relies on MDL-41492, so this patch must be applied to any Moodle prior to 3.9. Patches have been bundled with this plugin, to allow for quick application of the patch for various supported Moodle versions.

Step 3: Wire up the configuration

All configuration in this plugin is declared in code. You could do one of the following:

  • Set your configuration directly in a PHP array in config.php (Recommended)
  • Or Create your own configuration file (JSON), and specify the path to it in config.php
  • Or by updating the config.json that comes with the plugin, then moving it to an appropriate location.

Note: Only an array OR a path can be specified. It is not valid to declare both at once.

Defining the configuration array in PHP

The caching configuration can be set inside of config.php, by creating an associative PHP array with the appropriate structure.

Below is an example closely matching to our current production setup:

$CFG->tool_forcedcache_config_array = [
    'stores' => [
        // APCu is an in memory cache local to each front end.
        // It has a limited size and doesn't like being full.
        // See: https://docs.moodle.org/en/APC_user_cache_(APCu)
        'APCu' => [
            'type' => 'apcu',
            'config' => [
                'prefix' => 'apcu_',
            ],
        ],
        // Redis is an all round great workhorse cache which
        // we use as the main shared cache.
        // https://docs.moodle.org/en/Redis_cache_store
        'redis' => [
            'type' => 'redis',
            'config' => [
                'server' => '127.0.0.1:6379',
                'prefix' => 'mdl_',
                'password' => '',
                'serializer' => 1,
                'compressor' => 2,
            ],
        ],
        // This is a file cache local to each front end on fast SSD.
        // It is conceptual similar to but should not be confused with
        // $CFG->localcachedir which is outside of MUC.
        'local_file' => [
            'type' => 'file',
            'config' => [
                'path' => '/tmp/local-cache-file',
                'autocreate' => 1,
            ],
        ],
        // This is a shared file cache inside normal $CFG->dataroot.
        'shared_file' => [
            'type' => 'file',
            'config' => [
                'path' => '/mnt/path/to/shared-cache-file',
                'autocreate' => 1,
            ],
        ],
    ],
    'rules' => [
        'application' => [
            // These 3 definitions are localizable, but also very small and highly
            // requested so are great candidates for APCu.
            [
                'conditions' => [
                    'name' => 'core/plugin_functions',
                ],
                'stores' => ['APCu', 'redis'],
            ],
            [
                'conditions' => [
                    'name' => 'core/string',
                ],
                'stores' => ['APCu', 'redis'],
            ],
            [
                'conditions' => [
                    'name' => 'core/langmenu',
                ],
                'stores' => ['APCu', 'redis'],
            ],
            // This is another special case similar to coursemodinfo below,
            // this cache has a very large number items so we would put it
            // into local and shared files, but don't due to MDL-69088.
            // In practice this doesn't matter as rebuilding these items is
            // relatively quick, unlike coursemodinfo which is very costly.
            [
                'conditions' => [
                    'name' => 'core/htmlpurifier',
                ],
                'stores' => ['local_file'],
            ],
            // Course mod info is a special case because it is so large so we
            // use files instead of redis for the shared stacked cache.
            [
                'conditions' => [
                    'name' => 'core/coursemodinfo',
                ],
                'stores' => ['local_file', 'shared_file'],
            ],
            // Everything else which is localizable we have in both a local
            // cache backed by a shared case to warm up the local caches faster
            // while auto scaling in new front ends.
            [
                'conditions' => [
                    'canuselocalstore' => true,
                ],
                'stores' => ['local_file', 'redis'],
            ],
            // Anything left over which cannot be localized just goes into shared
            // redis as is.
            [
                'stores' => ['redis'],
            ]
        ],
        'session' => [
            [
                'stores' => ['redis'],
            ]
        ],
        'request' => [],
    ],
    'definitionoverrides' => [
        'core/plugin_functions' => [
            'canuselocalstore' => true,
        ],
    ],
];

Set a path to the JSON configuration

If 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:

$CFG->tool_forcedcache_config_path = 'path/to/config.json';

If 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 directory. Once the path is decided on, the configuration can be viewed. See Debugging for more information.

Step 4: Configure the cache settings

See Configuration for all options.

Step 5: Apply the cache rules

Please ensure that you have visited admin/tool/forcedcache/index.php and confirmed that the configuration is valid and would be applying the rules you expect BEFORE updating the factory class.

Once the plugin is installed and configured the way you want, the rules can be applied by setting a configuration variable inside config.php

$CFG->alternative_cache_factory_class = 'tool_forcedcache_cache_factory';

This will set cache configurations to be readonly, and force the configuration specified in the code.

Once 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 section below.

Configuration

Configuration Object

When creating a new configuration object, it must match to a certain structure, or the plugin will not activate. The configuration object must have:

  • a list of stores - which holds the list of cache stores available and their configuration.
  • 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.
  • a list of definitionoverrides - which lets you overide the configuration of a particular cache definitions.

Stores

'stores' => [
    'apcu-example' => [
        'type' => 'apcu',
        'config' => [
            'prefix' => 'mdl'
        ]
    ]
]

stores fields:

  • should be a hashmap of instance-name -> instance-configuration.

The example store here is an APCu store with an instance-name of apcu-example.

instance-configuration fields:

  • type is the plugin name of the matching store plugin, without the cachestore_ prefix. For example, cachestore_apcu would just be apcu.
  • config is a hashmap containing the key and value of settings that would be mapped 1:1 to control the store's instance configuration.

Rules

'rules' => [
    'application' => [
        [
            'conditions' => [ 'canuselocalstore' => true ],
            'stores' => [ 'apcu1', 'file1' ],
        ],
        [
            'stores' => [ 'file1' ],
        ],
    ],
    'session' => [
        [
            'conditions' => [ 'canuselocalstore' => true ],
            'stores' => [ 'apcu1', 'file1' ],
        ],
        [
            'stores' => [ 'file1' ],
        ],
    ],
    'request' => [],
],

rules fields:

  • a hashmap of cache-type -> rulesets (Learn about cache types - sometimes referred to as mode).
  • The 3 required cache types, are application, session and request.
  • 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.
    • order matters, the first matching set of conditions for a given ruleset will apply the stores configured.
    • 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.

ruleset fields:

  • stores: a flat array of store instance-names as defined in the previous section.
    • order matters, the stores will be applied are preferred in the order defined, the first taking preference.
  • conditions (optional) - a list of conditions which determines whether the list of stores defined in the same ruleset will apply.
    • The format for each condition is name -> value.
    • Each condition is checked against the cache definitions's properties, which could be the name, canuselocalstore, or a combination of other cache definition properties.

Definition overrides

'definitionoverrides' => [
    'core/plugin_functions' => [
        'canuselocalstore' => true
    ]
]

definitionoverrides fields:

  • a hashmap of cache-definition -> properties (to be overridden)

properties fields:

  • a hashmap of name -> value, which aligns with the property's name and value.

You can specify any config overrides here that should be applied to specific cache definitions. This is not always a safe operation, and the plugin makes no effort to ensure this won't cause issues.

Cache Stores Examples

Below are a list of cache stores and configuration boilerplates for cache stores that come pre-installed with Moodle.

APCu
'APCu' => [
    'type' => 'apcu',
    'config' => [
        'prefix' => 'apcu_'
    ]
],
File Cache
'local_file' => [
    'type' => 'file',
    'config' => [
        'path' => '/tmp/muc',
        'autocreate' => 1
    ]
],
Memcached
'memcached' => [
    'type' => 'memcached',
    'config' => [
        'servers' => [
            [
                '127.0.0.1',
                '11211',
            ]
        ],
        'compression' => 1,
        'serialiser' => 1,
        'prefix' => 'mdl',
        'hash' => 0,
        'bufferwrites' => 0,
        'clustered' => false,
        'setservers' => [],
        'isshared' => 0
    ]
],
MongoDB
'mongodb' => [
    'type' => 'mongodb',
    'config' => [
        'server' => 'mongodb://127.0.0.1:27017',
        'database' => 'mcache',
        'extendedmode' => false,
        'username' => 'username',
        'password' => 'password',
        'usesafe' => true
    ],
],
Redis
'redis' => [
    'type' => 'redis',
    'config' => [
        'server' => '127.0.0.1:6379',
        'prefix' => 'mdl_',
        'password' => 'password',
        'serializer' => 1,
        'compressor' => 2,
    ],
],

Debugging

To assist in debugging the configuration, admin/tool/forcedcache/index.php will display some information about the status of the plugin. If 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.

If 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.

Support

If you have issues please log them in github here

https://github.com/catalyst/moodle-tool_forcedcache/issues

Please note our time is limited, so if you need urgent support or want to sponsor a new feature then please contact Catalyst IT Australia:

https://www.catalyst-au.net/contact-us

This plugin was developed by Catalyst IT Australia:

https://www.catalyst-au.net/

Catalyst IT

moodle-tool_forcedcache's People

Contributors

aolley avatar brendanheywood avatar cameron1729 avatar dmitriim avatar jwalits avatar keevan avatar peterburnett avatar scottverbeek avatar srdjan-catalyst avatar tessa-fabry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moodle-tool_forcedcache's Issues

PHP Fatal error - incompatible declaration

Moodle: MOODLE_402_STABLE
tool_forcedcache: MOODLE_40_STABLE

PHP Fatal error: Declaration of tool_forcedcache_cache_factory::create_config_instance(bool $writer = false) must be compatible with cache_factory::create_config_instance($writer = false) in /path/to/moodle/admin/tool/forcedcache/classes/cache_factory.php on line 33

Pin CI to mariadb:10.5

New mariadb:10.6 makes the COMPRESSED row format read-only by default
because it's deprecated now and will be removed in mariadb:10.7

This is being tracked @ https://tracker.moodle.org/browse/MDL-72131

Once that issue is fixed and we switch to DYNAMIC or whichever the
final solution is, we'll unpin this.

(copy pasted from same issue different repository)

Expose the actual store settings in the admin ui

Previously you could see and edit the store settings, now we can't edit (yay) but we've lost visibility of the settings.

Tasks:

  • on /admin/tool/forcedcache/index.php add a head heading for stores
  • add a new sub heading for each store + a hash anchor
  • dump the type and config for the store in a table
  • from the main cache page under 'Configured store instances' deep link to each heading
  • move the forced status to be a sibling of the core caching page

Codechecker issues found since #31

For example: https://github.com/catalyst/moodle-tool_forcedcache/runs/4161249845?check_suite_focus=true


Run moodle-plugin-ci codechecker
 RUN  Moodle Code Checker on tool_forcedcache
........E..W... 15 / 15 (100%)

FILE: /home/runner/work/moodle-tool_forcedcache/moodle-tool_forcedcache/moodle/admin/tool/forcedcache/classes/check/enabled.php
------------------------------------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------
 26 | ERROR | Expected MOODLE_INTERNAL check or config.php inclusion. Change in global state detected.
    |       | (moodle.Files.MoodleInternal.MoodleInternalGlobalState)
------------------------------------------------------------------------------------------------------------------------------------


FILE: ...ner/work/moodle-tool_forcedcache/moodle-tool_forcedcache/moodle/admin/tool/forcedcache/classes/cache_administration_helper.php
------------------------------------------------------------------------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
------------------------------------------------------------------------------------------------------------------------------------
 261 | WARNING | Inline comments must end in full-stops, exclamation marks, or question marks
     |         | (moodle.Commenting.InlineComment.InvalidEndChar)
------------------------------------------------------------------------------------------------------------------------------------

Time: 1.37 secs; Memory: 18MB

Error: Process completed with exit code 1.

Can't make plugin working with Moodle 3.11 and AWS elasticache Redis

Hi,

I'm struggling to make the plugin work with my Moodle instance.
The test page says that my configuration (done in config.php) is OK.
But my Redis store isn't created and the store mappings aren't set.

My Moodle instance is deployed in AWS cluster.
The Redis host is an AWS Elastisearch host.
If I set everything manually in Moodle administration, everything is ok.

Here is the piece of my config.php dealing with the plugin.
The expression {{ ... }} are well-replaced and the resulting final file is correct.

`$CFG->tool_forcedcache_config_array = [
'stores' => [
// Redis is an all round great workhorse cache which
// we use as the main shared cache.
// https://docs.moodle.org/en/Redis_cache_store
'aws_elasticache_redis' => [
'type' => 'redis',
'name' => 'aws_elasticache_redis',
'config' => [
'server' => '{{ .Values.config.elasticacheClusterAddress }}',
'prefix' => '{{ .Values.config.redisPrefix }}',
'password' => '',
'serializer' => 1,
'compressor' => 0
]
]
],
'rules' => [
'application' => [
[
'stores' => ['aws_elasticache_redis']
]
],
'session' => [
[
'stores' => ['aws_elasticache_redis']
]
],
'request' => []
],
'definitionoverrides' => []
];

$CFG->alternative_cache_factory_class = 'tool_forcedcache_cache_factory';`

Double quote in lang string causing test failure

Double quote in lang string causing test failure:

$string['config_json_path_invalid'] = 'Invalid configuration path. Please ensure the path is outside of the "{$a}" directory.';

the double quotes inside this lang string are not being escaped correctly and cause a test failure in server/admin/tool/forcedcache/tests/cache_config_test.php

Failed asserting that exception message 'Invalid configuration path. Please ensure the path is outside of the &quot;/var/www/server&quot; directory.' contains 'Invalid configuration path. Please ensure the path is outside of the "/var/www/server" directory.'.```

Removing the quotes from the lang string as a workaround resolves this issue

Documentation Improvements Required

Documentation made it hard to get started without reading it from start to finish. There was a bit of jumping around, and certain terms weren't 100% on point.

Ideally, the documentation should allow you to:

  • get started more quickly,
  • avoid issues earlier on
  • be easier to understand.
  • quickly knowing what options are available to configure,
  • knowing how could I configure it (json, php and where)
  • understanding what does each of the fields in the configuration object means.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.