Use MediaWiki cache strategy and code

This removes all BugzillaCache* classes and code that used it,
replaces it with MediaWiki `wfGetCache($wgMainCacheType)`,
which provides sensibly the same set of options.

Doing so also simplifies a first install, by removing specific
configuration for this extension.
This commit is contained in:
Romain d'Alverny 2016-09-27 11:23:40 +02:00
Родитель 59c74e67be
Коммит eaa6d855c3
13 изменённых файлов: 48 добавлений и 375 удалений

Просмотреть файл

@ -5,11 +5,6 @@
$dir = dirname(__FILE__);
require_once ($dir . '/BugzillaOutput.class.php');
require_once ($dir . '/cache/BugzillaCacheI.class.php');
require_once ($dir . '/cache/BugzillaCacheDummy.class.php');
require_once ($dir . '/cache/BugzillaCacheApc.class.php');
require_once ($dir . '/cache/BugzillaCacheMemcache.class.php');
require_once ($dir . '/cache/BugzillaCacheSql.class.php');
// Factory
class Bugzilla {
@ -45,38 +40,4 @@ class Bugzilla {
return new $class($theconfig, $opts, $title);
}
/**
* Return the BugzillaCacheI extended class in charge
* for the cache backend in use.
*
* @param string $type
*
* @return string
*/
public static function getCacheClass( $type ) {
$suffix = 'dummy';
if ( in_array( $type, array( 'mysql', 'postgresql', 'sqlite' ) ) ) {
$suffix = 'sql';
} elseif ( in_array( $type, array( 'apc', 'memcache' ) ) ) {
$suffix = $type;
}
return 'BugzillaCache' . ucwords( $suffix );
}
/**
* Build and return a working cache, depending on config.
*
* @return BugzillaCacheI object
*/
public static function getCache() {
global $wgBugzillaCacheType;
$object = self::getCacheClass( $wgBugzillaCacheType );
return new $object();
}
}

Просмотреть файл

