Using stream adapter in Model cache
Config setting
'cache' => [
'adapter' => 'stream',
'options' => [
'defaultSerializer' => 'Json',
'storageDir' => BASE_PATH . '/storage/caches/',
'lifetime' => 172800,
'prefix' => 'data-'
]
],
In Provider
public function register(DiInterface $di): void
{
$di->setShared('modelsCache', function () {
$config = $this->getShared('config');
$cache = $config->cache->toArray();
$serializerFactory = new SerializerFactory();
$adapterFactory = new AdapterFactory($serializerFactory);
$adapter = $adapterFactory->newInstance($cache['adapter'], $cache['options']);
return new Cache($adapter);
});
}
In Controller
class Post extends BaseController{
public function index(){
$results = Model\Post\Post::find([
'cache' => [
'key' => 'my-cache',
],
]);
var_dump($this->cache->get('my-cache'));exit;
}
}
When first run Phalcon created folder
like
storage
--caches
--data
--my
---c
--ac
--my-cache
Second run get error
Cache didn't return a valid resultset
Phalcon 4.0 rc3
Macos Catalina
Mamp Pro
PHP 7.3.7
I am ready test for redis adapter working nice!
I also ran into this error.
I think the problem is here https://github.com/phalcon/cphalcon/blob/858d0e57ed42f0dcd8723cca64798dded84790ea/phalcon/Storage/Serializer/Json.zep#L29-L33
Before encoding in JSON, it is checked for isSerializable, but in the Storage\Serializer\Json::unserialize method, the data is returned without checking whether it is encoded or not.
I cannot reproduce this guys. I saw it once in my test and local environment but it was caused by a corrupted cache. Once I cleared the cache (deleted the file) it worked just fine.
This is the full build: https://travis-ci.com/niden/cphalcon/builds/138859860
I did make an enhancement and added a Json helper class to encode/decode with exceptions so that errors can be discovered earlier but with or without the new class it worked just fine.
This is the test I wrote based on the input
https://github.com/phalcon/cphalcon/blob/4.0.x/tests/integration/Mvc/Model/FindCest.php#L66
As you can see the find gets called and the cache file appears and also it exists in the cache. I get the resultset back from the cache (not calling the model find again) and it contains the same data.
Anything you guys can see and point out that I have missed please let me know. Alternatively I will just merge the branch with the new class and close this.
@niden
$data = Test::find([
'cache' => [
'key' => 'test-data'
]
]);
// The original data is an object.
var_dump($data); // $data is Object
// object(Phalcon\Mvc\Model\Resultset\Simple)[259]
// protected 'activeRow' => null
// ....
// But the data from the cache is an array
$dataFromCache = $this->modelsCache->get('test-data');
var_dump($dataFromCache); // $dataFromCache is Array
// array (size=2)
// 0 =>
// object(stdClass)[240]
// public 'id' => int 1
// public 'email' => string '__email__' (length=13)
// public 'fullname' => string '__fullname__' (length=56)
// 1 =>
// object(stdClass)[241]
// public 'id' => int 2
// public 'email' => string '__email1__' (length=25)
// public 'fullname' => string '__fullname__' (length=38)
// ......
And here the object is clearly expected.
https://github.com/phalcon/cphalcon/blob/84924803960de4e62c96afd401b3c2da08ec3d5f/phalcon/Mvc/Model/Query.zep#L3678-L3685
But let result = cache->get(key); $result is an array and we get this error.
Fatal error: Uncaught Phalcon\Mvc\Model\Exception: Cache didn't return a valid resultset in phalcon/Mvc/Model/Query.zep on line 3683
OK I get what is going on right now.
The long story short is we should not use the Json serializer for models because it will lead to the error you see above.
The JSON serializer uses json_encode and json_decode to serialize and unserialize data. When we are using objects, it calls the jsonSerialize method in the object to return an array which will allow the json_encode to work as expected.
For the Simple and Complex resultsets, the jsonSerialize method will return an array which will be json_encoded. When it is retrieved back it will return the relevant data but as an array AND as stdClass objects vs the correct Simple and Row respectively.
I have not seen any implementations that could pick up an object, store it with JSON and restore it back to the original object (unless it is an array of stdClass objects).
I will need to mark this as Won't Fix/Not a bug and ensure that the documentation clearly states that for modelsCache you cannot use the JSON serializer or others that cannot store objects properly. Php should work just fine, or any other serializer that can properly serialize and unserialize objects
Igbinary works just fine with the above as well as Php
OK I get what is going on right now.
The long story short is we should not use the
Jsonserializer for models because it will lead to the error you see above.The JSON serializer uses
json_encodeandjson_decodeto serialize and unserialize data. When we are using objects, it calls thejsonSerializemethod in the object to return an array which will allow thejson_encodeto work as expected.For the
SimpleandComplexresultsets, thejsonSerializemethod will return an array which will bejson_encoded. When it is retrieved back it will return the relevant data but as an array AND asstdClassobjects vs the correctSimpleandRowrespectively.I have not seen any implementations that could pick up an object, store it with JSON and restore it back to the original object (unless it is an array of
stdClassobjects).I will need to mark this as Won't Fix/Not a bug and ensure that the documentation clearly states that for
modelsCacheyou cannot use the JSONserializer or others that cannot store objects properly.Php` should work just fine, or any other serializer that can properly serialize and unserialize objects
I agree! I wanted to write about it
Added the note
@duongkhoangiam Thanks for reporting this.
Also can you test for folder struct ?
in phalcon 3.x the cache file generate to /storage/cache/data/my-cache
in phalcon 4
storage
--caches
--data
--my
---c
--ac
--my-cache
That mean cache created folder with key, prefix , and name .
This problem come in Stream Adapter only, another Adapter working fine !
i think problem in
directory = this->getDir(key);
@duongkhoangiam These subfolders are intentionally created like that. Using this methodology we avoid the "too many files in a folder" for busy applications that need to store a lot of cached entities.
Most helpful comment
I also ran into this error.
I think the problem is here https://github.com/phalcon/cphalcon/blob/858d0e57ed42f0dcd8723cca64798dded84790ea/phalcon/Storage/Serializer/Json.zep#L29-L33
Before encoding in JSON, it is checked for
isSerializable, but in theStorage\Serializer\Json::unserializemethod, the data is returned without checking whether it is encoded or not.https://github.com/phalcon/cphalcon/blob/858d0e57ed42f0dcd8723cca64798dded84790ea/phalcon/Storage/Serializer/Json.zep#L41