Skip to content

Add hydrator decorators and a tracing extension - #213

Closed
DavidBadura wants to merge 4 commits into
2.1.xfrom
tracing
Closed

DavidBadura wants to merge 4 commits into
2.1.xfrom
tracing

Conversation

@DavidBadura

@DavidBadura DavidBadura commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Extensions can now wrap the built hydrator with a HydratorDecorator, registered via StackHydratorBuilder::addDecorator() with a priority like middlewares. Decorators are applied by the new buildHydrator(): Hydrator. build() is deprecated because its StackHydrator return type can't carry decorators, and it throws a DecoratorsNotApplied exception if decorators are registered instead of silently dropping them. In 3.0 the builder can be renamed and build() return the decorated hydrator.

The decorated hydrator is also set as root hydrator on the StackHydrator, which passes it to the hydrator aware normalizers instead of itself. That way nested objects go through the decorators as well.

The first user is a new TracingExtension. It passes every hydrate and extract call to a Tracer, and a StopwatchTracer for the Symfony Stopwatch ships with it (symfony/stopwatch is only suggested). Nested objects are traced too, inside the trace of their parent.

This is also the groundwork for the generated middleware branch, which can then register its fast path as a decorator instead of replacing the hydrator.

Extensions can now register decorators on the builder which wrap the built
hydrator. They are applied by the new buildHydrator() method, build() is
deprecated since it can only return the plain StackHydrator.

The TracingExtension is the first decorator: it passes every hydrate and
extract call to a Tracer, a StopwatchTracer for the Symfony Stopwatch is
included.
build() can only return the plain StackHydrator, so silently dropping the
decorators would hide that e.g. tracing is not active. It now throws a
DecoratorsNotApplied exception pointing to buildHydrator().
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Hello 👋

here is the most recent benchmark result:

StackHydratorBench
==================

+-------------------------------+-------------------------------------+------------------------------+
|                               | time (kde mode)                     | memory                       |
+-------------------------------+------------------+------------------+-----------------+------------+
| subject                       | Tag: <current>   | Tag: base        | Tag: <current>  | Tag: base  |
+-------------------------------+------------------+------------------+-----------------+------------+
| benchHydrate1Object ()        | 4.000μs (±0.00%) | 3.800μs (±0.00%) | 3.262mb         | 3.296mb    |
| benchExtract1Object ()        | 4.000μs (±0.00%) | 4.000μs (±0.00%) | 3.262mb         | 3.296mb    |
| benchHydrate1000Objects ()    | 2.559ms (±0.00%) | 2.640ms (±0.00%) | 3.493mb         | 3.500mb    |
| benchExtract1000Objects ()    | 2.467ms (±0.00%) | 2.515ms (±0.00%) | 3.482mb         | 3.490mb    |
| benchHydrate1000000Objects () | 1.435s (±0.00%)  | 1.464s (±0.00%)  | 3.493mb         | 3.500mb    |
| benchExtract1000000Objects () | 1.573s (±0.00%)  | 1.506s (±0.00%)  | 3.009mb         | 3.009mb    |
+-------------------------------+------------------+------------------+-----------------+------------+

HydratorWithLazyBench
=====================

+------------------------------------------+-----------------------------------------+------------------------------+
|                                          | time (kde mode)                         | memory                       |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| subject                                  | Tag: <current>     | Tag: base          | Tag: <current>  | Tag: base  |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| benchHydrate1Object ()                   | 0.800μs (±0.00%)   | 0.600μs (±0.00%)   | 2.772mb         | 2.772mb    |
| benchHydrate1ObjectTriggerInit ()        | 4.800μs (±0.00%)   | 4.600μs (±0.00%)   | 2.772mb         | 2.772mb    |
| benchHydrate1000Objects ()               | 435.667μs (±0.00%) | 440.000μs (±0.00%) | 2.817mb         | 2.817mb    |
| benchHydrate1000ObjectsTriggerInit ()    | 3.130ms (±0.00%)   | 3.375ms (±0.00%)   | 2.927mb         | 2.931mb    |
| benchHydrate1000000Objects ()            | 291.020ms (±0.00%) | 297.025ms (±0.00%) | 2.817mb         | 2.817mb    |
| benchHydrate1000000ObjectsTriggerInit () | 2.131s (±0.00%)    | 2.159s (±0.00%)    | 2.927mb         | 2.931mb    |
+------------------------------------------+--------------------+--------------------+-----------------+------------+

