Skip to content

Commit 5d505e1

Browse files
committed
fix(cache): add support for cache with namespaces
1 parent 9e54447 commit 5d505e1

File tree

6 files changed

+144
-1
lines changed

6 files changed

+144
-1
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tracing\Cache;
6+
7+
use Sentry\State\HubInterface;
8+
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
9+
use Symfony\Component\Cache\PruneableInterface;
10+
use Symfony\Component\Cache\ResettableInterface;
11+
use Symfony\Contracts\Cache\NamespacedPoolInterface;
12+
use Symfony\Contracts\Cache\TagAwareCacheInterface;
13+
14+
/**
15+
* This implementation of a cache adapter aware of cache tags supports the
16+
* distributed tracing feature of Sentry.
17+
*
18+
* @internal
19+
*/
20+
final class TraceableTagAwareCacheAdapterForV3WithNamespace implements TagAwareAdapterInterface, TagAwareCacheInterface, NamespacedPoolInterface, PruneableInterface, ResettableInterface
21+
{
22+
/**
23+
* @phpstan-use TraceableCacheAdapterTrait<TagAwareAdapterInterface>
24+
*/
25+
use TraceableCacheAdapterTrait;
26+
27+
/**
28+
* @param HubInterface $hub The current hub
29+
* @param TagAwareAdapterInterface $decoratedAdapter The decorated cache adapter
30+
*/
31+
public function __construct(HubInterface $hub, TagAwareAdapterInterface $decoratedAdapter)
32+
{
33+
$this->hub = $hub;
34+
$this->decoratedAdapter = $decoratedAdapter;
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*
40+
* @param mixed[] $metadata
41+
*/
42+
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null): mixed
43+
{
44+
return $this->traceGet($key, $callback, $beta, $metadata);
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function invalidateTags(array $tags): bool
51+
{
52+
return $this->traceFunction('cache.invalidate_tags', function () use ($tags): bool {
53+
return $this->decoratedAdapter->invalidateTags($tags);
54+
});
55+
}
56+
57+
public function withSubNamespace(string $namespace): static
58+
{
59+
if (!$this->decoratedAdapter instanceof NamespacedPoolInterface) {
60+
throw new \BadMethodCallException(\sprintf('The %s::withSubNamespace() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, NamespacedPoolInterface::class));
61+
}
62+
63+
$clone = clone $this;
64+
$clone->decoratedAdapter = $this->decoratedAdapter->withSubNamespace($namespace);
65+
66+
return $clone;
67+
}
68+
}

src/aliases.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapter;
1414
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapterForV2;
1515
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapterForV3;
16+
use Sentry\SentryBundle\Tracing\Cache\TraceableTagAwareCacheAdapterForV3WithNamespace;
1617
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriver;
1718
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnection;
1819
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverConnectionFactory;
@@ -53,7 +54,11 @@ class_alias(TraceableCacheAdapterForV3::class, TraceableCacheAdapter::class);
5354
}
5455

5556
if (!class_exists(TraceableTagAwareCacheAdapter::class, false)) {
56-
class_alias(TraceableTagAwareCacheAdapterForV3::class, TraceableTagAwareCacheAdapter::class);
57+
if (interface_exists(NamespacedPoolInterface::class)) {
58+
class_alias(TraceableTagAwareCacheAdapterForV3WithNamespace::class, TraceableTagAwareCacheAdapter::class);
59+
} else {
60+
class_alias(TraceableTagAwareCacheAdapterForV3::class, TraceableTagAwareCacheAdapter::class);
61+
}
5762
}
5863
} else {
5964
if (!class_exists(TraceableCacheAdapter::class, false)) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tests\End2End\App\Controller;
6+
7+
use Symfony\Component\HttpFoundation\Response;
8+
use Symfony\Contracts\Cache\ItemInterface;
9+
use Symfony\Contracts\Cache\TagAwareCacheInterface;
10+
11+
class NamespacedCacheController
12+
{
13+
/**
14+
* @var TagAwareCacheInterface
15+
*/
16+
private $cache;
17+
18+
public function __construct(TagAwareCacheInterface $cache)
19+
{
20+
$this->cache = $cache;
21+
}
22+
23+
public function populateNamespacedCache(): Response
24+
{
25+
$namespaced = $this->cache->withSubNamespace('tests');
26+
27+
$namespaced->get('namespaced-key', function (ItemInterface $item) {
28+
$item->tag(['a tag name']);
29+
30+
return 'namespaced-value';
31+
});
32+
33+
return new Response();
34+
}
35+
}

tests/End2End/App/routing.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ psr_tracing_cache_delete:
7070
path: /tracing/cache/psr/delete
7171
defaults: { _controller: 'Sentry\SentryBundle\Tests\End2End\App\Controller\PsrTracingCacheController::testDelete' }
7272

73+
namespaced_tracing_cache_populate:
74+
path: /tracing/cache/namespaced/populate
75+
defaults: { _controller: 'Sentry\SentryBundle\Tests\End2End\App\Controller\NamespacedCacheController::populateNamespacedCache' }
76+
7377
just_logging:
7478
path: /just-logging
7579
defaults: { _controller: 'Sentry\SentryBundle\Tests\End2End\App\Controller\LoggingController::justLogging' }

tests/End2End/App/tracing.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ services:
2323
autowire: true
2424
tags:
2525
- { name: controller.service_arguments }
26+
27+
Sentry\SentryBundle\Tests\End2End\App\Controller\NamespacedCacheController:
28+
arguments:
29+
$cache: '@cache.app.taggable'
30+
tags:
31+
- { name: controller.service_arguments }

tests/End2End/TracingCacheEnd2EndTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,29 @@ public function testPsrCacheDelete(): void
223223
$this->assertEquals('cache.remove', $span->getOp());
224224
$this->assertNull($span->getData('cache.item_size'));
225225
}
226+
227+
public function testNamespacedTagAwareCache(): void
228+
{
229+
if (!interface_exists(\Symfony\Contracts\Cache\NamespacedPoolInterface::class)) {
230+
$this->markTestSkipped('Namespaced caches are not supported by this Symfony version.');
231+
}
232+
233+
$client = static::createClient(['debug' => false]);
234+
235+
$client->request('GET', '/tracing/cache/namespaced/populate');
236+
$this->assertSame(200, $client->getResponse()->getStatusCode());
237+
238+
$this->assertCount(1, StubTransport::$events);
239+
$event = StubTransport::$events[0];
240+
241+
$cacheGetSpans = array_values(array_filter($event->getSpans(), static function ($span) {
242+
return 'cache.get' === $span->getOp();
243+
}));
244+
$this->assertNotEmpty($cacheGetSpans);
245+
246+
$cachePutSpans = array_filter($event->getSpans(), static function ($span) {
247+
return 'cache.put' === $span->getOp();
248+
});
249+
$this->assertNotEmpty($cachePutSpans);
250+
}
226251
}

0 commit comments

Comments
 (0)