Implement CRM_Utils_Cache_FastArrayDecorator
authorTim Otten <totten@civicrm.org>
Mon, 21 Jan 2019 11:06:10 +0000 (03:06 -0800)
committerTim Otten <totten@civicrm.org>
Wed, 30 Jan 2019 00:47:08 +0000 (16:47 -0800)
CRM/Utils/Cache/FastArrayDecorator.php [new file with mode: 0644]
tests/phpunit/E2E/Cache/FastArrayDecoratorTest.php [new file with mode: 0644]

diff --git a/CRM/Utils/Cache/FastArrayDecorator.php b/CRM/Utils/Cache/FastArrayDecorator.php
new file mode 100644 (file)
index 0000000..5926f23
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5                                                  |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2019                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2019
+ */
+
+/**
+ * Class CRM_Utils_Cache_FastArrayDecorator
+ *
+ * Like CRM_Utils_Cache_ArrayDecorator, this creates a two-tier cache.
+ * But it's... faster. The speed improvements are achieved by sacrificing
+ * compliance with PSR-16. Specific trade-offs:
+ *
+ * 1. TTL values are not tracked locally. Any data cached locally will stay
+ *    active until the instance is destroyed (i.e. until the request ends).
+ *    You won't notice this is you have short-lived requests and long-lived caches.
+ * 2. If you store an *object* in the local cache, the same object instance
+ *    will be used through the end of the request. If you modify a property
+ *    of the object, the change will endure within the current pageview but
+ *    will not pass-through to the persistent cache.
+ *
+ * But... it is twice as fast (on high-volume reads).
+ *
+ * Ex: $cache = new CRM_Utils_Cache_FastArrayDecorator(new CRM_Utils_Cache_Redis(...));
+ *
+ * @see CRM_Utils_Cache_ArrayDecorator
+ */
+class CRM_Utils_Cache_FastArrayDecorator implements CRM_Utils_Cache_Interface {
+
+  use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
+
+  /**
+   * @var int
+   *   Default time-to-live (seconds) for cache items that don't have a TTL.
+   */
+  protected $defaultTimeout;
+
+  /**
+   * @var CRM_Utils_Cache_Interface
+   */
+  private $delegate;
+
+  /**
+   * @var array
+   *   Array(string $cacheKey => mixed $cacheValue).
+   */
+  private $values = [];
+
+  /**
+   * CRM_Utils_Cache_FastArrayDecorator constructor.
+   * @param \CRM_Utils_Cache_Interface $delegate
+   * @param int $defaultTimeout
+   *   Default number of seconds each cache-item should endure.
+   */
+  public function __construct(\CRM_Utils_Cache_Interface $delegate, $defaultTimeout = 3600) {
+    $this->defaultTimeout = $defaultTimeout;
+    $this->delegate = $delegate;
+  }
+
+  public function set($key, $value, $ttl = NULL) {
+    if (is_int($ttl) && $ttl <= 0) {
+      return $this->delete($key);
+    }
+
+    if ($this->delegate->set($key, $value, $ttl)) {
+      $this->values[$key] = $value;
+      return TRUE;
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  public function get($key, $default = NULL) {
+    if (array_key_exists($key, $this->values)) {
+      return $this->values[$key];
+    }
+
+    $nack = CRM_Utils_Cache::nack();
+    $value = $this->delegate->get($key, $nack);
+    if ($value === $nack) {
+      return $default;
+    }
+
+    $this->values[$key] = $value;
+    return $value;
+  }
+
+  public function delete($key) {
+    unset($this->values[$key]);
+    return $this->delegate->delete($key);
+  }
+
+  public function flush() {
+    return $this->clear();
+  }
+
+  public function clear() {
+    $this->values = [];
+    return $this->delegate->clear();
+  }
+
+  public function has($key) {
+    return $this->delegate->has($key);
+  }
+
+}
diff --git a/tests/phpunit/E2E/Cache/FastArrayDecoratorTest.php b/tests/phpunit/E2E/Cache/FastArrayDecoratorTest.php
new file mode 100644 (file)
index 0000000..fbc37b4
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5                                                  |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2019                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License along with this program; if not, contact CiviCRM LLC       |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Verify that CRM_Utils_Cache_FastArrayDecorator complies with PSR-16.
+ *
+ * @group e2e
+ */
+class E2E_Cache_FastArrayDecoratorTest extends E2E_Cache_ArrayDecoratorTest {
+
+  /**
+   * @var CRM_Utils_Cache_Interface
+   */
+  protected $a;
+
+  public function createSimpleCache() {
+    return new CRM_Utils_Cache_FastArrayDecorator(
+      $this->a = CRM_Utils_Cache::create([
+        'name' => 'e2e fast-arr-dec test',
+        'type' => ['ArrayCache'],
+      ])
+    );
+  }
+
+  public function testSetTtl() {
+    $this->markTestSkipped('FastArrayDecorator breaks convention: Does not track TTL locally. However, TTL is passed along to delegate.');
+  }
+
+  public function testSetMultipleTtl() {
+    $this->markTestSkipped('FastArrayDecorator breaks convention: Does not track TTL locally. However, TTL is passed along to delegate.');
+  }
+
+  public function testDoubleLifeWithDelete() {
+    $this->markTestSkipped('FastArrayDecorator breaks convention: Does not track TTL locally. However, TTL is passed along to delegate.');
+  }
+
+  public function testDoubleLifeWithClear() {
+    $this->markTestSkipped('FastArrayDecorator breaks convention: Does not track TTL locally. However, TTL is passed along to delegate.');
+  }
+
+  public function testObjectDoesNotChangeInCache() {
+    $this->markTestSkipped('FastArrayDecorator breaks convention: No deep-copying cache content');
+  }
+
+}