StackHydratorWithCryptographyBench
==================================

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 7.200μs (±0.00%)  | 16.400μs (±0.00%) | 2.820mb         | 2.820mb    |
| benchExtract1Object ()        | 21.600μs (±0.00%) | 80.600μs (±0.00%) | 2.820mb         | 2.836mb    |
| benchHydrate1000Objects ()    | 4.194ms (±0.00%)  | 4.291ms (±0.00%)  | 3.115mb         | 3.115mb    |
| benchExtract1000Objects ()    | 9.227ms (±0.00%)  | 8.974ms (±0.00%)  | 3.201mb         | 3.203mb    |
| benchHydrate1000000Objects () | 2.267s (±0.00%)   | 2.322s (±0.00%)   | 3.115mb         | 3.115mb    |
| benchExtract1000000Objects () | 6.636s (±0.00%)   | 6.739s (±0.00%)   | 3.201mb         | 3.201mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

HydratorBench
=============

+-------------------------------+-------------------------------------+------------------------------+
|                               | time (kde mode)                     | memory                       |
+-------------------------------+------------------+------------------+-----------------+------------+
| subject                       | Tag: <current>   | Tag: base        | Tag: <current>  | Tag: base  |
+-------------------------------+------------------+------------------+-----------------+------------+
| benchHydrate1Object ()        | 3.800μs (±0.00%) | 3.600μs (±0.00%) | 2.772mb         | 2.772mb    |
| benchExtract1Object ()        | 3.600μs (±0.00%) | 3.800μs (±0.00%) | 2.772mb         | 2.772mb    |
| benchHydrate1000Objects ()    | 2.542ms (±0.00%) | 2.592ms (±0.00%) | 3.019mb         | 3.019mb    |
| benchExtract1000Objects ()    | 2.472ms (±0.00%) | 2.415ms (±0.00%) | 3.008mb         | 3.008mb    |
| benchHydrate1000000Objects () | 1.436s (±0.00%)  | 1.452s (±0.00%)  | 3.019mb         | 3.019mb    |
| benchExtract1000000Objects () | 1.493s (±0.00%)  | 1.513s (±0.00%)  | 3.009mb         | 3.008mb    |
+-------------------------------+------------------+------------------+-----------------+------------+

HydratorWithCryptographyBench
=============================

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 7.200μs (±0.00%)  | 7.400μs (±0.00%)  | 2.820mb         | 2.820mb    |
| benchExtract1Object ()        | 16.400μs (±0.00%) | 88.400μs (±0.00%) | 2.820mb         | 2.836mb    |
| benchHydrate1000Objects ()    | 4.161ms (±0.00%)  | 4.314ms (±0.00%)  | 3.115mb         | 3.114mb    |
| benchExtract1000Objects ()    | 9.109ms (±0.00%)  | 9.065ms (±0.00%)  | 3.201mb         | 3.203mb    |
| benchHydrate1000000Objects () | 2.296s (±0.00%)   | 2.300s (±0.00%)   | 3.115mb         | 3.115mb    |
| benchExtract1000000Objects () | 6.728s (±0.00%)   | 6.711s (±0.00%)   | 3.201mb         | 3.201mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

This comment gets update everytime a new commit comes in!

The StackHydrator injected itself into the hydrator aware normalizers, so
nested objects bypassed all decorators and were e.g. not traced.
buildHydrator() now sets the outermost hydrator as root hydrator on the
stack, which is then passed to the normalizers instead.
@DavidBadura DavidBadura added this to the 2.1.0 milestone Oct 6, 2026
@DavidBadura DavidBadura added the enhancement New feature or request label Oct 6, 2026
This way the tracing is wrapped around decorators with the default priority
and also measures them.
@DavidBadura DavidBadura closed this Oct 6, 2026
@DavidBadura DavidBadura removed this from the 2.1.0 milestone Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants