-
-
Notifications
You must be signed in to change notification settings - Fork 188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
!!!TASK: Reduce complexity of ReflectionService #3443
!!!TASK: Reduce complexity of ReflectionService #3443
Conversation
This change tackles some problems within the reflection service that stem from historically increasing complexity due to various caching mechanisms depending on application context and compile time status. The aim was to cut down on this complexity, while ensuring that all existing use-cases continue working as intended. This ultimately also fixes issue neos#3402 by providing the same reflection data across all possible contexts. A few features and caches got deprecated with this change and could be breaking in the rare case you used the freeze package api in your code: The entire concept of freezing a package is deprecated What remains are the commands in the package controller, which are now all no-ops and deprecated to be removed with 9.0. This is to ensure deployment pipelines possibly calling freeze commands do not break with the 8.4 update. Additionally the single method `PackageManager::isPackageFrozen` remains, while the rest was removed. None of the methods was ever api and it seems unlikely that someone used them in user-land code. `isPackageFrozen` however is at the very least used in Framework and Neos code and therefore remains until 9.0, but will now return false for every package. Caches deprecated and unused With the simplification two caches are no longer needed, both are still declared so that possibly existing cache configuration in user projects doesn't error, but both `Flow_Reflection_Status` and `Flow_Reflection_CompiletimeData` will no longer be used and any content can be removed. The only reflection cache is now `Flow_Reflection_RuntimeData`, which makes the name somewhat deceptive as it is also used in compile time. To avoid backwards compatibility issues however it makes sense to keep the name for the foreseeable future. Quick performance comparisons suggest that especially the initial compile from empty cache benefits from this change. Reflection updates in Development context afterwards seem to be on par with the existing code base.
Future steps here IMHO could/should be to transfer the huge array structure of reflection data into DTOs and possibly split code that creates the reflection into a Compiletime ReflectionService as we shouldn't need to reflect anything during runtime, therefore making the code easier to reason with. But I resisted from adding those changes here to avoid making it a needlessly large change for 8.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you sooo much for the change! Ill yet have to actually test it and also fully understand the new reflection logic but from reading it briefly i love it. I remember attempting to fix things already in the reflection but the whole initialisation processes is just soo hard to wrap your head around and every-time i got a grasp later i forgot again. Because of that could you please just shortly describe what the expected difference in cache behaviour/performance during Development/Testing and Production might be? Anyway all this freezing functionally will be finally a thing of the past and it definitely does not justify any of the complexity! So super glad to have it gone.
So what this thing do before:
Then the second big part is the Additionally in production context we would write the runtime cache (the only one now) and freeze it afterwards, BUT this production cache did not contain all data the compiletime cache contained leading to bug #3402 And finally in development we would write an additional big cache file outside of any cache backend, that woudl contain about the same as the compiletime cache. This was called
What happens WITH THIS CHANGE: We will discuss this in a slightly different order to make more sense.
Finally
The class schemata weren't mentioned before, they are basically a sideline and contain data for doctrine ORM retrieved via reflection, so it's kinda in the right place but kinda not. Might also warrant another refactoring, but another day. |
Note I might have found two little things to imrpove / fix, shouldn't stop you from reviewing, but should be done before merge. |
The new caching loads lazily we need to make sure that the interfaces are actually loaded when trying to remove implementation classes.
Otherwise the initialize might happen for no good reason if the ReflectionService was never used.
Some docblock changes and minimal code improvements, might be nice to have for future refactorings.
Here we go, split in separate commits to make it easier to review. the last commit is a lot of foobar adaptions to docblocks and code style, nothing functional. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a close look, left some comments. Generally fine, so 👍 already.
foreach ($classNames as $className) { | ||
if (!$this->statusCache->has($this->produceCacheIdentifierFromClassName($className))) { | ||
if (is_string($className) && !$this->reflectionDataRuntimeCache->has($this->produceCacheIdentifierFromClassName($className))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What – if not a string – can $className
be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had an example were that was for some reason I couldn't figure out a bool, so I jjust wanted to be sure here....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think that must have been in a previous iteration of the code as array_keys
is used which can NEVER return a bool as that is not a valid array key.
Neos.Flow/Tests/Functional/Reflection/ReflectionServiceTest.php
Outdated
Show resolved
Hide resolved
The method was deprecated and the concept gutted, this would return empty array anyways.
Co-authored-by: Karsten Dambekalns <[email protected]>
Co-authored-by: Karsten Dambekalns <[email protected]>
use ReflectionIntersectionType; | ||
use ReflectionNamedType; | ||
use ReflectionType; | ||
use ReflectionUnionType; | ||
use RuntimeException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we aggreed that we dont want to import first level things and use \
- will fix that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed via 82452e8
Also removes declarations for not needed cache configurations: Flow_Reflection_Status Flow_Reflection_CompiletimeData if you have any custom declaration for these please remove them.
Neos part for neos/flow-development-collection#3446 See flows refactoring in 8.4: neos/flow-development-collection#3443
…kslash As discussed in https://neos-project.slack.com/archives/C04Q0TC15/p1683558218323329 And voted (8 vs 5) https://neos-project.slack.com/archives/C04Q0TC15/p1683558099679779 We want to use FQN instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay now im wondering whats with the FreezableBackendInterface
- do we want to deprecate that as well?
Also two times the word "frozen" still appears in the inline docs of the reflection service. Probably the whole doc block of how it works is outdated - but maybe we can put in there youre nice outline from above?
Tested it by upmerging into 9.0 (my 8.4 setup is broken^^) Anyways there are only little merge conflict and besides an error at start (which is expected) and cache flushing the Neos Demo runs fine.
Warning: unserialize(): Extra data starting at offset 283 of 307 bytes in Framework/Neos.Cache/Classes/Frontend/VariableFrontend.php line 94
I also prepared a followup for 9.0 to remove the dead apis #3446
and a neos change to remove the usages as well: neos/neos-development-collection#5467
use ReflectionIntersectionType; | ||
use ReflectionNamedType; | ||
use ReflectionType; | ||
use ReflectionUnionType; | ||
use RuntimeException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed via 82452e8
FreezableBackendInterface yep it's also for shit like this, that said, can be deprecated but didn't really feel like the scope here. |
Right that's because of the change to simplefilebackend, which doesn't expect stuff after hte payload, but it's alos faster therefore. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just some other things i found when looking at the code - id still also like the inline docs to be fixed as i wrote above ^^ -> will make a followup pr to tackle my code findings ;)
Edit: #3447
$availableClassnames = []; | ||
foreach ($this->availableClassNames as $classNamesInPackage) { | ||
$availableClassnames[] = $classNamesInPackage; | ||
} | ||
$classNamesToReflect = array_merge([], ...$availableClassnames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay that part got me first confused ^^ clever though :D
it first looked like a Object.assign({}, copyFrom)
in javascript but its way better, we unpack all arrays and then merge them together to a flat thing
we can make that a little more simple even:
$availableClassnames = []; | |
foreach ($this->availableClassNames as $classNamesInPackage) { | |
$availableClassnames[] = $classNamesInPackage; | |
} | |
$classNamesToReflect = array_merge([], ...$availableClassnames); | |
// flatten nested array structure to a list of classes | |
$availableClassnames = array_merge(...array_values($this->availableClassNames)); |
We dont need the []
as since 7.4.0 this function can be called without any parameter, and it will return empty array.
And we still need the array_values
to prevent: Uncaught ArgumentCountError: array_merge() does not accept unknown named parameters
return; | ||
} | ||
|
||
if (!$this->initialized) { | ||
$this->initialize(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initialising here when shutting down doesnt make sense and can never be the case i think - but was so beforehand as well ...
if (!isset($this->classReflectionData[$className][self::DATA_INTERFACE_IMPLEMENTATIONS]) && $class->isInterface()) { | ||
$this->classReflectionData[$className][self::DATA_INTERFACE_IMPLEMENTATIONS] = []; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we dont need that explicitly here but it doesnt hurt either right? should be done automatically in addImplementedInterface
Ha funny i found the original refactoring that introduced this production vs development distinction :D And i did the least of performance testing one could do in dev mode and ran |
Also removes declarations for not needed cache configurations: Flow_Reflection_Status Flow_Reflection_CompiletimeData if you have any custom declaration for these please remove them.
This change tackles some problems within the reflection service that stem from historically increasing complexity due to various caching mechanisms depending on application context and compile time status.
The aim was to cut down on this complexity, while ensuring that all existing use-cases continue working as intended.
This ultimately also fixes issue #3402 by providing the same reflection data across all possible contexts.
A few features and caches got deprecated with this change and could be breaking in the rare case you used the freeze package api in your code:
The entire concept of freezing a package is deprecated
What remains are the commands in the package controller, which are now all no-ops and deprecated to be removed with 9.0. This is to ensure deployment pipelines possibly calling freeze commands do not break with the 8.4 update.
Additionally the single method
PackageManager::isPackageFrozen
remains, while the rest was removed. None of the methods was ever api and it seems unlikely that someone used them in user-land code.isPackageFrozen
however is at the very least used in Framework and Neos code and therefore remains until 9.0, but will now return false for every package.Caches deprecated and unused
With the simplification two caches are no longer needed, both are still declared so that possibly existing cache configuration in user projects doesn't error, but both
Flow_Reflection_Status
and
Flow_Reflection_CompiletimeData
will no longer be used and any content can be removed.
The only reflection cache is now
Flow_Reflection_RuntimeData
, which makes the name somewhat deceptive as it is also used in compile time. To avoid backwards compatibility issues however it makes sense to keep the name for the foreseeable future.Quick performance comparisons suggest that especially the initial compile from empty cache benefits from this change. Reflection updates in Development context afterwards seem to be on par with the existing code base.
Checklist
FEATURE|TASK|BUGFIX
!!!
and have upgrade-instructions