Working with the new Task Service Worker

Hey Folks,

As you might know, v9 introduced a new way of working wiht Automated Tasks. That is a wee-bit more complicated to use then we are used to with the old Jobs System.
However, it does have the advantage, that it has a proper Queuing Mechanism with a Background Service to process the Queue.

I am trying to make use of that. But i hit a brick wall, as soon as i try to make my Command work asyncroniously.
I’v got my Task and Commands set up and they are running smoothly when started directly over the CLI and run synroniously. - Like most core tasks do.

When i add the “AsyncCommandInterface” to the command i can still run the command, and i can tell from the MessengerMessages Table in my DB, that my Messages are beeing queued.
Then when i start the Service Workder by doing messenger:consume async i get a Exception:

[Symfony\Component\Messenger\Exception\MessageDecodingFailedException]
Could not decode stamp: Cannot create an instance of “Concrete\Core\Command\Task\Output\ConsoleOutput” from serialized data beca
use its constructor requires parameter “symfonyOutput” to be present.

I’ve looked at the ReindexPageCommand as an Example a hundert times and tried to tweak various things in my Commands and Handlers with no progress at all.

Does anyone here have a Idea on what i could be doing wrong? - The Page Reindexing Task is running fine, and it’s doing things in a very similar fashion.

I have also noticed a few shortcomings of the Service Worker in general and opened a Issue here:

Can you post the actual code of your task controller, along with the code for any commands that the task controller creates?

Posting the whole Job would have been a bit much. It does a lot. - But i striped it down to a minimal example that still yields the same error when the messenger service worker comes across it:

This is the Task Controller:

class DummyController extends AbstractController
{
    public function getName(): string
    {
        return t('Dummy Task');
    }

    public function getDescription(): string
    {
        return t('Does nothing.');
    }

    public function getTaskRunner(TaskInterface $task, InputInterface $input): TaskRunnerInterface
    {
        $this->app = Application::getFacadeApplication();
        $batch = Batch::create('Dummy Batch');
        $count = 100;
        for ($i = 1; $i < $count; $i++) {
            $batch->add(new DummyCommand($i));
        }

        return new BatchProcessTaskRunner($task, $batch, $input, t('Processing Dummy Task started. ' . $count . ' elements to process...'));
    }
}

The Command:

class DummyCommand extends Command implements AsyncCommandInterface
{
    public $row;

    public function __construct($row)
    {
        $this->row = $row;
    }

    public static function getHandler(): string
    {
        return DummyCommandHandler::class;
    }
}

And the CommandHandler:

class DummyCommandHandler implements OutputAwareInterface
{
    use OutputAwareTrait;

    public function __invoke(DummyCommand $command)
    {
        $this->app = Application::getFacadeApplication();
        try {
            $msg = 'Dummy command executed for row '.$command->row;
            Log::addInfo($msg);
            return true;
        } catch (\Exception $e) {
            Log::addWarning('Error occured while processing dummy command: '.$e->getMessage());
            return false;
        }
    }
}

Hi @haeflimi,
Are you still experiencing this issue with the tasks? I also had the same error about the symfonyOutput. I managed to pin point not the exact cause but a work around that does not seem to interfere with the rest of the task and makes it possible to finish what it’s doing.
In the public/concrete/src/Command/Task/Output/ConsoleOutput.php the constructor allows SymfonyConsoleOutputInterface and if it does not receive that you get the error you submitted.
I made the constructor a little bit more forgiving.

public function __construct(SymfonyConsoleOutputInterface $symfonyOutput = null)
{
    if ($symfonyOutput === null) {
        $symfonyOutput = new \Symfony\Component\Console\Output\ConsoleOutput();
    }
    $this->symfonyOutput = $symfonyOutput;
}

Perhaps this (temporarily) fixes your issue. I submitted an issue to Github hoping that someone figures out the real issue.

Yes, i still experience the same Thing (currently on 9.3.2) and still havent gotten it to work properly, even after sinking many hours into the issue. Its quite frustrating.

My Task runs fine, when run synchroniously and started through the CLI.
The Problem there is, that i run a quite a large badge with the command including multiple API Calls. So i run into Memory and Timeout Issues when i do it this way. Whenever one Command Fails for some Reason the whole process stops and there is no way to continue where it left.

So i really want to run this one Asyncroniously.

As soon as i Add the “implement AsyncCommandInterface” to the Command things just stop working

  • I can see the Task and its supposed progress in the Dashboard and it seems to work through the Batch faster then it should be able to.
  • When i run the Task Worker on CLI while Commands are Qued i still get the before mentioned Error. → This goes away after applying the Core Hack suggested by @DeanZL
  • Regardles of getting the error or not: While it seems like the Worker is working throught the Batch and executing those commands: Nothing is beeing done or executed and nothing is beeing logged.

I have now managed to successfully Queue a BatchProcessTaskRunner that acctually gets processed by the Worker. - Seems like there was a Bug early within in my CommandHandler Code that caused an error that simply didnt show up anywhere and caused the command to do and report nothing at all while technically beeing processed.

While testing/ running and observing my Task i noticed two things that confuse me and tripped me up greatly:

  • When i use the OutputAwareTrait and Interface in my CommandHandler to output Command Status messages, they wont show up in the CLI at all (even when letting the worker run with -vv). They also didnt show up the Job Logs.
    So i simply use echo() now. At least that way i can see in real Time what is going on when observing the worker execution output.

  • When i start the Task, the progress bar rapidly rises and reports Task completion after less than a minute. But when i observe the output of the worker i can see that it is still busy churning away at the Queued commands way after that. - This is also reflected in the “MessengerMessages” DB Table, so the progress should be trackabel correctly. - But somehow in the CMS dashboard my task is reported as completed very prematurely.

1 Like