web-dev-qa-db-fra.com

Comment annuler un travail en file d'attente à Laravel ou Redis

Comment puis-je parcourir tous les travaux en attente au sein de ma file d'attente Redis afin d'annuler le Mailable comportant une certaine paire emailAddress-sendTime?

J'utilise Laravel 5.5 et un Mailable que j'utilise avec succès comme suit:

$sendTime = Carbon::now()->addHours(3);
Mail::to($emailAddress)
      ->bcc([config('mail.supportTeam.address'), config('mail.main.address')])
                    ->later($sendTime, new MyCustomMailable($subject, $dataForMailView));

Lorsque ce code est exécuté, un travail est ajouté à ma file d'attente Redis.

J'ai déjà lu les Laravel docs mais je reste confus.

Comment puis-je annuler un Mailable (l'empêcher de l'envoyer)?

J'adorerais coder une page Web dans mon application Laravel, ce qui me facilite la tâche.

Ou peut-être existe-t-il des outils qui facilitent déjà la tâche (peut-être FastoRedis?)? Dans ce cas, des instructions sur la manière d'atteindre ce but seraient également très utiles. Merci!

Mettre à jour:

J'ai essayé de parcourir la file d'attente Redis en utilisant FastoRedis, mais je ne vois pas comment supprimer un Mailable, tel que la flèche rouge pointe ici: enter image description here

METTRE À JOUR:

Regardez l'ensemble réponse que j'ai fournie ci-dessous .

7
Ryan

Nouvelle réponse complète:

J'utilise maintenant mon propre trait personnalisé DispatchableWithControl au lieu du trait Dispatchable.

Je l'appelle comme ça:

$executeAt = Carbon::now()->addDays(7)->addHours(2)->addMinutes(17);
SomeJobThatWillSendAnEmailOrDoWhatever::dispatch($contactId, $executeAt);

namespace App\Jobs;

use App\Models\Tag;
use Carbon\Carbon;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;

class SomeJobThatWillSendAnEmailOrDoWhatever implements ShouldQueue {

    use DispatchableWithControl,
        InteractsWithQueue,
        Queueable,
        SerializesModels;

    protected $contactId;
    protected $executeAt;

    /**
     * 
     * @param string $contactId
     * @param Carbon $executeAt
     * @return void
     */
    public function __construct($contactId, $executeAt) {
        $this->contactId = $contactId;
        $this->executeAt = $executeAt;
    }

    /**
     * Execute the job. 
     *
     * @return void
     */
    public function handle() {
        if ($this->checkWhetherShouldExecute($this->contactId, $this->executeAt)) {
            //do stuff here
        }
    }

    /**
     * The job failed to process. 
     *
     * @param  Exception  $exception
     * @return void
     */
    public function failed(Exception $exception) {
        // Send user notification of failure, etc...
        Log::error(static::class . ' failed: ' . $exception);
    }

}

namespace App\Jobs;

use App\Models\Automation;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\PendingDispatch;
use Log;

trait DispatchableWithControl {

    use \Illuminate\Foundation\Bus\Dispatchable {//https://stackoverflow.com/questions/40299080/is-there-a-way-to-extend-trait-in-php
        \Illuminate\Foundation\Bus\Dispatchable::dispatch as parentDispatch;
    }

    /**
     * Dispatch the job with the given arguments.
     *
     * @return \Illuminate\Foundation\Bus\PendingDispatch
     */
    public static function dispatch() {
        $args = func_get_args();
        if (count($args) < 2) {
            $args[] = Carbon::now(TT::UTC); //if $executeAt wasn't provided, use 'now' (no delay)
        }
        list($contactId, $executeAt) = $args;
        $newAutomationArray = [
            'contact_id' => $contactId,
            'job_class_name' => static::class,
            'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
        ];
        Log::debug(json_encode($newAutomationArray));
        Automation::create($newAutomationArray);
        $pendingDispatch = new PendingDispatch(new static(...$args));
        return $pendingDispatch->delay($executeAt);
    }

