AWS SWF Respond Activity Task Completed - php

I can see in the SWF Management Console a Workflow has around 18 events, with the 16th event being my ActivityTaskCompleted event however whenever i poll for decisions i only get up to the 15th event so i never get to call RespondDecisionTaskCompleted with the decision type CompleteWorkflowExecution as such my workflows are always sitting in the Active state until they timeout.
The flow i'm using is from a PHP SWF git i found a while ago, i unfortunately do not have the link to it anymore though.
$response = $this->swf->PollForDecisionTask($opts);
$decision_list = self::_decide(new HistoryEventIterator($this->swf, $opts, $response), $this->swf);
if(count($decision_list) > 0)
{
//Some decisions
}
else
{
//No decisions
}
Where the HistoryEventIterator looks like so
public function __construct(Aws\Swf\SwfClient $swf_client, $original_request, $original_response) {
$this->swf = $swf_client;
$this->events = array();
$this->event_index = 0;
$this->next_page_token = null;
$this->original_poll_request = $original_request;
$this->_process_poll_response($original_response);
$this->workflow_id = $original_response->get('workflowExecution')['workflowId'];
$this->run_id = $original_response->get('workflowExecution')['runId'];
}
protected function _process_poll_response($response) {
if ($response->hasKey("nextPageToken")) {
$this->next_page_token = (string) $response->get("nextPageToken");
} else {
$this->next_page_token = null;
}
$next_events = $response->get("events");
$next_events_object = new ArrayObject($next_events);
$next_events_copy = $next_events_object->getArrayCopy();
$this->events = array_merge($this->events, $next_events);
}
I have omitted error checking and functions of HistoryEventIterator that would not be called in this scenario.
I have output the next_page_token of HistoryEventIterator and found it was always NULL.
Should the RespondDecisionTaskCompleted called from an Activity reach the decider? If so, what could be the cause for mine not? Surely it wouldn't be paging after 15 events, and simply not paging correctly.
I can verify that the Domain, Activity Task List, and Decider Task List are accurate as the Workflow shows up in the SWF Management Console, as does the decisions and the Activity (The Activity even has the status Completed) There is appropriate error checking and Try/Catch blocks and in no cases are there any exceptions.

Related

Laravel CRON or Event process respond to api request via long poll - how to re-vitalise the session

