Symfony/Propel 1.4: Read from one, write to other DB - php

We have an existing project (SNS website+android/Iphone games) in Symfony 1.4/ Propel 1.4
We are experiencing extra load on DB server (say DB1). We are doing DB Optimization but as immediate solution we decided to create one more DB server in the way DB2 is exact replica of DB1 all the time. Currently we have only DB1, used for both read and write operations.
Now we need to move all read operations to DB2 and keep write operations (generally in transactions) on DB1 as it is now.
What are the possible ways to make those changes (On production server without much downtime) and if possible, with minimal code changes.
Edit after first comment
Based on link given by J0k and some other links, I'd done following on local dev environment.
Created a test symfony 1.4 project
Updated database.yml as follow
all:
propel:
class: sfPropelDatabase
param:
classname: PropelPDO
dsn: 'mysql:host=localhost;dbname=wzo;'
username: root
password: mysql
encoding: utf8
persistent: true
pooling: true
slaves:
slave1:
dsn: 'mysql:host=localhost;dbname=wzoslv;'
username: root
password: mysql
encoding: utf8
Where database wzoslv is exact replica of database wzo except change in one test entry. On table odd_play row 26 (PK) column result entries are WON1 and WON respectively.
run symfony tasks
php symfony propel:build-schema
php symfony propel:build-model
php symfony cc
Created a module and added following code:
class wzoActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$con_write = Propel::getConnection(OddPlayPeer::DATABASE_NAME, Propel::CONNECTION_WRITE);
$con_read = Propel::getConnection(OddPlayPeer::DATABASE_NAME, Propel::CONNECTION_READ);
$oddPlay = OddPlayPeer::retrieveByPK(26,0,$con_write);
echo "on write connection, result=".$oddPlay->getResult();
$oddPlayRead = OddPlayPeer::retrieveByPK(26,0,$con_read);
echo "<br/>on Read connection, result=".$oddPlayRead->getResult();
exit;
$this->setLayout('layout');
}
}
Run http://local.sftest.com/index.php/wzo/index in the browser, output was,
on write connection, result=WON //Correct expected output
on Read connection, result=WON //Not correct. That should be WON1
I guess passing OddPlayPeer::DATABASE_NAME while creating both read/write connection is the issue but that how it was suggested in online examples. Can someone please suggest where I'm making the mistake?
Edit: Few more input
I updated debug echos in lib\vendor\symfony\lib\plugins\sfPropelPlugin\lib\vendor\propel\Propel.php to check how it is returning the connection. Found that it is entering in following if (line 544-549)
$slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
if (empty($slaveconfigs)) {
echo "inelseifif<br/>";// no slaves configured for this datasource
self::$connectionMap[$name]['slave'] = false;
return self::getConnection($name, Propel::CONNECTION_WRITE); // Recurse to get the WRITE connection
}
where $slaveconfigs are empty so returning write connection. Now the question is, why slaveconfigs is empty?
I also try editing sfDatabaseConfigHandler.class.php as defined in old forums but doing so, break symfony somewhere and nothing gets display on web and even in logs.

