\Drupal\strata\Storage RecordingProvider

Wraps any provider and records what every call to it cost.

The engine asks a store for objects and gets no bill back. This decorator sits between the two and writes each request into a ProviderStats, so a budget reading is taken from the requests that were actually issued rather than from an estimate of how many a flush should have needed.

Two verbs are recorded under the name of the request they really make, because that is what the endpoint charges for:

  • StorageProviderInterface::exists() is a head.
  • StorageProviderInterface::stream() is a get, with no byte count; the body is read by whoever holds the stream, not here.

A delete counts once per call rather than once per key: a provider that batches sends one request for the whole array, and the array size is visible in the caller's own code.

Identity is forwarded untouched. Reading an id, a label, a capability set or a reachability flag either costs nothing or is already cached by the provider, and counting a settings-form render as store traffic would put the budget guard on the wrong side of the numbers.

A failure is recorded and rethrown. Nothing is swallowed; a caller that cannot tell a failed write from a successful one has no way to keep its own state honest.

Summary

Methods
Properties
Constants
__construct
stats
inner
id
label
capabilities
isReachable
unreachableReason
put
delete
list
get
stream
head
exists
No public properties found
No public constants found
No protected methods found
No protected properties found
No protected constants found
recordFailure
elapsed
now
clock
inner
stats
No private constants found

Properties

$clock

$clock : \Closure

Returns the current time in seconds as a float.

Type

Closure

$stats

$stats : \Drupal\strata\Storage\ProviderStats

Type

ProviderStats

Methods

__construct()

__construct(\Drupal\strata\Storage\StorageProviderInterface  $inner, \Drupal\strata\Storage\ProviderStats  $stats, callable|null  $clock = null) : mixed

Constructs the decorator.

Parameters

\Drupal\strata\Storage\StorageProviderInterface $inner

The provider every call is forwarded to.

\Drupal\strata\Storage\ProviderStats $stats

The accumulator to write into. Sharing one accumulator across several decorators totals them; giving each its own keeps them separate.

callable|null $clock

Returns seconds as a float or an int. NULL uses microtime(true). Injected so a test can assert an exact duration instead of sleeping for one.

Returns

mixed —

stats()

stats() : \Drupal\strata\Storage\ProviderStats

The accumulator this decorator writes into.

Returns

\Drupal\strata\Storage\ProviderStats —

The same instance that was handed to the constructor.

inner()

inner() : \Drupal\strata\Storage\StorageProviderInterface

The provider every call is forwarded to.

Exposed for the same reason SiteScopedProvider::unscoped() is: code that has to know which endpoint is really configured should not have to match on a decorator.

Returns

\Drupal\strata\Storage\StorageProviderInterface —

The wrapped provider.

id()

id() : string

The plugin id this provider registers under.

Returns

string —

A short lowercase token such as "s3", "r2", "sftp" or "local".

label()

label() : string

Human-readable name for the settings form.

Returns

string —

The label.

capabilities()

capabilities() : \Drupal\strata\Storage\Capabilities

What this endpoint can do.

Returns

\Drupal\strata\Storage\Capabilities —

The capability set.

isReachable()

isReachable() : bool

Whether the endpoint is reachable and the credentials work.

Returns

bool —

TRUE when a request would succeed right now.

unreachableReason()

unreachableReason() : string|null

Why the endpoint is unreachable.

Returns

string|null —

A short human-readable reason, or NULL when it is reachable.

put()

put(string  $key, mixed  $body, array  $options = []) : \Drupal\strata\Storage\PutResult

Writes an object.

Parameters

string $key

Object key relative to the store root.

mixed $body

The bytes, or an open readable stream. A stream is read once and not rewound, so a caller that needs it again must seek itself.

array $options

Provider options. ifNoneMatch (write only when the key is absent) is honoured wherever Capabilities::$conditionalWrite is true. metadata, contentType and storageClass are honoured only by providers whose endpoint carries them; a provider that cannot store an option refuses the write rather than dropping it, because an object written without the metadata its reader expects is indistinguishable from a corrupt one. Nothing in the engine depends on user metadata: a stored object carries whatever a reader needs in its own bytes.

Returns

\Drupal\strata\Storage\PutResult —

What was written.

delete()

delete(array  $keys) : int

Deletes objects.

Parameters

array $keys

Object keys relative to the store root.

Returns

int —

How many keys the endpoint accepted. A store that can tell an absent key from a removed one counts only what it removed; S3 reports success for an absent key and cannot distinguish, so it counts everything it was given. Do not read this as "how many existed" - a prune receipt counts from the frame index, which knows.

list()

list(string  $prefix = '', ?string  $cursor = null, int  $limit = 1000, ?string  $delimiter = null) : \Drupal\strata\Storage\ObjectPage

Lists one page of objects under a prefix.

Parameters

string $prefix

Key prefix relative to the store root; an empty string lists everything.

?string $cursor

Continuation token from a previous page, or NULL to start.

int $limit

Most objects to return in this page.

?string $delimiter

Grouping delimiter, or NULL for a flat listing.

Returns

\Drupal\strata\Storage\ObjectPage —

The page.

get()

get(string  $key, ?\Drupal\strata\Storage\ByteRange  $range = null) : string

Reads an object.

Parameters

string $key

Object key relative to the store root.

?\Drupal\strata\Storage\ByteRange $range

A byte range, or NULL for the whole object.

Returns

string —

The bytes.

stream()

stream(string  $key) : resource

Opens an object as a stream.

Parameters

string $key

Object key relative to the store root.

Returns

resource —

An open readable stream the caller must close.

head()

head(string  $key) : \Drupal\strata\Storage\ObjectMeta|null

Metadata for one object without reading it.

Parameters

string $key

Object key relative to the store root.

Returns

\Drupal\strata\Storage\ObjectMeta|null —

The metadata, or NULL when the object is absent. Absence is not an error, because every caller of this method is asking precisely in order to find out.

exists()

exists(string  $key) : bool

Whether an object exists.

Parameters

string $key

Object key relative to the store root.

Returns

bool —

TRUE when it is present.

recordFailure()

recordFailure(string  $operation, float  $started) : void

Records a failed attempt.

No byte count, because a request that raised gives no honest one back; the failure count is what tells an operator the requests were spent on nothing.

Parameters

string $operation

The billed verb the call maps to.

float $started

Clock reading taken before the call.

Returns

void —

elapsed()

elapsed(float  $started) : float

How long a call took.

Clamped at zero. A clock that steps backwards - an ntp correction, or a test clock rewound between reads - would otherwise make ProviderStats::record() raise on top of whatever the call itself was doing, replacing the real error with a bookkeeping one.

Parameters

float $started

Clock reading taken before the call.

Returns

float —

Seconds elapsed, never negative.

now()

now() : float

Reads the injected clock.

Throws

\UnexpectedValueException

When the injected clock returns anything but a number. Caught here rather than left to the subtraction, where a string clock would turn every duration into zero.

Returns

float —

Seconds. An int reading is widened, so a test clock counting whole seconds works.