Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capability to schedule periodic jobs dynamically #288

Merged
merged 1 commit into from
Mar 28, 2024

Conversation

brandur
Copy link
Contributor

@brandur brandur commented Mar 27, 2024

Here, attempt to resolve #242 by providing a way for new periodic jobs
to be added dynamically after a client's already started (i.e. not only
by passing them to the client's initial constructor).

The new API puts all functions on a separate PeriodicJobs bundle to
avoid further polluting Client's namespace, and to make all related
functions easy to find.

riverClient.PeriodicJobs().Add(
    river.NewPeriodicJob(
        river.PeriodicInterval(15*time.Minute),
        func() (river.JobArgs, *river.InsertOpts) {
            return PeriodicJobArgs{}, nil
        },
        nil,
    ),
)

Additions return a periodic job "handle" which can be used to remove an
added job:

periodicJobHandle := riverClient.PeriodicJobs().Add(...)

riverClient.PeriodicJobs().Remove(periodicJobHandle)

I used a handle because the river.PeriodicJob construct isn't
particularly pretty to keep around, and contains a number of fields that
are aren't comparable so its unsuitable for use in equality checks or as
a map key.

Adding a new periodic job bumps the enqueuer's run loop so that it
enqueues the job immediately if it's configured with RunOnStart, and
schedules its initial target run time. In other words, adding or
removing periodic jobs should take effect ~instantly.

Fixes #242.

@brandur brandur force-pushed the brandur-dynamic-periodic-jobs branch from 4fd4c50 to 85ceb9c Compare March 27, 2024 10:22
@@ -0,0 +1,189 @@
package river
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed this from periodic.go to periodic_job.go to be a bit more descriptive.

// leader because only the leader enqueues periodic jobs. To make sure that a
// new periodic job is fully enabled or disabled, it should be added or removed
// from _every_ active River client across all processes.
func (b *PeriodicJobBundle) Remove(periodicJobHandle rivertype.PeriodicJobHandle) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debated on whether this should return (1) nothing, (2) an error, (3) an OK boolean, or even (4) panic when a job handle isn't found.

Decided to go with no return value because I figure an unknown job will be an edge most people don't have to handle, and it mirrors the Go API of delete(map, key).

@brandur brandur requested a review from bgentry March 27, 2024 10:31
Copy link
Contributor

@bgentry bgentry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Only minor comments ✨

periodic_job.go Outdated
// leader because only the leader enqueues periodic jobs. To make sure that a
// new periodic job is fully enabled or disabled, it should be added or removed
// from _every_ active River client across all processes.
func (b *PeriodicJobBundle) RemoveManyAll(periodicJobHandles []rivertype.PeriodicJobHandle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason for the All suffix? I feel like RemoveMany would suffice here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn, good catch. Totally a refactor problem where I renamed something, but apparently left an outdated artifact in there. That was close ...

t.Run("NoJobsConfigured", func(t *testing.T) {
t.Parallel()

svc, _ := setup(t)

svc.periodicJobs = []*PeriodicJob{}
// TODO: How about just don't configure the jobs in the first place.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missed TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, oops. I ended up changing the setup function to not add default jobs, but forgot to fix this.

Here, attempt to resolve #242 by providing a way for new periodic jobs
to be added dynamically after a client's already started (i.e. not only
by passing them to the client's initial constructor).

The new API puts all functions on a separate `PeriodicJobs` bundle to
avoid further polluting `Client`'s namespace, and to make all related
functions easy to find.

    riverClient.PeriodicJobs().Add(
        river.NewPeriodicJob(
            river.PeriodicInterval(15*time.Minute),
            func() (river.JobArgs, *river.InsertOpts) {
                return PeriodicJobArgs{}, nil
            },
            nil,
        ),
    )

Additions return a periodic job "handle" which can be used to remove an
added job:

    periodicJobHandle := riverClient.PeriodicJobs().Add(...)

    riverClient.PeriodicJobs().Remove(periodicJobHandle)

I used a handle because the `river.PeriodicJob` construct isn't
particularly pretty to keep around, and contains a number of fields that
are aren't comparable so its unsuitable for use in equality checks or as
a map key.

Adding a new periodic job bumps the enqueuer's run loop so that it
enqueues the job immediately if it's configured with `RunOnStart`, and
schedules its initial target run time. In other words, adding or
removing periodic jobs should take effect ~instantly.

Fixes #242.
@brandur brandur force-pushed the brandur-dynamic-periodic-jobs branch from 85ceb9c to 785c353 Compare March 28, 2024 03:07
@brandur
Copy link
Contributor Author

brandur commented Mar 28, 2024

Thanks!

@brandur brandur merged commit 9043557 into master Mar 28, 2024
10 checks passed
@brandur brandur deleted the brandur-dynamic-periodic-jobs branch March 28, 2024 03:10
brandur added a commit that referenced this pull request Mar 28, 2024
Tees up the project for version `v0.2.0`. Contains a few small changes
and bug fixes, and with a new periodic job feature from #288.
@brandur brandur mentioned this pull request Mar 28, 2024
brandur added a commit that referenced this pull request Mar 29, 2024
Tees up the project for version `v0.2.0`. Contains a few small changes
and bug fixes, and with a new periodic job feature from #288.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add periodic jobs dynamically
2 participants