Add the apcu caching ability for performance (#7276)
* add the apcu caching ability for performance When the PHP extension APCu is loaded, the settings class and the auto_loader will cache their results across requests in RAM. For more information about the APCu extension visit the PHP page: https://www.php.net/apcu * use global instead of default terminology
This commit is contained in:
parent
305f585b17
commit
d919a3cc1b
|
|
@ -331,4 +331,3 @@
|
|||
require_once "resources/footer.php";
|
||||
|
||||
?>
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ $search = $_REQUEST['search'] ?? '';
|
|||
$domain_uuid = $_GET['id'] ?? null;
|
||||
|
||||
//reload default settings
|
||||
require "resources/classes/domains.php";
|
||||
settings::clear_cache();
|
||||
$domain = new domains();
|
||||
$domain->set();
|
||||
|
||||
|
|
|
|||
|
|
@ -307,6 +307,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
//clear domain apcu cache due to changes
|
||||
settings::clear_cache('domain');
|
||||
|
||||
//redirect the browser
|
||||
if ($action == "update") {
|
||||
message::add($text['message-update']);
|
||||
|
|
|
|||
|
|
@ -298,6 +298,9 @@ if (!empty($_POST) && empty($_POST["persistformvar"])) {
|
|||
}
|
||||
}
|
||||
|
||||
//clear the user settings cache
|
||||
settings::clear_cache('user');
|
||||
|
||||
//redirect the browser
|
||||
if ($action == "update") {
|
||||
message::add($text['message-update']);
|
||||
|
|
|
|||
|
|
@ -27,10 +27,21 @@
|
|||
class auto_loader {
|
||||
|
||||
const FILE = 'autoloader_cache.php';
|
||||
const CACHE_KEY = 'autoloader_classes';
|
||||
|
||||
private $classes;
|
||||
|
||||
/**
|
||||
* Tracks the APCu extension for caching to RAM drive across requests
|
||||
* @var bool
|
||||
*/
|
||||
private $apcu_enabled;
|
||||
|
||||
public function __construct($project_path = '') {
|
||||
|
||||
//set if we can use RAM cache
|
||||
$this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled();
|
||||
|
||||
//classes must be loaded before this object is registered
|
||||
if (!$this->load_cache()) {
|
||||
//cache miss so load them
|
||||
|
|
@ -43,34 +54,49 @@ class auto_loader {
|
|||
}
|
||||
|
||||
public function update_cache(string $file = ''): bool {
|
||||
//guard against writing an empty file
|
||||
if (!empty($this->classes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//update RAM cache when available
|
||||
if ($this->apcu_enabled) {
|
||||
apcu_store(self::CACHE_KEY, $this->classes);
|
||||
}
|
||||
|
||||
//ensure we have somewhere to put the file
|
||||
if (empty($file)) {
|
||||
$file = sys_get_temp_dir() . '/' . self::FILE;
|
||||
}
|
||||
|
||||
//guard against writing an empty file
|
||||
if (!empty($this->classes)) {
|
||||
//export the classes array using PHP engine
|
||||
$data = var_export($this->classes, true);
|
||||
//export the classes array using PHP engine
|
||||
$data = var_export($this->classes, true);
|
||||
|
||||
//put the array in a form that it can be loaded directly to an array
|
||||
$result = file_put_contents($file, "<?php\n return " . $data . ";\n");
|
||||
if ($result !== false) {
|
||||
return true;
|
||||
}
|
||||
$error_array = error_get_last();
|
||||
//send to syslog when debugging
|
||||
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
||||
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
||||
syslog(LOG_WARNING, "[php][auto_loader] " . $error_array['message']);
|
||||
closelog();
|
||||
}
|
||||
//put the array in a form that it can be loaded directly to an array
|
||||
$result = file_put_contents($file, "<?php\n return " . $data . ";\n");
|
||||
if ($result !== false) {
|
||||
return true;
|
||||
}
|
||||
$error_array = error_get_last();
|
||||
//send to syslog when debugging
|
||||
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
||||
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
||||
syslog(LOG_WARNING, "[php][auto_loader] " . $error_array['message']);
|
||||
closelog();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function load_cache(string $file = ''): bool {
|
||||
$this->classes = [];
|
||||
|
||||
//use apcu when available
|
||||
if ($this->apcu_enabled && apcu_exists(self::CACHE_KEY)) {
|
||||
$this->classes = apcu_fetch(self::CACHE_KEY, $success);
|
||||
if ($success) return true;
|
||||
}
|
||||
|
||||
//use a standard file
|
||||
if (empty($file)) {
|
||||
$file = sys_get_temp_dir() . '/'. self::FILE;
|
||||
|
|
@ -81,6 +107,10 @@ class auto_loader {
|
|||
}
|
||||
//assign to an array
|
||||
if (!empty($this->classes)) {
|
||||
//cache edge case of first time using apcu cache
|
||||
if ($this->apcu_enabled) {
|
||||
apcu_store(self::CACHE_KEY, $this->classes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -125,6 +155,11 @@ class auto_loader {
|
|||
return true;
|
||||
}
|
||||
|
||||
//Smarty has it's own autoloader so reject the request
|
||||
if ($class_name === 'Smarty_Autoloader') {
|
||||
return false;
|
||||
}
|
||||
|
||||
//cache miss
|
||||
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
||||
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
||||
|
|
@ -186,4 +221,10 @@ class auto_loader {
|
|||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function clear_cache() {
|
||||
if (function_exists('apcu_enabled') && apcu_enabled()) {
|
||||
apcu_delete(self::CACHE_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,6 +187,17 @@ class cache {
|
|||
closelog();
|
||||
}
|
||||
|
||||
//check for apcu extension
|
||||
if (function_exists('apcu_enabled') && apcu_enabled()) {
|
||||
//flush everything
|
||||
apcu_clear_cache();
|
||||
}
|
||||
|
||||
//remove the autoloader file cache
|
||||
if (file_exists(sys_get_temp_dir() . '/' . auto_loader::FILE)) {
|
||||
@unlink(sys_get_temp_dir() . '/' . auto_loader::FILE);
|
||||
}
|
||||
|
||||
//cache method memcache
|
||||
if ($this->method === "memcache") {
|
||||
// connect to event socket
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ class settings {
|
|||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* Tracks if the APCu extension is loaded for database queries
|
||||
* @var bool
|
||||
*/
|
||||
private $apcu_enabled;
|
||||
|
||||
/**
|
||||
* Create a settings object using key/value pairs in the $setting_array.
|
||||
*
|
||||
|
|
@ -63,6 +69,16 @@ class settings {
|
|||
*/
|
||||
public function __construct($setting_array = []) {
|
||||
|
||||
//try to use RAM cache by default
|
||||
if (!isset($setting_array['allow_caching'])) {
|
||||
$setting_array['allow_caching'] = true;
|
||||
}
|
||||
|
||||
//track ram caching
|
||||
if ($setting_array['allow_caching']) {
|
||||
$this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled();
|
||||
}
|
||||
|
||||
//open a database connection
|
||||
if (empty($setting_array['database'])) {
|
||||
$this->database = database::new();
|
||||
|
|
@ -155,7 +171,7 @@ class settings {
|
|||
* @param bool $enabled (optional) True or False. Default is True.
|
||||
* @param string $description (optional) Description. Default is empty string.
|
||||
*/
|
||||
public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $type = 'text', string $value = "", bool $enabled = true, string $description = "") {
|
||||
public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $value = "", string $type = 'text', bool $enabled = true, string $description = "") {
|
||||
|
||||
//set the table name
|
||||
$table_name = $table_prefix.'_settings';
|
||||
|
|
@ -207,6 +223,15 @@ class settings {
|
|||
*/
|
||||
private function default_settings() {
|
||||
|
||||
//set the key for global defaults
|
||||
$key = 'settings_global_' . $this->category;
|
||||
|
||||
//if the apcu extension is loaded get the already parsed array
|
||||
if ($this->apcu_enabled && apcu_exists($key)) {
|
||||
$this->settings = apcu_fetch($key);
|
||||
return;
|
||||
}
|
||||
|
||||
//get the default settings
|
||||
$sql = "select * from v_default_settings ";
|
||||
$sql .= "where default_setting_enabled = 'true' ";
|
||||
|
|
@ -247,7 +272,11 @@ class settings {
|
|||
}
|
||||
}
|
||||
}
|
||||
unset($sql, $result, $row);
|
||||
|
||||
//if the apcu extension is loaded store the result
|
||||
if ($this->apcu_enabled) {
|
||||
apcu_store($key, $this->settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -255,13 +284,22 @@ class settings {
|
|||
* @access private
|
||||
*/
|
||||
private function domain_settings() {
|
||||
|
||||
$sql = "select * from v_domain_settings ";
|
||||
$sql .= "where domain_uuid = :domain_uuid ";
|
||||
$sql .= "and domain_setting_enabled = 'true' ";
|
||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
||||
$result = $this->database->select($sql, $parameters, 'all');
|
||||
unset($sql, $parameters);
|
||||
$key = 'settings_domain_'.$this->domain_uuid;
|
||||
$result = '';
|
||||
//if the apcu extension is loaded get the cached database result
|
||||
if ($this->apcu_enabled && apcu_exists($key)) {
|
||||
$result = apcu_fetch($key);
|
||||
} else {
|
||||
$sql = "select * from v_domain_settings ";
|
||||
$sql .= "where domain_uuid = :domain_uuid ";
|
||||
$sql .= "and domain_setting_enabled = 'true' ";
|
||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
||||
$result = $this->database->select($sql, $parameters, 'all');
|
||||
//if the apcu extension is loaded store the result
|
||||
if ($this->apcu_enabled) {
|
||||
apcu_store($key, $result);
|
||||
}
|
||||
}
|
||||
if (!empty($result)) {
|
||||
//domain setting array types override the default settings set as type array
|
||||
foreach ($result as $row) {
|
||||
|
|
@ -305,7 +343,6 @@ class settings {
|
|||
|
||||
}
|
||||
}
|
||||
unset($result, $row);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -314,15 +351,25 @@ class settings {
|
|||
* @depends $this->domain_uuid
|
||||
*/
|
||||
private function user_settings() {
|
||||
|
||||
$sql = "select * from v_user_settings ";
|
||||
$sql .= "where domain_uuid = :domain_uuid ";
|
||||
$sql .= "and user_uuid = :user_uuid ";
|
||||
$sql .= " order by user_setting_order asc ";
|
||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
||||
$parameters['user_uuid'] = $this->user_uuid;
|
||||
$result = $this->database->select($sql, $parameters, 'all');
|
||||
if (is_array($result)) {
|
||||
$key = 'settings_user_'.$this->user_uuid;
|
||||
$result = '';
|
||||
//if the apcu extension is loaded get the cached database result
|
||||
if ($this->apcu_enabled && apcu_exists($key)) {
|
||||
$result = apcu_fetch($key);
|
||||
} else {
|
||||
$sql = "select * from v_user_settings ";
|
||||
$sql .= "where domain_uuid = :domain_uuid ";
|
||||
$sql .= "and user_uuid = :user_uuid ";
|
||||
$sql .= " order by user_setting_order asc ";
|
||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
||||
$parameters['user_uuid'] = $this->user_uuid;
|
||||
$result = $this->database->select($sql, $parameters, 'all');
|
||||
//if the apcu extension is loaded store the result
|
||||
if ($this->apcu_enabled) {
|
||||
apcu_store($key, $result);
|
||||
}
|
||||
}
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $row) {
|
||||
if ($row['user_setting_enabled'] == 'true') {
|
||||
$name = $row['user_setting_name'];
|
||||
|
|
@ -406,4 +453,34 @@ class settings {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the settings cache
|
||||
* @param string $type Empty clears all settings. Values can be global, domain, or user.
|
||||
*/
|
||||
public static function clear_cache(string $type = '') {
|
||||
if (function_exists('apcu_enabled') && apcu_enabled()) {
|
||||
switch ($type) {
|
||||
case 'all':
|
||||
case '':
|
||||
default:
|
||||
$type = "";
|
||||
break;
|
||||
case 'global':
|
||||
case 'domain':
|
||||
case 'user':
|
||||
$type .= "_";
|
||||
break;
|
||||
}
|
||||
$cache = apcu_cache_info(false);
|
||||
if (!empty($cache['cache_list'])) {
|
||||
foreach ($cache['cache_list'] as $entry) {
|
||||
$key = $entry['info'];
|
||||
if (str_starts_with($key, 'settings_' . $type)) {
|
||||
apcu_delete($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue