ProfilerWrapperCache.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. declare(strict_types = 1);
  3. /**
  4. * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
  5. *
  6. * @author Carl Schwan <carl@carlschwan.eu>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace OC\Memcache;
  25. use OC\AppFramework\Http\Request;
  26. use OCP\AppFramework\Http\Response;
  27. use OCP\DataCollector\AbstractDataCollector;
  28. use OCP\IMemcacheTTL;
  29. /**
  30. * Cache wrapper that logs profiling information
  31. * @template-implements \ArrayAccess<string,mixed>
  32. */
  33. class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL, \ArrayAccess {
  34. /** @var Redis $wrappedCache*/
  35. protected $wrappedCache;
  36. /** @var string $prefix */
  37. protected $prefix;
  38. /** @var string $type */
  39. private $type;
  40. public function __construct(Redis $wrappedCache, string $type) {
  41. $this->prefix = $wrappedCache->getPrefix();
  42. $this->wrappedCache = $wrappedCache;
  43. $this->type = $type;
  44. $this->data['queries'] = [];
  45. $this->data['cacheHit'] = 0;
  46. $this->data['cacheMiss'] = 0;
  47. }
  48. public function getPrefix(): string {
  49. return $this->prefix;
  50. }
  51. /** @inheritDoc */
  52. public function get($key) {
  53. $start = microtime(true);
  54. $ret = $this->wrappedCache->get($key);
  55. if ($ret === null) {
  56. $this->data['cacheMiss']++;
  57. } else {
  58. $this->data['cacheHit']++;
  59. }
  60. $this->data['queries'][] = [
  61. 'start' => $start,
  62. 'end' => microtime(true),
  63. 'op' => $this->getPrefix() . '::get::' . $key,
  64. 'hit' => $ret !== null,
  65. ];
  66. return $ret;
  67. }
  68. /** @inheritDoc */
  69. public function set($key, $value, $ttl = 0) {
  70. $start = microtime(true);
  71. $ret = $this->wrappedCache->set($key, $value, $ttl);
  72. $this->data['queries'][] = [
  73. 'start' => $start,
  74. 'end' => microtime(true),
  75. 'op' => $this->getPrefix() . '::set::' . $key,
  76. ];
  77. return $ret;
  78. }
  79. /** @inheritDoc */
  80. public function hasKey($key) {
  81. $start = microtime(true);
  82. $ret = $this->wrappedCache->hasKey($key);
  83. $this->data['queries'][] = [
  84. 'start' => $start,
  85. 'end' => microtime(true),
  86. 'op' => $this->getPrefix() . '::hasKey::' . $key,
  87. ];
  88. return $ret;
  89. }
  90. /** @inheritDoc */
  91. public function remove($key) {
  92. $start = microtime(true);
  93. $ret = $this->wrappedCache->remove($key);
  94. $this->data['queries'][] = [
  95. 'start' => $start,
  96. 'end' => microtime(true),
  97. 'op' => $this->getPrefix() . '::remove::' . $key,
  98. ];
  99. return $ret;
  100. }
  101. /** @inheritDoc */
  102. public function clear($prefix = '') {
  103. $start = microtime(true);
  104. $ret = $this->wrappedCache->clear($prefix);
  105. $this->data['queries'][] = [
  106. 'start' => $start,
  107. 'end' => microtime(true),
  108. 'op' => $this->getPrefix() . '::clear::' . $prefix,
  109. ];
  110. return $ret;
  111. }
  112. /** @inheritDoc */
  113. public function add($key, $value, $ttl = 0) {
  114. $start = microtime(true);
  115. $ret = $this->wrappedCache->add($key, $value, $ttl);
  116. $this->data['queries'][] = [
  117. 'start' => $start,
  118. 'end' => microtime(true),
  119. 'op' => $this->getPrefix() . '::add::' . $key,
  120. ];
  121. return $ret;
  122. }
  123. /** @inheritDoc */
  124. public function inc($key, $step = 1) {
  125. $start = microtime(true);
  126. $ret = $this->wrappedCache->inc($key, $step);
  127. $this->data['queries'][] = [
  128. 'start' => $start,
  129. 'end' => microtime(true),
  130. 'op' => $this->getPrefix() . '::inc::' . $key,
  131. ];
  132. return $ret;
  133. }
  134. /** @inheritDoc */
  135. public function dec($key, $step = 1) {
  136. $start = microtime(true);
  137. $ret = $this->wrappedCache->dec($key, $step);
  138. $this->data['queries'][] = [
  139. 'start' => $start,
  140. 'end' => microtime(true),
  141. 'op' => $this->getPrefix() . '::dev::' . $key,
  142. ];
  143. return $ret;
  144. }
  145. /** @inheritDoc */
  146. public function cas($key, $old, $new) {
  147. $start = microtime(true);
  148. $ret = $this->wrappedCache->cas($key, $old, $new);
  149. $this->data['queries'][] = [
  150. 'start' => $start,
  151. 'end' => microtime(true),
  152. 'op' => $this->getPrefix() . '::cas::' . $key,
  153. ];
  154. return $ret;
  155. }
  156. /** @inheritDoc */
  157. public function cad($key, $old) {
  158. $start = microtime(true);
  159. $ret = $this->wrappedCache->cad($key, $old);
  160. $this->data['queries'][] = [
  161. 'start' => $start,
  162. 'end' => microtime(true),
  163. 'op' => $this->getPrefix() . '::cad::' . $key,
  164. ];
  165. return $ret;
  166. }
  167. /** @inheritDoc */
  168. public function setTTL(string $key, int $ttl) {
  169. $this->wrappedCache->setTTL($key, $ttl);
  170. }
  171. public function getTTL(string $key): int|false {
  172. return $this->wrappedCache->getTTL($key);
  173. }
  174. public function compareSetTTL(string $key, mixed $value, int $ttl): bool {
  175. return $this->wrappedCache->compareSetTTL($key, $value, $ttl);
  176. }
  177. public function offsetExists($offset): bool {
  178. return $this->hasKey($offset);
  179. }
  180. public function offsetSet($offset, $value): void {
  181. $this->set($offset, $value);
  182. }
  183. /**
  184. * @return mixed
  185. */
  186. #[\ReturnTypeWillChange]
  187. public function offsetGet($offset) {
  188. return $this->get($offset);
  189. }
  190. public function offsetUnset($offset): void {
  191. $this->remove($offset);
  192. }
  193. public function collect(Request $request, Response $response, \Throwable $exception = null): void {
  194. // Nothing to do here $data is already ready
  195. }
  196. public function getName(): string {
  197. return 'cache/' . $this->type . '/' . $this->prefix;
  198. }
  199. public static function isAvailable(): bool {
  200. return true;
  201. }
  202. }