Optimizing variables cache in Drupal 6

In Drupal 6, a number of caching strategies are incorporated to handle large traffic. One of them is the serialization of the whole variable table. It is being cached in the database and gets extracted into global $conf variable in each invoke.

In one of our production sites, we faced hard time to keep up with the memory requirement of PHP for the unserialization of this variable from the cache. The variables table was so large that we had to assign around 1GB memory to each PHP thread so that the value can be unserialized without memory exhaustion. This made it much harder to scale the application.

So, we decided to do something about it and successfully handled it by doing the following:

1. First of all, we installed the memcache module to move the cache storage from DB to memory

2. We then edited the memcache module’s cache_get and cache_set functions to store/retrieve individual items from the variables array and split/join them when requested.

3. This requires a memcache call for each of the items in the variable array, but consumes a small amount of memory as there is no huge unserialize operation going on.

4. We performed a few tests to see if the site is working as before, and found its working perfectly!

Here is the code in case you are facing similar issue:

/sites/all/modules/contrib/memcache/memcache.inc

1<?php
2
3// ...beginning part of the file
4
5function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
6
7 // Handle database fallback first.
8 $bins = variable_get('memcache_bins', array());
9 if (!is_null($table) && isset($bins[$table]) && $bins[$table] == 'database') {
10 return _cache_set($cid, $data, $table, $expire, $headers);
11 }
12
13 // In case of special cache items, we keep the individual items as
14 // separate cache items. Later in the retrieval time, we join them together.
15 if (memcache_is_special_cache_item($cid)) {
16
17 $keys = array_keys($data);
18 foreach ($keys as $key) {
19 cache_set($cid . '_' . $key, $data[$key]);
20 }
21
22 cache_set($cid . '_keys', $keys);
23 return true;
24
25 }
26
27 // ...remaining part of the function
28}
29
30function cache_get($cid, $table = 'cache') {
31
32 // Handle excluded bins first.
33 $bins = variable_get('memcache_bins', array());
34 if (!is_null($table) && isset($bins[$table]) && $bins[$table] == 'database') {
35 return _cache_get($cid, $table);
36 }
37
38 // The special cache item was previously saved as individual items,
39 // so now we have to retrieve them separately and join them together
40 // and send as one item.
41 if (memcache_is_special_cache_item($cid)) {
42
43 $keys = cache_get($cid . '_keys');
44 if (is_null($keys->data)) {
45 return false;
46 }
47
48 $data = array();
49 foreach ($keys->data as $key) {
50 $data[$key] = cache_get($cid . '_' . $key);
51 }
52
53 $cache = new stdClass();
54 $cache->data = $data;
55
56 return $cache;
57 }
58
59 // ...remaining part of the function
60}
61
62function memcache_is_special_cache_item($cid) {
63 $specials = array('variables', 'strongarm');
64 return in_array($cid, $specials);
65}
66
67// ...remaining part of the file

JSONize your PHP classes using traits

I was having a discussion with a colleague regarding how to add generic JSON based representation to a number of classes without making a big effort. The immediate solution that came in my mind is to use the Traits for it (introduced in PHP 5.4).

So I wrote the following example for him:

JSONized.php

1 
2<?php
3
4trait JSONized {
5
6 public function toJson()
7 {
8 $properties = get_object_vars($this);
9 return json_encode($properties);
10 }
11
12}

User.php

1<?php
2
3class User
4{
5 use JSONized;
6
7 public $username;
8 public $password;
9 public $email;
10
11 public function __construct($username, $password, $email)
12 {
13 $this->username = $username;
14 $this->password = md5($password);
15 $this->email = $email;
16 }
17}

test.php

1<?php
2
3include_once 'JSONized.php';
4include_once 'User.php';
5
6$emran = new User('phpfour', 'emran123', 'phpfour@gmail.com');
7echo $emran->toJson();

The result of running the above code will be like this:

1{
2 "username" : "phpfour",
3 "password" : "02c2ec1e3f21e97dd5ac747147a141e0",
4 "email" : "phpfour@gmail.com"
5}

Using Mockery as a mocking framework with PHPUnit

A few days ago I was explaining mocking to a few colleagues. I came up with the following quick n dirty code to show them the elegance of Mockery as a mocking framework.

The code is straight-forward: it checks the health of a server using a tiny heart beat script.

1. The script residing in the remote server:

1<?php
2
3$response = array('status' => 'online');
4
5header("Content-type: application/json");
6echo json_encode($response);

