Laravel 5 - Queued Commands throwing spl_autoload_call() error - laravel

UPDATE - This has been narrowed down to beanstalkd, sync works
I am receiving the following error when attempting to run queued commands in my production environment:
exception 'ErrorException' with message 'unserialize(): Function spl_autoload_call() hasn't defined the class it was called for'
in /home/forge/default/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:74
I have tried both the beanstalkd and database drivers, no change. For simplicity, I am using the following command:
<?php namespace App\Commands;
use App\Commands\Command;
use App\User;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldBeQueued;
class TestQueueCommand extends Command implements SelfHandling, ShouldBeQueued {
use InteractsWithQueue, SerializesModels;
/**
* #var User
*/
private $user;
/**
* Create a new command instance.
*
* #param User $user
*/
public function __construct(User $user)
{
//
$this->user = $user;
}
/**
* Execute the command.
*
* #return void
*/
public function handle()
{
\Log::info("You gave me " . $this->user->fullName());
}
}
Dispatch code:
get('queue-test', function()
{
Bus::dispatch(new TestQueueCommand(User::first()));
});
This works in my Homestead environment, fails in production (Digital Ocean, Forge). I have several beanstalkd workers and I have tried restarting them. I have also run php artisan queue:flush.
Here is the code where the error is occurring (from source):
/**
* Handle the queued job.
*
* #param \Illuminate\Contracts\Queue\Job $job
* #param array $data
* #return void
*/
public function call(Job $job, array $data)
{
$command = $this->setJobInstanceIfNecessary(
$job, unserialize($data['command'])
);
$this->dispatcher->dispatchNow($command, function($handler) use ($job)
{
$this->setJobInstanceIfNecessary($job, $handler);
});
if ( ! $job->isDeletedOrReleased())
{
$job->delete();
}
}

In the past, I also ran into a similar issue while unserializing. The problem was the default Beanstalk job size (65,535 bytes), which might not be big enough if the class being serialized contains lots of properties that need to be kept (increasing the size of the serialized string and using more than 65K for storage).
In order to solve this, try setting the size to 131,072 or even 262,144 bytes using the -z option, on the configuration file (/etc/default/beanstalkd):
BEANSTALKD_EXTRA="-z 262144"
After that, you should restart the service.
Also note that the configuration file path might be other, depending on the distribution you're using.
And since you're using Digital Ocean, you might find their documentation useful.

Related

Laravel queue config stop working after some time

Currently I have a cron running that calls a command and adds this job to my queue.
This works normally up to a point, then the job runs but doesn't add anything to the queue, so I have to log into the server and give an artisan config:clear to get everything running again.
Does anyone have an idea what it could be? I'm using forge to perform server deployments and management, my queue is using redis driver, laravel 9, horizon and octane, php 8.1, mysql
Just to be clear: my problem is not happening while running the jobs, when the job arrives in the queue the horizon is processing perfect. the biggest problem is when adding item to the queue, that when cron goes to run, all of a sudden it doesn't find the settings of which queue it should use anymore and doesn't add anything to the queue :(
Example of command running using crontab:
namespace App\Console\Commands;
use App\Jobs\MyJob;
use Illuminate\Console\Command;
class MyCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'cron:myCommand';
/**
* The console command description.
*
* #var string
*/
protected $description = '';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
MyJob::dispatch()->onQueue('my_queue');
return Command::SUCCESS;
}
}

Laravel: Set/mock application time globally for development

On my application, users have lists of emails they can send to. Their accounts have settings for the time of day they want emails automatically sent and the timezone they're in.
I would like to test certain scenarios on when my queues are triggered since each user's send time may differ drastically.
I'd like to globally set a fake time with carbon.
In public/index.php, I tried to set:
$time = Carbon::create('2020-09-16 00:00:00');
Carbon::setTestNow($time);
but pieces of my application are not affected.
Is there a global way to set a fake time?
Original question below:
On my application, users have lists of emails they can send to. Their accounts have settings for the time of day they want emails automatically sent and the timezone they're in.
I have a command that will trigger an event that sends email.
Inside the listener, the handle method looks like:
public function handle(ReviewRequested $event)
{
$time = Carbon::create(2020, 9, 15, 0);
Carbon::setTestNow($time);
$reviewRequest = $event->reviewRequest;
Log::info('email sending at ' . $reviewRequest->sent_at . ' and current time is ' . Carbon::now());
Mail::to($reviewRequest->customer->email)
->later($reviewRequest->sent_at, new ReviewRequestMailer($reviewRequest));
}
Note that I'm faking the time with Carbon and setting it to midnight. In this example, The emails should be sent at 9am. The logged info is as follows:
local.INFO: email sending at 2020-09-15 09:00:00 and current time is 2020-09-15 00:00:00
So the current time is 12AM and I'm queuing these up to get sent at 9AM.
As soon as I run php artisan queue:work, the pending jobs (emails) are immediately run and sent. Why is this happening? They should remain queued until 9AM.
Perhaps queuing is using system time and doesn't care about what I set in Carbon? How can I resolve this?
Edit: I forgot to mention that I'm using Redis
Check what queue driver you're using in your .env file. QUEUE_CONNECTION=sync does not allow for any delaying (sync stands for synchronous).
The quickest way to fix this would be doing the following:
change the driver to database QUEUE_CONNECTION=database
clear the cached configuration php artisan config:clear
publish the migration for the jobs table php artisan queue:table
migrate this new table php artisan migrate
After following these steps, you can now have delayed execution in your queues when you run it with php artisan queue:work
I think you should use Laravel Cron Job for this purpose. you should make a file in App/Console/Commands/YourCronJobFile.php
<?php
namespace App\Console\Commands;
use App\TestingCron;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class TestingCronJob extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'send:Mail';
/**
* The console command description.
*
* #var string
*/
protected $description = 'This command is use for test cron jobs.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
DB::table('testing_cron')->insert(['created_at' => now(),'updated_at' => now()]);
}
}
Then go to directory App/Console/Kernel.php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
Commands\TestingCronJob::class
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('send:Mail')->dailyAt('09:00');
}
/**
* RegisterController the commands for the application.
*
* #return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
https://laravel.com/docs/7.x/scheduling

Laravel queue jobs with attempts, correct way to trigger new attempt?

I'm trying to figure out what the correct way to do this is.
public $tries = 10;
/**
* Execute the job.
*
* #return void
*/
public function handle(){
$result_of_some_logic = false;
if($result_of_some_logic){
// for the purpose of this example, we're all done here.
} else{
// we need to retry 10 minutes from now. how to trigger this attempt, and this attempt only!, to fail?
}
}
I read the laravel documentation but it just isn't clear to me what the correct way to do this is. I noticed that if I create a php error (for instance throw new whateverisnotdeclaredinnamespace()) the job attempt will fail, the worker will retry until the amount of $tries have exceeded. This is pretty much the behavior I want but I obviously want a clean code solution.
So to summarise: in Laravel 5.8 what is the correct way to "mark" the attempt to have failed in the handle() function?
You could try releasing the job back to the queue with a delay:
$this->release($delayInSeconds);
Why not to use failed function to handle Your error?
https://laravel.com/docs/5.8/queues#dealing-with-failed-jobs
<?php
namespace App\Jobs;
use Exception;
use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ProcessPodcast implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
/**
* Create a new job instance.
*
* #param Podcast $podcast
* #return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
/**
* Execute the job.
*
* #param AudioProcessor $processor
* #return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
/**
* The job failed to process.
*
* #param Exception $exception
* #return void
*/
public function failed(Exception $exception)
{
// Send user notification of failure, etc...
}
}

Processing Laravel job with many HTTP requests inside, it fails trying by timeout

