Documentation
¶
Overview ¶
Package activejob is a pure-Go (no cgo) reimplementation of the foundation of Rails' ActiveJob: the job model, the argument-serialization core, and the queue-adapter framework, faithful to MRI 4.0.5's `activejob` gem.
It mirrors ActiveJob's observable behaviour without any Ruby runtime:
- Arguments serializes/deserializes job arguments in exactly the wire format the `activejob` gem produces (`_aj_serialized`, `_aj_symbol_keys`, `_aj_hash_with_indifferent_access`, `_aj_globalid`, …), so payloads interoperate byte-for-byte with a real Rails queue.
- Base models a job class (queue_as, retry_on/discard_on, callbacks) and Job a job instance (perform_later, perform_now, set).
- Adapter is the queue-adapter interface, with InlineAdapter, TestAdapter and AsyncAdapter implementations plus a Registry.
The Ruby `perform` method body and Ruby class dispatch are injectable seams (PerformFunc and the Registry); the GlobalID conversion/location is a seam on Arguments. This makes the package the ActiveJob backend for a future go-embedded-ruby binding while remaining a standalone, reusable module.
Index ¶
- Constants
- Variables
- func AssertEnqueuedJobs(t TestingT, ta *TestAdapter, n int)
- func AssertNoEnqueuedJobs(t TestingT, ta *TestAdapter)
- func AssertNoPerformedJobs(t TestingT, ta *TestAdapter)
- func AssertPerformedJobs(t TestingT, ta *TestAdapter, n int)
- func MatchArgs(args ...any) []any
- func PerformAllLater(jobs ...*Job) error
- func RegisterAdapter(name string, factory func() Adapter)
- type Adapter
- type Arguments
- type AroundFunc
- type AsyncAdapter
- type Base
- func (b *Base) AfterEnqueue(fn CallbackFunc) *Base
- func (b *Base) AfterPerform(fn CallbackFunc) *Base
- func (b *Base) AroundEnqueue(fn AroundFunc) *Base
- func (b *Base) AroundPerform(fn AroundFunc) *Base
- func (b *Base) BeforeEnqueue(fn CallbackFunc) *Base
- func (b *Base) BeforePerform(fn CallbackFunc) *Base
- func (b *Base) DiscardOn(match ErrorMatcher, opts DiscardOptions) *Base
- func (b *Base) New(args ...any) *Job
- func (b *Base) QueueAs(name string) *Base
- func (b *Base) QueueAsFunc(fn func(*Job) string) *Base
- func (b *Base) RescueFrom(match ErrorMatcher, handler func(job *Job, err error) error) *Base
- func (b *Base) RetryOn(match ErrorMatcher, opts RetryOptions) *Base
- func (b *Base) WithAdapter(a Adapter) *Base
- func (b *Base) WithPerform(fn PerformFunc) *Base
- func (b *Base) WithPriority(p int) *Base
- func (b *Base) WithRetryJitter(fraction float64) *Base
- type BigDecimal
- type BulkAdapter
- type CallbackFunc
- type Date
- type DateTime
- type DeserializationError
- type DiscardOptions
- type Duration
- type DurationPart
- type ErrorMatcher
- type GlobalID
- type Hash
- type IndifferentHash
- type InlineAdapter
- type Job
- type JobMatcher
- type Module
- type Object
- type PerformFunc
- type Range
- type Registry
- type RetryOptions
- type SerializationError
- type SetOptions
- type Symbol
- type TestAdapter
- func (t *TestAdapter) Enqueue(job *Job) error
- func (t *TestAdapter) EnqueueAll(jobs []*Job) (int, error)
- func (t *TestAdapter) EnqueueAt(job *Job, _ time.Time) error
- func (t *TestAdapter) EnqueuedJobs() []*Job
- func (t *TestAdapter) PerformEnqueuedJobs() error
- func (t *TestAdapter) PerformedJobs() []*Job
- type TestingT
- type Time
- type TimeWithZone
Constants ¶
const DefaultJitter = 0.15
DefaultJitter is Rails' config.active_job.retry_jitter default (15%). The bare gem's class attribute defaults to 0.0; call Base.WithRetryJitter to opt in.
Variables ¶
var ( // ErrNoAdapter is returned by perform_later when the job class has no queue // adapter configured. ErrNoAdapter = errors.New("activejob: no queue adapter configured") // ErrNoPerform is returned by perform_now when the job class has no perform // seam configured. ErrNoPerform = errors.New("activejob: no perform function configured") )
Functions ¶
func AssertEnqueuedJobs ¶
func AssertEnqueuedJobs(t TestingT, ta *TestAdapter, n int)
AssertEnqueuedJobs asserts that exactly n jobs are recorded as enqueued on ta, mirroring assert_enqueued_jobs.
func AssertNoEnqueuedJobs ¶
func AssertNoEnqueuedJobs(t TestingT, ta *TestAdapter)
AssertNoEnqueuedJobs asserts that no jobs are recorded as enqueued on ta, mirroring assert_no_enqueued_jobs.
func AssertNoPerformedJobs ¶
func AssertNoPerformedJobs(t TestingT, ta *TestAdapter)
AssertNoPerformedJobs asserts that no jobs are recorded as performed on ta, mirroring assert_no_performed_jobs.
func AssertPerformedJobs ¶
func AssertPerformedJobs(t TestingT, ta *TestAdapter, n int)
AssertPerformedJobs asserts that exactly n jobs are recorded as performed on ta, mirroring assert_performed_jobs.
func MatchArgs ¶
MatchArgs returns a non-nil argument slice for JobMatcher.Args, so that MatchArgs() matches a job enqueued with no arguments (distinct from the nil "do not match arguments").
func PerformAllLater ¶
PerformAllLater enqueues several jobs at once (ActiveJob.perform_all_later). When every job shares one adapter that implements BulkAdapter, it enqueues them in a single call; otherwise it enqueues them one by one. It stops at the first error.
func RegisterAdapter ¶
RegisterAdapter registers a named queue-adapter factory (QueueAdapters.register).
Types ¶
type Adapter ¶
Adapter is the queue-adapter interface every backend implements, mirroring ActiveJob::QueueAdapters. Enqueue schedules a job to run as soon as possible; EnqueueAt schedules it to run at (or after) a timestamp.
func LookupAdapter ¶
LookupAdapter constructs the adapter registered under name.
type Arguments ¶
type Arguments struct {
// ToGlobalID converts a host object to its GlobalID URI. It is consulted
// for values not matched by any built-in type. Return ok=false to fall
// through to an "unsupported type" error; return a non-nil err to abort.
ToGlobalID func(obj any) (uri string, ok bool, err error)
// LocateGlobalID resolves a GlobalID URI back to a host object during
// deserialization. When nil, a [GlobalID] value is produced instead.
LocateGlobalID func(uri string) (any, error)
}
Arguments serializes and deserializes ActiveJob job arguments in MRI's exact wire format. The GlobalID conversion (serialize) and location (deserialize) are injectable seams; when unset, GlobalID values round-trip as GlobalID.
func NewArguments ¶
func NewArguments() *Arguments
NewArguments returns an Arguments serializer with no seams configured.
func (*Arguments) Deserialize ¶
Deserialize is the inverse of Serialize. Its input is the parsed-JSON shape (objects as map[string]any or *Object, numbers as json.Number/float64/int).
func (*Arguments) DeserializeJSON ¶
DeserializeJSON parses a JSON array of serialized arguments (with UseNumber so integers stay integers) and deserializes it.
type AroundFunc ¶
AroundFunc is an around_* callback. It receives the job and a `next` closure it must invoke to run the wrapped action; it may skip `next` to halt.
type AsyncAdapter ¶
type AsyncAdapter struct {
// contains filtered or unexported fields
}
AsyncAdapter performs jobs on background goroutines, mirroring ActiveJob's :async adapter. Call Drain to wait for all in-flight jobs and collect errors. (EnqueueAt runs the job without honouring the delay in this v0.1 foundation.)
func (*AsyncAdapter) Drain ¶
func (a *AsyncAdapter) Drain() []error
Drain waits for all in-flight jobs to finish and returns any errors they raised, in completion order.
func (*AsyncAdapter) Enqueue ¶
func (a *AsyncAdapter) Enqueue(job *Job) error
Enqueue performs the job on a new goroutine.
type Base ¶
type Base struct {
// Name is the job class name recorded in the "job_class" payload field.
Name string
// Args is the argument serializer (carrying the GlobalID seams). Never nil.
Args *Arguments
// contains filtered or unexported fields
}
Base models an ActiveJob job class: its perform seam, queue adapter, queue name, priority, retry/discard rules and callbacks. Configure it with the chainable builder methods, then create instances with Base.New.
func (*Base) AfterEnqueue ¶
func (b *Base) AfterEnqueue(fn CallbackFunc) *Base
AfterEnqueue registers an after_enqueue callback and returns b.
func (*Base) AfterPerform ¶
func (b *Base) AfterPerform(fn CallbackFunc) *Base
AfterPerform registers an after_perform callback and returns b.
func (*Base) AroundEnqueue ¶
func (b *Base) AroundEnqueue(fn AroundFunc) *Base
AroundEnqueue registers an around_enqueue callback and returns b.
func (*Base) AroundPerform ¶
func (b *Base) AroundPerform(fn AroundFunc) *Base
AroundPerform registers an around_perform callback and returns b.
func (*Base) BeforeEnqueue ¶
func (b *Base) BeforeEnqueue(fn CallbackFunc) *Base
BeforeEnqueue registers a before_enqueue callback and returns b.
func (*Base) BeforePerform ¶
func (b *Base) BeforePerform(fn CallbackFunc) *Base
BeforePerform registers a before_perform callback and returns b.
func (*Base) DiscardOn ¶
func (b *Base) DiscardOn(match ErrorMatcher, opts DiscardOptions) *Base
DiscardOn registers a discard_on rule and returns b. A matched error is dropped (optionally via Block), mirroring ActiveJob::Exceptions#discard_on.
func (*Base) New ¶
New builds a job instance of class b with the given (raw) arguments, a fresh job id, and the class defaults for queue and priority.
func (*Base) QueueAsFunc ¶
QueueAsFunc sets a queue name computed per job at enqueue time (queue_as { … }).
func (*Base) RescueFrom ¶
RescueFrom registers a rescue_from handler and returns b. When perform raises a matching error, handler runs and its result becomes perform_now's result (return nil to swallow, or re-enqueue via the job to retry), mirroring ActiveSupport::Rescuable#rescue_from — the primitive retry_on / discard_on build on. Handlers are searched bottom-to-top.
func (*Base) RetryOn ¶
func (b *Base) RetryOn(match ErrorMatcher, opts RetryOptions) *Base
RetryOn registers a retry_on rule and returns b. Errors matched by match are caught and the job is re-enqueued up to Attempts times with the configured backoff, mirroring ActiveJob::Exceptions#retry_on.
func (*Base) WithAdapter ¶
WithAdapter sets the queue adapter and returns b.
func (*Base) WithPerform ¶
func (b *Base) WithPerform(fn PerformFunc) *Base
WithPerform sets the perform seam and returns b.
func (*Base) WithPriority ¶
WithPriority sets the default priority and returns b.
func (*Base) WithRetryJitter ¶
WithRetryJitter sets the class-level retry jitter (Rails' retry_jitter, a fraction 0..1 of the computed delay) and returns b. Use DefaultJitter for the Rails app default of 15%.
type BigDecimal ¶
type BigDecimal string
BigDecimal is a Ruby BigDecimal, carrying its canonical `to_s` text (e.g. "0.15e1"). Serialized/deserialized through the BigDecimalSerializer.
type BulkAdapter ¶
BulkAdapter is an optional capability: adapters that can enqueue a batch in one call implement it, and PerformAllLater / Registry will prefer it. EnqueueAll returns the number of jobs successfully enqueued.
type CallbackFunc ¶
CallbackFunc is a before_* / after_* callback body. Returning an error halts the chain (mirroring `throw :abort`).
type DateTime ¶
DateTime is a Ruby DateTime. Like Time but always renders a numeric offset ("+00:00" for UTC) rather than "Z", matching Ruby's DateTime#iso8601(9).
type DeserializationError ¶
type DeserializationError struct{ Cause error }
DeserializationError is raised when a payload cannot be deserialized. It mirrors ActiveJob::DeserializationError and wraps the underlying cause.
func (*DeserializationError) Error ¶
func (e *DeserializationError) Error() string
func (*DeserializationError) Unwrap ¶
func (e *DeserializationError) Unwrap() error
type DiscardOptions ¶
type DiscardOptions struct {
// Block runs when a matching error is discarded; if nil, the error is swallowed.
Block func(job *Job, err error) error
}
DiscardOptions configures a discard_on rule.
type Duration ¶
type Duration struct {
Value int64
Parts []DurationPart
}
Duration is a Ruby ActiveSupport::Duration: a total value in seconds plus the ordered parts it was built from.
type DurationPart ¶
DurationPart is one component of a Duration (e.g. {minutes: 5}).
type ErrorMatcher ¶
ErrorMatcher reports whether a raised error matches a retry_on / discard_on rule. Use MatchError for a errors.Is-based matcher or MatchAny to match all.
func MatchError ¶
func MatchError(target error) ErrorMatcher
MatchError returns an ErrorMatcher matching errors that wrap target (errors.Is).
type GlobalID ¶
type GlobalID struct{ URI string }
GlobalID is a serialized GlobalID reference (`gid://app/Class/id`). It is both an input value (serialize it directly to `{"_aj_globalid": URI}`) and the default output of deserialization when no LocateGlobalID seam is configured, so GlobalID payloads round-trip without a locator.
type Hash ¶
type Hash struct {
// contains filtered or unexported fields
}
Hash is a Ruby Hash with String and/or Symbol keys, preserving insertion order. Its serialization appends "_aj_symbol_keys" listing which keys were Symbols so deserialization can restore them.
type IndifferentHash ¶
type IndifferentHash struct {
// contains filtered or unexported fields
}
IndifferentHash is a Ruby ActiveSupport::HashWithIndifferentAccess: all keys are Strings and its serialization carries the "_aj_hash_with_indifferent_access" marker instead of "_aj_symbol_keys".
func NewIndifferentHash ¶
func NewIndifferentHash() *IndifferentHash
NewIndifferentHash returns an empty ordered IndifferentHash.
func (*IndifferentHash) Get ¶
func (h *IndifferentHash) Get(key string) (any, bool)
Get returns the value for key and whether it was present.
func (*IndifferentHash) Keys ¶
func (h *IndifferentHash) Keys() []string
Keys returns the keys in insertion order (a copy).
func (*IndifferentHash) Set ¶
func (h *IndifferentHash) Set(key string, value any) *IndifferentHash
Set stores key with value, preserving insertion order. Returns h for chaining.
type InlineAdapter ¶
type InlineAdapter struct{}
InlineAdapter performs jobs immediately, on the enqueuing goroutine, mirroring ActiveJob's :inline adapter. EnqueueAt ignores the delay and performs at once.
func (InlineAdapter) Enqueue ¶
func (InlineAdapter) Enqueue(job *Job) error
Enqueue performs the job immediately.
type Job ¶
type Job struct {
Base *Base
JobID string
QueueName string
Priority *int
Arguments []any
Executions int
ExceptionExecutions map[string]int
Locale string
Timezone string
EnqueuedAt time.Time
ScheduledAt *time.Time
ProviderJobID string
}
Job is a job instance: a job class plus its arguments and per-run state.
func AssertEnqueuedWith ¶
func AssertEnqueuedWith(t TestingT, ta *TestAdapter, m JobMatcher) *Job
AssertEnqueuedWith asserts that a job matching m is recorded as enqueued on ta and returns it (nil on no match), mirroring assert_enqueued_with.
func AssertPerformedWith ¶
func AssertPerformedWith(t TestingT, ta *TestAdapter, m JobMatcher) *Job
AssertPerformedWith asserts that a job matching m is recorded as performed on ta and returns it (nil on no match), mirroring assert_performed_with.
func (*Job) PerformLater ¶
PerformLater serializes the arguments and enqueues the job through its adapter (EnqueueAt when scheduled, Enqueue otherwise), running the enqueue callbacks.
func (*Job) PerformNow ¶
PerformNow runs the job body inline: it increments the execution count, runs the perform callbacks around the perform seam, and applies retry_on/discard_on rules to any error the seam returns.
func (*Job) Serialize ¶
Serialize renders the job's transport payload as an ordered *Object, matching the shape MRI's ActiveJob::Core#serialize produces (job_class, job_id, provider_job_id, queue_name, priority, arguments, executions, exception_executions, locale, timezone, enqueued_at, scheduled_at).
func (*Job) SerializeJSON ¶
SerializeJSON serializes the job and marshals the payload to JSON.
func (*Job) Set ¶
func (j *Job) Set(o SetOptions) *Job
Set applies the options to the job (queue, priority, scheduled time) and returns it for chaining before perform_later, mirroring MyJob.set(…).
type JobMatcher ¶
type JobMatcher struct {
// Job is the expected job-class name (job:). Empty matches any class.
Job string
// Args are the expected raw (pre-serialization) arguments (args:). Nil means
// "do not match arguments"; a non-nil slice, including an empty one, is
// compared by their serialized wire form. Use [MatchArgs] to require zero
// arguments explicitly.
Args []any
// Queue is the expected queue name (queue:). Empty matches any queue.
Queue string
// Priority is the expected priority (priority:). Nil matches any priority.
Priority *int
// At is the expected scheduled time (at:), matched within ±1s. Nil matches
// any (including unscheduled) job.
At *time.Time
}
JobMatcher describes the expected attributes of an enqueued or performed job, mirroring the keyword arguments of assert_enqueued_with / assert_performed_with. A zero-valued field is not matched; a non-nil Args slice (even empty) is.
type Module ¶
type Module string
Module is a Ruby Module or Class reference, carrying its constant name.
type Object ¶
type Object struct {
// contains filtered or unexported fields
}
Object is the serialized form of any Ruby Hash-shaped value (a plain Hash, a HashWithIndifferentAccess, or an object routed through a serializer). It is an insertion-ordered, string-keyed map whose MarshalJSON preserves key order, so the JSON bytes match MRI's `ActiveJob::Arguments.serialize(...).to_json` exactly (MRI hashes are insertion-ordered; Go's built-in maps are not).
func (*Object) MarshalJSON ¶
MarshalJSON renders the object as a JSON object with keys in insertion order.
type PerformFunc ¶
PerformFunc is the injectable seam for a Ruby job's `perform` method body. It receives the job class name and its (deserialized) arguments.
type Range ¶
Range is a Ruby Range. Begin and End are themselves serializable arguments (and may be nil for begin-less / end-less ranges).
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps job-class names to their Base, providing the Ruby class dispatch seam used to reconstruct a job from a serialized payload.
func (*Registry) Deserialize ¶
Deserialize reconstructs a job instance from a transport payload, dispatching the "job_class" field through the registry to find its Base (the Ruby class dispatch seam). The payload's numbers may be json.Number, float64 or int.
type RetryOptions ¶
type RetryOptions struct {
// Attempts is the maximum number of executions before giving up (Rails
// default: 5, and the count includes the original run). Ignored when
// Unlimited is set.
Attempts int
// Unlimited retries until the job succeeds (attempts: :unlimited).
Unlimited bool
// Wait is a custom delay algorithm (wait: ->(executions) { … }). It receives
// the current per-exception execution count and its result is used verbatim
// (jitter is not applied, matching Rails' Proc case). It takes precedence
// over WaitSeconds / Polynomial.
Wait func(executions int) time.Duration
// WaitSeconds is a constant delay in seconds (wait: N.seconds). Jitter is
// applied. When zero and neither Wait nor Polynomial is set, the Rails
// default of 3 seconds is used.
WaitSeconds int
// Polynomial selects the :polynomially_longer backoff:
// ((executions**4) + jitter) + 2 seconds (≈3s, 18s, 83s, 258s, …).
Polynomial bool
// Jitter overrides the class-level retry jitter for this rule (0..1). Nil
// uses the job class's [Base.WithRetryJitter] value.
Jitter *float64
// Queue re-enqueues the retry on a different queue (queue:).
Queue string
// Priority re-enqueues the retry with a different priority (priority:).
Priority *int
// Key names the exception_executions bucket incremented on each retry. When
// empty, [defaultExceptionKey] is used.
Key string
// Block runs when attempts are exhausted; if nil, the error is re-raised.
Block func(job *Job, err error) error
}
RetryOptions configures a retry_on rule, mirroring ActiveJob's retry_on keyword options.
type SerializationError ¶
type SerializationError struct {
// contains filtered or unexported fields
}
SerializationError is raised when an argument cannot be serialized (an unsupported type, a reserved or non-string/symbol Hash key, …). It mirrors ActiveJob::SerializationError.
func (*SerializationError) Error ¶
func (e *SerializationError) Error() string
type SetOptions ¶
SetOptions configures a single enqueue (ActiveJob's `set`).
type Symbol ¶
type Symbol string
Symbol is a Ruby Symbol (`:name`). It serializes through the SymbolSerializer.
type TestAdapter ¶
type TestAdapter struct {
Enqueued []*Job
Performed []*Job
// contains filtered or unexported fields
}
TestAdapter records enqueued jobs instead of running them, mirroring ActiveJob's :test adapter. Call PerformEnqueuedJobs to drain and run them.
func (*TestAdapter) Enqueue ¶
func (t *TestAdapter) Enqueue(job *Job) error
Enqueue records job as enqueued.
func (*TestAdapter) EnqueueAll ¶
func (t *TestAdapter) EnqueueAll(jobs []*Job) (int, error)
EnqueueAll records every job as enqueued and reports how many were recorded.
func (*TestAdapter) EnqueueAt ¶
func (t *TestAdapter) EnqueueAt(job *Job, _ time.Time) error
EnqueueAt records job as enqueued (the scheduled timestamp is on the job).
func (*TestAdapter) EnqueuedJobs ¶
func (t *TestAdapter) EnqueuedJobs() []*Job
EnqueuedJobs returns a snapshot of the currently enqueued jobs.
func (*TestAdapter) PerformEnqueuedJobs ¶
func (t *TestAdapter) PerformEnqueuedJobs() error
PerformEnqueuedJobs runs every enqueued job with perform_now, moving each to the performed list. It stops and returns the first error encountered.
func (*TestAdapter) PerformedJobs ¶
func (t *TestAdapter) PerformedJobs() []*Job
PerformedJobs returns a snapshot of the jobs performed so far.
type TestingT ¶
TestingT is the slice of *testing.T that the assertion helpers need, so they work with any test framework (mirroring ActiveJob::TestHelper's Minitest assertions). A failing assertion calls Errorf, exactly as Minitest's assert records a failure without aborting.
type Time ¶
Time is a Ruby Time. It serializes as ISO-8601 with 9 fractional digits, rendering UTC as a trailing "Z" (matching Ruby's Time#iso8601(9)).
type TimeWithZone ¶
TimeWithZone is a Ruby ActiveSupport::TimeWithZone: an instant plus the IANA zone name (e.g. "Etc/UTC"). Serialized with a "time_zone" field.