I have a poll route on an API on Laravel 5.7 server, where the api user can request any information since the last poll.
The easy part is to respond immediately to a valid request if there is new information return $this->prepareResult($newData);
If there is no new data I am storing a poll request in the database, and a cron utility can then check once a minute for all poll requests and respond to any polls where data has been updated. Alternatively I can create an event listener for data updates and fire off a response to the poll when the data is updated.
I'm stuck with how to restore each session to match the device waiting for the update. I can store or pass the session ID but how do I make sure the CRON task / event processor can respond to the correct IP address just as if it was to the original request. Can php even do this?
I am trying to avoid websockets as will have lots of devices but with limited updates / interactions.
Clients poll for updates, APIs do not push updates.
REST API's are supposed to be stateless, so trying to have the backend keep track goes against REST.
To answer your question specifically, if you do not want to use websockets, the client app is going to have to continue to poll the endpoint till data is available.
Long poll is a valid technique. i think is a bad idea to run poll with session. since session are only for original user. you can run your long poll with php cli. you can check on your middleware to allow cli only for route poll. you can use pthreads
to run your long poll use pthreads via cli. and now pthreads v3 is designed safely and sensibly anywhere but CLI. you can use your cron to trigger your thread every one hour. then in your controller you need to store a $time = time(); to mark your start time of execution. then create dowhile loop to loop your poll process. while condition can be ($time > time()+3600) or other condition. inside loop you need to check is poll exist? if true then run it. then on the bottom of line inside loop you need to sleep for some second, for example 2 second.
on your background.php(this file is execute by cron)
<?php
error_reporting(-1);
ini_set('display_errors', 1);
class Atomic extends Threaded {
public function __construct($data = NULL) {
$this->data = $data;
}
private $data;
private $method;
private $class;
private $config;
}
class Task extends Thread {
public function __construct(Atomic $atomic) {
$this->atomic = $atomic;
}
public function run() {
$this->atomic->synchronized(function($atomic)
{
chdir($atomic->config['root']);
$exec_statement = array(
"php7.2.7",
$atomic->config['index'],
$atomic->class,
$atomic->method
);
echo "Running Command".PHP_EOL. implode(" ", $exec_statement)." at: ".date("Y-m-d H:i:s").PHP_EOL;
$data = shell_exec(implode(" ", $exec_statement));
echo $data.PHP_EOL;
}, $this->atomic);
}
private $atomic;
}
$config = array(
"root" => "/var/www/api.example.com/api/v1.1",
"index" => "index.php",
"interval_execution_time" => 200
);
chdir($config['root']);
$threads = array();
$list_threads = array(
array(
"class" => "Background_workers",
"method" => "send_email",
"total_thread" => 2
),
array(
"class" => "Background_workers",
"method" => "updating_data_user",
"total_thread" => 2
),
array(
"class" => "Background_workers",
"method" => "sending_fcm_broadcast",
"total_thread" => 2
)
);
for ($i=0; $i < count($list_threads); $i++)
{
$total_thread = $list_threads[$i]['total_thread'];
for ($j=0; $j < $total_thread; $j++)
{
$atomic = new Atomic();
$atomic->class = $list_threads[$i]['class'];
$atomic->method = $list_threads[$i]['method'];
$atomic->thread_number = $j;
$atomic->config = $config;
$threads[] = new Task($atomic);
}
}
foreach ($threads as $thread) {
$thread->start();
usleep(200);
}
foreach ($threads as $thread)
$thread->join();
?>
and this on your controller
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Background_workers extends MX_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
$this->output->enable_profiler(FALSE);
$this->configuration = $this->config->item("configuration_background_worker_module");
}
public function sending_fcm_broadcast() {
$time_run = time();
$time_stop = strtotime("+1 hour");
do{
$time_run = time();
modules::run("Background_worker_module/sending_fcm_broadcast", $this->configuration["fcm_broadcast"]["limit"]);
sleep(2);
}
while ($time_run < $time_stop);
}
}
this is a sample runing code from codeigniter controller.
Long polling requires holding the connection open. That can only happen through an infinite loop of checking to see if the data exists and then adding a sleep.
There is no need to revitalize the session as the response is fired only on a successful data hit.
Note that this method is very CPU and memory intensive as the connection and FPM worker will remain open until a successful data hit. Web sockets is a much better solution regardless of the number of devices and frequency of updates.
You can use notifications. "browser notification" for web clients and FCM and APN notification for mobile clients.
Another option is using SSE (server sent events). It's a connection like socket but over http. Client sends a normal request, and server can just respond to client multiple times and any time if client is available (In the same request that has been sent).

Why is my controller so slow?

I'm on an app that retrieve datas (a 7k lines CSV formated string) from an external server to update my own entity. Each row is an item in a stock.
Today the job is nicely done but it's very very very slow: more than 60s (prod env) to retrieve datas, push it in a 2D array, update the BDD, and finally load a page that display the bdd content.
When only displaying the page it's about 20s (still prod).
This the profiler's timeline result while only displaying records : Symfony's profiler timeline
Anymore, i'm not able to profile the "updateAction" cause i't don't appear in the last ten request list.
2 days ago I was checking each row of the CSV file to add it only if needed, I was soft-deleting items to restore it later when back in the stock etc. but with that speed I tried many things to have normal performances.
At the begening everything was in the controler, I moved the function that add/remove in a dedicated service, then in the repository to finally get it back in my controler. To have decent results I tried to empty the database and then refill it without checking. First, using LOAD DATA LOCAL INFILE but it is not compatible with my table pattern (or I mis understood something) and now I'm simply emptying the table before filling it with the CSV (without any control). The time score I gave earlier was with this last try (which is the best one).
But enought talk
here is my controler:
public function majMatosClanAction()
{
$resMaj = $this->majClanCavernes();
if ($resMaj === NULL)
{
$this->get('session')->getFlashBag()->add('alert-danger', 'Unidentified');
return $this->redirect($this->generateUrl('loki_gbl'));
} else if ($resMaj === FALSE)
{
$this->get('session')->getFlashBag()->add('alert-warning','password update required');
return $this->redirect($this->generateUrl('loki_gbl_ST'));
} else
{
$this->get('session')->getFlashBag()->add('alert-success','success');
return $this->redirect($this->generateUrl('loki_gbl_voirMatosClan'));
}
}
here is the function that my controller call:
public function majClanCavernes()
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$outils = $this->container->get('loki_gbl.outils');
if ($user !== NULL)
{
$pwd = $user->getGob()->getPwd();
$num = $user->getGob()->getNum();
if($outils->checkPwd($num, $pwd) !== TRUE) return FALSE;
$em = $this->getDoctrine()->getManager();
//This is a temporary solution
//////////////////////////////////////////////
$connection = $em->getConnection();
$platform = $connection->getDatabasePlatform();
$connection->executeUpdate($platform->getTruncateTableSQL('MatosClan', true ));
//////////////////////////////////////////////
$repository = $em->getRepository('LokiGblBundle:MatosClan');
$urlMatosClan = "http://ie.gobland.fr/IE_ClanCavernes.php?id=".$num."&passwd=".$pwd;
//encode and format the string via a service
$infosBrutes = $outils->fileGetInfosBrutes($urlMatosClan);
//$csv is a 2D array containing the datas
$csv = $outils->getDatasFromCsv($infosBrutes);
foreach($csv as $item)
{
$newItem = new MatosClan;
$newItem->setNum($item[0]);
$newItem->setType($item[1]);
[...]
$em->persist($newItem);
}
$em->flush();
return TRUE;
}
else{
return NULL;
}
}
What is wrong? 7k lines is not that big!
Could it be a lack of hardware issue?
Check out doctrine's batch processing documentation here.
You can also disable logging:
$em->getConnection()->getConfiguration()->setSQLLogger(null);