I have an API REST built using Laravel 5.8 framework.
I have to get the data for my app from a public REST API. For this, i have to do a lot of requests. I made a seeder what do this, and it takes 2 minutes for the entire migration data process approximately (take from the public Api and insert in my application database).
I cant run the seeder by a cronjob because it does not work (it looks like need an user to execute the command to work). So, i created a job class what i call from my kernel file, the queue configuration was seted as database (QUEUE_CONNECTION=database), like a scheduled task (to execute with Supervisor: https://laravel.com/docs/5.8/queues#supervisor-configuration).
Despite this, the job fails, because it takes a long time to execute, so my data is not updated.
What i can do process my jobs by batch successfully?
This is my kernel.php
<?php
namespace App\Console;
use App\Jobs\Seed\ApiPlayerStatisticJob;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(new ApiPlayerStatisticJob)->everyFiveMinutes();
}
/**
* Register the commands for the application.
*
* #return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
My ApiPlayerStatisticJob.php
<?php
namespace App\Jobs\Seed;
use App\ApiExternal;
use App\ApiPlayer;
use App\ApiTeam;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class ApiPlayerStatisticJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$api_teams = ApiTeam::get();
foreach ($api_teams as $api_team) {
// echo 'TEAM: '.$api_team->id;
$api_external_players = ApiExternal::getTeamPlayerStatistics($api_team->id);
foreach ($api_external_players as $api_external_player) {
// echo PHP_EOL.'PLAYER: '.$api_external_player['id'];
$api_player = ApiPlayer::find($api_external_player['id']);
if ($api_player != null) {
$api_player->update($api_external_player);
// echo PHP_EOL.'> PLAYER UPDATED ';
} else {
// echo PHP_EOL.'X PLAYER DIDNT UPDATED ';
}
}
// echo PHP_EOL;
// echo PHP_EOL;
}
}
}
And my Seeder what i used for construct my job (by replication, excluding the print expressions):
<?php
use Illuminate\Database\Seeder;
use App\ApiExternal;
use App\ApiPlayer;
use App\ApiTeam;
class ApiPlayerStatisticsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$api_teams = ApiTeam::get();
foreach ($api_teams as $api_team) {
echo 'TEAM: '.$api_team->id;
$api_external_players = ApiExternal::getTeamPlayerStatistics($api_team->id);
foreach ($api_external_players as $api_external_player) {
echo PHP_EOL.'PLAYER: '.$api_external_player['id'];
$api_player = ApiPlayer::find($api_external_player['id']);
if ($api_player != null) {
$api_player->update($api_external_player);
echo PHP_EOL.'> PLAYER UPDATED ';
} else {
echo PHP_EOL.'X PLAYER DIDNT UPDATED ';
}
}
echo PHP_EOL;
echo PHP_EOL;
}
}
}
Finally, the static function ApiExternal::getTeamPlayerStatistics(int id) do all requests necessaries to get th data (like 30 requests), so what i can do to process this job (or the seeder directly) in background without it fails?
Did you configure the config/queue.php file correctly?
Quote from the documentation in the Job expirations paragraph:
In your config/queue.php configuration file, each queue connection defines a retry_after option. This option specifies how many seconds the queue connection should wait before retrying a job that is being processed.

Custom laravel migration command "[Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable"

I'm trying to create a custom laravel (5.2) migration command that basically works the same as migrate:status except it just lists the pending migrations instead of all the migrations.
To do this i've very simply copied the migrate:status into another class within my app/console directory and adjusted the code to suit my needs. However whenever I try to run it I get an error:
[Illuminate\Contracts\Container\BindingResolutionException]
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\PendingMigrations, Illuminate\Database\Migrations\Migrator].
The contents of the class itself and the fire() method doesn't seem to matter as it doesn't get that far, it fails within the __construct() method.
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Migrations\Migrator;
class PendingMigrations extends Command
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'migrate:pending';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Shows a list of pending migrations';
/**
* The migrator instance.
*
* #var \Illuminate\Database\Migrations\Migrator
*/
protected $migrator;
/**
* Create a new migration rollback command instance.
*
* #param \Illuminate\Database\Migrations\Migrator $migrator
* #return \Illuminate\Database\Console\Migrations\StatusCommand
*/
public function __construct(Migrator $migrator)
{
parent::__construct();
$this->migrator = $migrator;
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
}
}
The reason for it is likely to be something to do with the IoC container and the order with which things are loaded, but I don't know enough about the inner workings of Laravel to figure out any more than that.
It surely must be possible?
I am currently stuck on 5.2, so i'm not sure if this problem exists in more recent versions.
The only thing i've attempted so far is added the migration service provider to the top of the list in config/app.php however it didn't seem to have an affect and it was just a random guess anyway.
providers' => [
Illuminate\Database\MigrationServiceProvider::class,`
]
I got around this using:
$this->migrator = app('migrator');
but it is not necessarily the best way to do this
The Migrator instance is not bound to the class name in the IoC container, it is bound to the migrator alias.
From Illuminate\Database\MigrationServiceProvider:
/**
* Register the migrator service.
*
* #return void
*/
protected function registerMigrator()
{
// The migrator is responsible for actually running and rollback the migration
// files in the application. We'll pass in our database connection resolver
// so the migrator can resolve any of these connections when it needs to.
$this->app->singleton('migrator', function ($app) {
$repository = $app['migration.repository'];
return new Migrator($repository, $app['db'], $app['files']);
});
}
Since the class name is not bound in the IoC container, when Laravel resolves your command and attempts to resolve the Migrator dependency, it attempts to build a new one from scratch and fails because the Illuminate\Database\Migrations\MigrationRepositoryInterface is also not bound in the IoC container (hence the error you're receiving).
Since Laravel can't figure this out itself, you need to either register the binding for the Migrator class name, or you need to register the binding for your command. Laravel itself registers all the bindings for the commands in the Illuminate\Foundation\Providers\ArtisanServiceProvider. An example of the command.migrate binding:
/**
* Register the command.
*
* #return void
*/
protected function registerMigrateCommand()
{
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator']);
});
}
So, in your AppServiceProvider, or another service provider you setup, you can add one of the following:
Register the command in the IoC:
$this->app->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
Or, register the Migrator class name in the IoC:
$this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ($app) {
return $app['migrator'];
});
As I don't want to register the migrator everywhere in the app, but I still want to extend the MigrateCommand itself, I came up with this approach to maintain my app as it is:
public function __construct()
{
app()->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
parent::__construct(app('migrator'));
}

Resources