\Drupal\strata\Codec CodecRegistry

Chooses which codec writes a frame, and finds the one that can read an existing frame.

Two separate jobs, and conflating them is how a store becomes unreadable. Writing wants the best codec this host can run. Reading wants whichever codec understands the id already recorded in a frame header, even when that is not the one this host would choose - a bucket written on a box with ext-zstd must stay readable on a box that only has the zstd binary, and a bucket written years ago on gzip must stay readable forever.

Write preference follows the figures in CodecCatalog. At 16 KiB frames zstd level 1 reaches 4.28x at 422.9 MB/s against gzip level 9's 4.40x at 67.0 MB/s, so zstd wins the flush path on throughput. Dictionary support outranks raw ratio, because DeltaCodec cannot produce a delta without it.

A PipeCodec is never the hot-path choice. A process spawn dominates the work at frame sizes - 1,000 invocations measured 5.88 seconds - so zstd and brotli on disk are registered for reading and for batch work, and a host with neither extension gets deflate on the flush path instead. What they buy is that a frame stays readable when a site moves to a host whose PHP was built without the extension that wrote it.

Summary

Methods
Properties
Constants
withShippedCodecs
register
prefer
canRead
canWritePerFrame
reader
writer
dictionaryWriter
bulkWriter
get
available
unavailable
No public properties found
No public constants found
No protected methods found
No protected properties found
No protected constants found
No private methods found
pinned
writers
bulkWriters
readers
WRITE_PREFERENCE

Constant

WRITE_PREFERENCE

WRITE_PREFERENCE = ['zstd', 'brotli', 'gzip', 'none']

Write preference, best first.

Ordered by measured ratio with dictionary support weighted above it, because a codec that cannot take a dictionary cannot produce a delta.

Properties

$pinned

$pinned : ?string

Codec id pinned by configuration, or NULL to follow the write preference.

Type

string|null

$writers

$writers : array

Codecs able to write a single frame, keyed by id. First registration for an id wins.

Type

array<string, CompressionCodecInterface> —

$bulkWriters

$bulkWriters : array

Codecs able to write in bulk but not per frame, keyed by id.

Type

array<string, CompressionCodecInterface> —

$readers

$readers : array>

Every registered codec able to read a given id, in registration order.

Type

array<string, array<int, CompressionCodecInterface>> —

Methods

withShippedCodecs()

withShippedCodecs() : self

Builds a registry holding every codec Strata ships.

Registration order matters for readers: the extension is tried before the binary, so a host with both never pays a process spawn to read a frame.

Returns

self —

A registry with the shipped codecs registered.

register()

register(\Drupal\strata\Codec\CompressionCodecInterface  $codec, bool  $perFrame = true) : $this

Adds a codec.

Registering an unavailable codec is not an error and is in fact required: it is what lets CodecRegistry::unavailable() explain to an administrator why a better codec is not being used.

Parameters

\Drupal\strata\Codec\CompressionCodecInterface $codec

The codec to register.

bool $perFrame

Whether this codec may be chosen to compress a single frame on the flush path. Pass FALSE for an implementation whose per-call overhead dominates at frame sizes, such as one that shells out; it stays available for reading and for batch work.

Returns

$this —

The registry, for chaining.

prefer()

prefer(string  $id) : $this

Pins the codec that writes frames, overriding the measured preference.

An administrator who has calibrated on their own data outranks the shipped ordering. Pinning only changes what is WRITTEN; every registered reader stays available, so pinning gzip on a bucket full of zstd frames leaves that bucket readable.

Parameters

string $id

The codec id to write with, or an empty string to follow the preference.

Throws

\InvalidArgumentException

When the id is not registered as a per-frame writer on this host. Falling back silently would leave an administrator believing a setting took effect when it did not.

Returns

$this —

The registry, for chaining.

canRead()

canRead(string  $id) : bool

Whether any registered codec can read frames written under an id.

Parameters

string $id

A codec id as recorded in a frame header.

Returns

bool —

TRUE when at least one available codec understands it.

canWritePerFrame()

canWritePerFrame(string  $id) : bool

Whether a codec can be chosen to compress a single frame on the flush path.

Distinct from CodecRegistry::canRead(). A host with the zstd binary but no ext-zstd can read every zstd frame in the bucket and can use it for compaction, but must not use it per frame, because a process spawn dominates at frame sizes. Reporting that as plain availability would tell an administrator the extension is unnecessary while the flush path has fallen back to gzip.

Parameters

string $id

A codec id.

Returns

bool —

TRUE when this codec may compress an individual frame here.

reader()

reader(string  $id) : \Drupal\strata\Codec\CompressionCodecInterface

The codec that can read frames written under an id.

Parameters

string $id

A codec id as recorded in a frame header.

Throws

\RuntimeException

When nothing on this host can read it. The message names what is missing, because this is the error an administrator sees when a restore hits a frame their PHP cannot decode.

Returns

\Drupal\strata\Codec\CompressionCodecInterface —

An available codec that understands that id.

writer()

writer() : \Drupal\strata\Codec\CompressionCodecInterface

The codec to write the flush path with.

Throughput matters more than ratio here, and the level the caller passes decides the rest.

Throws

\RuntimeException

When no codec at all is registered, which means the registry was built by hand and left empty.

Returns

\Drupal\strata\Codec\CompressionCodecInterface —

The best available writer, never NULL because NoneCodec is always available.

dictionaryWriter()

dictionaryWriter() : \Drupal\strata\Codec\CompressionCodecInterface|null

The codec to write with when a dictionary is required.

Delta coding on the flush path needs this. Returns NULL rather than falling back, because a caller that silently accepted a non-dictionary codec would write frames whose headers claim a dictionary they were not compressed with. A host with only the zstd binary gets NULL here and a usable codec from CodecRegistry::bulkWriter(): it can produce deltas during compaction but not on the request path.

Returns

\Drupal\strata\Codec\CompressionCodecInterface|null —

The best available per-frame dictionary-capable writer, or NULL when this host has none.

bulkWriter()

bulkWriter() : \Drupal\strata\Codec\CompressionCodecInterface

The codec to run bulk work with, which may spawn a process.

Compaction, dictionary training and export are batch operations off the request path, so a binary that costs a spawn per batch is acceptable there and buys the better ratio.

Throws

\RuntimeException

When no codec at all is registered.

Returns

\Drupal\strata\Codec\CompressionCodecInterface —

The best available codec for batch work.

get()

get(string  $id) : \Drupal\strata\Codec\CompressionCodecInterface

A registered codec by id, whether or not it can run.

Parameters

string $id

A codec id.

Throws

\InvalidArgumentException

When nothing is registered under that id.

Returns

\Drupal\strata\Codec\CompressionCodecInterface —

The first codec registered under that id.

available()

available() : list<string>

Ids of every codec that can run on this host.

Returns

list

Codec ids, in write-preference order followed by any unranked additions.

unavailable()

unavailable() : array<string,list<string>>

Why each unavailable codec cannot run, for the settings form and hook_requirements().

A codec with several implementations reports one reason per implementation, because "ext-zstd is not loaded" and "the zstd binary was not found on PATH" are different things to fix.

Returns

array> —

Codec id keyed to the distinct reasons its implementations gave.