phalcon / cphalcon

High performance, full-stack PHP framework delivered as a C extension.
https://phalcon.io
BSD 3-Clause "New" or "Revised" License
10.76k stars 1.98k forks source link

[BUG]: Failed to store metaData to the cache adapter - Phalcon 5.1.2 on linux #16419

Open VovaSlipchenko opened 10 months ago

VovaSlipchenko commented 10 months ago

Good day, i faced the following problem: I have my phalcon project running on windows: PHP 8.1.6 Phalcon 5.2.1

Everything works good!

But, when im trying to make some linux setup, like this: Ubuntu 20.04.6 LTS PHP 8.1.22 Phalcon 5.1.2

php -m [PHP Modules]

apcu
calendar
Core
ctype
curl
date
exif
FFI
fileinfo
filter
ftp
gettext
hash
iconv
json
libxml
mbstring
openssl
pcntl
pcre
PDO
pdo_pgsql
pgsql
phalcon
Phar
posix
readline
Reflection
session
shmop
sockets
sodium
SPL
standard
sysvmsg
sysvsem
sysvshm
tokenizer
Zend OPcache
zip
zlib

i got the following error in one of scripts (it's a CLI service to execute tasks from queue) i start script with command: php app/tasks.php

And get this exception:

object(Phalcon\Mvc\Model\Exception)#65 (7) {
  ["message":protected]=> string(45) "Failed to store metaData to the cache adapter"
  ["string":"Exception":private]=> string(0) ""
  ["code":protected]=> int(0)
  ["file":protected]=> string(46) "/var/www/common_bcovery/app/models/Options.php"
  ["line":protected]=> int(32)
  ["trace":"Exception":private]=>
  array(9) {
    [0]=> array(3) {
      ["function"]=>string(19) "throwWriteException"
      ["class"]=>string(26) "Phalcon\Mvc\Model\MetaData"
    }
    [1]=> array(3) {
      ["function"]=>string(5) "write"
      ["class"]=>string(26) "Phalcon\Mvc\Model\MetaData"
    }
    [2]=> array(3) {
      ["function"]=>string(10) "initialize"
      ["class"]=> string(26) "Phalcon\Mvc\Model\MetaData"
    }
    [3]=> array(3) {
      ["function"]=>string(18) "readColumnMapIndex"
      ["class"]=>string(26) "Phalcon\Mvc\Model\MetaData"
    }
    [4]=> array(3) {
      ["function"]=>string(19) "getReverseColumnMap"
      ["class"]=>string(26) "Phalcon\Mvc\Model\MetaData"
    }
    [5]=> array(3) {
      ["function"]=>string(12) "invokeFinder"
      ["class"]=>string(17) "Phalcon\Mvc\Model"
    }
    [6]=> array(5) {
      ["file"]=> string(46) "/var/www/common_bcovery/app/models/Options.php"  ["line"]=> int(32)
      ["function"]=> string(12) "__callStatic" ["class"]=>string(17) "Phalcon\Mvc\Model"
    }
    [7]=> array(5) {
      ["file"]=>string(40) "/var/www/common_bcovery/public/tasks.php"  ["line"]=>int(394)
      ["function"]=>string(10) "set_option" ["class"]=>string(20) "MyApp\Models\Options"
    }
    [8]=> array(5) {
      ["file"]=>string(40) "/var/www/common_bcovery/public/tasks.php" ["line"]=>int(525)
      ["function"]=>string(4) "main" ["class"]=>string(11) "Application"
    }
  }
  ["previous":"Exception":private]=>NULL
}

Event i change cache provider from Apcu to Memory - i got new error, like “Autoloader can't find some class” - but this class exists in the folder and i added this folder to autoloader. It works like phalcon is broken

Also, have strange moment: When i execute: apt install php8.1-phalcon5 It install php 8.2 and switch php version to 8.2, where phalcon is not presented

Code of tasks.php file:


<?php

use Phalcon\Di\Di as DI;
use Phalcon\Autoload\Loader;
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Http\Response;
use Phalcon\Http\Request;
use Phalcon\Mvc\View;
use Phalcon\Db\Adapter\Pdo\Pgsql;
use Phalcon\Mvc\Application as BaseApplication;
use Phalcon\Mvc\Model\Metadata\Memory as MemoryMetaData;
use Phalcon\Mvc\Model\Manager as ModelsManager;
use Phalcon\Db\Adapter\PdoFactory as Factory;
use Phalcon\Config\Config;
use Phalcon\Http\Response\Cookies;
use Phalcon\Mvc\View\Engine\Volt;
use Phalcon\Flash\Direct as FlashDirect;
use Phalcon\Escaper;
use Phalcon\Mvc\Url;
use Phalcon\Flash\Session as FlashSession;
use Phalcon\Encryption\Security;
use Phalcon\Mvc\Micro;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use MyApp\Libs\Auth\Auth;
use MyApp\Libs\Acl\Acl;
use MyApp\Libs\GData;
use MyApp\Libs\CliLogger;
use MyApp\Libs\JsonResponse;
use MyApp\Libs\PermissionsManager;
use MyApp\Middleware\CheckPermission;
use MyApp\Models\Options;
use MyApp\Models\Tasks;

use Phalcon\Cache\AdapterFactory;
use Phalcon\Di\FactoryDefault;
use Phalcon\Mvc\Model\MetaData\Apcu;
use Phalcon\Mvc\Model\MetaData\Memory;
use Phalcon\Storage\SerializerFactory;

define('BASEPATH', dirname(__DIR__));
define('APP_PATH', BASEPATH . '/app');
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);

require APP_PATH."/config/constants.php";

class Application extends BaseApplication
{

    protected function registerAutoloaders()
    {
        $loader = new Loader();
        $loader->setDirectories([
                APP_PATH . '/controllers/',
                APP_PATH . '/models/',
                APP_PATH . '/tasks/system/',
                APP_PATH . '/tasks/',
        ]);
        $loader->register();
    }