I'm sure I'm doing some mistake but whatever suggested on official documents of Propel/symfony and even here at stackoverflow, seems not working for me. Probably official documents should take better care of programmers who do not have lot of symfony experience.
Although we do not prefer to edit core files of any framework/third party libraries but this force me to edit core files to make a working solution for me. The solution that worked for me is as follow:
database.yml
My database.yml file is as follow:
all:
propel:
class: sfPropelDatabase
param:
classname: PropelPDO
dsn: 'mysql:host=localhost;dbname=wzo;'
username: testuserwzo
password:
encoding: utf8
persistent: true
pooling: true
slave:
class: sfPropelDatabase
param:
classname: PropelPDO
dsn: 'mysql:host=localhost;dbname=wzoslv;'
username: testuserwzoslv
password:
encoding: utf8
persistent: true
pooling: true
After that, I edited Propel.php file as follow
For Propel 1.4
File: lib/vendor/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel/Propel.php
Change line 542-543
// we've already ensured that the configuration exists, in previous if-statement
$slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
with (added one line inbetween)
// we've already ensured that the configuration exists, in previous if-statement
self::$configuration['datasources'][$name]['slaves'] = isset(self::$configuration['datasources']['slave']) ? self::$configuration['datasources']['slave'] : null;
$slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
Then in same file, changed line 560
$con = Propel::initConnection($conparams, $name);
to
$con = Propel::initConnection($conparams, 'slave'); //I know its bad practive to put hard-coded value but at that moment, I was more interested in working solution without caring about best practices.
For propel 1.6 (We upgraded propel just to make this working but reverted back to propel 1.4 later as upgrade on production needs to be well tested.)
File: plugins/sfPropelORMPlugin/lib/vendor/propel/runtime/lib/Propel.php
Changed line 601
$slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
to (Added one line before)
self::$configuration['datasources'][$name]['slaves'] = isset(self::$configuration['datasources']['slave']) ? self::$configuration['datasources']['slave'] : null;
$slaveconfigs = isset(self::$configuration['datasources'][$name]['slaves']) ? self::$configuration['datasources'][$name]['slaves'] : null;
Then in same file, changed line 629
$con = Propel::initConnection($conparams, $name);
to
$con = Propel::initConnection($conparams, 'slave');
Then following test file was giving expected result
class kapsActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$con_write = Propel::getConnection(OddPlayPeer::DATABASE_NAME, Propel::CONNECTION_WRITE);
$con_read = Propel::getConnection(OddPlayPeer::DATABASE_NAME, Propel::CONNECTION_READ);
$oddPlay = OddPlayPeer::retrieveByPK(28,0,$con_write);
echo "on write connection, result=".$oddPlay->getResult().$oddPlay->getPlayscore();
$oddPlayRead = OddPlayPeer::retrieveByPK(27,0,$con_read);
echo "<br/>on Read connection, result=".$oddPlayRead->getResult().$oddPlayRead->getPlayscore();
exit;
//$this->setLayout('layout');
}
}
I still do not recommend editing core files but this solution worked for us as in emergency condition. Someone else, if needed, may use it in emergency condition. Still looking for perfect solution.

You should add a new level to slaves plus a connection entry, according to the doc (on github)
all:
propel:
class: sfPropelDatabase
param:
classname: PropelPDO
dsn: 'mysql:host=localhost;dbname=wzo;'
username: root
password: mysql
encoding: utf8
persistent: true
pooling: true
slaves:
connection:
slave1:
dsn: 'mysql:host=localhost;dbname=wzoslv;'
username: root
password: mysql
encoding: utf8

Related

Reading external file to configure doctrine in Symfony 5

I'm looking for a way to have doctrine (in a symfony 5 application) connect with the database details stored in an external XML file (instead of .env).
However I can't seem to be finding a solution.
Reading the XML file isn't the problem, I just can't find how to pass the parameters on the doctrine.
I've tried to use a doctrine.php file but fails to connect.
I've also tried to use a wrapper class and called the parent construct method but it seems it isn't recommended.
Has anybody every managed to achieve something of the sort ?
Thanks !
Symfony lets you add configuration written in php. See https://symfony.com/doc/current/configuration.html#configuration-formats
That allows to do the following:
Import an additional configuration file in services.yaml
imports:
- { resource: 'my-db-config.php' }
my-db-config.php:
<?php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
function readConfig() {
$myConfig = '' // read from your xml file
return $myConfig
}
return static function (ContainerConfigurator $container) {
$container
->parameters()
->set('my.param', readConfig());
};
in use your new param in doctrine.yaml
doctrine:
dbal:
connections:
default:
url: '%my.param%'

Symfony/Doctrine keeps creating the same migration

