Cron job auto retry if any job get failed - php

I have a cron job that run every 5 Hours. It calls a PHP script , this script will do a call to an external API to sync some data.
The problem is sometimes I'm getting timeout from the API and the job will fail.
Are there any mechanisms to let cron tab do auto retry or auto recover the jobs that are failed?
I have tried to do an extra job and call it in case of any failures manually.
What is the best approach to do so?

Cron does only run once at specific time or every minutes/hours/days etc. It doesn't check the return code. So it's not that easy peasy lemon squeezy at all...
In my opinion you have a few options how to do it:
Create a some kind of scheduler where you can write your CRON job again if it fails, in this case you will need one more CRON job to read you scheduler and run proper command. Scheduler can be database / file / NoSQL based. In scheduler you can have flag like (bool) executed which will let scheduler know which tasks are already done.
Use queues (f.ex. Rabbit) to call it self again when fail.
Use framework, I'm using Symfony to manage own created commands to execute them (check second link below) based on database, using also enqueue/enqueue-bundle package to manage queues in Symfony.
I think if you are not so advanced with PHP I'd recommend to go for self made scheduler based on database (MySQL / PostgreSQL / NoSQL) with Symfony (check second link below). In this case you just have to SELECT all non executed record (commands) from database and just run them.
Lecture
Laravel - Queues, retrying failed jobs
Symfony - calling another commands in command
Queues package for PHP (incl. Symfony)
enqueue/enqueue-bundle

What you can do is something like this:
https://crontab.guru/#1_0-23_13__
1 0-23 13 * *
Start the job past 1 minute at every hour on the 13th of each month.
“At minute 1 past every hour from 0 through 23 on day-of-month 13.”
...then in your code you'd have some logic to detect if the process\script already ran correclty... if yes, skip the run attempt; otherwise let it run and then have a flag set to check against on the subsequent attempt run.
Hope you get the idea.

You can use supervisord :
supervisord website
Or handle API timeout in code.

Related

How to make PHP listening to the job tables

I am working on the system which developed in php without framework.
It has the function is automatically run some jobs via third party api every night. It loops all the jobs in table and call api using curl.
// run cron job to loop this table
ID JOB
1 updateUser
2 getLatestInfo/topicA
……
//code
// if UpdateUser
loop user table and call api to get latest info…
Also other curl task will do here like send email / notification …
It works perfectly before. But recently we have many new users. It will call 50-100 API at the same time.
Each api call will take 10-20 seconds to respond, and we will retry the api if it is timeout.
I checked the log it totally take 3-4 hours for only first job (with many errors)
Although I can make the cron job for queueing the curl, like get first 5 curl and run them each 1 minutes. But if we keep increasing the users or task, and the third party api keep slow. It may take more hours to finish the task.
Is there any solution can make it keep listening to the job table, and run the curl one by one?
I want it can be auto Triggered if new row is added to the table. (Like websocket?) and not single php to run and infinite loop ( to prevent some error occurred and need to rerun the php task manually )
(The API keys is in the php project, so I hope that I can do this in same project)
PHP scripts need to be triggered in order to do something, they can't really "run in background" (I mean, they can, technically, but PHP isn't supposed to be used that way).
Instead, one of three options is usually used to do job management:
call jobs on every call from web, along with the actual code to generate output
use external web cron service to query specific URLs tied to job execution
use local cron job on your system to call the php executable and have it execute jobs periodically
If you want an event based system, PHP is likely the wrong option. Depending on your DB system though you might be able to create a small wrapper code that subscribes to DB changes and is triggered on inserts, that then calls PHP again - but it's definitely a cleaner solution to use a more suitable programming language / environment.

Laravel: Schedule a job or an artisan command?