Laravel REST API and high CPU load

I am developing a simple RESTful API using Laravel 4.
I have set a Route that calls a function of my Controller that basically does this:
If information is in the database, pack it in a JSON object and return a response
Else try to download it (html/xml parsing), store it and finally pack the JSON response and send it.
I have noticed that the CPU load while doing a total of 1700 requests, only 2 at a time together, raises to 70-90%.
I am a complete php and laravel beginner and I've made the API following this tutorial, maybe I'm probably doing something wrong or it's just a proof of concept lacking of optimzations. How can I improve this code? (starting function is getGames)
Do you think the root of all problems is Laravel or I should obtain the same result even changing framework/using raw PHP?
UPDATE1 I also set a file Cache, but the CPU load is still ~50%.
UPDATE2 I set the query rate at two each 500ms and the CPU load lowered at 12%, so I guess this code is missing queue handling or something like this.
class GameController extends BaseController{
private static $platforms=array(
"Atari 2600",
"Commodore 64",
"Sega Dreamcast",
"Sega Game Gear",
"Nintendo Game Boy",
"Nintendo Game Boy Color",
"Nintendo Game Boy Advance",
"Atari Lynx",
"M.A.M.E.",
"Sega Mega Drive",
"Colecovision",
"Nintendo 64",
"Nintendo DS",
"Nintendo Entertainment System (NES)",
"Neo Geo Pocket",
"Turbografx 16",
"Sony PSP",
"Sony PlayStation",
"Sega Master System",
"Super Nintendo (SNES)",
"Nintendo Virtualboy",
"Wonderswan");
private function getDataTGDB($name,$platform){
$url = 'http://thegamesdb.net/api/GetGame.php?';
if(null==$name || null==$platform) return NULL;
$url.='name='.urlencode($name);
$xml = simplexml_load_file($url);
$data=new Data;
$data->query=$name;
$resultPlatform = (string)$xml->Game->Platform;
$data->platform=$platform;
$data->save();
foreach($xml->Game as $entry){
$games = Game::where('gameid',(string)$entry->id)->get();
if($games->count()==0){
if(strcasecmp($platform , $entry->Platform)==0 ||
(strcasecmp($platform ,"Sega Mega Drive")==0 &&
($entry->Platform=="Sega Genesis" ||
$entry->Platform=="Sega 32X" ||
$entry->Platform=="Sega CD"))){
$game = new Game;
$game->gameid = (string)$entry->id;
$game->title = (string)$entry->GameTitle;
$game->releasedate = (string)$entry->ReleaseDate;
$genres='';
if(NULL!=$entry->Genres->genre)
foreach($entry->Genres->genre as $genre){
$genres.=$genre.',';
}
$game->genres=$genres;
unset($genres);
$game->description = (string)$entry->Overview;
foreach($entry->Images->boxart as $boxart){
if($boxart["side"]=="front"){
$game->bigcoverurl = (string)$boxart;
$game->coverurl = (string) $boxart["thumb"];
} continue;
}
$game->save();
$data->games()->attach($game->id);
}
}
else foreach($games as $game){
$data->games()->attach($game->id);
}
}
unset($xml);
unset($url);
return $this->printJsonArray($data);
}
private function getArcadeHits($name){
$url = "http://www.arcadehits.net/index.php?p=roms&jeu=";
$url .=urlencode($name);
$html = file_get_html($url);
$data = new Data;
$data->query=$name;
$data->platform='M.A.M.E.';
$data->save();
$games = Game::where('title',$name)->get();
if($games->count()==0){
$game=new Game;
$game->gameid = -1;
$title = $html->find('h4',0)->plaintext;
if("Derniers jeux commentés"==$title)
{
unset($game);
return Response::json(array('status'=>'404'),200);
}
else{
$game->title=$title;
$game->description="(No description.)";
$game->releasedate=$html->find('a[href*=yearz]',0)->plaintext;
$game->genres = $html->find('a[href*=genre]',0)->plaintext;
$minithumb = $html->find('img.minithumb',0);
$game->coverurl = $minithumb->src;
$game->bigcoverurl = str_replace("/thumb/","/jpeg/",$minithumb->src);
$game->save();
$data->games()->attach($game->id);
}
}
unset($html);
unset($url);
return $this->printJsonArray($data);
}
private function printJsonArray($data){
$games = $data->games()->get();
$array_games = array();
foreach($games as $game){
$array_games[]=array(
'GameTitle'=>$game->title,
'ReleaseDate'=>$game->releasedate,
'Genres'=>$game->genres,
'Overview'=>$game->description,
'CoverURL'=>$game->coverurl,
'BigCoverURL'=>$game->bigcoverurl
);
}
$result = Response::json(array(
'status'=>'200',
'Game'=>$array_games
),200);
$key = $data->query.$data->platform;
if(!Cache::has($key))
Cache::put($key,$result,1440);
return $result;
}
private static $baseImgUrl = "";
public function getGames($apikey,$title,$platform){
$key = $title.$platform;
if(Cache::has($key)) return Cache::get($key);
if(!in_array($platform,GameController::$platforms)) return Response::json(array("status"=>"403","message"=>"non valid platform"));
$datas = Data::where('query',$title)
->where('platform',$platform)
->get();
//If this query has already been done we return data,otherwise according to $platform
//we call the proper parser.
if($datas->count()==0){
if("M.A.M.E."==$platform){
return $this->getArcadeHits($title);
}
else{
return $this->getDataTGDB($title,$platform);
}
} else{
else return $this->printJsonArray($datas->first());
}
}
}
?>
You're trying to retrieve data from others' servers. That is putting your CPU "on hold" until the data is fully retrieved. That's what is making your code be so "CPU expensive" (couldn't find other stuff that fits here =/ ), cause your script is waiting until the data is received and then release the script (CPU) work.
I strongly suggest that you make asynchronous calls. That would release your CPU to work on the code, while other part of your system is getting the information you need.
I hope that'll be some help! =D
UPDATE
To make examples, I'd have to re-factor your code (and I'm lazy as anything!). But, I can tell you for sure: If you put your request code, those who make calls to others site's XML, onto a queue you would gain a lot of free CPU time. Every request are redirected for a queue. Once they're ready, you treat them as you wish. Laravel has a beautiful way for dealing with queues.
what I would do first is to use a profiler to find out which parts would need an optimization. You can use for example this:
http://xdebug.org/docs/profiler
As well you didn't specify what kind of cpu is it, how many cores are you using? Is this a problem that your cpu is getting used that high?
you should use Laravel's Queue system along with beanstalkd for example and then monitor the queue (worker) with artisan queue:listen

