I have installed cron on server who runs each 3 minutes some functions.
this are functions:
$xmldb->sendOddsToDb();
$xmldb->copyHAtoHandicap();
$xmldb->sendFixturesToDb();
$xmldb->fillBaby();
Each function has:
try{
$this->conn->connect(); //connect to database
$this->PDO->beginTransaction(); // begin
$stmt = $this->PDO->prepare($this->insTLS);
//some params not important
$this->PDO->commit(); //SAVE
$this->conn->close(); //CLOSE
}
catch(Exception $e){
$this->PDO->rollBack();
}
Now my question, is better to use transactions like this, for each function new transaction or is better to start just once, and commit on end of all functions?
For example:
try{
$this->conn->connect(); //connect to database
$this->PDO->beginTransaction(); // begin
$xmldb->sendOddsToDb();
$xmldb->copyHAtoHandicap();
$xmldb->sendFixturesToDb();
$xmldb->fillBaby();
$this->PDO->commit(); //SAVE
$this->conn->close(); //CLOSE
}
catch(Exception $e){
$this->PDO->rollBack();
}
I need to insert as fastest possible data to db, because i get data from feed where more than 100 000 rows each 3 minutes.
I'd suggest reading this PHP PDO Transactions Documentation.
First off, there's no difference if you explicitly begin a transaction, execute a PDOStatement, and commit that transaction or just simply execute the transaction.
Second, if the four database functions are dependent on each other, wrap them all in a transaction.
Third, regardless of whether or not the functions are related, wrapping them in a single transaction will definitely be faster.
Related
The Eloquent ORM is quite nice, though I'm wondering if there is an easy way to setup MySQL transactions using innoDB in the same fashion as PDO, or if I would have to extend the ORM to make this possible?
You can do this:
DB::transaction(function() {
//
});
Everything inside the Closure executes within a transaction. If an exception occurs it will rollback automatically.
If you don't like anonymous functions:
try {
DB::connection()->pdo->beginTransaction();
// database queries here
DB::connection()->pdo->commit();
} catch (\PDOException $e) {
// Woopsy
DB::connection()->pdo->rollBack();
}
Update: For laravel 4, the pdo object isn't public anymore so:
try {
DB::beginTransaction();
// database queries here
DB::commit();
} catch (\PDOException $e) {
// Woopsy
DB::rollBack();
}
If you want to use Eloquent, you also can use this
This is just sample code from my project
/*
* Saving Question
*/
$question = new Question;
$questionCategory = new QuestionCategory;
/*
* Insert new record for question
*/
$question->title = $title;
$question->user_id = Auth::user()->user_id;
$question->description = $description;
$question->time_post = date('Y-m-d H:i:s');
if(Input::has('expiredtime'))
$question->expired_time = Input::get('expiredtime');
$questionCategory->category_id = $category;
$questionCategory->time_added = date('Y-m-d H:i:s');
DB::transaction(function() use ($question, $questionCategory) {
$question->save();
/*
* insert new record for question category
*/
$questionCategory->question_id = $question->id;
$questionCategory->save();
});
If you want to avoid closures, and happy to use facades, the following keeps things nice and clean:
try {
\DB::beginTransaction();
$user = \Auth::user();
$user->fill($request->all());
$user->push();
\DB::commit();
} catch (Throwable $e) {
\DB::rollback();
}
If any statements fail, commit will never hit, and the transaction won't process.
I'm Sure you are not looking for a closure solution, try this for a more compact solution
try{
DB::beginTransaction();
/*
* Your DB code
* */
DB::commit();
}catch(\Exception $e){
DB::rollback();
}
For some reason it is quite difficult to find this information anywhere, so I decided to post it here, as my issue, while related to Eloquent transactions, was exactly changing this.
After reading THIS stackoverflow answer, I realized my database tables were using MyISAM instead of InnoDB.
For transactions to work on Laravel (or anywhere else as it seems), it is required that your tables are set to use InnoDB
Why?
Quoting MySQL Transactions and Atomic Operations docs (here):
MySQL Server (version 3.23-max and all versions 4.0 and above) supports transactions with the InnoDB and BDB transactional storage engines. InnoDB provides full ACID compliance. See Chapter 14, Storage Engines. For information about InnoDB differences from standard SQL with regard to treatment of transaction errors, see Section 14.2.11, “InnoDB Error Handling”.
The other nontransactional storage engines in MySQL Server (such as MyISAM) follow a different paradigm for data integrity called “atomic operations.” In transactional terms, MyISAM tables effectively always operate in autocommit = 1 mode. Atomic operations often offer comparable integrity with higher performance.
Because MySQL Server supports both paradigms, you can decide whether your applications are best served by the speed of atomic operations or the use of transactional features. This choice can be made on a per-table basis.
If any exception occurs, the transaction will rollback automatically.
Laravel Basic transaction format
try{
DB::beginTransaction();
/*
* SQL operation one
* SQL operation two
..................
..................
* SQL operation n */
DB::commit();
/* Transaction successful. */
}catch(\Exception $e){
DB::rollback();
/* Transaction failed. */
}
The best and clear way:
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
I need to run a query outside the transaction that I have started:
$transaction = \Yii::$app->db->beginTransaction();
try {
//... other database queries within the transaction ...
//Query I want to be inserted regardless:
\Yii::$app->db->createCommand("INSERT INTO...")->execute();
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
}
Is there a smart way to do that, or do I need to clone/create a new database connection - and if so, what's the best way to do that without having to specify the database parameters again and just use the same config?
You need to use separate connection or move this query after/before transaction.
For copying DB component you can simply use clone - new instance should open new connection on first query:
$connection = clone Yii::$app->db;
$connection->createCommand("INSERT INTO...")->execute();
It is used in this way in yii\log\DbTarget.
But you may consider declaring separate DB component for this task (Yii::$app->db2) - then you will be able to reuse this additional connection.
A few weeks back I had an interesting inquiry regarding the handling/processing of an inventory/payment system and I've decided to implement the method of the accepted answer being it was both the most simplistic and more likely the most efficient solution given.
I'm using Laravel's Eloquent ORM in conjunction with the Slim Framework, and I'd like to perform a MYSQL transaction with a specific query regarding the decrementation of the stock of items within an inventory system. The code that I'd like to use a transaction for is the following:
// decrement each items stock following successful payment
try {
// here is where i'd like to perform a transaction, rather than simple eloquent query
foreach ($this->c->basket->all() as $product) {
$product->decrement('stock', $product->quantity);
}
} catch (Exception $e) {
// if error is caused during decremention of item(s),
// handle accordingly
}
Now being that I am using Slim as aforementioned, I believe I don't have native access to the Laravel DB Facade which to my knowledge handles transactions.
Suggestions?
SIDE-NOTE
My DB Connection is set globally in an initialization file like so:
$capsule = new Illuminate\Database\Capsule\Manager();
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
You could get the Capsule object from the slim container and then just use the Connection object to start and commit a database transaction. If an exception occurs you just need to call the rollBack() method.
Pseudo code:
$capsule = new \Illuminate\Database\Capsule\Manager();
// Get a registered connection instance.
$connection = $capsule->getConnection();
// begin a transaction manually
$connection->beginTransaction();
try {
// do something...
$product->decrement('stock', $product->quantity);
// commit a transaction via the commit method
$connection->commit();
} catch (Exception $e) {
// you can rollback the transaction via the rollBack method
$connection->rollBack();
}
i'm using Slim framework to provide an API to our clients. I'm focused on Java so that could the the point, because the problem is related with database connection.
Every request connect to the database throw PDO like
$dbh = new PDO($param1, $param2, $param3);//php 7.1
So as php don't have a connection pool i understand each request (maybe at the same time) starts a new connection throw the database.
But i'm experimenting a kind of situation here.
If one request starts a transaction for inserting or updating something, next requests will wait until this transaction finish, but it is on a different thread, so i'm thinking on table locks? or something like each request is getting the same connection.
THE PROBLEM:
So if they are in different threads with a different connection how
they need to wait until transaction ends for the first request.
My database source class is something like
class DataBaseConfig {
public static function getConn()
{
$dbh = new PDO($param1, $param2, $param3);
$dbh->exec("set names utf8");
return $dbh;
}
}
And i use it like
$db = DataBaseConfig::getConn();
$db->beginTransaction();
//code
$db->commit();
I'm missing something sure, so can someone help me with this problem?
Thanks!
NEWS! EDIT
#YourCommonSense thank you it was finally a session configuration. I
didn't know that php start_session(); locks the session file so that's
what was happening. Thanks a lot!
link: https://ma.ttias.be/php-session-locking-prevent-sessions-blocking-in-requests/
The Eloquent ORM is quite nice, though I'm wondering if there is an easy way to setup MySQL transactions using innoDB in the same fashion as PDO, or if I would have to extend the ORM to make this possible?
You can do this:
DB::transaction(function() {
//
});
Everything inside the Closure executes within a transaction. If an exception occurs it will rollback automatically.
If you don't like anonymous functions:
try {
DB::connection()->pdo->beginTransaction();
// database queries here
DB::connection()->pdo->commit();
} catch (\PDOException $e) {
// Woopsy
DB::connection()->pdo->rollBack();
}
Update: For laravel 4, the pdo object isn't public anymore so:
try {
DB::beginTransaction();
// database queries here
DB::commit();
} catch (\PDOException $e) {
// Woopsy
DB::rollBack();
}
If you want to use Eloquent, you also can use this
This is just sample code from my project
/*
* Saving Question
*/
$question = new Question;
$questionCategory = new QuestionCategory;
/*
* Insert new record for question
*/
$question->title = $title;
$question->user_id = Auth::user()->user_id;
$question->description = $description;
$question->time_post = date('Y-m-d H:i:s');
if(Input::has('expiredtime'))
$question->expired_time = Input::get('expiredtime');
$questionCategory->category_id = $category;
$questionCategory->time_added = date('Y-m-d H:i:s');
DB::transaction(function() use ($question, $questionCategory) {
$question->save();
/*
* insert new record for question category
*/
$questionCategory->question_id = $question->id;
$questionCategory->save();
});
If you want to avoid closures, and happy to use facades, the following keeps things nice and clean:
try {
\DB::beginTransaction();
$user = \Auth::user();
$user->fill($request->all());
$user->push();
\DB::commit();
} catch (Throwable $e) {
\DB::rollback();
}
If any statements fail, commit will never hit, and the transaction won't process.
I'm Sure you are not looking for a closure solution, try this for a more compact solution
try{
DB::beginTransaction();
/*
* Your DB code
* */
DB::commit();
}catch(\Exception $e){
DB::rollback();
}
For some reason it is quite difficult to find this information anywhere, so I decided to post it here, as my issue, while related to Eloquent transactions, was exactly changing this.
After reading THIS stackoverflow answer, I realized my database tables were using MyISAM instead of InnoDB.
For transactions to work on Laravel (or anywhere else as it seems), it is required that your tables are set to use InnoDB
Why?
Quoting MySQL Transactions and Atomic Operations docs (here):
MySQL Server (version 3.23-max and all versions 4.0 and above) supports transactions with the InnoDB and BDB transactional storage engines. InnoDB provides full ACID compliance. See Chapter 14, Storage Engines. For information about InnoDB differences from standard SQL with regard to treatment of transaction errors, see Section 14.2.11, “InnoDB Error Handling”.
The other nontransactional storage engines in MySQL Server (such as MyISAM) follow a different paradigm for data integrity called “atomic operations.” In transactional terms, MyISAM tables effectively always operate in autocommit = 1 mode. Atomic operations often offer comparable integrity with higher performance.
Because MySQL Server supports both paradigms, you can decide whether your applications are best served by the speed of atomic operations or the use of transactional features. This choice can be made on a per-table basis.
If any exception occurs, the transaction will rollback automatically.
Laravel Basic transaction format
try{
DB::beginTransaction();
/*
* SQL operation one
* SQL operation two
..................
..................
* SQL operation n */
DB::commit();
/* Transaction successful. */
}catch(\Exception $e){
DB::rollback();
/* Transaction failed. */
}
The best and clear way:
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}