    protected function registerServices()
    {

        $di = new DI();
        $di->set("voltService", function ($view, $di) {
                $volt = new Volt($view, $di);
                $volt->setOptions([
                    "compiledPath"      => "../app/compiled-templates/",
                    "compiledExtension" => ".compiled",
                ]);
                return $volt;
        });

        $di->set('acl', function () {
            $acl = new Acl();
            $pr = $this->getShared('AclResources')->privateResources->toArray();
            $acl->addPrivateResources($pr);
            return $acl;
        });

        $di->set('js_version', function(){
            $version = 0;
            $configData = require "".get_config_path();
            require $configData['js_libs'].'/version.php';
            $version = $SCRIPT_VERSION;
            return $version;
        });

        $di['modelsMetadata'] = function() {
            $serializerFactory = new SerializerFactory();
            $adapterFactory    = new AdapterFactory($serializerFactory);
            $options = [
                'lifetime' => 86400,
                'prefix'   => 'my-prefix',
            ];
            return new Apcu($adapterFactory, $options);
        };

        $di->set('auth', function () {
            return new Auth();
        });

        $di->set("JsonResponse",function () {
            return new JsonResponse();
        });

        $di->set("config",function () {
            $configData = require "".get_config_path();
            return new Config($configData);
        });

        $di->set("permissionsManager",function () {
           return new PermissionsManager();
        });

        $di->set(
            "view", function () {
                $view = new View();
                $view->setViewsDir("../app/views/");
                $view->registerEngines(
                    [
                        ".volt" => "voltService",
                    ]
                );
                return $view;
            }
        );

        $di->set('cookies', function() {
                $cookies = new Cookies();
                $cookies->useEncryption(false);
                return $cookies;
        });

        $di->set("dispatcher",function() {
                return new Dispatcher();
        });

        $di->set("escaper",function() {
                return new Escaper();
        });

        $di->set('url',function() {
                $url = new Url();
                $url->setBaseUri('/invo/');
                return $url;
        });

        $di->set("dispatcher",function() {
                $eventsManager = new EventsManager();
                $eventsManager->attach(
                    "dispatch:beforeExecuteRoute",
                    new CheckPermission()
                );
                $dispatcher = new Dispatcher();
                $dispatcher->setEventsManager($eventsManager);
                return $dispatcher;
       });

        $di->set("security", function() {
            $security = new Security();
            $security->setWorkFactor(12);
            return $security;
        },true);

        $di->set("response", function() {
            return new Response();
        });

        $di->set("request", function() {
            return new Request();
        });

        $di->set("view", function() {
            $view = new View();
            $view->setViewsDir(APP_PATH . '/views/');
            return $view;
        });

        $di->set("db", function() {
                $config = new \Phalcon\Config\Adapter\Php(get_config_path());
                $f = new Factory;
                $config = [
                    'host'     => $config->database->host,
                    'dbname'   => $config->database->dbname,
                    'port'     => $config->database->port,
                    'username' => $config->database->username,
                    'password' => $config->database->password,
                ];
                $connection = new \Phalcon\Db\Adapter\Pdo\Postgresql($config);
                return $connection;
        });

        $di->set("loader", function (){
            return new MemoryMetaData();
        });

        $di->set("flash", function (){
            return new FlashDirect();
        });

        $di->set("flashSession", function (){
            return new FlashSession();
        });

        $loader = new \Phalcon\Autoload\Loader();

        $loader
            ->setNamespaces(
                [
                    'MyApp\Controllers' => __DIR__ . '/../app/controllers/',
                    'MyApp\Middleware' => __DIR__ . '/../app/middleware/',
                    'MyApp\Controllers\Auth' => __DIR__ . '/../app/controllers/Auth',
                    'MyApp\Libs' =>  __DIR__ . '/../app/libs',
                    'MyApp\Tasks' =>  __DIR__ . '/../app/tasks',
                    'MyApp\Libs\fpdf' =>  __DIR__ . '/../app/libs/fpdf',
                    'MyApp\Models' =>  __DIR__ . '/../app/models',
                    'MyApp\Libs\Auth' =>  __DIR__ . '/../app/libs/Auth',
                    'MyApp\Libs\Acl' =>  __DIR__ . '/../app/libs/Acl',
                    'MyApp\Forms' =>  __DIR__ . '/../app/forms',
                    'Carbon' => __DIR__ . '/../vendor/nesbot/carbon/src/Carbon',
                ]
            )->register();

        $di->set("modelsManager", function (){
            return new ModelsManager();
        });

        $di->set('router', function() {
            require __DIR__ . '/../app/config/routes.php';
            return $router;
        });

        $this->setDI($di);

    }

    public function main()
    {

        $this->registerServices();
        $this->registerAutoloaders();

        Options::set_option('task_service_status', 1);

        while(true){
            sleep(1);
            if(Options::get_option('task_service_status') == 1) {

                $last_task = Tasks::lastTaskExecuted();
                $task = Tasks::getAwaitingTask($this->config->app_name);

                if ($task) {

                    $time = time();

                    $options = $task->params;
                    $options = base64_decode($options);
                    $options = json_decode($options, true);

                    $className = "MyApp\\Tasks\\Task".ucfirst(toCamelCase($task->method));
                    CliLogger::info('Class: '.$className);
                    $task_controller = new $className();

                    if ($task_controller) {
                        $task_controller->set_config($this->config);
                        $task_controller->set_db_object($task);                        
                        try {
                            $task_controller->execute($options);
                        } catch (\Exception $e) {
                            echo $e->getMessage();
                            $task_controller->set_status(Tasks::STATUS_ERROR);
                        }
                    }
                }
            } else {
                sleep(5);
            }
        }

    }

}

try {
    $application = new Application();
    $application->main();
} catch (\Exception $e) {
    var_dump($e);
}

function get_config_path(){
    global $argv;

    $path = false;
    if(!$path) {
        $path = APP_PATH."/config/conf/config.php";
    }
    return $path;
}

function toCamelCase($string, $capitalizeFirstCharacter = false)
{
    $str = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
    if (!$capitalizeFirstCharacter) {
        $str[0] = strtolower($str[0]);
    }
    return $str;
}

Code of Options model:


<?php
    namespace MyApp\Models;

    use Phalcon\Mvc\Model;
    use Phalcon\Mvc\Model\Relation;

    class Options extends Model
    {

        public function initialize()
        {

        }

        public static function get_option($key){

            $option = self::findFirstByKey($key);
            if($option){
                return $option->value;
            }
            return null;

        }

        public static function set_option($key, $value){

            $option = self::findFirstByKey($key);

            if($option){
                $option->value = $value;
                $option->save();
            } else {
                $option = new Options;
                $option->key = $key;
                $option->value = $value;
                $option->save();
            }

        }

    }
rudiservo commented 10 months ago

To install phalcon use pecl, to install for diferent versions you need the dev package for that php version, i.e. php8.2-dev. to run pecl for each php version you probably have to go directly to the pecl binary of each version.

Phalcon is already on 5.3 version.

Check if APCU is active in cli/php.ini

VovaSlipchenko commented 10 months ago

To install phalcon use pecl, to install for diferent versions you need the dev package for that php version, i.e. php8.2-dev. to run pecl for each php version you probably have to go directly to the pecl binary of each version.

Phalcon is already on 5.3 version.

Check if APCU is active in cli/php.ini

Hello rudiservo, problem is that when i solve problem with APCU (switch to Memory), i got next error, when i solve this error - i get next. Like all phalcon is broken.

rudiservo commented 10 months ago

The error of APCU in php cli is that APCU might not be enabled in CLI php.ini or miss configured

Try updating Phalcon to the latest version and check again.

VovaSlipchenko commented 10 months ago

Hi, i tried to make assembly on Ubuntu 22, with Phalcon 5.3.0 + PHP 8.1, i have no more cache problem, but i still have this problem: image

It says class Task is not found Class tasks placed in app->tasks->system->Task.php defined as:

<?php
namespace MyApp\Tasks\System;
use ...
abstract class Task{
...
}

used as parent class for other tasks instances:

<?php

namespace MyApp\Tasks;

...
use MyApp\Tasks\System\Task;
...

class TaskCompileTag extends Task{

in autoloader i define:

 protected function registerAutoloaders()
    {
        $loader = new Loader();
        $loader->setDirectories([
                APP_PATH . '/controllers/',
                APP_PATH . '/models/',
                APP_PATH . '/tasks/system/',
                APP_PATH . '/tasks/',
        ]);
        $loader->register();
    }

this works perfect on Windows, but all my attempts to run script on linux are failed. I always tested all of my Phalcon projects on windows environment and never had problems with migration to Linux server

rudiservo commented 10 months ago

@VovaSlipchenko, ok, my guess is that you forgot that linux is Case Sensitive. The file or folder must be in lower case or upper case, it could also be the case the loader is not well configured.

My advice is always develop in the environment closer to production. Use docker, in windows you got Linux Subsystem.

VovaSlipchenko commented 10 months ago

@rudiservo Yes, thank you for reminding about case sensitivity. But same file worked well on linux with Phalcon 3+ problem appeared after upgrade to Phalcon 5+

the path of Task class file is /app/tasks/system/Task.php

same as model or controller, like /app/models/SomeTable.php

I tryed to put file Task.php to folder /app/tasks (same folder with child class = TaskCompoleTag, where i have a problem "Class Task not found") And it works!

rudiservo commented 10 months ago

Have you tried to register the complete namespace, MyApp\Tasks\System?

niden commented 5 months ago

@VovaSlipchenko Is this still an problem? If not I will close the issue.

noone-silent commented 1 month ago

When i execute: apt install php8.1-phalcon5

It looks like you're installing phalcon via the sury package repository.

It install php 8.2 and switch php version to 8.2, where phalcon is not presented

This means you have some mixed packages or some virtual packages, that automatically install multiple version of PHP linked to this package.

If you want to specifically use php8.1 you could run:

sudo update-alternatives --set php /usr/bin/php8.1

Else you have to install all required packages to the php8.2 version too, like @rudiservo mentioned.

To check some package differences, you could compare the outputs and install missing packages:

dpkg -l | grep -e '^ii' | grep php8.1
dpkg -l | grep -e '^ii' | grep php8.2