flow-development-collection icon indicating copy to clipboard operation
flow-development-collection copied to clipboard

PdoBackend: Iterator state drift compared to database content

Open stephanschuler opened this issue 2 years ago • 1 comments

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.

stephanschuler avatar Apr 14 '22 14:04 stephanschuler

I tried to solve this here: https://github.com/neos/flow-development-collection/pull/2829

stephanschuler avatar Apr 14 '22 14:04 stephanschuler

Replacement for the first PR is at #2884…

kdambekalns avatar Nov 29 '22 15:11 kdambekalns

Now that #2884 is merged, should we close this one as well?

fcool avatar Jul 06 '23 12:07 fcool