Question Adding models to vBoptimise opcache

Status
Not open for further replies.
Hello,

I am writing a data cache extension to vBoptimise to use redis as a data store rather than memcache. Long story short, the memcache store is having issues with our site because the forumcache object is so large; and I have rewritten the core vbulletin data store to use redis master/slave caching.

Anyway, I seem to be running into a few issues. I'm not sure where to register the new model. I used this code to register it in init_startup.php hook:

vb_optimise::$prefix = $vbulletin->options['vbo_prefix'];
/* XDA Modification */
vb_optimise::register('models', 'redis');
vb_optimise::assign('redis');
//vb_optimise::assign($vbulletin->options['vbo_operator']);
/* End of XDA Modification */
vb_optimise::cache('datastore', $datastore_fetch);

I initially added in as a plugin to vBulletin, perhaps due to execution order it was not actually being used. I added it to the init_startup.php hook location and it does seem to be loading it, but vBoptimise isn't using it as a store. Also, in the admin interface I see that Redis is not added as an option, the options are hard coded into the installation .xml file.

How should I properly add this as an extension for data store?

Thank you.
 
Last edited:
There is one spot where it does seem to work, I use the 'test' functionality to see if it reports any errors. It reports connection to "memcache" successfull even though I have turned off memcache.

In redis I see one key:

vbo_systest_temp

So I think it works for the test but not for any live caching.
 
I think all that's needed is to add a new file to /dbtech/vboptimise/includes/cachers/ called cacher_redis.php - look at /dbtech/vboptimise/includes/class_operator_model.php and override methods as required in the cacher_redis.php file.

Then, modify /dbtech/vboptimise/includes/class_vboptimise.php and add redis at the end of the $models array at the top of the file.

Lastly, modify the Opcache Operator setting (using vB debug mode) and add redis|Redis

Once all that has been done, I believe selecting Redis in the setting will work as intended. If that doesn't work for you, could you point me towards the Redis documentation / installation instructions and I'll have a crack at it myself. I am using a Mac as a dev server, but using Homebrew I can most likely get Redis installed if it doesn't support Macs natively :)


I see the register() function, which should work with your code, but the problem is that the vBOption is hardcoded, so it doesn't seem to dynamically fetch the supported operators. That's something I'll need to correct in the future.
 
Last edited:
If I read your code right, I believe you mean to put redis.php into the ./vboptimise/includes/operators/ folder as it is an operator and not a cacher (not the data type). Here is my code for redis.php:

PHP:
<?php
class vb_optimise_redis extends vb_optimise_operator
{
	protected $cacheType = 'redis';	
	var $redis, $redis_read;
	var $connected = false;

	public function connect()
	{
		global $vbulletin;

		if (!class_exists('Redis'))
		{
			return false;
		}

		if ($this->connected)
		{
			return $this->connected;
		}

		$this->redis_read = new Redis();
                $this->redis = new Redis();  

		// first, connect to redis server, find out if we are master or slave; make master connection
        foreach ($vbulletin->config['Misc']['redisServers'] as $server) {
            if ($this->redis_read->pconnect($server[0], $server[1], $vbulletin->config['Misc']['redisTimeout'], NULL, $vbulletin->config['Misc']['redisRetry'] )) {
                break;
            } 
        }

        try {
            $redis_info = $this->redis_read->info();
        } catch (Exception $ex) {
            trigger_error('No valid caching servers found.', E_USER_ERROR);
        }

        // If this server is master, just create a copy
        if ($redis_info['role'] == 'master') {
            $this->redis = &$this->redis_read;
        // else read master info from the slave server, and make a connection to that master
        } else if ($redis_info["master_link_status"] == "up") {
            // find the master server
            $master_host = $redis_info["master_host"];
            $master_post = $redis_info["master_port"];

            if (!$this->redis->pconnect($master_host, $master_port, $vbulletin->config['Misc']['redisTimeout'], NULL, $vbulletin->config['Misc']['redisRetry'])) {
                trigger_error('Master cache server is offline.', E_USER_ERROR);
            }

            if ($redis_info['master_last_io_seconds_ago'] > $vbulletin->config['Misc']['redisMaxDelay']) {
                // if this slave gets out of sync with master, switch to master redis instance to both read/write
                $this->redis_read = &$this->redis;
            }

        } else {
            trigger_error('Can not find write cache redis server.', E_USER_ERROR);
        }

		$this->connected = true;

		return true;
	}

	public function _get($id = '')
	{
		if (!$this->connect())
		{
			return false;
		}

		return $this->redis_read->get($this->id($id));
	}

