From 8a98f8f09a999f28e5532963d04ed12733344733 Mon Sep 17 00:00:00 2001 From: HitkoDev Date: Thu, 11 Nov 2021 22:23:01 +0100 Subject: [PATCH] feat: use redis to store cache --- .github/workflows/main.yml | 4 +- breeze.php | 1 + inc/breeze-configuration.php | 6 +- inc/cache/config-cache.php | 2 +- inc/cache/execute-cache.php | 71 +++++++++--------- inc/cache/purge-cache.php | 14 +--- inc/cache/purge-per-time.php | 1 - inc/cache/redis-client.php | 97 ++++++++++++++++++++++++ inc/functions.php | 32 ++++++-- inc/helpers.php | 4 +- views/tabs/basic.php | 142 ++++++++++++++++++++++------------- 11 files changed, 260 insertions(+), 114 deletions(-) create mode 100644 inc/cache/redis-client.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9fb68d8..bf4a434 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,7 @@ jobs: run: | mkdir -p breeze rsync -a --exclude=breeze --exclude="\.*" --exclude=composer.lock --exclude=composer.json . breeze - zip -r breeze.zip breeze + zip -r breeze-redis.zip breeze - name: Release uses: marvinpinto/action-automatic-releases@latest @@ -35,4 +35,4 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} prerelease: false files: | - breeze.zip + breeze-redis.zip diff --git a/breeze.php b/breeze.php index 51454dd..d5ff3ca 100644 --- a/breeze.php +++ b/breeze.php @@ -79,6 +79,7 @@ require_once BREEZE_PLUGIN_DIR . 'inc/functions.php'; //action to purge cache +require_once BREEZE_PLUGIN_DIR . 'inc/cache/redis-client.php'; require_once BREEZE_PLUGIN_DIR . 'inc/cache/purge-varnish.php'; require_once BREEZE_PLUGIN_DIR . 'inc/cache/purge-cache.php'; require_once BREEZE_PLUGIN_DIR . 'inc/cache/purge-per-time.php'; diff --git a/inc/breeze-configuration.php b/inc/breeze-configuration.php index f991faa..0ea3ac6 100644 --- a/inc/breeze-configuration.php +++ b/inc/breeze-configuration.php @@ -84,6 +84,7 @@ public function afterLoadConfigPage() { $basic = [ 'breeze-active' => (isset($_POST['cache-system']) ? '1' : '0'), + 'breeze-redis-uri' => (isset($_POST['cache-redis-uri']) ? $_POST['cache-redis-uri'] : 'redis://127.0.0.1/0'), 'breeze-cross-origin' => (isset($_POST['safe-cross-origin']) ? '1' : '0'), 'breeze-ttl' => (int) $_POST['cache-ttl'], 'breeze-minify-html' => (isset($_POST['minification-html']) ? '1' : '0'), @@ -759,10 +760,7 @@ public static function breeze_clean_cache() { // Check whether we're clearing the cache for one subsite on the network. $is_subsite = is_multisite() && !is_network_admin(); - // analysis size cache - $cachepath = untrailingslashit(breeze_get_cache_base_path(is_network_admin())); - - $size_cache = breeze_get_directory_size($cachepath); + $size_cache = 0; // Analyze minification directory sizes. $files_path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze-minification'; diff --git a/inc/cache/config-cache.php b/inc/cache/config-cache.php index 2dddd00..21ca92d 100644 --- a/inc/cache/config-cache.php +++ b/inc/cache/config-cache.php @@ -509,7 +509,7 @@ public function clean_up() { $folder = untrailingslashit(breeze_get_cache_base_path()); - if (!$wp_filesystem->delete($folder, true)) { + if (!@RedisClient::factory()->delete($folder . '/*')) { $ret = false; } diff --git a/inc/cache/execute-cache.php b/inc/cache/execute-cache.php index 81c6268..cdf1a88 100644 --- a/inc/cache/execute-cache.php +++ b/inc/cache/execute-cache.php @@ -13,6 +13,7 @@ // Load helper functions. require_once dirname(__DIR__) . '/functions.php'; +require_once __DIR__ . '/redis-client.php'; if (isset($GLOBALS['breeze_config'], $GLOBALS['breeze_config']['disable_per_adminuser'])) { $wp_cookies = ['wordpressuser_', 'wordpresspass_', 'wordpress_sec_', 'wordpress_logged_in_']; @@ -211,11 +212,6 @@ function breeze_cache($buffer, $flags) { if (is_404() || is_search() || post_password_required()) { return $buffer; } - global $wp_filesystem; - if (empty($wp_filesystem)) { - require_once ABSPATH . '/wp-admin/includes/file.php'; - WP_Filesystem(); - } $path = $GLOBALS['breeze_filename']; $X1 = $GLOBALS['breeze_x1']; @@ -341,8 +337,11 @@ function breeze_cache($buffer, $flags) { ] ); - $wp_filesystem->put_contents($path, $data); - $wp_filesystem->touch($path, $modified_time); + $ttl = (isset($GLOBALS['breeze_config']['cache_options']['breeze-ttl']) ? (int) $GLOBALS['breeze_config']['cache_options']['breeze-ttl'] : 0) * 60; + if ($ttl <= 0) { + $ttl = 86400; + } + RedisClient::factory()->set($path, $data, $ttl); //set cache provider header if not exists cache file header('Cache-Provider:CLOUDWAYS-CACHE-' . $X1 . 'C'); @@ -406,9 +405,9 @@ function breeze_serve_cache($filename, $url_path, $X1, $opts) { } if (function_exists('gzencode') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) { - $file_name = md5($filename . '/index.gzip.html') . '.php'; + $file_name = md5($filename . '/index.gzip.html'); } else { - $file_name = md5($filename . '/index.html') . '.php'; + $file_name = md5($filename . '/index.html'); } $blog_id_requested = isset($GLOBALS['breeze_config']['blog_id']) ? $GLOBALS['breeze_config']['blog_id'] : 0; @@ -417,39 +416,37 @@ function breeze_serve_cache($filename, $url_path, $X1, $opts) { $GLOBALS['breeze_filename'] = $path; $GLOBALS['breeze_x1'] = $X1; - if (@file_exists($path)) { - $cacheFile = file_get_contents($path); + $cacheFile = @RedisClient::factory()->get($path); - if ($cacheFile != false) { - $datas = unserialize($cacheFile); - foreach ($datas['headers'] as $data) { - header($data['name'] . ': ' . $data['value']); - } - //set cache provider header - header('Cache-Provider:CLOUDWAYS-CACHE-' . $X1 . 'E'); + if ($cacheFile) { + $datas = unserialize($cacheFile); + foreach ($datas['headers'] as $data) { + header($data['name'] . ': ' . $data['value']); + } + //set cache provider header + header('Cache-Provider:CLOUDWAYS-CACHE-' . $X1 . 'E'); - $client_support_gzip = true; + $client_support_gzip = true; - //check gzip request from client - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') === false)) { - $client_support_gzip = false; - } - - if ($client_support_gzip && function_exists('gzdecode') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) { - //if file is zip + //check gzip request from client + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') === false)) { + $client_support_gzip = false; + } - $content = gzencode($datas['body'], 9); - header('Content-Encoding: gzip'); - header('Content-Length: ' . strlen($content)); - header('Vary: Accept-Encoding'); - echo $content; - } else { - header('Content-Length: ' . strlen($datas['body'])); - //render page cache - echo $datas['body']; - } - exit; + if ($client_support_gzip && function_exists('gzdecode') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) { + //if file is zip + + $content = gzencode($datas['body'], 9); + header('Content-Encoding: gzip'); + header('Content-Length: ' . strlen($content)); + header('Vary: Accept-Encoding'); + echo $content; + } else { + header('Content-Length: ' . strlen($datas['body'])); + //render page cache + echo $datas['body']; } + exit; } } diff --git a/inc/cache/purge-cache.php b/inc/cache/purge-cache.php index 70e48af..385b235 100644 --- a/inc/cache/purge-cache.php +++ b/inc/cache/purge-cache.php @@ -84,9 +84,7 @@ public function purge_post_on_new_comment($comment_ID, $approved, $commentdata) } $url_path = get_permalink($post_id); - if ($wp_filesystem->exists(breeze_get_cache_base_path() . md5($url_path))) { - $wp_filesystem->rmdir(breeze_get_cache_base_path() . md5($url_path), true); - } + RedisClient::factory()->delete(breeze_get_cache_base_path() . md5($url_path) . '/*'); } } @@ -106,9 +104,7 @@ public function purge_post_on_comment_status_change($comment_ID, $comment_status $url_path = get_permalink($post_id); - if ($wp_filesystem->exists(breeze_get_cache_base_path() . md5($url_path))) { - $wp_filesystem->rmdir(breeze_get_cache_base_path() . md5($url_path), true); - } + RedisClient::factory()->delete(breeze_get_cache_base_path() . md5($url_path) . '/*'); } } } @@ -122,7 +118,7 @@ public static function breeze_cache_flush() { WP_Filesystem(); $cache_path = breeze_get_cache_base_path(is_network_admin()); - $wp_filesystem->rmdir(untrailingslashit($cache_path), true); + RedisClient::factory()->delete($cache_path . '*'); if (function_exists('wp_cache_flush')) { wp_cache_flush(); @@ -141,9 +137,7 @@ public function clean_up() { $ret = false; } - $folder = untrailingslashit(breeze_get_cache_base_path()); - - if (!$wp_filesystem->delete($folder, true)) { + if (!@RedisClient::factory()->delete(breeze_get_cache_base_path() . '*')) { $ret = false; } diff --git a/inc/cache/purge-per-time.php b/inc/cache/purge-per-time.php index 5c6d0fe..182fb18 100644 --- a/inc/cache/purge-per-time.php +++ b/inc/cache/purge-per-time.php @@ -94,7 +94,6 @@ public function schedule_varnish() { // Purge normal cache if ($this->normalcache) { - Breeze_PurgeCache::breeze_cache_flush(); Breeze_MinificationCache::clear_minification(); } } diff --git a/inc/cache/redis-client.php b/inc/cache/redis-client.php new file mode 100644 index 0000000..4e03070 --- /dev/null +++ b/inc/cache/redis-client.php @@ -0,0 +1,97 @@ +redis = new Redis(); + $url = parse_url($uri); + $scheme = 'tcp'; + + switch ($url['scheme']) { + case 'unix': + $this->redis->connect($url['path']); + break; + + case 'rediss': + $scheme = 'tls'; + // no break + default: + $db = strlen($url['path']) > 1 ? (int) filter_var(substr($url['path'], 1), FILTER_VALIDATE_INT) : 0; + $db = $db ? $db : 0; + $host = ($scheme ? $scheme . '://' : '') . $url['host']; + $port = isset($url['port']) ? $url['port'] : 6379; + $this->redis->connect($host, $port); + $this->redis->select($db); + break; + } + } + + public function delete($key) { + try { + return $this->redis->eval("local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys", [$key], 0); + } catch (exception $e) { + throw $this->redis->getLastError(); + } + } + + public function set($key, $data, $ttl = 0) { + try { + if ($ttl) { + return $this->redis->setEx($key, $ttl, $data); + } + return $this->redis->set($key, $data); + } catch (exception $e) { + throw $this->redis->getLastError(); + } + } + + public function get($key) { + try { + return $this->redis->get($key); + } catch (exception $e) { + throw $this->redis->getLastError(); + } + } + + /** + * Singleton instance. + * + * @return RedisClient + */ + public static function factory() { + static $instance; + + if (!$instance) { + $config = isset($GLOBALS['breeze_config']['cache_options']['breeze-redis-uri']) ? $GLOBALS['breeze_config']['cache_options']['breeze-redis-uri'] : breeze_get_option('basic_settings')['breeze-redis-uri']; + $instance = new self($config); + } + + return $instance; + } +} diff --git a/inc/functions.php b/inc/functions.php index 318ea09..27f9865 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -21,6 +21,20 @@ define('BREEZE_PLUGIN_FULL_PATH', dirname(__DIR__) . '/'); require_once BREEZE_PLUGIN_FULL_PATH . 'inc/class-breeze-query-strings-rules.php'; +/** + * Generate UUIDv4. + * + * @return string + */ +function guidv4() { + $data = random_bytes(16); + + $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100 + $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 + + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); +} + /** * Get base path for the page cache directory. * @@ -30,6 +44,14 @@ * @return string */ function breeze_get_cache_base_path($is_network = false, $blog_id_requested = 0) { + $guild_path = rtrim(WP_CONTENT_DIR, '/\\') . '/breeze-guid.txt'; + $guid = file_get_contents($guild_path); + if (empty($guid)) { + $guid = guidv4(); + file_put_contents($guild_path, $guid); + } + $guid = rtrim($guid, '/\\') . '/'; + if (empty($blog_id_requested)) { $blog_id_requested = isset($GLOBALS['breeze_config']['blog_id']) ? $GLOBALS['breeze_config']['blog_id'] : 0; } @@ -37,16 +59,16 @@ function breeze_get_cache_base_path($is_network = false, $blog_id_requested = 0) if (!$is_network && is_multisite()) { if (empty($blog_id_requested)) { global $blog_id; - $path = rtrim(WP_CONTENT_DIR, '/\\') . '/cache/breeze/'; + $path = $guid; if (!empty($blog_id)) { - $path .= abs(intval($blog_id)) . DIRECTORY_SEPARATOR; + $path .= abs(intval($blog_id)) . '/'; } } else { - $path = rtrim(WP_CONTENT_DIR, '/\\') . '/cache/breeze/'; - $path .= abs(intval($blog_id_requested)) . DIRECTORY_SEPARATOR; + $path = $guid; + $path .= abs(intval($blog_id_requested)) . '/'; } } else { - $path = rtrim(WP_CONTENT_DIR, '/\\') . '/cache/breeze/'; + $path = $guid; } return $path; diff --git a/inc/helpers.php b/inc/helpers.php index 26d6bda..49ee67c 100644 --- a/inc/helpers.php +++ b/inc/helpers.php @@ -631,8 +631,8 @@ function breeze_varnish_purge_cache($url = '', $purge_varnish = false, $check_va } // Clear the local cache using the product URL. - if (!empty($url) && $wp_filesystem->exists(breeze_get_cache_base_path() . md5($url))) { - $wp_filesystem->rmdir(breeze_get_cache_base_path() . md5($url), true); + if (!empty($url)) { + RedisClient::factory()->delete(breeze_get_cache_base_path() . md5($url) . '/*'); } if ($purge_varnish === false && $check_varnish === true) { diff --git a/views/tabs/basic.php b/views/tabs/basic.php index 5f7eacb..61ef170 100644 --- a/views/tabs/basic.php +++ b/views/tabs/basic.php @@ -20,6 +20,21 @@ + + + + + + ' /> + + + + + @@ -208,38 +223,52 @@ class="breeze_tool_tip"> $basic['breeze-mobile-cache'] = '1'; } ?> - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - + + - - + + - +