I have a fixtures that loads a huge amount of data and all the time I run into this error:
Fatal error: Allowed memory size of 2147483648 bytes exhausted (tried
to allocate 16777224 bytes) in
/var/www/html/platform-cm/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php
on line 65
[Symfony\Component\Debug\Exception\OutOfMemoryException] Error:
Allowed memory size of 2147483648 bytes exhausted (tried to allocate
16777224 bytes)
After research a bit I found this post where I read that logging could be the cause of the issue because AppKernel is instantiated with debug set to true by default and then the SQL commands get stored in memory for each iteration.
The first attempt without disable the debug at AppKernel was run the command as:
doctrine:fixtures:load --no-debug
But I didn't get luck since the same error still.
The second attempt was disable the debug at config_dev.yml but this is not recommended since I am getting ride of every logs but didn't work neither.
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
# console:
# type: console
# bubble: false
# verbosity_levels:
# VERBOSITY_VERBOSE: INFO
# VERBOSITY_VERY_VERBOSE: DEBUG
# channels: ["!doctrine"]
# console_very_verbose:
# type: console
# bubble: false
# verbosity_levels:
# VERBOSITY_VERBOSE: NOTICE
# VERBOSITY_VERY_VERBOSE: NOTICE
# VERBOSITY_DEBUG: DEBUG
# channels: ["doctrine"]
So, this is how my fixture looks like:
class LoadMsisdn extends AbstractFixture implements OrderedFixtureInterface
{
public function getOrder()
{
return 13;
}
public function load(ObjectManager $manager)
{
$content = file_get_contents('number.txt');
$numbers = explode(',', $content);
shuffle($numbers);
foreach ($numbers as $key => $number) {
$msisdn = new Msisdn();
$msisdn->setMsisdn($number);
$msisdn->setBlocked((rand(1, 1000) % 10) < 7);
$msisdn->setOperator($this->getReference('operator-' . rand(45, 47)));
$this->addReference('msisdn-' . $key, $msisdn);
$manager->persist($msisdn);
}
$manager->flush();
}
}
How do I disable the logger if I need to do it from EntityManager as shown in a answer on the same post?
$em->getConnection()->getConfiguration()->setSQLLogger(null);
The object manager that is being passed into the load method is an instance of the the entity manager (Doctrine\Common\Persistence\ObjectManager is just an interface that the entity/document/etc managers implement).
This mean that you can use the same command as in your question to nullify the SQL logger like..
$manager->getConnection()->getConfiguration()->setSQLLogger(null);
One thing to note is that the default logging setting for a DBAL connection is %kernel.debug% meaning that, unless you have overridden it in your config, the logging should only happen in the dev environment. I can see you have tried using the --no-debug option but I can only assume that, as the logger is set during the container compilation, that it doesn't unset it to the container not being rebuilt.
Symfony 3/4/5 approach in test case
final class SomeTestCase extends KernelTestCase
{
protected function setUp(): void
{
$this->bootKernel('...');
// #see https://stackoverflow.com/a/35222045/1348344
// disable Doctrine logs in tests output
$entityManager = self::$container->get(EntityManagerInterface::class);
$entityManager->getConfiguration();
$connection = $entityManager->getConnection();
/** #var Configuration $configuration */
$configuration = $connection->getConfiguration();
$configuration->setSQLLogger(null);
}
}
I am not sure if it will help with the memory limit issue, but you can also try to change logging (more comfortably) via YAML configuration:
Disable Doctrine log channel
(as in your commented code)
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!doctrine"] # "!event", "!php"
or Setting a custom dummy logger
In services.yaml set the class for the default Doctrine logger:
doctrine.dbal.logger:
class: App\ORM\Doctrine\DBAL\Logging\DummySQLLogger
And enabling it in doctrine.yaml:
doctrine:
dbal:
driver: 'pdo_mysql'
logging: true
The custom logger must implement the interface Doctrine\DBAL\Logging\SQLLogger. The concrete methods startQuery and stopQuery can be left empty, to skip logging; or just use error_log instead (like in my case).
I am not sure though if this will prevent the memory exceeded problem. Eventually increase the PHP_MEMORY_LIMIT (via env variables if supported by your environment, or via ini_set()). And pay attention to use the correct unit symbol (M for Megabytes, G for Gigabytes), I so often write it wrongly as MB.
Related
I want to configure the Doctrine bundle to have a DBAL connection. For some reason the configuration needs a bit of logic to retrieve. I tried to use a container extension and then a compiler pass to execute the logic while the container is compiled and store the configuration as container parameters.
During my attempts, I registered the extension and compiler pass like this in the Kernel class:
protected function build(ContainerBuilder $container)
{
// Those lines weren't there at the same time
$container->registerExtension(new MyCustomExtension());
$container->addCompilerPass(new MyCustomCompilerPass());
}
It seemed to work well as I could see my parameters in the console:
# ./bin/console debug:container --parameters
Symfony Container Parameters
============================
------------------------------------------------------------- ------------------------------------------------------------------------
Parameter Value
------------------------------------------------------------- ------------------------------------------------------------------------
...
some.prefix.host some-mariadb-host
some.prefix.dbname some-database-name
...
The problem is that when I try to use those parameters in my config/packages/doctrine.yaml I get an error on my next console command:
doctrine:
dbal:
driver: pdo_mysql
host: '%some.prefix.host%'
dbname: '%some.prefix.dbname%'
# ...
# ./bin/console debug:container --parameters
In ParameterBag.php line 98:
You have requested a non-existent parameter "some.prefix.host".
I am using Symfony 5.3 and Doctrine bundle 2.4.
Why do my parameters seem inaccessible for 3rd party bundle configuration ?
How can I make this work ?
Is there a better way to achieve this ?
I think the Doctrine bundle configuration gets processed before my compiler pass can declare the parameters. It probably can't be solved using the DependencyInjection component.
Solved it by importing a PHP configuration file in the services.yaml:
imports:
- { resource: my_custom_file.php }
With the following content:
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return function(ContainerConfigurator $configurator) {
// My specific logic
// Saving the configuration as parameters
$configurator->parameters()->set('some.prefix.host', $host);
$configurator->parameters()->set('some.prefix.dbname', $dbname);
// ...
};
I am trying to use different cache system on my environments. I would like to have, for example, Filesystem for dev and memcached for prod.
I am using symfony 3.3.10.
To achieve this, I would like to autowire the CacheInterface as follow:
use Psr\SimpleCache\CacheInterface;
class Api {
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
}
Here are my configuration files:
config_dev.yml:
framework:
cache:
app: cache.adapter.filesystem
config_prod.yml:
framework:
cache:
app: cache.adapter.memcached
...
Here is the error I get:
The error disappears when the FilesystemCache is declared as a service:
services:
Symfony\Component\Cache\Simple\FilesystemCache: ~
But now I cannot have another cache system for the test environment like NullCache. In fact, I have to declare only one service inheriting from CacheInterface. It is not possible as config_test is using config_dev too.
This is the beginning of services.yml if it can help:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
Any idea on how to autowire different cache system depending on the environment?
EDIT:
Here is the working configuration:
use Psr\Cache\CacheItemPoolInterface;
class MyApi
{
/**
* #var CacheItemPoolInterface
*/
private $cache;
public function __construct(CacheItemPoolInterface $cache)
{
$this->cache = $cache;
}
}
config.yml:
framework:
# ...
cache:
pools:
app.cache.api:
default_lifetime: 3600
services.yml:
# ...
Psr\Cache\CacheItemPoolInterface:
alias: 'app.cache.api'
Even though factory pattern is a good option to solve this kind of problem, normally you don't need to do that for Symfony cache system. Typehints CacheItemPoolInterface instead:
use Psr\Cache\CacheItemPoolInterface;
public function __construct(CacheItemPoolInterface $cache)
It automatically injects the current cache.app service depending on the active environment, so Symfony does the job for you!
Just make sure to configure the framework.cache.app for each environment config file:
# app/config/config_test.yml
imports:
- { resource: config_dev.yml }
framework:
#...
cache:
app: cache.adapter.null
services:
cache.adapter.null:
class: Symfony\Component\Cache\Adapter\NullAdapter
arguments: [~] # small trick to avoid arguments errors on compile-time.
As cache.adapter.null service isn't available by default, you might need to define it manually.
In Symfony 3.3+/4 and 2017/2019 you can omit any config dependency and keep full control of the behavior with factory pattern:
// AppBundle/Cache/CacheFactory.php
namespace AppBundle\Cache;
final class CacheFactory
{
public function create(string $environment): CacheInterface
{
if ($environment === 'prod') {
// do this
return new ...;
}
// default
return new ...;
}
}
And services.yml of course:
# app/config/services.yml
services:
Psr\SimpleCache\CacheInterface:
factory: 'AppBundle\Cache\CacheFactory:create'
arguments: ['%kernel.environment%']
See more about service factory in Symfony Documentation.
You can read more about this in my Why Config Coding Sucks post.
I have configured logger for different channel in different files, but it does not work for me. It's work but it writes in console not in pointed file.
And I need write log to file in channel search.
Here is my code:
#app/config/config_dev.yml
monolog:
handlers:
search:
type: stream
level: error
path: "%kernel.logs_dir%/search_log.log"
channels: [search]
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: [!event, !search]
console:
type: console
channels: [!event, !doctrine, !search]
Defined service:
#MyBundle/Resources/config/services.yml
services:
app.logger_search:
class: Symfony\Bridge\Monolog\Logger
arguments: ["#logger"]
tags:
- {name: monolog.logger, channel: search}
Now use it service, try to test it:
#MyController.php
/**
* #Route("/test")
*/
public function test()
{
$this->get("app.logger_search")->error("Test");
return $this->json("test");
}
But it writes into console insted of file.
Console I meant where I ran my server: php bin\console server:run.
Creating your own Channel. This is done either via the configuration or by tagging your service with monolog.logger and specifying which channel the service should log to (just as you have done).
Both ways are valid and in both cases you logger will be named:
monolog.logger.<you-channel-name>
So use monolog.logger.search instead of your service id app.logger_search to fix the issue.
I you don't have a strong reason to change the logger behavior, I suggest to configure additional channels without tagged services:
# app/config/config.yml
monolog:
channels: ['foo', 'bar']
With this, you can now send log messages to the foo channel by using the automatically registered logger service monolog.logger.foo.
I have read some docs here but still not clear to me how to write and use a custom Monolog handler and channel. Let me explain a bit what I want to achieve. I have a custom function and I want that log to be logged into a file called custom.log. I have enabled Doctrine logging into another file by setting this in config.yml file:
monolog:
handlers:
#Logs Doctrine to a different channel
doctrine:
level: debug
type: stream
path: "%kernel.logs_dir%/doctrine.log"
channels: [doctrine]
How do I achieve the same for a custom.log?
You can try that way,
monolog:
channels: ["testchannel"]
handlers:
test:
# log all messages (since debug is the lowest level)
level: debug
type: stream
path: "%kernel.logs_dir%/testchannel.log"
channels: ["testchannel"]
And in the controller you can get the logger and do your thing;
class DefaultController extends Controller
{
public function indexAction()
{
$logger = $this->get('monolog.logger.testchannel');
$logger->info("This one goes to test channel!!");
return $this->render('AcmeBundle:Default:index.html.twig');
}
}
Also you can check which monolog handlers and loggers are registered by running the command php app/console container:debug monolog
I implementing Mmoreram gearman bundle in my symfony(2.4) project.
I have website that users make action and triggers jobs.
like:
# Get Gearman and tell it to run in the background a 'job'
$id = $this->params['gearman']->doHighBackgroundJob('MYBundleServicesPublishWorker~publish',
json_encode($parameters)
);
And i have one worker that run infinitely and do the jobs (iterations: 0)
I run it from command line once in background:
nohup php /myproject/app/console gearman:worker:execute MYBundleServicesPublishWorker > /tmp/error_log.txt > /tmp/output_log.txt &
The config look like:
gearman:
# Bundles will parsed searching workers
bundles:
# Name of bundle
MyBundle:
# Bundle name
name: myBundle
# Bundle search can be enabled or disabled
active: true
# If any include is defined, Only these namespaces will be parsed
# Otherwise, full Bundle will be parsed
include:
- Services
- EventListener
# Namespaces this Bundle will ignore when parsing
ignore:
- DependencyInjection
- Resources
# default values
# All these values will be used if are not overwritten in Workers or jobs
defaults:
# Default method related with all jobs
# do // deprecated as of pecl/gearman 1.0.0. Use doNormal
# doNormal
# doBackground
# doHigh
# doHighBackground
# doLow
# doLowBackground
method: doNormal
# Default number of executions before job dies.
# If annotations defined, will be overwritten
# If empty, 0 is defined by default
iterations: 0
# execute callbacks after operations using Kernel events
callbacks: true
# Prefix in all jobs
# If empty name will not be modified
# Useful for rename jobs in different environments
job_prefix: null
# Autogenerate unique key in jobs/tasks if not set
# This key is unique given a Job name and a payload serialized
generate_unique_key: true
# Prepend namespace when callableName is built
# By default this variable is set as true
workers_name_prepend_namespace: true
# Server list where workers and clients will connect to
# Each server must contain host and port
# If annotations defined, will be full overwritten
#
# If servers empty, simple localhost server is defined by default
# If port empty, 4730 is defined by efault
servers:
localhost:
host: 127.0.0.1
port: 4730
doctrine_cache:
providers:
gearman_cache:
type: apc
namespace: doctrine_cache.ns.gearman
my problem is when i run app/console cache:clear and after that job come in the worker crash
its throw error :
PHP Warning:
require_once(/myproject/app/cache/dev/jms_diextra/doctrine/EntityManager_53a06fbf221b4.php):
failed to open stream: No such file or directory in
/myproject/app/cache/dev/appDevDebugProjectContainer.php on line 787
PHP Fatal error: require_once(): Failed opening required
'/myproject/app/cache/dev/jms_diextra/doctrine/EntityManager_53a06fbf221b4.php'
(include_path='.:/usr/share/php:/usr/share/pear') in
/myproject/app/cache/dev/appDevDebugProjectContainer.php on line 787
How can i fix it, i try to change the doctrine bundle cache type: file_system/array/apc
but it did not help
How can i overcome this?
what i am doing wrong?
Thanks in advance
i found the problem, i have in my worker this line :
$this->doctrine->resetEntityManager();
that cause this,
now i am only open connection and close it like:
$em = $this->doctrine->getEntityManager();
$em->getConnection()->connect();
# run publish command
............
# close connection
$em->getConnection()->close();