@ -51,33 +51,15 @@ $wgExtensionMessagesFiles['Bugzilla'] = "$cwd/Bugzilla.i18n.php";
$wgAutoloadClasses['Bugzilla'] = $cwd . '/Bugzilla.class.php';
$wgAutoloadClasses['BugzillaQuery'] = $cwd . '/BugzillaQuery.class.php';
$wgAutoloadClasses['BugzillaOutput'] = $cwd . '/BugzillaOutput.class.php';
$wgAutoloadClasses['BugzillaCacheI'] = $cwd . '/cache/BugzillaCacheI.class.php';
$wgAutoloadClasses['BugzillaCacheDummy'] = $cwd . '/cache/BugzillaCacheDummy.class.php';
$wgAutoloadClasses['BugzillaCacheApc'] = $cwd . '/cache/BugzillaCacheApc.class.php';
$wgAutoloadClasses['BugzillaCacheMemcache'] = $cwd . '/cache/BugzillaCacheMemcache.class.php';
$wgAutoloadClasses['BugzillaCacheSql'] = $cwd . '/cache/BugzillaCacheSql.class.php';
/**
* These hooks are used by mediawiki to properly display the plugin information
* and properly interpret the tags used.
*/
$wgHooks['LoadExtensionSchemaUpdates'][] = 'BugzillaCreateCache';
$wgHooks['BeforePageDisplay'][] = 'BugzillaIncludeHTML';
$wgHooks['ParserFirstCallInit'][] = 'BugzillaParserInit';
// Schema updates for the database cache
function BugzillaCreateCache($updater) {
global $wgBugzillaCacheType;
$class = Bugzilla::getCacheClass($wgBugzillaCacheType);
$class::setup($updater);
// Let the other hooks keep processing
return true;
}
// Add content to page HTML
function BugzillaIncludeHTML( &$out, &$sk ) {
@ -185,10 +167,11 @@ $wgBugzillaMethod = 'REST'; // XML-RPC and JSON-RPC may be supported later
// Cache
// NOTE: $wgBugzillaUseCache has been removed. Use $wgBugzillaCacheType below only:
// - any valid value for using it
// - equivalent to previous $wgBugzillaUseCache = false; is $wgBugzillaCacheType = 'dummy';
$wgBugzillaCacheType = 'mysql'; // valid values are: memcache, apc, mysql, postgresql, sqlite, dummy.
$wgBugzillaCacheMins = 5; // Minutes to cache results (default: 5)
// NOTE: $wgBugzillaUseCache has been removed. $wgBugzillaCacheType has been removed as well.
// NOTE: This extension now relies on what cache is available through MediaWiki directly;
// NOTE: see $wgMainCacheType in LocalSettings.php
$wgBugzillaCacheTimeOut = 5; // Minutes to cache results (default: 5)
$wgBugzillaJqueryTable = true; // Use a jQuery table for display (default: true)

Просмотреть файл

@ -3,7 +3,6 @@
abstract class BugzillaOutput {
public $response;
public $cache;
public function __construct($config, $options, $title='') {
$this->title = $title;
@ -58,15 +57,6 @@ abstract class BugzillaOutput {
}
protected function _getCache()
{
if (!$this->cache) {
$this->cache = Bugzilla::getCache();
}
return $this->cache;
}
abstract protected function setup_template_data();
}
@ -146,15 +136,22 @@ abstract class BugzillaGraph extends BugzillaOutput {
include_once 'pchart/class/pData.class.php';
global $wgBugzillaChartUrl;
global $wgBugzillaCacheTimeOut;
global $wgMainCacheType;
$key = md5($this->query->id . $this->_get_size() . get_class($this));
$cache = $this->_getCache();
if($result = $cache->get($key)) {
$image = $result;
$this->response->image = $wgBugzillaChartUrl . '/' . $image;
} else {
$this->response->image = $wgBugzillaChartUrl . '/' . $this->generate_chart($key) . '.png';
$fileName = sha1(serialize([$this->query->id, $this->_get_size(), get_class($this)]));
$key = implode(':', ['mediawiki', 'bugzilla', 'chart', $fileName]);
$cache = wfGetCache($wgMainCacheType);
// We use the cache only to invalidate/recompute the charts:
// the key is its own value. Only the TTL is useful here.
if ($cache->get($key) === false) {
if ($this->generate_chart($fileName)) {
$cache->set($key, $fileName);
}
}
$this->response->image = $wgBugzillaChartUrl.'/'.$fileName.'.png';
}
}
@ -227,8 +224,7 @@ class BugzillaPieGraph extends BugzillaGraph {
$pPieChart->drawPieLegend(2*$radius + 2*$padding, $padding, array("Alpha"=>20));
$pImage->render($wgBugzillaChartStorage . '/' . $chart_name . '.png');
$cache = $this->_getCache();
$cache->set($chart_name, $chart_name . '.png');
return $chart_name;
}
}
@ -252,10 +248,8 @@ class BugzillaBarGraph extends BugzillaGraph {
$pImage->drawBarChart();
$pImage->render($wgBugzillaChartStorage . '/' . $chart_name . '.png');
$cache = $this->_getCache();
$cache->set($chart_name, $chart_name . '.png');
return $chart_name;
}
}

Просмотреть файл

