flow-development-collection
flow-development-collection copied to clipboard
PdoBackend: Iterator state drift compared to database content
Description
The PdoBackend implements the IterableBackendInterface. To do so, it keeps an internal object of type "ArrayAccess" named $cacheEntriesIterator holding all keys and values of the database once initialized.
When the cache state changes during runtime by e.g. flushing, the $cacheEntriesIterator holds on to the previous data.
This occurred to me in functional tests where (at least in my setup) the very same PdoBackend object is used for multiple tests.
As far as I can tell, the same holds true for Apcu and SimpleFile backends. Redis does this differently.
Steps to Reproduce
assert($cacheManager instanceof CacheManager);
$cache = $cacheManager
->getCache('CACHE_WITH_PDO_BACKEND');
$backend = $cache
->getBackend();
assert($backend instanceof IterableBackendInterface);
$cache->set('key_before', 'value_before');
$dataBefore = \iterator_to_array($backend);
self::assertEquals(['key_before' => 'value_before'], $dataBefore);
$cache->flush();
$cache->set('key_after', 'value_after');
$dataAfter = \iterator_to_array($backend);
self::assertEquals(['key_after' => 'value_after'], $dataAfter);
/*
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
- 'key_after' => 'value_after'
+ 'key_before' => 'value_before'
)
*/
Expected behavior
If there's any user interaction with a certain cache, every change should be reflected in the iteration. I do realize taking lifetime into account might be a bit too much to ask, but at least when I manually flush or add new values, the iterator should be set to null.
Actual behavior
The first time of doing any iteration interaction (method call to key, current, rewind, valid) with an iterable cache backend constitutes the value of iterable until the PHP process terminates.
To be discussed
What's the reason behind having the iterable state cached inside of the PdoBackend object?
I could e.g. change the $this->cacheEntriesIterator from ArrayAccess to Generator. Rewind could aways create a new one and the internal state of all database rows would be removed from the PHP runtime.
I'd even think about making the IterableBackendInterface not extend Iterator but Traversable to allow exposing the Generator directly, reducing the clutter of having all methods forced by Iterator just proxy to $this->cacheEntriesIterator. But I realize that's a breaking change and maybe not a good idea to be changed for such a detail.
I tried to solve this here: https://github.com/neos/flow-development-collection/pull/2829
Replacement for the first PR is at #2884…
Now that #2884 is merged, should we close this one as well?