Foeign Key Locking Transaction timeout when a second database connection is opened - mysql

I'm having a problem involving a database transaction in one class that is timing out due to a secondary database connection being opened within the transaction; the problem started occurring when I added a foreign key constraint. And, testing using:
SET foreign_key_checks = 0;
I've been able to confirm this.
My database class looks like this (I've left off all of the methods):
class Db {
function __construct($config) {
$this->config = $config;
}
private function connect($config) {$dsn = 'mysql:host=' . $config['host'] . ';dbname=' . $config['dbname'] . ';charset=utf8';
$options = array(
// PDO::ATTR_PERSISTENT => true,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$dbh = new PDO($dsn, $config['username'], $config['password'], $options);
$dbh->exec("SET NAMES utf8;");
return $dbh;
}
}
My model looks like this:
class Model {
function __construct() {
$this->db = new Db(array('host'=>DB_HOST,'dbname'=>DB_NAME,'username'=>DB_USERNAME,'password'=>DB_PASSWORD));
}
}
The code below then performs a little bit of logic, then an insert into the question_orders table: question_orders has a column question_id, with a foreign key index, which references the parent table questions; I think that the problem is that Assessment_Question_Orders extends the Model and creates a new database connection? Any thoughts on how to maintain both the transaction and foreign key aspects would be appreciated.
class This_Is_A_Problem extends Model() {
public function __construct() {
parent::construct();
}
public function problemFunction() {
/*variable init code left out*/
$this->db->beginTransaction();
$db_result = false;
try {
$db_result = $this->db->insert('questions', $questions_data);
$new_insert_id = $this->db->lastInsertId();
$assessment_question_orders = new Assessment_Question_Orders();
$question_number = $assessment_question_orders->insertSingleQuestionOrder($module_id, $new_insert_id);
$db_result = $this->db->commit();
}
} catch (PDOException $e) {
$this->db->rollBack();
}}}

One thread should (usually) have only one connection to the database. So I recommend one of these patterns:
Plan A: a single $db passed into all classes:
$db = new PDO(...);
$my_obj = new My_Class($db); -- $db is saved in $this->db for use within the methods of My_Class.
Plan B: a singleton Db class with a getter method:
// Singleton (of sorts)
class Db
{
private static $db;
function __construct()
{
self::$db = new PDO(...);
// A variant would include "lazy" instantiation of self::$Db.
}
function Get_Db() { return self::$db; } // All calls get the same `db`
}
class My_class
{
function My_Method()
{
$db = Db::Get_Db();
$db->...
}
}
new Db(); // one time call at start of program
There is only rarely a need to have two db connections in a single program. Plan A easily allows for such. (But see if you can avoid it -- you are in trouble now because of it.)

Related

on delete cascade laravel

Morning everyone,
I have foreign keys set to on delete cascade in laravel 5 and mysql on a couple of tables where the existence of a employee depends on the existence of the country he was born in.
$table->foreign('id_estado_municipio')
->references('id_estado_municipio')
->on('cat_municipios')
->onDelete('cascade')
->onUpdate('cascade');
Problem is when I delete the country, the employee that must get erased along with it, gets erased from the database, but the employee's record keeps on showing at the index view.
Have tried setting engine to InnoDB in the config file, model observers to delete dependents but still can't figure out how to make it work.
Hopefully someone can give some light. It would be very much appreciate it.
Here you are my models and modelcontrollers
class Municipio extends Model
{
//
protected $table = 'cat_municipios';
protected $primaryKey = 'id_estado_municipio';
...
public function trabajadores()
{
return $this->hasMany(Trabajador::class,'id_trabajador');
}
protected static function boot() {
parent::boot();
static::deleting(function($municipio) {
// delete related stuff ;)
$municipio -> trabajadores() -> delete();
});
}
class MunicipioController extends Controller
{
...
public function destroy($id)
{
//
$municipio = Municipio::findOrFail($id);
$municipio -> trabajadores() -> delete();
$destruido = Municipio::destroy($id);
if($destruido)
return redirect()->route('Municipio.index')->with('info','Municipio eliminado con éxito.');
else
return redirect()->route('Municipio.index')->with('error','Imposible borrar Municipio.');
}
}
////////////////////////////////////////
class Trabajador extends Model
{
//
protected $table = 'trabajadors';
protected $primaryKey = 'id_trabajador';
...
public function municipio()
{
return $this->belongsTo(Municipio::class,'id_estado_municipio');
}
...
}
class TrabajadorController extends Controller
{
public function index()
{
//
$criterio = \Request::get('search'); //<-- we use global request to get the param of URI
$estadosciviles = EstadoCivil::orderBy('id_estado_civil')->paginate(50);
$estados = Estado::orderBy('id_estado') -> paginate(50);
$municipios = Municipio::orderBy('id_estado_municipio')->paginate();
$religiones = Religion::orderBy('id_religion')->paginate();
$trabajadores = Trabajador::where('nombre', 'like', '%'.$criterio.'%')
->orwhere('id_trabajador',$criterio)
->orwhere('a_paterno',$criterio)
->orwhere('a_materno',$criterio)
->orwhere('curp',$criterio)
->orwhere('rfc',$criterio)
->orwhere('seguro_social',$criterio)
->sortable()
->orderBy('id_trabajador')
->orderBy('nombre')
->paginate();
return view('Trabajador.index', array('trabajadores' => $trabajadores,'estadosciviles' => $estadosciviles,'estados' => $estados,'municipios' => $municipios,'religiones' => $religiones));
}
...
}

Yii2 pass query result to a action in another controller

I'm trying to insert record into my audit table upon update of record in any other table. For example, if a user update his profile I want to store the old record and the newly updated record in my audit table. For this in my user model I'm trying to use beforeSave() and pass the value to my audit controller
public function beforeSave($insert)
{
if((parent::beforeSave($insert))){
// Place your custom code here
$query = DepCustomer::findOne($this->customer_id);
Yii::$app->runAction('audit-trial/createaudit', ['query' => $query]);
return true;
}
}
And the action code in audit controller for now
public function actionCreateaudit($query)
{
$model = new Audit();
$model->old = '';
foreach($query as $name => $value){
//$temp = $name .': '. $value.', ';
//$contentBefore[] = $temp;
$audit->old = $audit->old.$name .': '. $value. ', ';
}
// I've not yet any other code for now I'm trying to get the old value
$model->save();
}
I'm getting 404 not found error. What do I need to change in my code to make it work? Thank you!
instead of runAction() . If you want to perform operation on another model, prefer to create a static function in that model (in your case Audit model) to save the data
public function beforeSave($insert)
{
if((parent::beforeSave($insert))){
// Place your custom code here
$query = DepCustomer::findOne($this->customer_id);
Audit::saveOldDetails($query);
return true;
}
}
and write saveOldDetails function in Audit Model
public static saveOldDetails($query){
// your business logic here
}
Refer this link
http://www.yiiframework.com/doc-2.0/yii-base-controller.html#runAction()-detail

PHPUnit/DbUnit "Too many connections"

While running PHPUnit tests, a lot of connections are created but not closed.
I can see this in
mysql> show processlist;
In my database class I create a db connection by implementing PHPUnit_Extensions_Database_TestCase#getConnection().
I make sure that in the teardown the connection gets closed. See snippet:
<?php
abstract class My_Tests_DatabaseTestCase extends \PHPUnit_Extensions_Database_TestCase
{
static private $pdo = null;
private $conn = null;
/**
* #throws RuntimeException
* #return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
final public function getConnection()
{
$iniFilePath = __DIR__ . '/../../../db-config.ini';
$iniFile = parse_ini_file($iniFilePath, true);
$dsn = "mysql:dbname=".$iniFile['phpunit']['dbname'].";host=".$iniFile['phpunit']['host'];
if ( $this->conn === null ) {
if ( self::$pdo == null ) {
self::$pdo = new \PDO($dsn, $iniFile['phpunit']['user'], $iniFile['phpunit']['password']);
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, $iniFile['phpunit']['dbname']);
}
return $this->conn;
}
protected function getSetUpOperation()
{
return new \PHPUnit_Extensions_Database_Operation_Composite(array(
new \TestsExtensions\TruncateDatabaseOperation(),
\PHPUnit_Extensions_Database_Operation_Factory::INSERT()
));
}
protected function getTearDownOperation() {
return \PHPUnit_Extensions_Database_Operation_Factory::TRUNCATE();
}
protected function setUp()
{
parent::setUp();
$em = \ORM\Provider::getInstance()->getEntityManager(\ORM\Provider::DEFAULT_ID);
$em->clear();
}
protected function tearDown()
{
parent::tearDown();
$em = \ORM\Provider::getInstance()->getEntityManager(\ORM\Provider::DEFAULT_ID);
$em->getConnection()->close();
if ($this->conn) {
$this->conn->close();
}
}
}
Debugging showed that the connections were closed, but the processlist showed them with status "sleep".
The amount of connections rises until I get the "too many connections" error.
I do not want to increase the number of connections. I want to close the connection.
What can I modify to make this happen?
Snippet from PDO Connection management:
Upon successful connection to the database, an instance of the PDO
class is returned to your script. The connection remains active for
the lifetime of that PDO object. To close the connection, you need to
destroy the object by ensuring that all remaining references to it are
deleted--you do this by assigning NULL to the variable that holds the
object. If you don't do this explicitly, PHP will automatically close
the connection when your script ends.
Don't store the PDO or set it to null in the teardown:
Remove static private $pdo = null;
or
protected function tearDown()
{
parent::tearDown();
$em = \ORM\Provider::getInstance()->getEntityManager(\ORM\Provider::DEFAULT_ID);
$em->getConnection()->close();
if ($this->conn) {
$this->conn->close();
}
self::$pdo = null;
}
I don't think you need to store the PDO if you are going to pass it into the PHPUnit DB connection

CodeIgniter active records' problems calling multiple stored procedures

class Registration_model extends CI_Model {
function __construct() {
parent::__construct();
}
function check_email_availability($email)
{
$sql = "CALL proc_1301('$email');";
$query = $this->db->query($sql) or die(mysql_error());
return $query->row_array();
}
function check_username_availability($username)
{
$sqlt = "CALL proc_1303('$username');";
$query = $this->db->query($sqlt) or die(mysql_error());
return $query->row_array();
}
function process_registration($username, $email, $password)
{
$sql = "CALL `proc_1302`('$username', '$email', '$password');";
$query = $this->db->query($sql) or die(mysql_error());
return $query->result_array();
}
this is my controller code which calls three functions from model one by one:
$emailCheckRes = $this->Registration_model->check_email_availability($email);
$usernameCheckRes = $this->Registration_model->check_username_availability($username);
$this->data['regRes'] = $this->Registration_model->process_registration($username, $email, $password);
my problem is when i run only one function it runs successfully but when i run two of them or all three it shows blank page... any idea why ???
SOLUTION
So finally the only solution we got for my own problem is :
/* ADD THIS FUNCTION IN SYSTEM/DATABASE/DB_ACTIVE_REC */
/* USAGE $this->db->freeDBResource($this->db->conn_id); */
function freeDBResource($dbh){
while(mysqli_next_result($dbh)){
if($l_result = mysqli_store_result($dbh)){
mysqli_free_result($l_result);
}
}
}
The problem is related to CodeIgniter's active recors and multiple database stored procedure calling.
First of all check that dbdriver parameter (application/config/database.php) is set to mysqli.
Then, as described in "Calling a stored procedure from CodeIgniter's Active Record class" question on StackOverflow, adding to system/database/DB_active_rec.php the following function:
function freeDBResource($dbh){
while(mysqli_next_result($dbh)){
if($l_result = mysqli_store_result($dbh)){
mysqli_free_result($l_result);
}
}
}
..And in your controller use:
$this->db->freeDBResource($this->db->conn_id);
after any stored procedure calling.
Model and Controller seems to be ok.
If you try a model like:
class Test_model extends CI_Model
{
function __construct()
{
parent::__construct();
}
function a($a_input)
{
return($a_input.': a');
}
function b($b_input)
{
return($b_input.': b');
}
}
...And call it functions from a controller like this:
$this->load->model('Test_model');
$a_result = $this->Test_model->a('aaa');
$b_result = $this->Test_model->b('bbb');
echo($a_result.'<br />'.$b_result);
You can see multiple function calling working fine.
Are you sure you can execute any of three function in model correctly, if you call only one?
If yes, maybe the problem can be find in your stored procedures... Can you try to execute a normal query instead of stored procedures, in model functions? For debug your problem, check also in your /application/config/database.php if db_debug is set to TRUE.

When Extending Zend_Db_Table_Abstract to Create a Join it Crashes MySQL

I want to understand why this works perfect with out a problem.
$this->db = Zend_Db_Table_Abstract::getDefaultAdapter();
public function getMessages()
{
$select = $this->db->select();
$select
->from('Mail_Text', '*')
->join(
array('Mail' => 'Mail'),
'Mail.id = Mail_Text.parent_id', '*'
);
return $this->db->fetchAll($select);
}
Now if I do this by extending Zend_Db_Table_Abstract
class Mail_Model_Text extends Zend_Db_Table_Abstract
{
protected $_name = 'Mail_Text';
public function fetchMessges(){
$select = $this->select();
$select->setIntegrityCheck(false)
->from($this->_name, '*')
->join(
array('Mail' => 'Mail'),
'Mail.id = Mail_Text.parent_id', '*'
);
return $this->fetchAll($select);
}
}
This crashes MySql I wanted to keep the code separate but I can join theses tables. All the Single select and updates query's work perfect. I have research all over the net and can't seem to find the solution to this puzzle. Any Help to his would be great Thanks in advance.
You don't need the from() statement or need to alias the table to the same name:
class Mail_Model_Text extends Zend_Db_Table_Abstract
{
protected $_name = 'Mail_Text';
public function fetchMessges()
{
$select = $this->select();
$select->setIntegrityCheck(false)
->join('Mail', 'Mail.id = Mail_Text.parent_id');
return $this->fetchAll($select);
}
}
Also, ensure that you have correctly indexed your tables.