I tried delaying each notification (or job) using Laravel queues. Still, instead of the notifications being delayed one after the other, the delay is applied to all the notifications at once. However, the delay is only applied to the first notification, and the rest are sent out simultaneously as the first message. Please, can anyone in this community help me?
This is part of the method that gets only the applicants whose admissions are accepted or rejected and then notify them individually. Unfortunately, the mail server we are using only allows sending a maximum of 30 emails per 1 minute; therefore, I need to use queuing to limit sending only (not more than) 1 email per 2 seconds.
foreach ($applications as $application)
{
$application_id = $application->id;
$admission = Admission::where('application_id', $application_id)->first();
if (empty($admission->admission_no) & $application->status === 5) {
$this->generateAdmissionNumber($application->id, $application->course_id);
$application->update(['done' => 1]);
$admissions = Admission::where('course_id', '=', $id)->get();
foreach ($admissions as $admission) {
$admission->update(['done' => 1]);
}
}
$application->done = 1;
$application->update();
$user = $application->users;
if ($user->notify_if_decision_is_made === 1)
{
$user->notify((new DecisionNotification($user))
->delay(Carbon::now()->addSeconds(2)));
}
}
I followed the guides but still have no solution. I looked at many solutions here but did not see any issues like this one.
I set QUEUE_CONNECTION=database in my .env file, added shouldQueue in the DecisionNotification file, and run php artisan queue:work from the terminal to execute the jobs. I appreciate any help.
The ->delay() only gives a delay to the running job. As your loop is running almost instantly, it won't delay put a delay of 2 seconds between each job.
One way to achieve what you want is:
foreach ($applications as $i => $application) {
// your code chunk
$user->notify((new DecisionNotification($user))->delay($i * 2)));
}
This results in delays as:
Job 1: 0s
Job 2: 2s
Job 3: 4s
Job 4: 6s
Job 5: 8s
...
By the way, ->delay() also accepts integer as second.
You can use sleep() function, which holds the loop for defined period, for example if you want to hold loop for 2 seconds, use sleep(2).
Your code should be:
foreach ($applications as $application) {
$application_id = $application->id;
$admission = Admission::where('application_id', $application_id)->first();
if (empty($admission->admission_no) & $application->status === 5) {
$this->generateAdmissionNumber($application->id, $application->course_id);
$application->update(['done' => 1]);
$admissions = Admission::where('course_id', '=', $id)->get();
foreach ($admissions as $admission) {
$admission->update(['done' => 1]);
}
}
$application->done = 1;
$application->update();
$user = $application->users;
if ($user->notify_if_decision_is_made === 1) {
$user->notify((new DecisionNotification($user))->delay(Carbon::now()->addSeconds(2)));
}
sleep(2);
}
Hope this will be useful.
Read More about sleep()
Related
I have a costly server ajax request which has one input (full: boolean). If full is false, the server can return either a partial or a full response (response.isFull == true); but if full is true, the server will return a full response. Normally the partial response is good enough, but there are certain conditions that will require a full response. I need to avoid requesting a full response explicitly as much as possible, so I thought I'd start with a BehaviorSubject which I can eventually feed with true and combine it with distinctUntilChanged if I ever need to get the full response. This will give me an observable with false initially and that can give me true if I feed that into it:
const fullSubject = new BehaviorSubject<boolean>(false);
Then I've got a function that takes a boolean parameter and returns an observable with the server request (retried, transformed, etc.). As said, the answer can be partial or full, but it can be full even if the input parameter was false at the server's discretion. For example:
interface IdentityData {
...
isFull: boolean;
}
private getSimpleIdentity(full: boolean): Observable<IdentityData> {
return Axios.get(`/api/identity${full?"?full=true":""}`)
.pipe( ... retry logic ...,
... transformation logic ...,
shareReplay(1) );
}
I need to know how can I combine these so that the following is true:
The server needs to be queried at most twice.
If the first answer is a full answer, no further queries must be performed to the server.
If the first answer is a partial answer, and true is fed into fullSubject, a full answer must be requested.
The expected output from all this is an observable that emits either one full response, or a partial response and, when asked, a full response.
Environment: Vue 2.6.11, RxJS 6.5.5, Axios 0.19.2, TypeScript 3.7.5.
Thanks in advance
Here would be my approach:
const fullSubject = new BehaviorSubject(false);
const src$ = fullSubject.pipe(
switchMap(isFull => Axios.get('...')),
take(2), // Server required at most twice
takeWhile(response => !response.isFull, true), // When `isFull`, it will complete & unsubscribe -> no more requests to the server
shareReplay(1),
);
src$.subscribe(() => { /* ... */ });
function getFullAnswer () {
fullSubject.next(true);
}
takeWhile takes a second argument, inclusive. When set to true, when the predicate function evaluates to false(e.g isFull is true) it will send that value as well. –
if I've got it correctly
private getSimpleIdentity(): Observable<IdentityData> {
return fullSubject.pipe(
switchMap(full => Axios.get(`/api/identity${full ? "?full=true" : ""}`)),
shareReplay(1),
);
}
Uses the retryWhen() operator
const source = of("").pipe(map(() => Math.floor(Math.random() * 10 + 1)));
const example = source
.pipe(
tap((val) => console.log("tap", val)),
map((val) => {
//error will be picked up by retryWhen
if (val !== 5) throw val;
return val;
}),
retryWhen((errors) =>
errors.pipe(
tap(() => console.log("--Wait 1 seconds then repeat")),
delay(1000)
)
)
)
.subscribe((val) => console.log("subscription", val));
/*
output:
tap 3
--Wait 1 seconds then repeat
tap 8
--Wait 1 seconds then repeat
tap 1
--Wait 1 seconds then repeat
tap 4
--Wait 1 seconds then repeat
tap 7
--Wait 1 seconds then repeat
tap 5
subscription 5
*/
When is laravel executing the yearly() function of it's task scheduling? Is it from the first time it was used or is it each year on 01.01.YYYY?
I checked up the laravel docs and several pages, but I wasn't able to find out the answer to this question.
$schedule->call(function () {
DB::table("invoice_settings")
->where("key", "invoice_number")
->update(["value" => 860001]);
})->yearly();
I expect that it's running on the 01.01.YYYY.
Laravel's yearly coresponds to crontab's yearly event. Described here:
https://crontab.guru/every-year
This is the cron produced:
0 0 1 1 *
Which runs:
At 00:00 on day-of-month 1 in January.
The yearly function is defined in:
vendor/laravel/framework/src/Illuminate/Console/Scheduling/ManagesFrequencies.php
Here you can find the daily, monthly and yearly, and other frequency functions.
public function daily()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, 0);
}
public function monthly()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, 0)
->spliceIntoPosition(3, 1);
}
public function yearly()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, 0)
->spliceIntoPosition(3, 1)
->spliceIntoPosition(4, 1);
}
As in the official Laravel documentation is written that the daily function runs daily at midnight, since the yearly function is defined in the same way it will run at the 1/1/YYYY at midnight.
I am using Observable.interval to schedule code execuiton at specified times:
let obs = Observable.interval(50).subscribe(()=>{
console.log(this.currentFrame+" "+new Date().getTime());
this.currentFrame++
});
This is the output. As you can see, after 6 iterations I already have a 10ms drift. How can I use Observable.interval, but also specify that it needs to recalculate next iteration based on the current drift?
0 1513972329849
1 1513972329901
2 1513972329952
3 1513972330004
4 1513972330057
5 1513972330110
Until #cartant's fix gets repulled, you could use expand and create the behavior yourself. Assuming delay will always drift forward, try the following:
function correcting_interval(interval) {
const start_time = new Date().getTime();
return Observable.of(-1)
.expand(v => Observable.of(v + 1).delay(interval - (new Date().getTime() - start_time) % interval))
.skip(1);
}
I have the job table contains post_at,day_open,block properties
The post_at property is the day when user post the job (Ex:2015-11-24)
The day_open property is the day that job is open (Ex:10)
The block property can contain 2 values 0 or 1. If post_at + day_open > right now the block value will change from 0 to 1
So how can I do that using cronjob?
First check wheather the post_at,day_open are edit in current date
$date = date('Y-m-d');
$job= DB::table('job') ->select('post_at','day_open','block')->where('updated_at',$date) -first();
$id=$job->id;
if(isset($job) && !empty($job))
{
$finding=CronJobCotroller::pushNotify($id);
}
if it's true it go to function pushNotify under the CronJobCotroller,here we write the coding for changing block field value 0 to 1.
public function pushNotify($id) {
$job= DB::table('job') ->select('block')->where('id',$id) ->get();
if(isset($job) && !empty($job))
{
foreach ($job as $cb)
{
$job= DB::table('job')->update('block',1)->where('block',0);
}
}
}
I've got a strange issue.
I use a custom command to share, via FB api, some url.
Now this command is scheduled to a fixed cron configuration but I would like this to be randomic, maybe between time ranges.
I tried using a random sleep function inside command, but I got strange behavior.
Did anyone already have to face this problem?
One solution is to put the code you want to run into a Queued Job, and then delay that job a random amount. For example, this code will run at a random time every hour (the same can easily be extrapolated to random time every day or on some other interval):
$schedule
->call(function () {
$job = new SomeQueuedJob();
// Do it at some random time in the next hour.
$job->delay(rand(0, 60 * 59));
dispatch($job);
})
->hourly();
You can generate random time at beginning.
protected function schedule(Schedule $schedule)
{
$expression = \Cache::get('schedule:expressions:inspire');
if (!$expression) {
$randomMinute = random_int(0, 59);
// hour between 8:00 and 19:00
$hourRange = range(8, 19);
shuffle($hourRange);
// execute twice a day
$randomHours = array_slice($hourRange, 0, 2);
$expression = $randomMinute . ' ' . implode($randomHours, ',') . ' * * * *';
\Cache::put('schedule:expressions:inspire', $expression, Carbon::now()->endOfDay());
}
$schedule->command('inspire')->cron($expression);
}