MyBB Community Forums

Full Version: How to use in-memory cache?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I have a plugin that generates a table with topposts and posts thumbnails. This is quite intensive and might cause concurrency problems, so I would like to generate it on a task hourly, save in memory, and make it available to my plugin when adding content to the index.

I read here that the cache is configured centrally so that all cache will use the same type (DB, disk, memory).

Is it possible to use the memory cache only for my plugin, as it will be a relatively small piece of html and will be shown to every single time someone views the index.

If not, just to clarify, is this below how the $cache is used?

Have the following inside my task:
global $cache;
$topposts_cached= "<table>...</table>";
$cache->update('topposts_table', $topposts_cached);

Have the following inside my plugin index hook:
global $cache;
$topposts_cached = $cache->read('topposts');
//check if $topposts_cached exists and show content
That's the DB backed cache; you can use PluginLibrary for a (by default) disk-only cache. You should be aware that cache may vanish at any time (if it's memcached and not backed by DB). So creating a memory-only cache in a task is not possible - instead you have to create whenever it's not there.
(2013-05-17, 08:24 PM)frostschutz Wrote: [ -> ]That's the DB backed cache; you can use PluginLibrary for a (by default) disk-only cache. You should be aware that cache may vanish at any time (if it's memcached and not backed by DB). So creating a memory-only cache in a task is not possible - instead you have to create whenever it's not there.

Just to clarify: the DB backed cache will hit the DB everytime a "$cache->read('topposts')" is executed?

And is the way I showed correct?
it will generate a query only if the $cache object does not contain a 'topposts' item and your plugin generates it.

you could run a hook on 'global_end' to call a function and then filter on THIS_SCRIPT so you only populate the cache once if it does not exist.

then when it is called, it is populated but on pages it is needed.

so for a plugin that wants to use it, you should do something like

$plugins->add_hook('global_end', 'topposts_loadcache');

function topposts_loadcache()
{
 global $cache, $topposts, $mybb, $db;
 if(THIS_SCRIPT == 'page your want it to run on')
 {
  $topposts = $cache->read('topposts');
  if(!$topposts)
  {
    //run query to get top posts
    //populate $toppposts as it would be in $cache
    //finally
    $cache->update('topposts', $topposts);
  }
 }
}
(2013-05-17, 08:45 PM)echofloripa Wrote: [ -> ]Just to clarify: the DB backed cache will hit the DB everytime a "$cache->read('topposts')" is executed?

It hits the DB on $cache->update().

For $cache->read() it depends. In DB cache mode, all DB caches are fetched in a single query for every request, regardless whether the cache data is actually required for that request or not. In all other cache modes, $cache->read() tries to load the cache from whereever (disk, memcached, etc.) and hits the DB only if it's not to be found otherwise.

PluginLibrary's cache never uses the DB, but you have to cope with cache missing, e.g. regenerate content on the fly if the file or memcached entry isn't there.


Is your way correct? If it works, yes, if it doesn't work, no.
(2013-05-17, 08:58 PM)pavemen Wrote: [ -> ]it will generate a query only if the $cache object does not contain a 'topposts' item and your plugin generates it.

you could run a hook on 'global_end' to call a function and then filter on THIS_SCRIPT so you only populate the cache once if it does not exist.

then when it is called, it is populated but on pages it is needed.

so for a plugin that wants to use it, you should do something like

$plugins->add_hook('global_end', 'topposts_loadcache');

function topposts_loadcache()
{
 global $cache, $topposts, $mybb, $db;
 if(THIS_SCRIPT == 'page your want it to run on')
 {
  $topposts = $cache->read('topposts');
  if(!$topposts)
  {
    //run query to get top posts
    //populate $toppposts as it would be in $cache
    //finally
    $cache->update('topposts', $topposts);
  }
 }
}

THIS_SCRIPT needs to be a standalone page? Can it be a function inside my own plugin or if it is it will hang the requester until if finishes it? I could also run it at the end of the activation process, couldn't it? Then there wouldn't need to run it on the global_end, is that correct?
THIS_SCRIPT is a constant defined in every default MyBB page that is accessible via the browser. Just look at the top of index.php.

If you create your own page, then you should define a similar line. You use THIS_SCRIPT to filter when you want to load/update your cache items so it only runs on pages that require it.
I understand now, thank you for explaining. Sorry for boldness to create a plugin without much knowledge of PHP neither MYBB framework Smile Learning a lot on the way though Wink

But I think that doing this would still cause concurrency issues, as when it creates the table it needs to fetch and resize all images of as many posts as it will show, and that might take many seconds. I suspect that this is what cause the issue in the first place.

Thanks a lot for all the answers
Eco
The query you posted in your other thread looks expensive.

There's no time for something that takes "many seconds", not unless you can do it in the background and cache it for a considerable amount of time. The problem with threads in particular is that every user group has different permissions so you must be careful what (not) to show.

The Overview plugin has the same issue, it creates a separate cache for every permission structure. And it doesn't overcomplicate things (such as querying which threads are (un)read - in which case you couldn't cache anything at all anymore as that changes all the time).

For images, if that's really necessary, it'd probably have to be done the same way it's done for avatars... make this data directly accessible somehow, without parsing things anew every time its needed.
(2013-05-17, 11:50 PM)frostschutz Wrote: [ -> ]The query you posted in your other thread looks expensive.

That's why I want to run as little as possible Smile

(2013-05-17, 11:50 PM)frostschutz Wrote: [ -> ]For images, if that's really necessary, it'd probably have to be done the same way it's done for avatars... make this data directly accessible somehow, without parsing things anew every time its needed.

Images are what are hot in this plugin Smile They way I do, when the table is first created with the top threads for each period of time, it checks if the thumbnail is already created, they are stored in images/topposts/thumbnails/[tid]-image.jpg. In the subsequent requests of the thread image it will get it directly.

Sometimes, when the first post of the thread doesn't have an image, it could try searching the other replies of the thread, but I just assign to it a default image. I plan in later adding a hook so that after a post is edited the plugin checks if an image was added to the updated thread and re-create its thumbnail. Or it could have a task that creates thumbnails in batch. I am planning to create other plugins or changes that would use the images in the same way, as showing the thread(s) that is (are) most viewed at a certain time, or show a list of the latest threads updated with the image. For this I might also change the sidebox plugin which I already use.