2. The class that tries to fetch the status from the remote server:

/test/Heartbeat.php

1<?php
2
3class Heartbeat
4{
5 private $http;
6
7 public function __construct(Http $http)
8 {
9 $this->http = $http;
10 }
11
12 public function checkHealth()
13 {
14 $url = 'http://localhost/remote/heart.php';
15 $response = $this->http->getResponse($url);
16
17 $beat = json_decode($response);
18 return (!is_null($beat) && $beat->status == 'online');
19 }
20}

3. The Http wrapper class that we will mock. Remember, in order to mock one object, we must be able to inject that object to the testing class using Dependency Injection.

/test/Http.php

1<?php
2class Http
3{
4 public function getResponse($url)
5 {
6 $response = @file_get_contents($url);
7 return $response;
8 }
9}

4. Finally, the test suite. Observe that we have mocked the Http class here using Mockery and have set expected function call and an expected result as per our need.

/test/HeartbeatTest.php

1<?php
2
3require_once 'gwc.autoloader.php';
4
5use Mockery as m;
6
7include_once 'Heartbeat.php';
8include_once 'Http.php';
9
10class HeartbeatTest extends PHPUnit_Framework_Testcase
11{
12 public function testSystemIsOnlineWhenServiceRuns()
13 {
14 $http = m::mock('Http');
15 $http->shouldReceive('getResponse')->andReturn('{"status":"online"}');
16
17 $heartbeat = new Heartbeat($http);
18 $response = $heartbeat->checkHealth();
19
20 $this->assertTrue($response);
21 }
22}

5. Make sure you have the following libraries installed (they have great install instructions):

6. Run the unit test using phpunit and it should pass:

1Emrans-MacBook-Air ~/Sites/test: phpunit HeartbeatTest.php
2
3PHPUnit 3.6.10 by Sebastian Bergmann.
4.
5
6Time: 0 seconds, Memory: 5.50Mb
7OK (1 test, 1 assertion)

7. Btw, the actual functionality is pretty simple to use as well. Check this script:

/test/check.php

1<?php
2
3include_once 'Heartbeat.php';
4include_once 'Http.php';
5
6$http = new Http();
7$heartbeat = new Heartbeat($http);
8$response = $heartbeat->checkHealth();
9
10echo ($response) ? 'Online' : 'Offline';

This is a very very simple use case and just meant to give an introduction. If you’re serious about unit testing, you should dig deeper into the concepts as well as into the PHPUnit and Mockery library documentations.

Node access rebuild using Drush

In one of our Drupal production site, we have around 84K nodes. Periodically we need to rebuild the content access permission, which takes around 6 hours to complete when done through the browser. Being very unhappy with it, today found the following drush use:

Emrans-MacBook-Air ~: drush php-eval 'node_access_rebuild();'

The results were immediate, the whole rebuild now takes only 20 minutes!

A clean way of integrating PHPUnit 3.5.x with CodeIgniter 2.x

Now a days, my default choice of framework is always Zend Framework. However, I have to maintain a couple live projects in CodeIgniter from early days. I feel its very important to have tests around critical applications, so I have attempted a couple times to integrate PHPUnit with CodeIgniter but failed every time – well, until now.

I’ve managed it this time with hooks. It provides a clean way of bootstrapping the framework and then performing tests on the Model layer – for me testing the model layer has been sufficient. The use is very simple as it does not require any change in how a regular CodeIgniter application is built.

Grab the code from github.

Example – /tests/PostTest.php

1<?php
2 
3class PostTest extends PHPUnit_Framework_TestCase
4{
5private $CI;
6 
7public function setUp()
8{
9$this->CI = &get_instance();
10$this->CI->load->database(‘testing’);
11}
12 
13public function testGetsAllPosts()
14{
15$this->CI->load->model(‘post’);
16$posts = $this->CI->post->getAll();
17$this->assertEquals(1, count($posts));
18}
19}

How it works

  • The provided PHPUnit bootstrap file sets the CI environment as testing and then loads the framework normally – the code is taken directly from the index.php file.
  • display_override hook checks if the environment is set to testing or not and when it is, it refrains from outputting the rendered view file
  • The PHPUnit test case file now can get a reference of the CI object using the commonly used &get_instance() method and can load models and other libraries as needed.
  • It is good to have separate database configuration for testing and it might be useful to load the test database with fresh data every time test runs – it can be easily added in the setUp method using the Database Forge classes

Let me know what you think!