Browse Source

Merge pull request #16484 from CakeDC/add-delete-async-and-clear-blocking-method-in-redis

Add deleteAsync and clearBlocking method for improving performance
Mark Story 4 years ago
parent
commit
87ea577202

+ 1 - 1
psalm-baseline.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <files psalm-version="4.x-dev@">
   <file src="src/Cache/Engine/RedisEngine.php">
-    <InvalidArgument occurrences="1">
+    <InvalidArgument occurrences="2">
       <code>Redis::OPT_SCAN</code>
     </InvalidArgument>
     <InvalidReturnStatement occurrences="4">

+ 46 - 0
src/Cache/Engine/RedisEngine.php

@@ -228,6 +228,21 @@ class RedisEngine extends CacheEngine
     }
 
     /**
+     * Delete a key from the cache asynchronously
+     *
+     * Just unlink a key from the cache. The actual removal will happen later asynchronously.
+     *
+     * @param string $key Identifier for the data
+     * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
+     */
+    public function deleteAsync(string $key): bool
+    {
+        $key = $this->_key($key);
+
+        return $this->_Redis->unlink($key) > 0;
+    }
+
+    /**
      * Delete all keys from the cache
      *
      * @return bool True if the cache was successfully cleared, false otherwise
@@ -257,6 +272,37 @@ class RedisEngine extends CacheEngine
     }
 
     /**
+     * Delete all keys from the cache by a blocking operation
+     *
+     * Faster than clear() using unlink method.
+     *
+     * @return bool True if the cache was successfully cleared, false otherwise
+     */
+    public function clearBlocking(): bool
+    {
+        $this->_Redis->setOption(Redis::OPT_SCAN, (string)Redis::SCAN_RETRY);
+
+        $isAllDeleted = true;
+        $iterator = null;
+        $pattern = $this->_config['prefix'] . '*';
+
+        while (true) {
+            $keys = $this->_Redis->scan($iterator, $pattern);
+
+            if ($keys === false) {
+                break;
+            }
+
+            foreach ($keys as $key) {
+                $isDeleted = ($this->_Redis->unlink($key) > 0);
+                $isAllDeleted = $isAllDeleted && $isDeleted;
+            }
+        }
+
+        return $isAllDeleted;
+    }
+
+    /**
      * Write data for key into cache if it doesn't exist already.
      * If it already exists, it fails and returns false.
      *

+ 39 - 0
tests/TestCase/Cache/Engine/RedisEngineTest.php

@@ -329,6 +329,19 @@ class RedisEngineTest extends TestCase
     }
 
     /**
+     * testDeleteCacheAsync method
+     */
+    public function testDeleteCacheAsync(): void
+    {
+        $data = 'this is a test of the emergency broadcasting system';
+        $result = Cache::write('delete_async_test', $data, 'redis');
+        $this->assertTrue($result);
+
+        $result = Cache::pool('redis')->deleteAsync('delete_async_test');
+        $this->assertTrue($result);
+    }
+
+    /**
      * testDecrement method
      */
     public function testDecrement(): void
@@ -452,6 +465,32 @@ class RedisEngineTest extends TestCase
     }
 
     /**
+     * testClearBlocking method
+     */
+    public function testClearBlocking(): void
+    {
+        Cache::setConfig('redis_clear_blocking', [
+            'engine' => 'Redis',
+            'prefix' => 'cake2_',
+            'duration' => 3600,
+            'port' => $this->port,
+        ]);
+
+        Cache::write('some_value', 'cache1', 'redis');
+        $result = Cache::pool('redis')->clearBlocking();
+        $this->assertTrue($result);
+        $this->assertNull(Cache::read('some_value', 'redis'));
+
+        Cache::write('some_value', 'cache2', 'redis_clear_blocking');
+        $result = Cache::pool('redis')->clearBlocking();
+        $this->assertTrue($result);
+        $this->assertNull(Cache::read('some_value', 'redis'));
+        $this->assertSame('cache2', Cache::read('some_value', 'redis_clear_blocking'));
+
+        Cache::pool('redis_clear_blocking')->clearBlocking();
+    }
+
+    /**
      * test that a 0 duration can successfully write.
      */
     public function testZeroDuration(): void