	public function _set($id = '', $value = '')
	{
		global $vbulletin;

		if (!$this->connect())
		{
			return false;
		}

		if (!is_array($value) && trim($value) == '')
		{
			$value = '{VBO_BLANK}';
		}
        /**
         * EX seconds -- Set the specified expire time, in seconds.
         * PX milliseconds -- Set the specified expire time, in milliseconds.
         * NX -- Only set the key if it does not already exist.
         * XX -- Only set the key if it already exist.
         * 
         */
		$this->redis->set($this->id($id), $value, Array('ex' => $vbulletin->options['vbo_ttl']));
	}

	public function do_flush($silent = false)
	{
		@$this->redis->flushAll();
	}
}
?>

If you use this you will need to add to vB's config.php:

PHP:
// Only use this for general vB redis data store
//$config['Datastore']['class'] = 'vB_Datastore_Redis';

$config['Misc']['redisServers'] = array(
    array('172.17.0.15' , 6379),
    array('172.17.0.16' , 6379),
);
$config['Misc']['redisMaxDelay'] = 10; // is slave server is out of sync by more than this many seconds, switch to master
$config['Misc']['redisTimeout'] = 3; // if redis server doesn't respond in this many seconds, disconnect
$config['Misc']['redisRetry'] = 100; // retry connection in this many milliseconds

And then as I said, we modified the hook init_startup.php:

PHP:
 vb_optimise::$prefix = $vbulletin->options['vbo_prefix'];
-vb_optimise::assign($vbulletin->options['vbo_operator']);
+/* XDA Modification */
+vb_optimise::register('models', 'redis');
+vb_optimise::assign('redis');
+//vb_optimise::assign($vbulletin->options['vbo_operator']);
+/* End of XDA Modification */
 vb_optimise::cache('datastore', $datastore_fetch);

This forces the redis model (I believe). Like I said it works for the admin functions apparently, but I do not see the styles & other information cached in Redis.

I checked the message and it looks like it is properly registering/assigning the redis model, I just don't see it caching the styles. I don't see any errors.

For launching a simple Redis environment, I highly recommend installing Docker. You can install docker and run this script and you'll have a working Redis master/slave cluster to experiment with.

Please let me know if you have any questions, you can also hit me up on Hangouts at ddrager @ gmail.
 
Looks like redis can't automatically store arrays, that's a bit of a shame. I've worked around this though, by serializing and unserializing the values in the _get and _set methods of redis.php.
The Redis implementation is fully working :)

Code:
Fillips-MacBook-Pro:~ filliph$ redis-cli
127.0.0.1:6379> keys *
  1) "vbo_template_35249"
  2) "vbo_template_24899"
  [...]
150) "vbo_notices_1"
[...]
127.0.0.1:6379> quit

I did not make any modifications to init_startup.php as per my previous post as this renders said modification unnecessary. I'll release a Beta build of vBO tonight with Redis implemented and you can see if it works for you.
 
Nice - can't wait to see it.

I am working on migrating to Redis because memcache has a bit of a bottleneck when your forum gets too big (data size, traffic size). The Forumcache object - which gets larger based on how many forums you have, is also requested on every php request possibly multiple times. Memcache shards data by putting each key onto one of the servers in the memcache cluster - meaning that 1 memcache server is doing 600+mbps while the others are at ~ 20mbps. We kept adding memcache server but one server always did 600mbps+

By switching to redis using master/slave - I am setting up a redis server on each php server as a slave, and having a single redis server serve as master for writes. Since write volume is relatively small, and now reads are all done locally on the machine through localhost, I am hoping this will eliminate some latency. I'll update when I find out how much exactly or if there are any drawbacks to this switch.
 
Interesting. I'm currently using XCache. I saw Redis is in the latest build of VBOptimise, and thinking of giving it ago, and came across this post here. My forum isn't as big as yours, but I'm looking at any performance increase I can get.
I will have to try some speed tests.
 
Great - this seems to be working. I will be deploying this out to our production servers tonight and will let you know how it goes :)
 
We are running all of our datastore cache under redis now - about 23k commands / sec. It did shave about 60ms time off of each request but doesn't seem to have made php overall more responsive.

Still having some issues where we have spikes, I believe when datastore cache is invalidated and needs to be rebuilt. Still doing some research on this.
 
Status
Not open for further replies.

Legacy vB Optimise

vBulletin 3.8.x vBulletin 4.x.x
Seller
DragonByte Technologies
Release date
Last update
Total downloads
1,972
Customer rating
0.00 star(s) 0 ratings
Back
Top