@ -31,19 +31,10 @@ abstract class BugzillaBaseQuery {
$this->error = FALSE;
$this->data = array();
$this->synthetic_fields = array();
$this->cache = FALSE;
$this->cached = FALSE;
$this->options = $this->prepare_options($options, $wgBugzillaDefaultFields);
}
protected function _getCache()
{
if (!$this->cache) {
$this->cache = Bugzilla::getCache();
}
return $this->cache;
}
public function id() {
if (!$this->id) {
$this->id = $this->_generate_id($this->options);
@ -112,33 +103,35 @@ abstract class BugzillaBaseQuery {
return $options;
}
// Connect and fetch the data
/**
* Wrap around sub-classes actual fetch action, with caching.
* Uses MediaWiki main cache strategy.
*
* TODO: use ObjectCache::getLocalServerInstance() once MW >= 1.27
*
* @return string
*/
public function fetch() {
global $wgBugzillaCacheMins;
global $wgMainCacheType;
global $wgBugzillaCacheTimeOut;
// Don't do anything if we already had an error
if( $this->error ) { return; }
if ($this->error) { return; }
$cache = $this->_getCache();
$row = $cache->get($this->id());
$key = implode(':', ['mediawiki', 'bugzilla', 'bugs', sha1(serialize($this->id()))]);
$cache = wfGetCache($wgMainCacheType);
$row = $cache->get($key);
// If the cache entry is older than this we need to invalidate it
$expiry = strtotime("-$wgBugzillaCacheMins minutes");
if ($row === false) {
$this->cached = false;
if( !$row ) {
// No cache entry
$this->cached = false;
$this->_fetch_by_options();
$cache->set($key, base64_encode(serialize($this->data)), $wgBugzillaCacheTimeOut * 60);
// Does the Bugzilla query in the background and updates the cache
$this->_fetch_by_options();
$this->_update_cache();
return $this->data;
return $this->data;
} else {
// Cache is good, use it
$this->cached = true;
$this->data = unserialize(base64_decode($row));
$this->cached = true;
return $this->data = unserialize(base64_decode($row));
}
}

Просмотреть файл

@ -19,16 +19,11 @@ Installation
Please substitute your installation path if it is different*
1. Install the requirements above
2. Check the project out into `/var/lib/mediawiki/extensions/Bugzilla`
3. Edit `/etc/mediawiki/LocalSettings.php` and add
`require_once("/var/lib/mediawiki/extensions/Bugzilla/Bugzilla.php");`
4. Edit `/etc/mediawiki/LocalSettings.php` and change/override any
configuration variables. Current configuration variables and their defaults
can be found at the end of `Bugzilla.php`
5. Run the MediaWiki update script to create the cache database table
`php /var/lib/mediawiki/maintenance/update.php`. __Note that you may need to
add `$wgDBadminuser` and `$wgDBadminpassword` to
`/etc/mediawiki/LocalSettings.php` depending on your MediaWiki version__
2. Check the project out into `/path/to/your/mediawiki/extensions/Bugzilla`
3. Edit `/path/to/your/mediawiki/LocalSettings.php` and add
`require_once("$IP/extensions/Bugzilla/Bugzilla.php");`
and change/override any configuration variables.
Current configuration variables and their defaults can be found at the end of `Bugzilla.php`
Usage
================================

23
cache/BugzillaCacheApc.class.php поставляемый
Просмотреть файл

@ -1,23 +0,0 @@
<?php
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
class BugzillaCacheApc implements BugzillaCacheI
{
public function set($key, $value, $ttl = 300) {
return apc_store($key, $value, $ttl);
}
public function get($key) {
return apc_fetch($key);
}
public function expire($key) {
return apc_delete($key);
}
public static function setup($updater) {
return;
}
}

31
cache/BugzillaCacheDummy.class.php поставляемый
Просмотреть файл

@ -1,31 +0,0 @@
<?php
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
/**
* Dummy cache for those times you want to test the functionality WITHOUT the
* cache.
*/
class BugzillaCacheDummy implements BugzillaCacheI
{
public function set($key, $value, $ttl = 300)
{
return true;
}
public function get($key)
{
return;
}
public function expire($key)
{
return true;
}
public static function setup($updater)
{
return;
}
}

14
cache/BugzillaCacheI.class.php поставляемый
Просмотреть файл

@ -1,14 +0,0 @@
<?php
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
interface BugzillaCacheI
{
public function set($key, $value, $ttl = 300);
public function get($key);
public function expire($key);
public static function setup($updater);
}

40
cache/BugzillaCacheMemcache.class.php поставляемый
Просмотреть файл

@ -1,40 +0,0 @@
<?php
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
class BugzillaCacheMemcache implements BugzillaCacheI
{
protected $_memcache;
public function __construct() {
// As much as I detest using a global here, it is necessary to avoid
// needing to inject the $wgMemc object, thus breaking the usefulness
// of the interface. Using the $wgMemc object is important for the
// consistency of the code.
global $wgMemc;
$this->_memcache = $wgMemc;
}
public function set($key, $value, $ttl = 300) {
// Get the wikimedia key style expected
$key = wfMemcKey($key);
return $this->_memcache->set($key, $value, $ttl);
}
public function get($key) {
// Get the wikimedia key style expected
$key = wfMemcKey($key);
return $this->_memcache->get($key);
}
public function expire($key) {
// Get the wikimedia key style expected
$key = wfMemcKey($key);
return $this->_memcache->delete($key);
}
public static function setup($updater) {
return;
}
}

123
cache/BugzillaCacheSql.class.php поставляемый
Просмотреть файл

@ -1,123 +0,0 @@
<?php
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
/**
*/
class BugzillaCacheSql implements BugzillaCacheI
{
protected function _getDatabase($type = DB_MASTER) {
return wfGetDB($type);
}
/**
* @param string $key
* @param string $value
* @param integer $ttl
*
* @return boolean
*/
public function set($key, $value, $ttl = 300)
{
$master = $this->_getDatabase();
$now = time(); // Using time() because it's a PHP built-in.
$expires = $now + $ttl;
if (null === $this->get($key)) {
$res = $master->insert(
'bugzilla_cache',
array(
$master->addIdentifierQuotes( 'key' ) => $key,
'data' => $value,
'expires' => $expires
),
__METHOD__
);
}
return $res;
}
/**
* @param string $key
*
* @return string|null
*/
public function get($key)
{
$slave = $this->_getDatabase(DB_SLAVE);
$res = $slave->select(
'bugzilla_cache',
array( 'id', 'data', 'expires' ),
array( $slave->addIdentifierQuotes( 'key' ) => $key ),
__METHOD__,
array( 'LIMIT' => 1 )
);
if( !$res ) {
$this->expire($key);
return null;
}
$row = $res->fetchRow();
if (!$row || ($row['expires'] < time())) {
$this->expire($key); // This won't hurt us if the first condition is true.
return null;
}
return $row['data'];
}
/**
* @param string $key
*
* @return boolean
*/
public function expire($key)
{
$master = $this->_getDatabase();
global $wgBugzillaCacheType;
if ($wgBugzillaCacheType == 'mysql') {
return $master->delete(
'bugzilla_cache',
array('`key`="' . $key . '"')
);
} else {
return $master->delete(
'bugzilla_cache',
array('key' => $key)
);
}
}
/**
* @param $updater DatabaseUpdater
*/
final public static function setup($updater)
{
global $wgBugzillaCacheType;
$sqlFile = sprintf('%s/sql/%s.sql',
dirname(__FILE__),
$wgBugzillaCacheType);
if ($updater === null) {
// <= 1.16 support
global $wgExtNewTables;
global $wgExtModifiedFields;
$wgExtNewTables[] = array(
'bugzilla_cache',
$sqlFile,
);
} else {
// >= 1.17 support
$updater->addExtensionUpdate(array('addTable',
'bugzilla_cache',
$sqlFile,
TRUE));
}
}
}

8
cache/sql/mysql.sql поставляемый
Просмотреть файл

@ -1,8 +0,0 @@
CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/bugzilla_cache (
`id` integer(40) NOT NULL AUTO_INCREMENT,
`key` varchar(255) NOT NULL DEFAULT '',
`data` longtext,
`expires` integer(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX /*i*/uniq_bugzilla_cache_key (`key`)
) /*$wgDBTableOptions*/;

6
cache/sql/postgresql.sql поставляемый
Просмотреть файл

@ -1,6 +0,0 @@
CREATE TABLE bugzilla_cache (
id SERIAL PRIMARY KEY,
key VARCHAR(255) UNIQUE NOT NULL DEFAULT '',
data TEXT,
expires INTEGER NOT NULL DEFAULT 0
);

8
cache/sql/sqlite.sql поставляемый
Просмотреть файл

@ -1,8 +0,0 @@
CREATE TABLE `bugzilla_cache` (
`id` integer primary key AUTOINCREMENT,
`key` TEXT NOT NULL DEFAULT '',
`data` TEXT,
`expires` integer(11) NOT NULL DEFAULT 0
);
CREATE UNIQUE INDEX uniq_bugzilla_cache_key ON `bugzilla_cache` (`key`);