How to Cache CakePHP DbAcl, ACO, and ARO checks

After enabling SQL query logging to Chrome Console I began noticing that queries against the ARO table appear running at 300 milliseconds or higher. This is data that doesn’t change often and is needed on every request so its a great candidate for caching. Unfortunately I couldn’t find any mechanism for caching DbAcl checks. Here is my solution.

Enter CacheDbAcl

This is fairly straight forward. First create a new file at app/Lib/CacheDbAcl.php and copy and paste the code in (hyperlink):

Next add the following configurations to app/Config/bootstrap.php:

 

Now add or edit the following in app/Config/core.php:

 

Code Explained

The CacheDbAcl library works the exact same as the DbAcl library that is shipped with CakePHP 2.x. The only changes made are to the check() method. What it does:

  1. CacheDbAcl verifies caching is enabled. If it’s not enabled it just resumes normal operation,
  2. Reads in the name of the cache config you want to use, see CacheDbAclConfig.
  3. Checks what portion of the ARO you want to use as your cache key (more on this later), see CacheDbAclAro.
  4. Checks if an entry for the Aro, Aco, and Action exists in the cache. If not it performs its normal operations and then caches the results. Otherwise it just reads from the cache.

More on the CacheDbAclAro Setting

Since the ARO array passed into DbAcl::check can be quite different depending on your database structure and application requirements I allow you to set exactly which portion of the ARO you want to use as a unique cache key. If you want a cache created for each user, just don’t set this setting at all.

If you only want cache entries created for each group then use something similar to what I posted above. Maybe you have a special use-case and only want it using a very specific portion of the ARO as a key, this gives you that flexibility.

I went with User.UserGroup for this applicaton because our permissions are group based and I wanted to reduce the overall amount of cache entries created. When a group’s permissions are changed the modified field in the UserGroups model will naturally be updated so new cache entries will automatically be created and the old ones will eventually expire.


6 Comments

  • Toni Lehtinen says:

    Hi,

    thank you for a great article! Your CacheDbAcl class does a great job, except if you’re using a custom cache config. It doesn’t work because the config isn’t passed to Cache::read. Here’s the fix.

    $check = Cache::read($cacheKey);
    Replace with:
    $check = Cache::read($cacheKey, $cacheConfig);

    Best regards,

    Toni

  • chris says:

    Thanks toni. I noticed this as well just hadn’t gotten around to posting the fix. Thanks for the response.

  • Toni Lehtinen says:

    No problem. Found another small issue. :)
    For denied permissions, FALSE is stored to cache. Next time that cached value is not used cause Cache::read also returns FALSE when Cache::read fails. One simple fix would be to cache 1 for allowed permissions, 0 otherwise:

    // if key exists then return value
    if( $check !== false ){
    return $check == 1;
    }
    // check database and write to cache
    else {
    $check = $this->Permission->check($aro, $aco, $action);
    Cache::write($cacheKey, $check ? 1 : 0,$cacheConfig);
    }
    return $check;

  • 1. Just extend DbAcl, and only override “DbAcl::check()”
    2. The correct path is: Controller/Component/Acl/MyCustomAcl.php

    My implementation:
    http://pastebin.com/crkuM3Yq

1 Trackback

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">