Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Scheduler] How to compose a Schedule from multiple places?#58431

Unanswered
melkamar asked this question inQ&A
Discussion options

How can I compose a single Schedule from different places in code through some kind of extension points?

My use case is that:

  • I want there to be a limited amount of Schedules, because each Schedule needs its ownbin/console messenger:consume scheduler_NAME worker running. I want to keep the infrastructure simple without running many different workers.
  • I want to add recurring messages from any place in the codebase

The "convenience" attributes#[AsPeriodicTask] and#[AsCronTask] do what I am looking for - they add new triggers to the schedule. However, I couldn't find a way to be able to add new triggers through "rich" PHP code where more customization can be made. See code below.


This defines thefoo schedule with a single trigger:

#[AsSchedule('foo')]class FooScheduleimplements ScheduleProviderInterface{privateSchedule$schedule;publicfunctiongetSchedule() :Schedule    {return$this->schedule ??= (newSchedule())            ->with(                RecurringMessage::every('10 seconds',newFooMessage('Hello'),                )            );    }}

Now, in a different service, I would like to add another trigger to the existing schedule. I tried two approaches:

  1. Through another#[AsSchedule] attribute.
#[AsSchedule('foo')]class ExtendingScheduleimplements ScheduleProviderInterface{privateSchedule$schedule;publicfunctiongetSchedule() :Schedule    {return$this->schedule ??= (newSchedule())            ->with(                RecurringMessage::every('20 seconds',newFooMessage('Extended hello'),                )            );    }}

This does not work (as could be expected) and only one of the duplicatefoo schedules will be registered - the other is completely ignored.


  1. Through dependency injection of theFooSchedule in a service. This one I thought should work, but I get container build errors.
class SomeService{publicfunction__construct(FooSchedule$fooSchedule,    )    {$fooSchedule->getSchedule()->add(            RecurringMessage::every('20 seconds',newFooMessage('Extended hello'))        );    }}

This results in:

Cannot autowire service "SomeService": argument "$fooSchedule" of method "__construct()" references class "FooSchedule" but no such service exists.

Looking at theFooSchedule class, which is registered as a service, I see that it is overwritten in the container and assigned a different class!

$ bin/console debug:container FooSchedule // This service is a private alias for the service scheduler.provider.defaultInformation for Service "scheduler.provider.default"==================================================== ----------------- ------------------------------------------------------------------------------------------------------  Option            Value ----------------- ------------------------------------------------------------------------------------------------------  Service ID        scheduler.provider.default  Class             Symfony\Component\Scheduler\Schedule  Tags              scheduler.schedule_provider (name: default)                    container.decorator (id: FooSchedule, inner: scheduler.provider.default.inner)  Calls             add  Public            no  Synthetic         no  Lazy              no  Shared            yes  Abstract          no  Autowired         no  Autoconfigured    no  Factory Service   scheduler.provider.default.inner  Factory Method    getSchedule  Usages            FooSchedule                    SomeService                    .service_locator.GQvl1cG ----------------- ------------------------------------------------------------------------------------------------------

MyFooSchedule is hidden behind a decorator (I think) and the DI fails.


So, given that these do not work, what is the recommended and supported way of composing schedules? I have scoured the documentation and all the examples I have come across only show a simplistic case where the schedule is defined in one class.

I am thinking that I could collect all the "extending services" myself through custom tags and autowiring in the Schedule class, but it feels there should be a more out-of-the-box way to do that.

Thank you

You must be logged in to vote

Replies: 2 comments 1 reply

Comment options

I want there to be a limited amount of Schedules, because each Schedule needs its own bin/console messenger:consume scheduler_NAME worker running. I want to keep the infrastructure simple without running many different workers.

But you can just writemessenger:consume SCHEDULER_1_NAME SCHEDULER_2_NAME ... and have a single worker

You must be logged in to vote
1 reply
@melkamar
Comment options

Thanks for the answer, though there are some more issues with this that I should have expanded on in the question. We have a containerized deployment, one of the containers is currently the "scheduler" and runscron as its entrypoint.

  • If we changed it to runmessenger:consume, then the entrypoint would need to be adjusted every time someone adds a new schedule somewhere in the codebase. I think this would be easy to forget about. I triedmessenger:consume scheduler_* to solve this, but the argument does not support a wildcard like this.
  • In addition, our project is deployed in various "configurations" with different bundles loaded, but the same deployment model. This means that the configuration of which schedules to consume will need to be dynamic per deployment. I looked at programmatically creating theScheduler consumers, butthe documentation states:If you are using it in the framework context, it is highly recommended to use the messenger:consume command as explained in the previous section. From that I assumed there is a better way.
  • I suppose an approach could be to have an entrypoint script which first collects all the schedule names and then build a list of args for themessenger:consume command. The only available command at the moment isdebug:scheduler though, which is not intended to be read from a script. (I can implement my own of course, but this not being already available tells me this is notthe supported way to work with this)

I think what I am missing from the documentation (this is not throwing blame) is what the best practice is. Is it expected that each logical "feature" has its own schedule with even just one or two tasks and the workers consume many different transports? Or is it recommended to have fewer large schedules (e.g. one with a lock and stateful for large tasks, one for quick tasks)?

Comment options

I ran into this myself today and decided to create a command that implements approach (2) from@melkamar's list:https://gist.github.com/rpander93/609ac5e429827577cacf79ddb7f58e22

You must be logged in to vote
0 replies
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Q&A
Labels
None yet
3 participants
@melkamar@rpander93@norkunas

[8]ページ先頭

©2009-2025 Movatter.jp