Pas 24: Rulând sarcinile Cron

5.0 version
Maintained

Rulând sarcinile Cron

Sarcinile Cron sunt utile pentru a executa sarcini de întreținere. Spre deosebire de procesele executate constant, acestea au program de executare pentru o perioadă scurtă de timp.

Curățarea comentariilor

Comentariile marcate ca spam sau respinse de administrator sunt păstrate în baza de date, deoarece administratorul ar putea dori să le inspecteze pentru un timp. Dar probabil că ar trebui eliminate după ceva timp. Păstrarea lor în jur la o săptămână după crearea lor este probabil suficient.

Creează câteva metode de utilitate în depozitul de comentarii pentru a găsi comentarii respinse, numără-le și șterge-le:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
--- a/src/Repository/CommentRepository.php
+++ b/src/Repository/CommentRepository.php
@@ -6,6 +6,7 @@ use App\Entity\Comment;
 use App\Entity\Conference;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
 use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\Tools\Pagination\Paginator;

 /**
@@ -16,12 +17,37 @@ use Doctrine\ORM\Tools\Pagination\Paginator;
  */
 class CommentRepository extends ServiceEntityRepository
 {
+    private const DAYS_BEFORE_REJECTED_REMOVAL = 7;
+
     public const PAGINATOR_PER_PAGE = 2;

     public function __construct(ManagerRegistry $registry)
     {
         parent::__construct($registry, Comment::class);
     }
+
+    public function countOldRejected(): int
+    {
+        return $this->getOldRejectedQueryBuilder()->select('COUNT(c.id)')->getQuery()->getSingleScalarResult();
+    }
+
+    public function deleteOldRejected(): int
+    {
+        return $this->getOldRejectedQueryBuilder()->delete()->getQuery()->execute();
+    }
+
+    private function getOldRejectedQueryBuilder(): QueryBuilder
+    {
+        return $this->createQueryBuilder('c')
+            ->andWhere('c.state = :state_rejected or c.state = :state_spam')
+            ->andWhere('c.createdAt < :date')
+            ->setParameters([
+                'state_rejected' => 'rejected',
+                'state_spam' => 'spam',
+                'date' => new \DateTime(-self::DAYS_BEFORE_REJECTED_REMOVAL.' days'),
+            ])
+        ;
+    }

     public function getCommentPaginator(Conference $conference, int $offset): Paginator
     {

Sfat

Pentru interogări mai complexe, uneori este util să aruncăm o privire asupra instrucțiunilor SQL generate (acestea pot fi găsite în jurnale și în depanatorul solicitărilor Web).

Folosind constante de clasă, parametri de container și variabile de mediu

7 zile? Am fi putut alege un alt număr, poate 10 sau 20. Acest număr ar putea evolua în timp. Am decis să îl stocăm ca o constantă pe clasă, dar am fi putut să o stocăm ca parametru în container, sau am fi putut chiar să o definim ca o variabilă de mediu.

Iată câteva reguli pentru a decide ce abstractizare trebuie să folosești:

  • Dacă valoarea este secretă (parole, token API, …), utilizează stocarea secretizată Symfony sau un seif;
  • Dacă valoarea este dinamică și ar trebui să poți să o schimbi fără relansare, folosește o variabilă de mediu;
  • Dacă valoarea poate fi diferită între medii, utilizează un parametru container;
  • Pentru orice altceva, păstrează valoarea în cod, de exemplu într-o constantă de clasă.

Crearea unei comenzi CLI

Eliminarea comentariilor vechi este sarcina perfectă pentru o lucrare cron. Ar trebui să se facă în mod regulat, iar o mică întârziere nu are niciun impact major.

Creează o comandă CLI numită app:comment:cleanup prin crearea unui fișier src/Command/CommentCleanupCommand.php:

src/Command/CommentCleanupCommand.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
namespace App\Command;

use App\Repository\CommentRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class CommentCleanupCommand extends Command
{
    private $commentRepository;

    protected static $defaultName = 'app:comment:cleanup';

    public function __construct(CommentRepository $commentRepository)
    {
        $this->commentRepository = $commentRepository;

        parent::__construct();
    }

    protected function configure()
    {
        $this
            ->setDescription('Deletes rejected and spam comments from the database')
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Dry run')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        if ($input->getOption('dry-run')) {
            $io->note('Dry mode enabled');

            $count = $this->commentRepository->countOldRejected();
        } else {
            $count = $this->commentRepository->deleteOldRejected();
        }

        $io->success(sprintf('Deleted "%d" old rejected/spam comments.', $count));

        return 0;
    }
}

Toate comenzile aplicației sunt înregistrate alături de cele incorporate în Symfony și sunt accesibile prin consola symfony. Deoarece numărul de comenzi disponibile poate fi mare, ar trebui să le categorizezi. Prin convenție, comenzile aplicației ar trebui stocate sub spațiul de nume app. Adaugă orice număr de categorii prin separarea lor cu două puncte (:).

O comandă primește intrarea (argumentele și opțiunile transmise comenzii) și poți utiliza ieșirea pentru a scrie în consolă.

Curăță baza de date rulând comanda:

1
$ symfony console app:comment:cleanup

Configurarea unei sarcini Cron pe SymfonyCloud

Unul dintre lucrurile frumoase al SymfonyCloud este că cea mai mare parte a configurației este stocată într-un singur fișier: .symfony.cloud.yaml. Containerul web, procesele și sarcinile cron sunt descrise împreună pentru a ajuta la întreținere:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--- a/.symfony.cloud.yaml
+++ b/.symfony.cloud.yaml
@@ -43,6 +43,15 @@ hooks:

         (>&2 symfony-deploy)

+crons:
+    comment_cleanup:
+        # Cleanup every night at 11.50 pm (UTC).
+        spec: '50 23 * * *'
+        cmd: |
+            if [ "$SYMFONY_BRANCH" = "master" ]; then
+                croncape symfony console app:comment:cleanup
+            fi
+
 workers:
     messages:
         commands:

Secțiunea crons definește toate sarcinile cron. Fiecare cron rulează conform unui grafic spec.

Utilitarul croncape monitorizează execuția comenzii și trimite un e-mail la adresele definite în variabila de mediu MAILTO dacă comanda returnează un cod de ieșire diferit de 0.

Configurează variabila de mediu MAILTO:

1
$ symfony var:set MAILTO=[email protected]

Poți forța un cron să ruleze de pe mașina locală:

1
$ symfony cron comment_cleanup

Notă: sarcinile cron sunt setate pe toate ramurile SymfonyCloud. Dacă nu dorești să rulezi unele pe medii non-producție, verifică variabila de mediu $SYMFONY_BRANCH:

1
2
3
if [ "$SYMFONY_BRANCH" = "master" ]; then
    croncape symfony app:invoices:send
fi

  • « Previous Pas 23: Redimensionarea imaginilor
  • Next » Pas 25: Notificarea prin toate mijloacele

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.