-1


I need to execute Symfony command quite frequently (like every 5-10 seconds) but my runtime environement restricts the lowest time possible of cron trigger to 1 minute. So, I've come up with a pretty nasty solution, looks like this. I've had created AbstractRepetitiveCommand class, which I then extended, providing implemenation of workToExecute method.

abstract class AbstractRepetitiveCommand extends Command
{
    protected int $overallMinutes = 1;

    protected int $timesPerMinute = 60;


    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $startTime = microtime(true);

        while (microtime(true) < $startTime + 60 * $this->overallMinutes - 60 / $this->timesPerMinute) {
            if (isset($last) && microtime(true) < $last + 60 / $this->timesPerMinute) {
                continue;
            }
            $last = microtime(true);
            $this->workToExecute($input, $output);
        }
    }


    protected function workToExecute(InputInterface $input, OutputInterface $output)
    {
        return;
    }
}

class DoChecksCommand extends AbstractRepetitiveCommand
{
    protected int $overallMinutes = 5;
    protected int $timePerMinute = 4;
    protected static $defaultName = 'checks:do';
    /**
     * @var InvestmentCheckerService
     */
    private InvestmentCheckerService $checkerService;
    /**
     * @var EntityManagerInterface
     */
    private EntityManagerInterface $entityManager;


    public function __construct(InvestmentCheckerService $checkerService, EntityManagerInterface $entityManager)
    {
        parent::__construct('DoChecks');
        $this->checkerService = $checkerService;
        $this->entityManager = $entityManager;
    }


    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @throws \Exception
     */
    protected function workToExecute(InputInterface $input, OutputInterface $output)
    {
        /** @var ArrayCollection | Pair[] $pairs */
        $pairs = $this->entityManager->getRepository(Pair::class)->findAll();
        foreach ($pairs as $pair) {
            $this->checkerService->processChecks($pair);
            $this->entityManager->persist($pair);
            $this->entityManager->flush();
        }
    }
}

There are two problems with this approach:

  1. When 2 o this kind of commands run simultanously, they don't recognize each other's changes made by flush. Is there a way to refresh connection, so with every run the data is fetched from database?
  2. This solution is really ugly overall. I'm probably overthinking/overcomplicating it, so please advise whether you see any better approach to the goal I want to achieve.

For now I've tried to use clear() method of EntityManager at the start of every loop but it didn't change anything.
I thought about closing the connection, but then I don't see any easy way to reconnect again after closing.
Thank you for any help.

Arkadiusz Galler
  • 305
  • 3
  • 18

1 Answers1

1

For now I've tried to use clear() method of EntityManager at the start of every loop but it didn't change anything.

That's exactly what you need to do. With EntityManager cleared Doctrine will issue a database query to fetch fresh data, so any change you made in other commands/request will be available to you. The only case when it wouldn't be true is unclosed transactions but I see no explicit transactions in your code so everything should be fine.

malarzm
  • 2,831
  • 2
  • 15
  • 25
  • Thank you, I don't know what did I mess up previously that it didn't work. I must've give up on this idea due to some other unrelated issue. – Arkadiusz Galler May 23 '20 at 00:27