I am using the wp_schedule_single_event() function to create a wp-cron job that sends an email to the specified user at the specified time.
Mostly this wp-cron job is successfully created and the users get informed in time. But sometimes it just doesn't work.
Whats especially strange is that wp_schedule_single_event() always returns true (which means that it was executed successfully) even when the wp-cron job isn't created (I check that with the WP Crontrol plugin).
My code (write_log: custom function to log the given strings, time: the corresponding timestamp):
write_log('User ' . get_current_user_id() . ' now tries to create the addProductsExpired cron job with timestamp: ' . time);
$success = wp_schedule_single_event(time, 'hook_addProductsExpired', array(get_current_user_id()));
if (!$success) {
write_log('The creation failed!');
}
write_log('User ' . get_current_user_id() . ' now tries to create the sendReminderMail cron job with timestamp: ' . time);
$success = wp_schedule_single_event(time - 60 * 60 * 24, 'hook_sendReminderMail', array(get_current_user_id()));
if (!$success) {
write_log('The creation failed!');
}
I should also note that I never accomplished it to reproduce the error by myself
I so far tried:
updating Wordpress
studying the logs
executing the function with accounts of users where it previously failed (it worked on my pc and also on the pc of the user in future executions)
modifying parameters in the user entry of affected users
manually executing the function with the parameters it previously failed
rewriting and optimising the whole function
None of them worked or threw an error i could debug.
I am now using actionsheduler.org as suggested by Terminator-Barbapapa
in his comment to my question. So far I haven't experienced any issues.
Related
I'm writing a chat program for a site that does live broadcasting, and like you can guess with any non application driven chat it relies on a looping AJAX call to get new information (messages) in my case once every 2 seconds. My JSON that is being created via PHP and populated by SQL is of some concern to me, while it shows no noticeable impact on my server at present I cannot predict what adding several hundred users to the mix may do.
<?PHP
require_once("../../../../wp-load.php");
global $wpdb;
$table_name = $wpdb->prefix . "chat_posts";
$posts = $wpdb->get_results("SELECT * FROM ". $table_name ." WHERE ID > ". $_GET['last'] . " ORDER BY ID");
echo json_encode($posts);
?>
There obviously isn't much wiggle room as far as optimizing the code itself, but I am a little worried about how well the Wordpress SQL engine is written and if it will bog my SQL down once it gets to the point where it is receiving 200 requests every 2 seconds. Would caching the json encoded results of the DB query to a file then age checking it against new calls to the PHP script and either re-constructing the file with a new query or passing the files contents based on its last modification date be a better way to handle this? At that point I am putting a bigger load on my file-system but reducing my SQL load to one query every 2 seconds regardless of number of users.
Or am I already on the right path with just querying the server on every call?
So this is what I came up with, I went the DB only route for a few tests and while response was snappy, it didn't scale well and connections quickly got eaten up. So I decided to write a quick little bit of caching logic. So far it has worked wonderfully and seems to allow me to scale my chat as big as I want.
$cacheFile = 'cache/chat_'.$_GET['last'].'.json';
if (file_exists($cacheFile) && filemtime($cacheFile) + QUERY_REFRESH_RATE > time())
{
readfile($cacheFile);
} else {
require_once("../../../../wp-load.php");
$timestampMin = gmdate("Y-m-d H:i:s", (time() - 7200));
$sql= "/*qc=on*/" . "SELECT * FROM ". DB_TABLE ."chat_posts WHERE ID > ". $_GET['last'] . " AND timestamp > '".$timestampMin."' ORDER BY ID;";
$posts = $wpdb->get_results($sql);
$json = json_encode($posts);
echo $json;
file_put_contents($cacheFile,$json);
}
Its also great in that it allows me to run my formatting functions against messages such as parsing URL's into actual links and such with much less overhead.
Guys I am using CakePHP and having an issue related to fetching the recent updated flag I set in the DB.
Here is the code
$this->loadModel('Setting');
$s=$this->Setting->find('first');
if($s['Setting']['inprogress']==1)
{
echo "still working...";
exit;
}
$s['Setting']['inprogress']=1;
$this->Setting->Save($s);
//// Some code that is using db table and process data for like 30-40 seconds
$s['Setting']['inprogress']=0;
$this->Setting->Save($s);
exit;
This code is run by the cron job and the check is to make sure the next cron job doesn't touch the data before the first one finishes. But apparently the cron jobs are running in parallel as they don't getting the inprogress=1 at all.
However, If I check the record manually using PHPMyAdmin the inprogress flag goes 1 immediately but somehow it won't be available for the next http call.
Any idea?
Why don't you just use saveField?
So, instead of
$s['Setting']['inprogress']=1;
$this->Setting->Save($s);
you can have
$this->Setting->saveField('inprogress', 1);
or better, to be sure, in case other finds are done on the Setting model between your saved, you can do
$id = $s['Setting']['id'];
$this->Setting->id = $id;
$this->Setting->saveField('inprogress', 1);
I have a wordpress website that allows a user to set up a reminder for a certain day, it stores it in a database where it can either be displayed when the user logs in, or send out an email to them.
I followed the example code given in http://codex.wordpress.org/Function_Reference/wp_cron and have placed the code below into the main .php file of a plugin I have written which functions perfectly in all other ways.
if ( ! wp_next_scheduled( 'checkReminderEmails' ) ) {
wp_schedule_event( 1411693200, 'daily', 'checkReminderEmails' );
} //1411693200 is the unix timestamp for 1am a couple of days ago
add_action( 'checkReminderEmails', 'sendReminderEmails' );
function sendReminderEmails()
{
$table_name = $wpdb->prefix."reminders";
$query = "SELECT * FROM $table_name WHERE sendReminder = 1 and reminderDate = CURRENT_DATE( )";
$appointments = $wpdb->get_results( $query);
foreach ( $appointments as $info )
{
$message = "Hello here is your reminder.";
$toAddress = $info->userEmail;
$subject = "This is your reminder.";
wp_mail( $toAddress, $subject, $message);
}
} // end sendReminderEmails()
I have checked the wp_options table in my PHP database and can see the following code there
{s:18:"sendReminderEmails";a:1:{s:32:"40cd750bba9870f18aada2478b24840a";a:3:{s:8:"schedule";s:5:"daily";s:4:"args";a:0:{}s:8:"interval";i:86400;}}}i:1411693200;a:1:
I can receive other emails from the website using wp_mail() so I know that the functionality is supported by the server, and I know that the wp_cron jobs aren't fired until the website is visited after the time has passed, but I have been unable to get this cron job to fire correctly. Am I missing something obvious?
Edit: For anybody wondering, I used the wp-cron-dashboard plugin (https://wordpress.org/plugins/wp-cron-dashboard/) despite the warning that it hasn't been updated in 2+ years to check that my cron job was scheduled properly.
I also had to add global $wpdb; to the top of my function, the reason it was failing to fire was because without declaring that I was unable to use the get_results() function.
I also discovered that if you go to wp-cron.php you will manually force any scheduled cron jobs to be fired, and all output will display there, so it is possible to debug a cron job by adding echo statements and going to that page.
I know this is old but one thing I see right away is you need global $wpdb; as the first line of the sendReminderEmails() function, otherwise it's going to bomb.
I often quickly test cron functions in my front-page.php by adding something like sendReminderEmails(); exit; to the top, just as a sanity check that the function works at all.
This is more of a logic question than language question, though the approach might vary depending on the language. In this instance I'm using Actionscript and PHP.
I have a flash graphic that is getting data stored in a mysql database served from a PHP script. This part is working fine. It cycles through database entries every time it is fired.
The graphic is not on a website, but is being used at 5 locations, set to load and run at regular intervals (all 5 locations fire at the same time, or at least within <500ms of each-other). This is real-time info, so time is of the essence, currently the script loads and parses at all 5 locations between 30ms-300ms (depending on the distance from the server)
I was originally having a pagination problem, where each of the 5 locations would pull a different database entry since i was moving to the next entry every time the script runs. I solved this by setting the script to only move to the next entry after a certain amount of time passed, solving the problem.
However, I also need the script to send an email every time it displays a new entry, I only want it to send one email. I've attempted to solve this by adding a "has been emailed" boolean to the database. But, since all the scripts run at the same time, this rarely works (it does sometimes). Most of the time I get 5 emails sent. The timeliness of sending this email doesn't have to be as fast as the graphic gets info from the script, 5-10 second delay is fine.
I've been trying to come up with a solution for this. Currently I'm thinking of spawning a python script through PHP, that has a random delay (between 2 and 5 seconds) hopefully alleviating the problem. However, I'm not quite sure how to run exec() command from php without the script waiting for the command to finish. Or, is there a better way to accomplish this?
UPDATE: here is my current logic (relevant code only):
//get the top "unread" information from the database
$query="SELECT * FROM database WHERE Read = '0' ORDER BY Entry ASC LIMIT 1";
//DATA
$emailed = $row["emailed"];
$Entry = $row["databaseEntryID"];
if($emailed == 0)
{
**CODE TO SEND EMAIL**
$EmailSent="UPDATE database SET emailed = '1' WHERE databaseEntryID = '$Entry'";
$mysqli->query($EmailSent);
}
Thanks!
You need to use some kind of locking. E.g. database locking
function send_email_sync($message)
{
sql_query("UPDATE email_table SET email_sent=1 WHERE email_sent=0");
$result = FALSE;
if(number_of_affacted_rows() == 1) {
send_email_now($message);
$result = TRUE;
}
return $result;
}
The functions sql_query and number_of_affected_rows need to be adapted to your particular database.
Old answer:
Use file-based locking: (only works if the script only runs on a single server)
function send_email_sync($message)
{
$fd = fopen(__FILE__, "r");
if(!$fd) {
die("something bad happened in ".__FILE__.":".__LINE__);
}
$result = FALSE;
if(flock($fd, LOCK_EX | LOCK_NB)) {
if(!email_has_already_been_sent()) {
actually_send_email($message);
mark_email_as_sent();
$result = TRUE; //email has been sent
}
flock($fd, LOCK_UN);
}
fclose($fd);
return $result;
}
You will need to lock the row in your database by using a transaction.
psuedo code:
Start transaction
select row .. for update
update row
commit
if (mysqli_affected_rows ( $connection )) >1
send_email();
I have a web app that has a few processes that can take up to 10 minutes to run. Sometimes these processes are triggered by a user and they need the output as it is processed.
For instance, the user is looking for
a few records that they need. The
click the button to retrieve the
records (this is the part that can
take 10 minutes). They can continue
to work on other things but when they
click back to view the returns, it is
updated as the records are downloaded
into the system.
Right now, the user is locked while the process runs. I know about pcntl_fork() to fork a child process so that the user doesn't have to wait until the long process completes.
I was wondering if it's possible to tie that forked process to the specific user that triggered the request in a $_SESSION variable so that I can update the user when the process is complete. Also, is this the best way to update a user on a long-running process?
I think gearman fits your needs. Look at this sample code, taken from the doc :
<?php
/* create our object */
$gmclient= new GearmanClient();
/* add the default server */
$gmclient->addServer();
/* run reverse client */
$job_handle = $gmclient->doBackground("reverse", "this is a test");
if ($gmclient->returnCode() != GEARMAN_SUCCESS)
{
echo "bad return code\n";
exit;
}
$done = false;
do
{
sleep(3);
$stat = $gmclient->jobStatus($job_handle);
if (!$stat[0]) // the job is known so it is not done
$done = true;
echo "Running: " . ($stat[1] ? "true" : "false") . ", numerator: " . $stat[2] . ", denomintor: " . $stat[3] . "\n";
}
while(!$done);
echo "done!\n";
?>
If you store the $job_handle in the session, you can adapt the sample to make a control script.