How to make Behat wait for an AJAX call?

Scenario: Modify and save an incomplete change to a Campaign
Given I click on the Campaign section folder
And I press Save in the selected Campaign
Then I should see an error balloon informing the changes cannot be saved
Point is that this 'error balloon' in the final step is a ajax call which will then bring a green or red balloon according to the success of the operation. Currently what I do is after
'And I press Save...' I will do a sleep(3) to give it time for this balloon to show up. This doesn't seem very smart coz you are wasting time and also because some times it can take more or less time for this call to be processed.
How do you guys make your behat tests wait for Ajax do be done instead of just putting the beasts to sleep?
thank you very much for any feedback!
This is done by waiting for your outstanding ajax calls to hit 0. jQuery.active will check just that for you.
In your FeatureContext.php, you can do something like;
public function iShouldSeeAnErrorBalloon($title)
{
$time = 5000; // time should be in milliseconds
$this->getSession()->wait($time, '(0 === jQuery.active)');
// asserts below
}
And do make sure you use a Mink Driver that runs javascript and ajax (the default does not).
I do it by waiting for the DOM to change as a result of the Ajax Call. I made a subclass of DocumentElement, calling it AsyncDocumentElement and overriding the findAll method:
public function findAll($selector, $locator, $waitms=5000)
{
$xpath = $this->getSession()->getSelectorsHandler()->selectorToXpath($selector, $locator);
// add parent xpath before element selector
if (0 === strpos($xpath, '/')) {
$xpath = $this->getXpath().$xpath;
} else {
$xpath = $this->getXpath().'/'.$xpath;
}
$page = $this->getSession()->getPage();
// my code to wait until the xpath expression provides an element
if ($waitms && !($this->getSession()->getDriver() instanceof \Behat\Symfony2Extension\Driver\KernelDriver)) {
$templ = 'document.evaluate("%s", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ).snapshotLength > 0;';
$waitJs = sprintf($templ, $xpath);
$this->getSession()->wait($waitms, $waitJs);
}
return $this->getSession()->getDriver()->find($xpath);
}
Then in \Behat\Mink\Session I changed the constructor to use that class.
public function __construct(DriverInterface $driver, SelectorsHandler $selectorsHandler = null)
{
$driver->setSession($this);
if (null === $selectorsHandler) {
$selectorsHandler = new SelectorsHandler();
}
$this->driver = $driver;
$this->page = new AsyncDocumentElement($this);
$this->selectorsHandler = $selectorsHandler;
}
Once I did this, I found my AngularJS tests were working. So far, I've only tested in Firefox.
In case you are using Prototypejs (e.g Magento), the equivalent code is:
public function iShouldSeeAnErrorBalloon($title)
{
$this->getSession()->wait($duration, '(0 === Ajax.activeRequestCount)');
// asserts below
}

