Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to structure cache storage / cache clearing in a massive monolithic web app?

We are developping an application that, for many reasons, is based on a aging code base. It is actively used by customers, and keeps evolving.

We recently came accross performance problems, which we chose to solve by adding caches. Lists, Menus, big Graphs, they are all stored as some sort of cache (separate files on the file system)

For each cache you basically have two processes:

  • a cache creation process (or update)
  • a cache deletion/refreshing process

Since the application is monolithic, the possible actions that cause a cache saving or clearing are not obvious and self-explaining. There are many entry points that could trigger such a process (build a new menu? clear the menu cache - and so forth).

So we sometimes have the problem that a very minor part of the app is not clearing the cache, for example. Or another one is not refreshing the cache when it should.

Basically, we lack overview of all cache clearing/saving triggers.

Two questions:

  • how can I accurately model such an overview?
  • how should I technically organize the triggers which clear or save caches ?
like image 599
darwin Avatar asked Dec 05 '25 10:12

darwin


1 Answers

Caching is a generic kind of cross-cutting concerns and it may be solved using the Aspect-oriented programming. In a nutshell this technique allows to add or modify a behavior to existing code without modifying the code itself. All you need is to spot some join points and attach an advice to them.

For the PHP a most powerful AOP implementation provided by Go! AOP:

For example, some function that should be cached:

class AnyClass
{
    /**
     * @Cacheable
     */     
    public function someVerySlowMethod() { /* ... */ }
}

and relevant advice implementation:

class CachingAspect implements Aspect
{
    private $cache = null;

    public function __construct(Memcache $cache)
    {
        $this->cache = $cache;
    }

    /**
     * This advice intercepts the execution of cacheable methods
     *
     * The logic is pretty simple: we look for the value in the cache and if we have a cache miss
     * we then invoke original method and store its result in the cache.
     *
     * @param MethodInvocation $invocation Invocation
     *
     * @Around("@annotation(Annotation\Cacheable)")
     */
    public function aroundCacheable(MethodInvocation $invocation)
    {
        $obj   = $invocation->getThis();
        $class = is_object($obj) ? get_class($obj) : $obj;
        $key   = $class . ':' . $invocation->getMethod()->name;

        $result = $this->cache->get($key);
        if ($result === false) {
            $result = $invocation->proceed();
            $this->cache->set($key, $result);
        }

        return $result;
    }   
}

That's all. Description from the original post ("Caching Like a PRO"):

This aspect then will be registered in the AOP kernel. AOP engine will analyze each loaded class during autoloading and if a method matches the @Around("@annotation(Annotation\Cacheable)") pointcut then AOP will change it on the fly to include a custom logic of invoking an advice. Class name will be preserved, so AOP can easily cache static methods and even methods in final classes.

Note that annotation (@Cacheable) is optional: you can various ways to find a pointcut.

like image 145
Timurib Avatar answered Dec 06 '25 23:12

Timurib



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!