Edit: Turns out other projects on the same machine are having the same issue. The question is for one specific project:
I have a Symfony4 project in which I have a few entities (created via make:entity). I just noticed that the last 4 migrations have the exact same query in them (and yes, I run migrations in between). Example:
$this->addSql('ALTER TABLE event CHANGE visible_from visible_from DATETIME DEFAULT NULL, CHANGE visible_till visible_till DATETIME DEFAULT NULL, CHANGE max_signups max_signups INT DEFAULT NULL');
I ran that query manually and that actually updated the table. I then created a new migration to test: The above query was there, again.
I've cleared cache, ran doctrine:cache:clear-metadata, double checked for weird stuff, but everything is normal.
I have the same 3 queries every time and they all have one thing in common: They have nullable=true. All other entities do not have nullable=true in them.
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $maxSignups;
The downmigration might be a hint:
ALTER TABLE event CHANGE visible_from visible_from DATETIME DEFAULT \'NULL\',
This I find odd---^
MariaDB version: 10.2.14
PHP: 7.2.4
symfony/orm-pack: "^1.0"
Anyone who knows why?
I still dont really know why ( So I'll leave this question open for a bit), but this is what I did:
I was running both MySQL and MariaDB and it defaulted to MySQL. Turned that off, switched to MariaDB and adding a version in doctrine.yaml:
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_mysql'
server_version: 'mariadb-10.2.14'
It doesnt really care about the version though. I made a typo and wrote 12.2.14 and it worked absolutally fine. I'm guessing it has a version>$n check somewhere.
Everything is explained here:
https://github.com/symfony/symfony-docs/pull/9547#issue-179389686
The most important is setting server_version with prefix "mariadb", the MariaDB version is less important.
Alternatively, you may not set server_version at all, then it should be detected automatically.
Below is the DBAL driver function responsible for parsing server_version:
public function createDatabasePlatformForVersion($version)
{
$mariadb = false !== stripos($version, 'mariadb');
if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) {
return new MariaDb1027Platform();
}
if ( ! $mariadb && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) {
return new MySQL57Platform();
}
return $this->getDatabasePlatform();
}
And here is the link to the whole class: https://github.com/doctrine/dbal/blob/f76bf5ef631cec551a86c2291fc749534febebf1/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php#L133

Symfony2 Doctrine Metadata Cache with Redis Issue

I'm trying to use Redis as a driver for caching doctrine metadata, query and results. Follwing is my configuration.
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
result_cache_driver:
type: redis
host: %redis_host%
instance_class: Redis
query_cache_driver: redis
#metadata_cache_driver: redis
When I remove the comment from line #metadata_cache_driver: redis, I get an error running a test I have with following error.
TypeError: Argument 1 passed to Doctrine\ORM\Mapping\ClassMetadataFactory::wakeupReflection() must implement interface Doctrine\Common\Persistence\Mapping\ClassMetadata, string given, called in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php on line 214
My Functional Test looks like Following:
public function testX()
{
//The data in prepared in setup..
$param1 = 'test-id';
$param2 = 'test-key';
$result = $this->em->getRepository('MyBundle:Test')
->findOneByXX($param1, $param2);
$this->assertTrue($result instanceof Test);
}
And My Query looks like following:
$qb->select('c')
->from('MyBundle:Test', 'c')
->where('c.id = :id')
->andWhere('c.key = :key')
->setParameter('id', $id)
->setParameter('key', $key);
$query = $qb->getQuery()
->useResultCache(true);
return $query->getOneOrNullResult();
Do I need additional configuration for Redis? Any Help would be appreciated??
I believe to resolve this you need to set the serializer for redis, the default serializer is probably not aware of the the PHP classes and when the object is removed from cache, and unserialized, it is not the same type as it was prior to serialization.
$redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
For you case you will probably need to set the configuration option as a part of the driver configuration.

Message: Configured database connection is persistent. Aborting