PHP Asynchronous Method Call In The Yii Framework

Question
I want to know if it is possible to asynchronously invoke a Yii controller method from one of its actions while the action renders a view, leaving the method to complete a long running operation. I would love to do something like the code below and I don't need to return a result from my_long_running_func.
public function actionCreate() {
$model = new Vacancies;
if (isset($_POST['Vacancies'])) {
$model->setAttributes($_POST['Vacancies']);
$model->save();
//I wish :)
call_user_func_async('my_long_running_func',$model);
}
$this->render('create', array( 'model' => $model));
}
Problem
I am trying to write a controller action in Yii that posts a vacancy and notifies interested subscribers of the post. The problem is that it takes a long time to execute the notification query.
Now I am searching for a way to asynchronously run the query so the poster sees his response in as little time as possible while the query runs in the background in a way similar to C# delegates or events.
The solutions I googled up performed asynchronous request(s) during the course of the controller action but all I want to do is to run a method of the controller asynchronously and the action had to wait till the request(s) were completed.
Attempted
I have tried the following methods but the query is still slow for my test data of about 1500 users.
Yii ActiveRecord
if ($vacancy->save()) {
if($vacancy->is_active == 1) {
$url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id));
$trainees = YumUser::getUsersByRole('Trainees');
if($trainees!=null) {
foreach($trainees as $trainee){
$message = new YumMessage;
$message->from_user_id = Yii::app()->user->id;
$message->title = 'Vacancy Notification: '.date('M j, Y');
$message->message = "A new vacancy has been posted at <a href='{$url}'>{$url}</a>.";
$message->to_user_id = $trainee->id;
$message->save();
}
}
}
}
Yii Data Access Objects
if ($vacancy->save()) {
if($vacancy->is_active == 1) {
$url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id));
$trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn();
$fid=Yii::app()->user->id;
$msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>.";
$ts = time();
$tt = 'Vacancy Notification: '.date('M j, Y');
if($trainee_ids!=null) {
foreach($trainee_ids as $trainee_id){
Yii::app()->db->createCommand()
->insert('message',array('timestamp'=>$ts,'from_user_id'=>$fid,'to_user_id'=>$tid,'title'=>$tt,'message'=>$msg));
}
}
}
}
Prepared Statements
if ($vacancy->save()) {
if($vacancy->is_active == 1) {
$url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id));
$trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn();
$fu=Yii::app()->user->id;
$msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>.";
$ts = time();
$tt = 'Vacancy Notification: '.date('M j, Y');
$sql="INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) VALUES (:ts,:fu,:tt,:msg,:tu)";
if($trainee_ids!=null) {
foreach($trainee_ids as $trainee_id){
$command=Yii::app()->db->createCommand($sql);
$command->bindParam(":ts",$ts,PDO::PARAM_INT);
$command->bindParam(":fu",$fu,PDO::PARAM_INT);
$command->bindParam(":tt",$tt,PDO::PARAM_STR);
$command->bindParam(":msg",$msg,PDO::PARAM_STR);
$command->bindParam(":tu",$trainee_id,PDO::PARAM_INT);
$command->execute();
}
}
}
}
Research
I have also checked the following websites (I'm only allowed to post two links) but they either require the action to wait for the request to be completed or need curl (which I don't have access to on the deployment server) or need an external library. I was hoping for a native PHP implementation.
PHP Simulated Multi-Threading
Multithreading in php
Asynchronous PHP calls?
Asynchronous processing in PHP
Edit
I was able to decrease response time considerably by rewriting my query in this way (moving the user loop to the database layer):
public function actionCreate() {
$user=YumUser::model()->findByPk(Yii::app()->user->id);
$model = new Vacancies;
$model->corporate_id=$user->professional->institution->corporate->id;
$model->date_posted=date('Y-m-d');
$model->last_modified=date('Y-m-d H:i:s');
if (isset($_POST['Vacancies'])) {
$model->setAttributes($_POST['Vacancies']);
if ($model->save()) {
if($model->is_active == 1) {
$url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id));
$fu=Yii::app()->user->id;
$msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>.";
$ts = time();
$tt = 'New Vacancy: '.$model->title;
$sql='INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) SELECT :ts,:fu,:tt,:msg,t.user_id FROM trainee t';
Yii::app()->db->createCommand($sql)->execute(array(':ts'=>$ts,':fu'=>$fu,':tt'=>$tt,':msg'=>$msg));
}
if (Yii::app()->getRequest()->getIsAjaxRequest())
Yii::app()->end();
else
$this->redirect(array('view', 'id' => $model->id));
}
}
$this->render('create', array( 'model' => $model));
}
Notwithstanding, it would be nice if someone could post a way to call functions asynchronously.
Typically, the solution for these kind of problems would be to integrate a message-bus in your system. You could consider a product like Beanstalkd. This requires installing software on your server.
I suppose this suggestion would be called "using an external library".
If you can access the deployment server and you can add cronjob (or maybe a sysadmin can) you could consider a cronjob that does a php-cli call to a script that reads jobs from a job queue in your database which is filled by the controller method.
If you cannot install software on the server you're running, you could consider using a SAAS solution like Iron.io to host the bus functionality for you. Iron.io is using what is called a push queue. With a push queue the message bus actively performs a request (push) to the registered listeners with the message content. This might work since it doesn't require you to do a curl request.
If none of the above is possible, your hands are tied. Another post which is quite relevant on the subject: Scalable, Delayed PHP Processing
I would try this, though I'm not 100% that Yii will work properly, but its relatively simple and worth a go:
public function actionCreate() {
$model = new Vacancies;
if (isset($_POST['Vacancies'])) {
$model->setAttributes($_POST['Vacancies']);
$model->save();
//I wish :)
}
HttpResponse::setContentType('text/html');
HttpResponse::setData($this->render('create', array( 'model' => $model), true);
HttpResponse::send();
flush(); // writes the response out to the client
if (isset($_POST['Vacancies'])) {
call_user_func_async('my_long_running_func',$model);
}
}
Here's an entirely different type of suggestion. What about registering for the onEndRequest event that is fired by CWebApplication's end() function?
public function end($status=0, $exit=true)
{
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
if($exit)
exit($status);
}
You'd need to register for the event and figure out how to pass your model in somehow, but the code would properly run after all the data has been flushed to the browser ...

Categories