    /**
     * @param int $contactId
     * @param Carbon $executeAt
     * @return boolean
     */
    public function checkWhetherShouldExecute($contactId, $executeAt) {
        $conditionsToMatch = [
            'contact_id' => $contactId,
            'job_class_name' => static::class,
            'execute_at' => $executeAt->format(TT::MYSQL_DATETIME_FORMAT)
        ];
        Log::debug('checkWhetherShouldExecute ' . json_encode($conditionsToMatch));
        $automation = Automation::where($conditionsToMatch)->first();
        if ($automation) {
            $automation->delete();
            Log::debug('checkWhetherShouldExecute = true, so soft-deleted record.');
            return true;
        } else {
            return false;
        }
    }

}

Donc, je peux maintenant regarder dans mon tableau 'automatisations' pour voir les travaux en attente, et je peux supprimer (ou supprimer à volonté) n'importe lequel de ces enregistrements si je veux empêcher le travail de s'exécuter.


Ancienne réponse:

J'ai configuré une nouvelle application sur mon serveur et installé (sur son propre sous-domaine) cette interface Web pour gérer mes Redis: https://github.com/ErikDubbelboer/phpRedisAdmin

Cela me permet d’éditer ou de supprimer des clés et des valeurs ZSet, ce qui semble être la façon dont Laravel Mailables retardé est sauvegardé dans la file d’attente.

Une autre approche qui a fonctionné pour moi a été d'installer Redis Desktop Manager sur mon PC Windows.

Je pense que je préférerai phpRedisAdmin car je pourrai y accéder depuis le Web (en utilisant n'importe quel appareil).

1
Ryan

Suppression de tous les travaux en file d'attente:

Redis::command('flushdb');
5

Rendre plus facile.

N'envoyez pas de courrier électronique avec l'option ultérieure. Vous devez envoyer un travail avec l'option ultérieure, et ce travail sera responsable de l'envoi du courrier électronique. 

Dans ce travail, avant d'envoyer l'e-mail, vérifiez la paire emailAddress-sendTime. Si c'est le cas, envoyez l'email, sinon, renvoyez true et l'email n'enverra pas et le travail sera terminé.

5
Sangar82

Peut-être qu'au lieu de l'annuler, vous pouvez réellement le supprimer des Redis, de ce que j'ai lu de de la documentation officielle sur la commande d'oubli sur Redis et de du document officiel de Laravel interagissant avec redis , vous pouvez appeler n'importe quelle commande Redis l’interface, si vous pouviez appeler la commande forget et réellement transmettre node_id qui, dans ce cas, je pense que c’est le numéro que vous avez dans votre image DEL 1517797158 Je pense que vous pouvez obtenir "annuler".

1
Nikola Gavric

Une approche peut être de faire vérifier votre travail pour voir si vous avez défini une adresse/heure spécifique à annuler (suppression de la file d'attente). Configurez une table de base de données ou mettez en cache une valeur pour toujours avec l'adresse/l'heure dans un tableau. Ensuite, dans la méthode handle de votre travail, vérifiez si un élément a été marqué pour suppression et comparez-le à l'adresse/à l'heure à laquelle il est disponible:

public function handle()
{
     if (Cache::has('items_to_remove')) {
         $items = Cache::get('items_to_remove');
         $removed = null;
         foreach ($items as $item) {
             if ($this->mail->to === $item['to'] && $this->mail->sendTime === $item['sendTime']) {
                  $removed = $item;
                  $this->delete();
                  break;
             }
         }
         if (!is_null($removed)) {
             $diff = array_diff($items, $removed);
             Cache::set(['items_to_remove' => $diff]);
         }
      }
  }
1
user320487

j'espère que cela t'aides 

$connection = null;
$default = 'default';

//For the delayed jobs
var_dump( \Queue::getRedis()->connection($connection)->zrange('queues:'.$default.':delayed' ,0, -1) );

//For the reserved jobs
var_dump( \Queue::getRedis()->connection($connection)->zrange('queues:'.$default.':reserved' ,0, -1) );

$connection est le nom de la connexion Redis qui est null par défaut et $queue est le nom de la file/tube qui est 'default' par défaut!

source: https://stackoverflow.com/a/42182586/6109499

1
melsaka