Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full disclaimer: Code was generated by AI. I did test it on a real project myself though.
Performance Optimizations
This branch introduces several optimizations to reduce the overhead of LOData's discovery and bootstrapping process, which previously ran expensive operations on every HTTP request.
Problem
With many registered entity models (e.g. 50+), every request triggered:
listTableDetails()queried database metadata for each tableDeclaredProperty, involving type resolution, default value handling, and annotation setupOperation::discover()scanned each model class forLodataOperationattributes via reflectiongetModel()calledApp::make()andgetDatabase()created a new DBAL connection on every invocationThe existing
Discovery::remember()cache (controlled byLODATA_DISCOVERY_TTL) only cached the raw DBALTableobject — all subsequent processing still ran on every request.Changes
1. Full Property Discovery Caching (
SQLSchema)Files:
src/Drivers/SQL/SQLSchema.phpReplaced the old two-step approach (cache raw
Table, re-process every request) with a fully cached pipeline:buildPropertyDescriptors()— Produces a serializable array of property descriptors containing type names, nullability, defaults, source names, and key information. This is the expensive part that gets cached.discoverProperties()— Caches the full descriptor array viaDiscovery::remember(), then hydrates into ODataDeclaredPropertyobjects (cheap).columnToTypeName()— Maps DBAL column types to string type names for serialization.resolveType()— Resolves type name strings back to ODataTypeinstances.resolveColumnName()— Handles namespaced column name resolution.Cache key format:
sql.properties.{connection}.{table}2. Eloquent-Specific Discovery Caching (
EloquentEntitySet)Files:
src/Drivers/EloquentEntitySet.phpOverrides the
SQLSchematrait methods to incorporate Eloquent-specific logic into the cached descriptors:buildPropertyDescriptors()— Extends the base descriptors with:$model->getAttributeValue()discoverProperties()— Uses model-scoped cache keys to avoid collisions between models sharing the same table.castToTypeName()— Maps Eloquent cast names (boolean,array,integer,datetime, enums, etc.) to serializable type name strings.resolveType()— Handles Eloquent-specific type names likecollection_string,double,enum:ClassName,int64_or_uint64.Cache key format:
sql.properties.{connection}.{table}.{modelClass}3. Model Instance Caching (
EloquentEntitySet)Files:
src/Drivers/EloquentEntitySet.phpgetModel()previously calledApp::make()on every invocation, creating a new Eloquent model instance each time. Now caches the instance in a$modelInstanceproperty.4. DBAL Connection Caching (
SQLConnection)Files:
src/Drivers/SQL/SQLConnection.phpgetDatabase()previously created a newDBALinstance (wrappingDoctrineConnection/PDO) on every call. Now caches the instance in a$dbalInstanceproperty.5. Operation Discovery Fast-Path (
Operation)Files:
src/Operation.phpAdded an early-exit check in
Operation::discover(): before entering the full reflection loop (which callsDiscovery::getReflectedMethods()and iterates all public methods), first scans forLodataOperationattributes. If none are found — which is the common case for Eloquent models — the method returns immediately, skipping the expensive reflection loop entirely.Cache Invalidation
After database schema changes (e.g. migrations), the discovery cache must be cleared:
The cache TTL is controlled by
LODATA_DISCOVERY_TTLin your.env:0(default)null300Performance Impact
With 58 registered entity models, measured on an OData endpoint (
/odata/Users?$top=1):