Skip to content

ObjectStoreCassetteStore

A ListableCassetteStoreInterface over any ListableObjectStoreClientInterface — Azure Blob, S3 or GCS.

A pod’s filesystem does not survive a restart/eviction/scale-down, which is disproportionately likely to be exactly when the interesting request happened.

put() writes to the deterministic, date-partitioned key CassetteKeyScheme derives from the cassette’s own recorded_at. get()/has()/delete()/slugs() take only a bare CassetteId or nothing at all — neither carries a date — so they cannot go straight to a key the way FileCassetteStore’s directory listing or PdoCassetteStore’s WHERE slug = ? can. Instead they probe backward hour by hour, from now, up to $lookbackHours (default a Docker-deployment-realistic 48h), checking each hour’s deterministic key with a cheap head() rather than listing. This makes the base CassetteStoreInterface contract honestly work with no further machinery — slower than an index-assisted lookup for an old cassette, but correct — and CassetteIndexInterface (an explicit key/date hint, or a Log Analytics query) is the faster path for exactly the case this probe is slow at, not a requirement for the store to function at all.

A stated, deliberate limitation, not a silent one: slugs() only enumerates the same $lookbackHours window get()/has()/delete() probe, not the object store’s entire history. A cassette older than that window exists and is still fetchable by key, but will not appear in cassette:list’s output.

final class ObjectStoreCassetteStore implements ListableCassetteStoreInterface

ImplementsListableCassetteStoreInterface
SourceObjectStoreCassetteStore.php

public function __construct(ListableObjectStoreClientInterface $client, CassetteKeyScheme $keyScheme, string $storeAlias, string $containerLabel, int $lookbackHours = 48, CassetteCodec $codec = new CassetteCodec(…), ClockInterface $clock = new SystemClock(…)): mixed

ParameterTypeDescription
$clientListableObjectStoreClientInterface
$keySchemeCassetteKeyScheme
$storeAliasstring
$containerLabelstring
$lookbackHoursint
$codecCassetteCodec
$clockClockInterface

Returns mixed

MethodDescription
delete(CassetteId $id): voidDeletes every copy, not just the newest.
get(CassetteId $id): ?CassetteNull when no cassette is stored under this id.
has(CassetteId $id): bool
put(CassetteId $id, Cassette $cassette): void
slugs(): list<string>Every slug the last $lookbackHours of hour-partitions hold — see this class’s own docblock for why that window, not the object store’s entire history.

public function delete(CassetteId $id): void

Deletes every copy, not just the newest.

put() keys by the cassette’s own recorded hour, so one slug can legitimately exist in several hour partitions — a re-recorded correlation id, or a cassette fetched from elsewhere and stored again. Stopping at the first hit meant cassette:prune reported a deletion while older copies survived indefinitely, which is the one thing a prune must not do.

ParameterTypeDescription
$idCassetteId

public function get(CassetteId $id): ?Cassette

Null when no cassette is stored under this id.

ParameterTypeDescription
$idCassetteId

Returns ?Cassette

public function has(CassetteId $id): bool

ParameterTypeDescription
$idCassetteId

Returns bool

public function put(CassetteId $id, Cassette $cassette): void

ParameterTypeDescription
$idCassetteId
$cassetteCassette

public function slugs(): list<string>

Every slug the last $lookbackHours of hour-partitions hold — see this class’s own docblock for why that window, not the object store’s entire history.

Returns list``<``string``>