Skip to content

Commit

Permalink
More bugs fixed, reverting to own db-table
Browse files Browse the repository at this point in the history
  • Loading branch information
adrolli committed Aug 30, 2023
1 parent ef51d19 commit a69f35a
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 34 deletions.
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ php artisan migrate

You can publish the config file with:

```bash
php artisan vendor:publish --tag="filament-job-manager-config"
```
php artisan vendor:publish --tag="filament-jobs-monitor-config"
```



This is the content of the published config file:

```
```php
<?php

return [
Expand Down Expand Up @@ -73,20 +71,58 @@ return [

```


You should publish and run the migrations with:

```
php artisan vendor:publish --tag="filament-jobs-monitor-migrations"
```bash
php artisan vendor:publish --tag="filament-job-manager-migrations"
php artisan migrate
```

## Usage

Just run a Background Job and go to the route `/admin/queue-monitors` to see the jobs.
Just run a Background Job and go to the route `/admin/jobs` to see the jobs.

## Example Job

You do not need to change anything in your Jobs to work with Filament Job Monitor. But especially for long running jobs you may find this example interesting:

```php
<?php

namespace App\Jobs;

use Adrolli\FilamentJobManager\Traits\JobProgress;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use
class JobMonitorDemo implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, QueueProgress;

public function __construct()
{
//
}

public function handle()
{
$count = 0;
$steps = 10;
$final = 100;

while ($count < $final) {
$this->setProgress($count);
$count = $count + $steps;
sleep(10);
}
}
}
```

## Authorization - outdated!
## Authorization - outdated!

Outdated. Use Shield instead.

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "adrolli/filament-job-manager",
"description": "A Filament manager for job queues including failed jobs and batches.",
"description": "A Filament panel for managing job queues including failed jobs and batches.",
"keywords": [
"laravel",
"filament",
Expand Down Expand Up @@ -42,7 +42,7 @@
"Adrolli\\FilamentJobManager\\JobMonitorProvider"
],
"aliases": {
"JobMonitor": "Adrolli\\FilamentJobManager\\QueueMonitorProvider\\Facade"
"JobMonitor": "Adrolli\\FilamentJobManager\\JobMonitorProvider\\Facade"
}
}
},
Expand Down
36 changes: 36 additions & 0 deletions database/migrations/create_filament-job-manager_table.php.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('job_manager', function (Blueprint $table) {
$table->id();
$table->string('job_id')->index();
$table->string('name')->nullable();
$table->string('queue')->nullable();
$table->timestamp('started_at')->nullable()->index();
$table->timestamp('finished_at')->nullable();
$table->boolean('failed')->default(false)->index();
$table->integer('attempt')->default(0);
$table->integer('progress')->nullable();
$table->text('exception_message')->nullable();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('job_manager');
}
};
107 changes: 107 additions & 0 deletions src/JobManagerProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace Adrolli\FilamentJobManager;

use Adrolli\FilamentJobManager\Models\JobManager;
use Illuminate\Contracts\Queue\Job as JobContract;
use Illuminate\Queue\Events\JobExceptionOccurred;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;

class JobManagerProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
Queue::before(static function (JobProcessing $event) {
self::jobStarted($event->job);
});

Queue::after(static function (JobProcessed $event) {
self::jobFinished($event->job);
});

Queue::failing(static function (JobFailed $event) {
self::jobFinished($event->job, true, $event->exception);
});

Queue::exceptionOccurred(static function (JobExceptionOccurred $event) {
self::jobFinished($event->job, true, $event->exception);
});
}

/**
* Get Job ID.
*/
public static function getJobId(JobContract $job): string|int
{
return JobManager::getJobId($job);
}

/**
* Start Queue Monitoring for Job.
*/
protected static function jobStarted(JobContract $job): void
{
$now = now();
$jobId = self::getJobId($job);

$monitor = JobManager::query()->create([
'job_id' => $jobId,
'name' => $job->resolveName(),
'queue' => $job->getQueue(),
'started_at' => $now,
'attempt' => $job->attempts(),
'progress' => 0,
]);

JobManager::query()
->where('id', '!=', $monitor->id)
->where('job_id', $jobId)
->where('failed', false)
->whereNull('finished_at')
->each(function (JobManager $monitor) {
$monitor->finished_at = now();
$monitor->failed = true;
$monitor->save();
});
}

/**
* Finish Queue Monitoring for Job.
*/
protected static function jobFinished(JobContract $job, bool $failed = false, ?\Throwable $exception = null): void
{
$monitor = JobManager::query()
->where('job_id', self::getJobId($job))
->where('attempt', $job->attempts())
->orderByDesc('started_at')
->first();

if (null === $monitor) {
return;
}

$attributes = [
'progress' => 100,
'finished_at' => now(),
'failed' => $failed,
];

if (null !== $exception) {
$attributes += [
'exception_message' => mb_strcut($exception->getMessage(), 0, 65535),
];
}

$monitor->update($attributes);
}
}
9 changes: 0 additions & 9 deletions src/Models/Job.php

This file was deleted.

112 changes: 112 additions & 0 deletions src/Models/JobManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace Adrolli\FilamentJobManager\Models;

use Illuminate\Contracts\Queue\Job as JobContract;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Support\Facades\Hash;

class JobManager extends Model
{
use HasFactory, Prunable;

protected $fillable = [
'job_id',
'name',
'queue',
'started_at',
'finished_at',
'failed',
'attempt',
'progress',
'exception_message',
];

protected $casts = [
'failed' => 'bool',
'started_at' => 'datetime',
'finished_at' => 'datetime',
];

/*
*--------------------------------------------------------------------------
* Mutators
*--------------------------------------------------------------------------
*/
public function status(): Attribute
{
return Attribute::make(
get: function () {
if ($this->isFinished()) {
return $this->failed ? 'failed' : 'succeeded';
}

return 'running';
},
);
}

/*
*--------------------------------------------------------------------------
* Methods
*--------------------------------------------------------------------------
*/

public static function getJobId(JobContract $job): string|int
{
if ($jobId = $job->getJobId()) {
return $jobId;
}

return Hash::make($job->getRawBody());
}

/**
* check if the job is finished.
*/
public function isFinished(): bool
{
if ($this->hasFailed()) {
return true;
}

return null !== $this->finished_at;
}

/**
* Check if the job has failed.
*/
public function hasFailed(): bool
{
return $this->failed;
}

/**
* check if the job has succeeded.
*/
public function hasSucceeded(): bool
{
if (! $this->isFinished()) {
return false;
}

return ! $this->hasFailed();
}

/**
* Get the prunable model query.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function prunable()
{
if (config('filament-job-manager.pruning.activate')) {
return static::where('created_at', '<=', now()->subDays(config('filament-job-manager.pruning.retention_days')));
}

return false;
}
}
Loading

0 comments on commit a69f35a

Please sign in to comment.