Jump to content
ethanbeyer

Questions about effective Caching

Recommended Posts

Hello, I'm hoping for a little help understanding the different types of caching baked in to ProcessWire, as well as when one should use which. As I understand it, there are two methods for caching, with several different interfaces:

1. Database Caching

Within the realm of Database Caching, there is the WireCache class, accessible through the $cache variable. This has various methods, but you can save just about anything to the caches table in the ProcessWire Database, and set a time for expiration, and retrieve it. Nice! It seems like this is the most feature-rich way to cache something that isn't a template, but I don't know how it works performance-wise against, say, MarkupCache.

2. File Caching

Then there's File Caching - and the two interfaces that I know of that use this method are Template Caching and the MarkupCache plugin.

Template Caching generates the full $page->render(), whereas MarkupCache takes whatever markup one wants to save and outputs that to a file that expires with the given time.

 

Questions and Considerations

  1. If saving Markup, which is faster: Database or File Caching?
  2. Should we use MarkupCache at all, when WireCache seems to get more attention?

Share this post


Link to post
Share on other sites
2 hours ago, ethanbeyer said:

If saving Markup, which is faster: Database or File Caching?

This is certainly nothing, which can be answered in absolute terms. File system speed depends on the filesystem. A RAM disk is way faster than some old-ish HDD. The database might be local and mostly in RAM, but could be on another machine with network latency to consider. You'd need to test this on the actual machine you're working with under the expected load you're working with. If you're already doing much filesystem IO WireCache might perform better. If you filesystem IO is not saturated, but your db is queried a ton, then adding even more load to the db might not be the best choice. The latter is probably the more usual case for ProcessWire usage.

2 hours ago, ethanbeyer said:

Should we use MarkupCache at all, when WireCache seems to get more attention?

Attention seems like one of the worst metric to judge the usefulness of features by. Afaik WireCache was added to allow for caching of actual data (like pagearrays), which is something MarkupCache simply could not do and cannot do. If you don't need that as a feature performance would be the only thing I would care for.

  • Like 5

Share this post


Link to post
Share on other sites

I do a lot with $cache, it's great and super flexible, and easy to implement. I expect i'll be using it more and more in the future.

I don't use markup or template cache much;

I always use ProCache, but i think it's still important to have pages load pretty fast without it, as the first page load that generates the cache needs to be something reasonable that doesn't overload the server, or bounce the visitor.

One thing i did recently on a few challenging situations was to use the $page to store markup. For example, using something like wireRenderFile, you can generate the  some bit of markup and store it with the page, in a hidden field; this markup can be regenerated on save of the page using a hook, and then it doesn't have to wait to get regenerated on the front end.

Another thing that is interesting to do is $cache within $cache, ... had to do it in a few places, it was tricky, but it really made a difference in speed..

  • Like 4

Share this post


Link to post
Share on other sites

As @Macrura said in most cases ProCache and WireCache is the way to go for me. Also what I have found super useful is namespaces for WireCache, as for the example you can load all cache for a page with namespaced cache and preloadFor method just with one query. 

7 hours ago, Macrura said:

Another thing that is interesting to do is $cache within $cache, ... had to do it in a few places, it was tricky, but it really made a difference in speed..

There is CacheNesting module, I haven't used it but it should help to handle such scenarios

https://modules.processwire.com/modules/cache-nesting/

  • Like 4

Share this post


Link to post
Share on other sites

Thank you all for your input!

16 hours ago, LostKobrakai said:

Attention seems like one of the worst metric to judge the usefulness of features by. Afaik WireCache was added to allow for caching of actual data (like pagearrays), which is something MarkupCache simply could not do and cannot do. If you don't need that as a feature performance would be the only thing I would care for.

This is really helpful, and how I had started seeing the issue as well. What I meant by "attention" was the development maintenance, but you're right: updates don't equate to usefulness.

 

11 hours ago, Macrura said:

I do a lot with $cache, it's great and super flexible, and easy to implement. I expect i'll be using it more and more in the future.

I'm with you - it's what I have used most, also, and for me and the sites I'm building with ProcessWire, I think it makes sense to use one method instead of both. I particularly like using the namespaces in the WireCache as @Zeka mentioned as well - it makes expiring similar things, or preloading similar things, very easy.

Share this post


Link to post
Share on other sites
On 1/29/2019 at 12:29 PM, Zeka said:

As @Macrura said in most cases ProCache and WireCache is the way to go for me. Also what I have found super useful is namespaces for WireCache, as for the example you can load all cache for a page with namespaced cache and preloadFor method just with one query. 

@Macrura I just stumbled across this interesting older post. Since I'm currently working on a module that accesses an external REST API, the topic of caching is very interesting. I use WireCache to cache the REST queries and am currently working on improving performance. I stumbled across preloadFor. Does anybody have a code example which shows how preloadFor can/should be used to improve performance?

This is a sample how I'm currently using WireCache:

    public function getSettings($key = '', $expires = WireCache::expireNever, $forceRefresh = false) {
        if (!$this->getHeaders()) {
            $this->error($this->noticesText['error_no_headers']);
            return false;
        }
        if ($forceRefresh) $this->wire('cache')->deleteFor('SnipWire', self::cacheNameSettings);

        // Try to get settings array from cache first
        $response = $this->wire('cache')->getFor('SnipWire', self::cacheNameSettings, $expires, function() {
            return $this->getJSON(self::apiEndpoint . self::resourcePathSettingsGeneral);
        });
        return ($key && isset($response[$key])) ? $response[$key] : $response;
    }

 

  • Like 1

Share this post


Link to post
Share on other sites

@Gadgetto

There is nothing special in using preloadFor method. All you have to do is to call it before any calls of get method

$cache->preloadFor($this->className());

As you can see I have a lot of chunks of cache namespaced with TestsDashboard. 

Instead of multiple select queries 

67	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2725, null)
68	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2726, null)
69	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2727, null)
70	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2728, null)
71	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2729, null)
72	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2730, null)
73	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2731, null)
74	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2732, null)
75	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2733, null)
76	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2734, null)
77	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2735, null)
78	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2736, null)
79	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2737, null)
80	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2738, null)
81	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2739, null)
82	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2740, null)
83	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2741, null)
84	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2742, null)
85	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2743, null)
86	SELECT name, data FROM caches WHERE (name=:name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__test-intro-2744, null)

using preloadFor I will load them to memory from DB only with one query and then all next calls of get will load them not from DB, but from memery

SELECT name, data FROM caches WHERE (name LIKE :name1) AND (expires>=:now OR expires<=:never) -- cache.get(TestsDashboard__*, null)

 

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...