I have a laravel project with many connections to different IP's.
I want laravel to connect to a backup database in the case that the main SQL server was down
Example.
192.168.1.2 -> SQL DB #1
192.168.1.3 -> SQL DB #1 Backup
If 192.168.1.2 goes down, laravel must connect to 192.168.1.3
I'd like to do this in database.php file, but I think that's impossible.
I was trying to test connection before make a query like this:
if(DB::connection('connection')->getDatabaseName())
but it seems that it save data in cache and it still throw database name even if I shutdown the SQL server
For this answer, I'm considering Laravel 5.
By debugging a model query, I've found out that Laravel connections support not a single host, but a list of them.
[
'driver' => 'sqlsrv',
'host' => [
'192.168.1.2',
'192.168.1.3',
],
'database' => 'database_name',
'username' => 'username',
'password' => 'password',
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
'transaction_isolation' => PDO::SQLSRV_TXN_READ_UNCOMMITTED, // Not required, but worth mentioning it's possible to define it here too
'options' => [],
]
The underlying method behind Laravel connections resolving is Illuminate\Database\Connectors::createPdoResolverWithHosts which has the following behavior:
protected function createPdoResolverWithHosts(array $config)
{
return function () use ($config) {
foreach (Arr::shuffle($hosts = $this->parseHosts($config)) as $key => $host) {
$config['host'] = $host;
try {
return $this->createConnector($config)->connect($config);
} catch (PDOException $e) {
continue;
}
}
throw $e;
};
}
Such behavior means that Laravel will randomly pick one of the connection's hosts and try to connect to them. If the attempt fails, it keeps trying until no more hosts are found.
You could define two mysql connections in app/config/database.php
and using a middleware you could define the db that should be connected to.
You can find a more elaborate explanation in this URL:
http://fideloper.com/laravel-multiple-database-connections
i recently started searching for the same thing and to change the connection as soon as possible you can either
add the check inside a service provider
or through a global middleware
try{
\DB::connection()->getPdo(); // check if we have a connection
}catch{
\DB::purge(config('database.default')); // disconnect from the current
\DB::setDefaultConnection('my-fallback-db'); // connect to a new one
}
also check laravel api docs for more info.
Related
I have some trouble with the Laravel transaction.
Laravel 9+
PHP 8+
Firebird 2.5
I have two DB connection MySQL (default) and Firebird. MySQL works fine, like this. I get the connection data.
DB::transaction(function ($conn) use ($request) {
dd($conn)
});
When I try to use with my other connection ('firebird'), it always throws "There is already an active transaction" error.
DB::connection('firebird')->transaction(function ($conn) use ($request) {
dd($conn);
$conn->table('A')->insert();
$conn->table('B')->insert();
$conn->table('C')->insert();
});
I tried this version too, but I get the same error if I use the 'firebird' connection:
DB::connection('firebird')->beginTransaction();
If I leave out the transaction, both are working just fine, but I want to use rollback if there is any error. Any thoughts why? I'm stuck at this.
Firebird always uses transactions. The transaction is started as soon as you make a change in the database and remains open for that session until you commit. Using your code, it's simply:
DB::connection('firebird')->insert();
DB::connection('firebird')->commit() or rollback();
When you do begin tran in SQL Server, it does not mean that you're starting the transaction now. You are already in transaction, since you are connected to the database! What begin tran really does is disable the "auto-commit at each statement", which is the default state in SQL Server (unless otherwise specified).
Respectively, commit tran commits and reverts the connection to "auto-commit at each statement" state.
In any database, when you are connected, you are already in transaction. This is how databases are. For instance, in Firebird, you can perform a commit or rollback even if only ran a query.
Some databases and connection libs, in the other hand, let you use the "auto-commit at each statement" state of connection, which is what SQL Server is doing. As useful as that feature might be, it's not very didactic and lead beginners to think they are "not in a transaction".
The solution:
Need to turn off auto commit(PDO::ATTR_AUTOCOMMIT), when you define the 'Firebird' connection in 'config/database.php'
Example:
'firebird' => [
'driver' => 'firebird',
'host' => env('DB_FIREBIRD_HOST', '127.0.0.1'),
'port' => env('DB_FIREBIRD_PORT', '3050'),
'database' => env('DB_FIREBIRD_DATABASE',
'path\to\db\DEFAULT.DATABASE'),
'username' => env('DB_FIREBIRD_USERNAME', 'username'),
'password' => env('DB_FIREBIRD_PASSWORD', 'password'),
'charset' => env('DB_FIREBIRD_CHARSET', 'UTF8'),
'options' => array(
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_AUTOCOMMIT => false,
)
],
Then you can use Laravel transactions like:
try{
DB::connection('firebird')->beginTransaction();
DB::connection('firebird')->insert();
DB::connection('firebird')->commit();
} catch (Exception $exception) {
DB::connection('firebird')->rollBack();
throw $exception;
}
Or you can use this too and this do the commit or rollback automatic:
DB::connection('firebird')->transaction(function () use
($request) {
DB::connection('firebird')->insert($request);
})
But dont forget! If you do this, you must start the transaction every time! Even when you are just Select some data.
DB::connection('firebird')->beginTransaction();
or you will get SQL error like:
SQLSTATE[HY000]: General error: -901 invalid transaction handle (expecting explicit transaction start)
Thank you everybody!
I want use Yii and MS Access together, but I don't know how..
I can use ODBC without Yii like this
$link = odbc_connect($name, $user, $pass)
but now i need it in Yii, like MySQL:
'db'=>array(
'connectionString' => 'mysql:host=localhost;dbname=ex1c',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => 'ex1c_',
)
what should i write in connection string
may be need something software setup in computer
how to set it?
If I'm understanding your question right, you have one set of data that you want to be able to reference from a YII application and from MS Access. The best approach I can think of here is to:
Put the data into a MySQL database
Have YII access it directly as you illustrate above
Set up the MySQL ODBC driver on the Windows machine where you are running access, and point Access at MySQL via that driver
Here is a pointer to MyODBC, which will provide the critical linkage between Access and MySQL:
http://dev.mysql.com/downloads/connector/odbc/
I have following database configuration in database.php file from my CakePHP app:
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'database',
'prefix' => '',
);
All is working fine, except of one queue shell script. The script is looping and waiting for commands to run (for example to update some reports). After a while 1-2 days database data is changing, but the script will still "see" the old data, and results of command is wrong. If I restart shell script, the results are OK... for few days.
I have to mention that I had "lost database connection" issue before in the script and I have solved it by runing every 10-15 min:
$user = $this->User->find('first');
Now I am affraid this is making the connection persistent somehow...
How can I reset the database connection ?
EDIT:
I was just refactoring the code to check if I can set $cacheQueries to false on the Model. But in few parts of the code I am using ConnectionManager directly, and only then I have "cache" problem. If I query database from Model->find results are ok. I need direct queries for performance reasons in few places...
$query = "SELECT COUNT(1) as result
FROM
......
";
$db = ConnectionManager::getDataSource('default');
$result = $db->query($query);
The property $cacheQueries which #burzum mentioned, seems not to be in use in any cake model method.
But I found another interesting fact in the source of the DboSource.
You need to use the second parameter of the DboSource::query() method to turn off the caching. Or the third if you want to provide additional parameters for the DboSource::fetchAll() method.
Eventhough this will fix your problem, you should write your queries with the Model::find() method that CakePHP offers.
You should only not use them if they are seriously impacting your performance.
func0der
Try to set these two model properties to false:
$cacheQuery http://api.cakephp.org/2.4/source-class-Model.html#265
$cacheSources http://api.cakephp.org/2.4/source-class-Model.html#499
FuelPHP seems to be doing something odd when I try to connect to a MSSQL database via PDO.
Working Outside of FuelPHP
I have tried the following in a vanilla PHP page on my local server:
<?php
$mssql = new \PDO('dblib:host=<IP OF HOST>;dbname=<DBNAME>', '<USER>', '<PASS>');
And everything works fine. It connects and I can easily query the DB like I would expect.
NOT Working Inside of FuelPHP
However, when I try adding the connection in config/db.php with basically the same information an exception is thrown.
<?php
return array(
'mssql' => array(
'connection' => array(
'dsn' => 'dblib:host=<IP OF HOST>;dbname=<DBNAME>',
'username' => '<USER>',
'password' => '<PASS>',
),
'type' => 'pdo',
'table_prefix' => '',
),
);
And subsequently try to query the DB within FuelPHP like so:
$q = \DB::select()
->from('table')
->limit(5);
$r = $q->execute('mssql');
The following exception is thrown: Fuel\Core\Database_Exception [ 20002 ]: SQLSTATE[01002] Adaptive Server connection failed (severity 9)
What the kicker is when I try to just do my original basic connection inside of a controller of FuelPHP I get the same error message. So it appears FuelPHP is playing havoc with even a normal PDO connection.
Example of basic connection attempt within a FuelPHP controller.
<?php
class Controller_Welcome extends Controller
{
public function action_index()
{
$mssql = new \PDO('dblib:host=<IP OF HOST>;dbname=<DBNAME>', '<USER>', '<PASS>');
}
}
For reference I am using PHP 5.3.16 and using FreeTDS to connect from my OS X laptop. (This shouldn't be an issue though since the vanilla version works.)
I'm hosting a Yii app on shared-host with some my friend, and keep database in private MySQL server. As you knew, database info can be found so very easy in protected\config\main.php by another host owner (my friend and more):
'db'=>array(
'connectionString' => 'mysql:host=211.113.2.45;dbname=FamilyBook',
'emulatePrepare' => true,
'username' => root,
'password' => 'xcute445',
'charset' => 'utf8',
),
Is there any solution to conceal connection information as IP mySQL server, username, password?
May MySQL server provide RSA mechanism to protect database info?
Example, any people can see as below but cannot understand or use:
'db'=>array(
'connectionString' => '57bf064b2166366a5ea61109006b8d5c',
'emulatePrepare' => true,
'username' => '63a9f0ea7bb98050796b649e85481845',
'password' => 'e04ccf211208f8c97e4a36e584926e60',
'charset' => 'utf8',
), // value by MD5 function, example only
No, you cannot conceal the credentials from someone who has access to your source as long as you are using native MySql authentication. That's because your code needs to pass the credentials as cleartext¹ to the server, so it needs to be able to "decrypt" them before connecting. Someone who has access to your source can follow the same procedure and decrypt them as well.
You could secure your system by relying on some type of PAM authentication instead of user-supplied credentials, but Yii does not support such.
¹note: This is not actually true. The client passes a hash to the server, but it needs to have access to the original password in order to hash it. This means that for the purposes of this discussion it makes no difference (it would make a difference for someone who is listening on the network).
Using Yii 1.x I did it using below method.
create a class, DbConnection inside protected/components extending from CDbConnection
class DbConnection extends CDbConnection
{
public function createPdoInstance()
{
// Decrypt the password used in config file
// e.g.: $this->password = mydecrypt($this->password);
return parent::createPdoInstance();
}
}
Adjust the config file (protected/config/main.php)
'db' => array(
'class' => 'DbConnection', // Use above classname
'password' => 'encryptedpassword',
),