I have a Product model with id,name,price.
The price value is stored in an external API and i need to fetch it every minute in order to update it in the database.
Looking through the Laravel documentation I found two ways to implement:
Create an artisan command (https://laravel.com/docs/8.x/artisan) and add it to task scheduling (https://laravel.com/docs/8.x/scheduling#scheduling-artisan-commands)
Create a job (https://laravel.com/docs/8.x/queues) and add it to task scheduling (https://laravel.com/docs/8.x/scheduling#scheduling-artisan-commands)
First of all, is there any other approach i should take in consideration?
If not, which one of the above would be the best approach and why is it correct for my use case?
As per my comments on one of your previous questions on this topic, whether you use a queue or not depends on your use case.
An Artisan command is a process that executes once and performs a task or tasks and then exits when that task is complete. It is generally run from the command line rather than through a user action. You can then use the task scheduling of your command's host operating system (e.g. a CRON job) to execute that command periodically. It will faithfully execute it when you schedule it to be done.
A Queued job will execute when the Job turns up next in the queue, in priority order. Let's say you send your API call (from your other post) to the queue to be processed. Another system then decides it needs to send out emails urgently (with a higher priority). Suddenly, your Job, which was next, is now waiting for 2000 other Jobs to finish (which might take a half hour). Then, you're no longer receiving new data until your Job executes.
With a scheduled job, you have a time critical system in place. With queues, you have a "when I get to it" approach.
Hope this makes the difference clearer.
With laravel it is a lot easy to use the built in scheduler. You have to add only one entry to the crontab and that is to run the command php artisan schedule:run EVERY MINUTE on your project. After that you dont have to thing about configuring the crontab on the server, you just add commands to the laravel scheduler and they will work as expected.
You should probably use Cron Job Task Scheduling which would be the first approach you mentioned.
Commonly for this type of use-cases commands are the easiest and cleanest approach.
There are a few things to do in order to make it work as expected:
Create a new command that will need to take care of hitting the endpoint and storing the retrieved data to the database
In Kernel.php file register your command and the frequency of running (each minute)
Run php artisan schedule:run
You can read more about how to create it here:

How can we allow user to set a cron JOB from frontend in Laravel?

I have a setting section in my laravel application. In this section I have a setting for defining a sync time. So the user can set their preferred time for sync data from laravel application to a third party CRM(Salesforce).
For now, I am storing a sync time into Database table. Now I want to run a cron JOB at that time for a particular user.
I have already created a cron job script and the cron job script is working fine. I am able to test cron job manually I just need to automate it with user setting(Preferred time).
My Cron JOB URL https://my-domain.org/cronjob?user_id=101
I tried to use laravel scheduling but this not fulfillig my requirement.
Is there any other better solution available at laravel?
There is some information missing but I try to assume a few things to hopefully propose a working solution using the Laravel scheduler.
Assuming you store the time at which the CRON runs in the database as 01:00 for 1 o'clock in the night you could do the following:
protected function schedule(Schedule $schedule)
{
User::query()->whereNotNull('cron_time')->each(function (User $user) use ($schedule) {
$schedule->command('app:import', ['user_id' => $user->id])
->withoutOverlapping() // prevents running the same command (+ parameters) twice at the same time, should only be a problem if the command can run > 24 hours, but still a safe thing to do
->dailyAt($user->cron_time);
});
}
I dreamed up that you stored the information on the User model
That you used a flag in the database called cron_time which contains the 01:00 or is null if the cron is disabled
The cron needs to run daily at that user supplied time
The import command is called app:import and is a console command that accept a user_id argument with the user id to run the import for
This might help you adapt the above to how it's structured in you own app.
If you want to use queued jobs to execute the imports you could also create the app:import CLI command to just dispatch that job instead of actually running the import. Depending on the amount of imports it might be a good idea to prevent long running scheduler commands and have the ability to retry and have a timeout.
Another option could also be to have a daily scheduled command that dispatches jobs to the queue scheduled to run at a specific time (which does not work with the SES driver). For more about that see: https://laravel.com/docs/5.8/queues#delayed-dispatching.

what i need to do to convert my current system which use cron to use RabbitMQ?

my current script using CRON to handle the checking to DB and do the requests
so every mint the CRON will be called and check which action should be done as per the schedule table entry so if now the time to send email/publish post etc...
and this entry getting more and more with time and with many users now my CRON take around 20 to 50 mint to be done
so if I have to send email on 10 AM it sends between 10:20 AM to 10:50 AM
after searching I found RabbitMQ and Redis and other systems and I choose RabbitMQ
so what is next, what I need to do next, as for my experience I never work with a system like Redis etc.. so its something totally new, so if someone has and resources to check read or watch and help me with migrating the whole system from CRON to RabbitMQ.
small note, the current script is built on top of custom PHP framework only for this script and don't have API.
Write a php shell script for create linux pid in a infinite loop and call a method by cron.
Every job push to rabbitMq basic_publish with data set.
this method create a basic_consume with rabbitMq for performing queue with queue data set.

Most efficient way of implementing an email queue in Laravel

I want to implement a queue for sending out emails in Laravel. I have the queue working fine, but am worried about efficiency. These are my settings:
I have created the jobs table and set up the .env file, to use the queues with my local database.
I have set up this crontab on the server:
* * * * * php /var/www/imagine.dev/artisan schedule:run >> /dev/null 2>&1
And have set up a schedule in app\Conosle\Kernel.php, so I dont have to manually enter the 'queue:listen' every time through console.
$schedule->command('queue:listen');
Now to my question. I would like to know if this is efficient? I am worried about having the queue:listen running all the time in the background consuming cpu and memory.
I have been trying to only run the queue:listen once every 5 minutes, and then put it to sleep with
$schedule->command('queue:listen --sleep 300');
but again, am not sure if this is the best approach.
Another thing I tried is using 'queue:work', but this only processes one queue at a time.
Ideally, I would like a way, to process all the queues every 5 minutes, avoiding a constant use of memory and cpu.
What is the best approach?
Not sure which version of Laravel you're using, but I suspect it's 5.2 or earlier.
You do not need to run this every minute, it continues to run until it's manually stopped.
From Laravel 5.2 documentation:
Note that once this task has started, it will continue to run until it is manually stopped. You may use a process monitor such as Supervisor to ensure that the queue listener does not stop running.
So maybe you want to look into Supervisor
Also, if this is helpful at all, you can chain onto $schedule, ->everyFiveMinutes(). There are several other methods available as well. Laravel Scheduling

Categories