Scheduling a Future Agent Call from Go Code
Overview
Method.Schedule(client, at, in) enqueues a one-off invocation of a method to run at a specific future time. It returns a *golem.ScheduledInvocation, whose Cancel() prevents the invocation if it has not started yet, and an error if the host refuses to schedule it; MustSchedule panics instead.
Like Trigger, Schedule does not wait for a result — so it is safe to target another agent or the same instance (a synchronous self-Call would deadlock). The scheduled time is recorded durably and survives restarts.
For a task that reschedules itself on an interval (a periodic / cron-like loop), see golem-recurring-task-go; this skill covers scheduling a single future call and canceling it.
Steps
- Get a client for the target instance:
counter.Agent.Get(counter.ID{Name: "my-counter"}). - Schedule the method at a
time.Time:counter.Increment.MustSchedule(c, when, in). - Keep the returned
*golem.ScheduledInvocationif you may need to cancel it. - Cancel with
inv.Cancel()before the scheduled time to prevent it.
Scheduling a future call on another agent
package impl
import (
"time"
"myapp/agents/counter"
"myapp/agents/scheduler"
"github.com/golemcloud/golem/sdks/go/golem"
)
type state struct{ pending *golem.ScheduledInvocation }
var agent = scheduler.Agent.Implement(func(scheduler.ID) *state { return &state{} })
func init() {
agent.Handle(scheduler.Arm, func(ctx *golem.Context[state], _ golem.Unit) golem.Unit {
c := counter.Agent.Get(counter.ID{Name: "my-counter"})
// Run counter.increment 60 seconds from now.
ctx.State.pending = counter.Increment.MustSchedule(c, time.Now().Add(60*time.Second), golem.Unit{})
return golem.Unit{}
})
}Schedule returns a *golem.ScheduledInvocation and an error; its ID field is the invocation’s InvocationID.
Scheduling a method that takes arguments
Pass the method’s input value as the last argument, exactly as with Call / Trigger:
c := counter.Agent.Get(counter.ID{Name: "my-counter"})
counter.Add.MustSchedule(c, time.Now().Add(24*time.Hour), counter.AddIn{By: 5})Canceling a scheduled call
Keep the *golem.ScheduledInvocation (e.g. in state) and call Cancel():
func init() {
agent.Handle(scheduler.Cancel, func(ctx *golem.Context[state], _ golem.Unit) golem.Unit {
if ctx.State.pending != nil {
ctx.State.pending.Cancel() // no-op if it has already started
ctx.State.pending = nil
}
return golem.Unit{}
})
}Cancel() is idempotent: it does nothing if already canceled, and cannot un-run an invocation that has already started.
Scheduling a future call to itself
To message its own instance an agent binds a client to its own id (see golem-recurring-task-go for the full pattern):
self := golem.Must(counter.Agent.Bind(ctx.AgentID()))
counter.Increment.MustSchedule(self, time.Now().Add(time.Minute), golem.Unit{})Schedule vs Trigger
Schedule(c, at, in) (*ScheduledInvocation, error)— run once at a futuretime.Time; cancelable viaCancel().MustSchedulepanics on the error.Trigger(c, in) (InvocationID, error)— enqueue immediately, no delay, not cancelable. Use it to advance to the next step now.- Never self-
Call— a synchronous call into the same instance deadlocks; self-messaging must useScheduleorTrigger.
Key Constraints
Schedule/Triggerare called on the method descriptor (counter.Increment.Schedule(c, ...)), and the client comes from the callee’s definition (counter.Agent.Get(id)).- Compute the time with
time.Now().Add(d)(atime.Time); it is recorded durably. - To cancel later, you must retain the
*golem.ScheduledInvocation— don’t discard it if cancellation may be needed. Hold it in agent state, not across a barevarinitializer. - For a self-scheduling recurring loop, don’t hand-roll it here — use
golem-recurring-task-go.
Related Guides
| Guide | Description |
|---|---|
golem-recurring-task-go | A self-rescheduling / periodic (cron-like) task |
golem-fire-and-forget-go | Enqueue an immediate invocation (Trigger) |
golem-call-another-agent-go | Await a result from another agent (Call / CallAsync) |
golem-schedule-agent-go | Schedule a future invocation from the CLI |