Codeigniter 2 to 3 version after upgrading, I get this error..
Why would that be?
An uncaught Exception was encountered
Type: Exception
Message: Configured database connection is persistent. Aborting.
Filename: /var/www/vhosts/xxx.com/app/system/libraries/Session/drivers/Session_database_driver.php
Line Number: 94
Backtrace:
File: /var/www/vhosts/xxx.com/app/application/core/MY_Controller.php
Line: 11
Function: __construct
File: /var/www/vhosts/xxx.com/app/application/core/MY_Controller.php
Line: 52
Function: __construct
File: /var/www/vhosts/xxx.com/app/application/controllers/Dashboard.php
Line: 7
Function: __construct
File: /var/www/vhosts/xxx.com/application/index.php
Line: 293
Function: require_once
I had the same issue, and found that it was just a matter of changing a setting:
Modify your database.php config file and turn 'pconnect' to false. As part of the CI 3 Framework, it would be part of this array:
$db['default'] = array(
'pconnect' => FALSE // This value
);
Or if your config file looks more like the CI 2 version:
$db['default']['pconnect'] = FALSE;
A bit of searching seems to suggest that the database doesn't like a persistent connection, possible because of security reasons.
Disable caching in database.php file, define caching folder in database.php by
'cachedir' => APPPATH.'cache/db/',
setting and only use
$this->db->cache_on();
command where you want your database query being cached.
Don't forget to use
$this->db->cache_off();
after select queries for unwanted cached results.
It seems like codeigniter 3.0 doesn't support sessions using database, when persistent is enabled.
form: http://www.codeigniter.com/user_guide/libraries/sessions.html?highlight=session#session-preferences
However, there are some conditions that must be met:
Only your default database connection (or the one that you access as
$this->db from your controllers) can be used. You must have the Query
Builder enabled. You can NOT use a persistent connection. You can NOT
use a connection with the cache_on setting enabled.
You need to ensure that FALSE goes out without quotes.If you use 'FALSE', the database driver will take that as a true boolean. The system expects you to use FALSE directly, without quotes. So, unset pconnect instead of using 'FALSE' in order to default to FALSE, or use FALSE as a value if you like to keep things ordered :)
* Persistent connection flag
*
* #var bool
*/
public $pconnect = FALSE;

mongo authorization zend shanty

I trying to add authorization to mongoDB.
I use mongodb 2.2.4, php-mongo 1.4.1 stable, apache2, php 5.4, ZendFramework 1.12 + ShantyMongo
Also I use replicaset with 3 instances.
When I set auth = true mongo create admin database, and when I add user to this database I got this error:
Fatal error: Uncaught exception 'MongoCursorException' with message 'test.server.com:27017: unauthorized db:database_test ns:database_test.options lock type:0 client:127.0.0.1' in /apps/test_app/libs/Shanty/Mongo/Collection.php:3501
If I add user to my database_test all work fine.
<?php
/* Configure logging */
MongoLog::setModule( MongoLog::ALL );
MongoLog::setLevel( MongoLog::ALL );
$m = new MongoClient(); // connect
$db = $m->selectDB("database_test");
$db->authenticate('admin','12345');
this code work fine even if I add user to both admin and database_test.
Maybe someone has similar problem and know what I need to do.
First of all, you should use the following syntax for specifying authentication:
<?php
$m = new MongoClient("mongodb://admin:12345#localhost/database_test");
$db = $m->database_test;
It's a bit difficult to answer your question though, as "when I add user to this database" is not really descriptive. In order to get a proper answer, you need to learn to list the exact steps or code that you used to do this.
I am thinking that you were trying to auth against a normal database (database_test), where you would only have a user/pass set for the admin database. In that case, you need to auth to the admin database, and then select your database_test:
<?php
$m = new MongoClient("mongodb://admin:12345#localhost/admin");
$db = $m->database_test;
Thank you for your answer I figured out this.
Maybe it's will be heplful for some one on future, Shanty-Mongo require username, password and port in array of servers.
It's should looks like:
; Shanty Mongo Config
hosts.0.host = '127.0.0.1'
hosts.0.port = '27017'
hosts.0.username = 'root'
hosts.0.password = 'pass'
hosts.1.host = '127.0.0.1'
hosts.1.port = '27018'
hosts.1.username = 'root'
hosts.1.password = 'pass'
hosts.2.host = '127.0.0.1'
hosts.2.port = '27019'
hosts.2.username = 'root'
hosts.2.password = 'pass'
database = 'database_test'
replicaSet = true
Also what I do:
in mongo:
1. edit config file, enable auth (auth = true)
2. create user in admin database
3. connect as new user
4. create user in target database
in ZF config
update config file, as I describe above.
Also some times I get error: Fatal error: php_network_getaddresses, I think it's because I have some trouble with DNS or php trying resolve address: mongo://root:pass#127.0.0.1:27017,root:pass#127.0.0.1:27018,root:pass#127.0.0.1:27019/database_test

Categories