Skip to content

fix(laravel): don't cache metadata read from a missing table - #8586

Merged
soyuka merged 2 commits into
api-platform:4.4from
Amoifr:fix-8585-laravel-metadata-cache
Sep 29, 2026
Merged

soyuka merged 2 commits into
api-platform:4.4from
Amoifr:fix-8585-laravel-metadata-cache

Conversation

@Amoifr

@Amoifr Amoifr commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #8585

Summary

Follows the suggested fix: the three Laravel cache decorators get the guard ModelMetadata::getAttributes() already has.

The decorators cannot tell a degraded result from a legitimate one by looking at it (an empty property list or a property without a type is valid for some models), so the signal comes from where the degradation happens:

  • ModelMetadata (@internal) counts the attribute reads that found no table, in the existing guard branch.
  • CachePropertyNameCollectionMetadataFactory, CachePropertyMetadataFactory and CacheResourceCollectionMetadataFactory take the ModelMetadata singleton as an optional last argument, read that count before and after the inner call, and when it moved they return the result without storing it, neither in the store nor in their in-memory cache. The next call, in this process or a new worker, reads the table again.
  • Since the three chains share the singleton, the resource metadata is not cached either when it was built through a property factory that hit the missing table.

A lookup served from the store does not touch the table, so there is no extra query once the metadata is cached, and nothing changes for a model whose table exists. With a metadata dump (api-platform.metadata_dump), the dumped models are answered from the seeded attributes and never reach the guard.

As the issue says, the routes registered by the boot that saw the missing table are still wrong for that process. The failure is now transient: the next worker start after the migration resolves the metadata correctly.

Test plan

src/Laravel/Tests/Metadata/CacheMetadataFactoriesMissingTableTest.php simulates two workers sharing the store (a new factory chain and a new ModelMetadata each): the first one boots while the table is missing, the table is created, the second one must see its columns.

  • Property names, property metadata and resource metadata: the three tests fail on 4.4 (the second worker gets the degraded value from the store) and pass with the fix.
  • A fourth test checks that metadata read from an existing table is still served from the store (the table is dropped before the second lookup).
  • Full Laravel suite under PHP 8.5 with SQLite: the only failures locally are the 3 CustomExceptionHandlerTest ones, which fail the same way on 4.4 without this change.

ModelMetadata already refused to keep the attributes of a missing table,
but the three cache decorators above it stored whatever they got with
rememberForever, in a file store in production. A worker booting during a
migration then served the degraded metadata forever.

ModelMetadata now counts the reads that found no table. The decorators
compare that count around the inner call and skip both the persistent
store and the in-memory cache when it moved, so the next call reads the
table again.

Fixes api-platform#8585
}

$store->forever($key, $propertyMetadata);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code is duplicated in 3 classes maybe worth a trait ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, done in 4401df1: MetadataCacheTrait::cached() now holds the lookup and the missing-table guard, and each factory only builds its key and the call to the decorated factory.

@soyuka
soyuka merged commit e46e6a1 into api-platform:4.4 Sep 29, 2026
112 of 117 checks passed
@soyuka

soyuka commented Sep 29, 2026

Copy link
Copy Markdown
Member

Thanks!

@Amoifr
Amoifr deleted the fix-8585-laravel-metadata-cache branch October 18, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants