I'm new to database transactions and what i've found is extremely confusing. I have a queue table that contains email address, send datetime, and sent datetime. My cron job is constantly firing and selecting rows with the 'send datetime' passed now. It sends an email to the address and updates the 'sent datetime' column.
If this cron job fired at the exact same time, there is potential for the them to grab the same rows, thus sending the email twice.
From what i understand, transactions all depend on the success or failure or queries. How do i check that in this scenario?
$this->db->trans_begin();
$query = $this->db->query('SELECT * FROM Queue_table where send >= now LIMIT 100');
foreach ($query->result() as $row)
{
//code to send email to $row->email;
$this->db->query('UPDATE Queue_table SET sent = now WHERE id = '$row->id'');
}
//the following doesn't make much sense to me. What would cause
// this to be false in this scenario?
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
}
else
{
$this->db->trans_commit();
}
Am i going about doing this completely wrong?
Related
I have a database record that I would like to update base on certain conditions. This condition is: when the pay_day is reached, I want to send the user that owns that record an email and then update the pay_day column to another date in the future using the interval_day column on the users' table. interval_day is just a number selected by the user.
Below is an illustration:
$now = Carbon::now();
User::where('approved', true)
->where('pay_day', '<', $now)
->chunkById(1000, function($users){
foreach ($users as $user) {
$interval = $user->interval;
$payDay = $now()->addDays($interval);
// update the user...
$user->update([
'pay_day' => $payDay,
]);
// if the user was updated, send an email next...
}
});
Now, let's say I have 100 different users with different interval values. I want their respective values in their interval columns to be what would be updated to their pay_day column and NOT the same date for all 100 users.
But when I run the above query it didn't update, neither the pay_day nor send email to the respective users. When I dd($interval & $payDay) it returns nothing.
Please what am I doing wrong? I need your suggestions. Thanks for your time in advance.
use like this
$updates = ([
'pay_day' => $payDay,
//other columns
]);
$x = User::where('approved', true)
->where('pay_day', '<', $now)->->update($updates);
if($x){
//succeed case
}
Not sure if $payDay = $now()->addDays($interval); is a typo or you've actually done that in your code, but $now is a variable not a function. Additionally you need to tell the closure in your chunkById function to use $now:
\App\Models\User::where('approved', true)
->where('pay_day', '<', \Carbon\Carbon::today())
->chunkById(1000, function ($users) {
$users->each(function ($user) {
$success = $user->update([
'pay_day' => \Carbon\Carbon::today()->addDays($user->interval)
]);
if ($success) {
// send email
}
});
});
The above finds all approved Users where their pay_day is before today then processes the results in chunks of 1000 and updates the pay_day for each of them to be today + the interval value.
I got stuck with this problem, I found many posts but seemed it's not useful. So I post again here and hope someone can help me.
Let say I have 2 button, 1 is Start button and 1 is Stop button. When I press start will call ajax function which query very long time. I need when I press Stop will stop immediately this query, not execute anymore.
this is function used to call query and fetch row. (customize Mysqli.php)
public function fetchMultiRowset($params = array()) {
$data = array();
$mysqli = $this->_adapter->getConnection();
$mysqli->multi_query($this->bindParams($this->_sql, $params));
$thread_id = mysqli_thread_id($mysqli);
ignore_user_abort(true);
ob_start();
$index = 0;
do {
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$data[$index] = $row;
$index++;
echo " ";
ob_flush();
flush();
}
$result->free();
}
}
while ($mysqli->more_results() && $mysqli->next_result());
ob_end_flush();
return $data;
}
Function in Model:
public function select_entries() {
$data = null;
try {
$db = Zend_Db_Adapter_Mysqlicustom::singleton();
$sql = "SELECT * FROM report LIMIT 2000000";
$data = $db->fetchMultiRowset($sql);
$db->closeConnection();
} catch (Exception $exc) {
}
return $data;
}
Controller:
public function testAction(){
$op = $this->report_test->select_entries();
}
In AJAX I used xhr.abort() to stop the AJAX function. But it still runs the query while AJAX was aborted.
How do I stop query? I used Zend Framework.
EDIT: I did not look in detail at your program, now I see that not the query itself is taking so long, but the reading of all the data. So just check every 1000 rows, if the ajax call is still active. Ajax Abort.
Solution in case of a long-running SQL-query:
You would have to allow the application to kill database queries, and you need to implement a more complex interaction between Client and Server, which could lead to security holes if done wrong.
The Start-Request should contain a session and a page id (secure id, so not 3 and 4 and 5 but a non-guessable but unique hash of some kind). The backend then connects this id with the query. This could be done in some extra table of the database, but also via comments in the SQL query, like "Session fid98a08u4j, Page 940jfmkvlz" => s:<session>p:<page>.
/* s:fid98a08u4jp:940jfmkvlz */ select * from ...
If the user presses "stop", you send session and page id to the server. The php-code then fetches the list of your running SQL Queries and searches for session and page and extracts the query id.
Then the php sends a
kill query <id>
to the MySQL-server.
This might lead to trouble when not using transactions, and this might damage replication. And even a kill query might take some time in the state 'killing'.
So be sure that you can and want not to split the long running query into subqueries, which check if the request is still valid every few seconds, or that you do not just want to kill the query for cosmetical reasons.
I have a simple SQL Query:
$stmt = $db_main->prepare("SELECT id FROM user WHERE username=? AND mail=? LIMIT 1");
$stmt->bind_param('ss', $username, $mail);
$stmt->execute();
And I want to know, if it found an user. So I want to count the rows found.
I already tried to use rowCount (Not safe for SELECT) or num_rows or just looking if the result id is numeric (Which '' would not be, I hoped...)
There has to be an easy way to count the selected row, hasn't be?
Check number of rows returned with:
$stmt->num_rows;
Check for instance this site.
p.s.: added as per question in comment: use fetch() in order to get the next record.
...
$stmt->execute();
$stmt->bind_result($user_id); // access id
$stmt->store_result(); // optional: buffering (see below)
if ($data = $stmt->fetch()) {
do {
print("Id: " . $user_id);
} while ($data = $stmt->fetch());
} else {
echo 'No records found.';
}
Regarding store_result() from the documentation:
"You must call mysqli_stmt_store_result() for every query ..., if and only if you want to buffer the complete result set by the client ..."
public function smart_query($query, $options = null, $bindoptions = null)
{
// Code to Reconnect incase of timeout
try {
$this->db->query('SELECT * FROM templates');
}
catch (PDOException $e)
{
echo $e;
$pdooptions = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->db = new PDO("mysql:host=localhost;dbname=$this->database", "$this->username", "$this->password", $pdooptions);
}
$this->statement = $this->db->prepare($query);
if($bindoptions != null)
{
$this->bind($bindoptions);
}
$this->execute();
if($options != null)
{
// Return Single Row
if($options['rows'] == 1)
{
return $this->statement->fetch(PDO::FETCH_ASSOC);
}
// Return Multiple Rows
elseif($options['rows'] != 1)
{
return $this->statement->fetchAll(PDO::FETCH_ASSOC);
}
}
}
I've saw this code today, and got really confused.
It looks like he is trying to process a simple query, before doing the actual query.
Why is he checking if the connection is still open?
I thought that PDO only destroys it's connection upon script finishing executing automatically?
Is that correct to check if it's open or closed?
This implements a form of lazy loading.
This first time a query is executed through this class/function, the database connection may not be established yet. This is the purpose of this check, so that the consumer (you) does not have to mind about it.
The connection is then stored in the $this->db class member, for future reuse when you call this method again in the course of your script (and yes, this connection will stay open until the script ends -- unless it is closed explicitely beforehand, of course).
For information, this check is slightly inefficient. A simple $this->db->query('SELECT 1') would suffice, without the need to read a table at all.
I am looking for a way to interpret data in a URL, server side.
Client side, I can control what the URL is, and I can programmatically produce Data for the URL, but I do not have direct access to the source code.
So ultimately, I am trying to build an online High Score system, for an offline game.
I have access to Azure and my dedicated LAMP server. I would like for my server to wait for a connections that use a URL something like "http://www.myappserver19002393859.com/HighScores?UserID=2fb44e3888?Score=25250"
I want to get the UserID & Score from the URL and then redirect to another page with the High Score Table...
Any suggestions?
(Edit: I can do anything server side, it is only client side where I have limitations.)
Use PHP's $_GET collection. It contains the key/value pairs of a URI's querystring.
In the example you've given, it would look like this:
HighScoresHandler.php
<?php
$userId = $_GET['UserID'];
$score = $_GET['Score'];
// Validation
if( empty( $userId ) || empty( $score ) ) {
echo "No UserID or Score provided.";
exit();
}
$score = intval( $score );
if( $score === 0 ) {
echo "Invalid score specified.";
exit();
}
// Verification
// TODO: Query your database to ensure the user specified by $userID exists.
// Make sure you do this safely as not to be subject to SQL injection.
$sql = "SELECT COUNT(*) FROM Users WHERE UserID = #userId";
// Action
$sql = "INSERT INTO HighScores ( UserID, Score ) VALUES ( #userId, #score )";
?>
Note that you would be advised to implement some form of MAC (Message Authentication Code) or some other system to verify that a score is real, otherwise there is nothing stopping anyone from submitting bogus scores using forged HTTP requests (this is why you sometimes see insanely high scores posted to online scoreboards).