failed

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package failed is where a job goes when it gives up.

It mirrors Illuminate\Queue\Failed: a FailedJobProvider interface and the implementations of it, so a dead letter list can live somewhere other than the queue that produced it.

The default is that it does not move

A github.com/arandu-io/hesape/queue.DatabaseQueue marks the job failed in the jobs table it was already in, and queue.Queue.Failed lists them and queue.Queue.Retry puts one back. Every driver implements those two, so an application that never wires a provider still has a dead letter list, one store and one answer to "is this job still queued" (RULE 9).

What this package is for is the case that arrangement cannot serve: a queue whose store is not durable enough to keep failures, or one that is flushed by something other than the application. A RESP queue is both. The provider is then a deliberate second place, wired on purpose, and the cost -- two stores, two retentions -- is paid knowingly.

Every read takes a Grant

A failed job carries a customer's payload, so listing them is a read like any other: every method takes an auth.Grant and filters by auth.Tenant(g) (RULE 17). Laravel's signatures have no equivalent because Laravel has no tenant, and a provider that answered across tenants would leak the arguments of every job every customer ever queued.

The files it answers to, in the clone at laravel_illuminate/queue/Failed:

CountableFailedJobProvider.php   -> CountableFailedJobProvider
DatabaseFailedJobProvider.php    -> DatabaseFailedJobProvider
DatabaseUuidFailedJobProvider.php -> DatabaseUUIDFailedJobProvider
DynamoDbFailedJobProvider.php    -- not coming, RULE 11
FailedJobProviderInterface.php   -> FailedJobProvider
FileFailedJobProvider.php        -> FileFailedJobProvider
NullFailedJobProvider.php        -> NullFailedJobProvider
PrunableFailedJobProvider.php    -> PrunableFailedJobProvider

DatabaseUUIDFailedJobProvider is an alias of DatabaseFailedJobProvider: Laravel has two because its ids are integers and the uuid is a second column, and here the id is the uuid, so the two would run the same query.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoTenant = errors.New("queue/failed: the Grant carries no tenant, and a failed job list cannot be scoped without one")

ErrNoTenant is returned when a Grant carries no tenant.

It mirrors jobs.ErrNoTenant and exists for the same reason: a query with no tenant to filter by is a query that reads every customer's failures.

View Source
var ErrNotFound = errors.New("queue/failed: no such failed job")

ErrNotFound is what Find wraps when there is no such failed job.

It is an error rather than a zero value, because "no such job" and "a job with no payload" are different answers and a command that retries the second one silently does nothing.

Functions

This section is empty.

Types

type CountableFailedJobProvider

type CountableFailedJobProvider interface {
	// Count is how many failed jobs there are. An empty connection or queue
	// means every one.
	Count(ctx context.Context, g auth.Grant, connection, queue string) (int, error)
}

CountableFailedJobProvider is a provider that can say how many.

It answers Illuminate\Queue\Failed\CountableFailedJobProvider. It is a second interface rather than a sixth method for the reason it is in PHP: counting is what a monitor does every minute, and a provider that would have to load every row to answer should not pretend it can.

type DatabaseFailedJobProvider

type DatabaseFailedJobProvider struct {
	// contains filtered or unexported fields
}

DatabaseFailedJobProvider keeps the failed jobs in a table.

It answers Illuminate\Queue\Failed\DatabaseFailedJobProvider. It is what an application wires when it wants the dead letter list to survive the queue -- a job that failed on a Redis queue that was then flushed is still a job somebody has to answer for.

The table is its own, and that is the point of the type: a queue whose store is not a database has nowhere else to keep them.

func NewDatabaseFailedJobProvider

func NewDatabaseFailedJobProvider(db *database.DB, table string) *DatabaseFailedJobProvider

NewDatabaseFailedJobProvider returns the provider over db.

The table defaults to failed_jobs, which is Laravel's name.

func (*DatabaseFailedJobProvider) All

All is this tenant's failed jobs, newest first. It answers all().

func (*DatabaseFailedJobProvider) Count

func (p *DatabaseFailedJobProvider) Count(ctx context.Context, g auth.Grant, connectionName, queue string) (int, error)

Count is how many of this tenant's jobs have failed. It answers count().

func (*DatabaseFailedJobProvider) Find

Find is one of this tenant's failed jobs. It answers find().

func (*DatabaseFailedJobProvider) Flush

Flush removes this tenant's failed jobs older than age, or all of them when age is zero. It answers flush().

func (*DatabaseFailedJobProvider) Forget

Forget removes one of this tenant's failed jobs. It answers forget().

func (*DatabaseFailedJobProvider) GetTable

func (p *DatabaseFailedJobProvider) GetTable() string

GetTable is the name of the table this provider reads.

It answers getTable(), which in PHP returns a query builder over it. Here it returns the name: a builder handed out is a caller writing its own SQL against a table it does not own, and the tenant filter is not optional (RULE 17).

func (*DatabaseFailedJobProvider) IDs

IDs is the identifiers of this tenant's failed jobs, newest first. It answers ids().

func (*DatabaseFailedJobProvider) Log

Log records a job that gave up. It answers log().

func (*DatabaseFailedJobProvider) Migrations

func (p *DatabaseFailedJobProvider) Migrations() []database.Migration

Migrations returns the failed jobs table.

It answers Laravel's `queue:failed-table`. It is on the provider rather than on the queue module because it belongs to whoever wired this provider: an application that keeps its failures in the jobs table declares nothing here.

func (*DatabaseFailedJobProvider) Prune

func (p *DatabaseFailedJobProvider) Prune(ctx context.Context, g auth.Grant, before time.Time) (int, error)

Prune removes this tenant's failed jobs that failed before an instant, and returns how many went. It answers prune().

type DatabaseUUIDFailedJobProvider

type DatabaseUUIDFailedJobProvider = DatabaseFailedJobProvider

DatabaseUUIDFailedJobProvider is DatabaseFailedJobProvider under the name Laravel gives the version keyed by the job's uuid.

It answers Illuminate\Queue\Failed\DatabaseUuidFailedJobProvider, and it is an alias because there is nothing left to distinguish: Laravel has two providers because its ids are auto-increment integers and the uuid is a second column, so retrying by uuid needs a second index and a second class. Here the id is the uuid (see database.NewID), so the two providers would run the same query (RULE 9).

type FailedJob

type FailedJob struct {
	// ID identifies this failure. In Laravel it is an auto-increment integer
	// and the job's own uuid is a second column; here both are the job's UUID,
	// because the id is minted by the application and a second one would only
	// be a second thing to quote at somebody.
	ID string
	// UUID is the job's identifier, which is the same string as ID. It is kept
	// because it is what Laravel's DatabaseUuidFailedJobProvider indexes and
	// what `aru queue:retry` takes.
	UUID string
	// TenantID is who the work belonged to. It is not in Laravel, which has no
	// tenant, and it is not optional: a failed job list that crossed customers
	// would be one customer reading another's payloads (RULE 14).
	TenantID string
	// Connection is the queue connection it was on.
	Connection string
	// Queue is the queue it was on.
	Queue string
	// Name is what routes the job to a handler.
	Name string
	// Payload is the job's arguments, as they were stored.
	Payload []byte
	// Exception is why it gave up.
	Exception string
	// FailedAt is when.
	FailedAt time.Time
}

FailedJob is one job that gave up.

It answers the row Laravel's failed_jobs table holds, field for field, plus the tenant every record in this collection carries.

type FailedJobProvider

type FailedJobProvider interface {
	// Log records a job that gave up, and returns the id it was recorded under.
	Log(ctx context.Context, g auth.Grant, job FailedJob) (string, error)

	// IDs is the identifiers of the failed jobs, newest first. An empty queue
	// means every queue.
	IDs(ctx context.Context, g auth.Grant, queue string) ([]string, error)

	// All is the failed jobs, newest first.
	All(ctx context.Context, g auth.Grant) ([]FailedJob, error)

	// Find is one failed job, or an error wrapping [ErrNotFound].
	Find(ctx context.Context, g auth.Grant, id string) (FailedJob, error)

	// Forget removes one failed job, and reports whether there was one.
	Forget(ctx context.Context, g auth.Grant, id string) (bool, error)

	// Flush removes the failed jobs older than age. A zero age removes all of
	// them, which is what `queue:flush` with no --hours does.
	Flush(ctx context.Context, g auth.Grant, age time.Duration) error
}

FailedJobProvider is where a job goes when it gives up.

It answers Illuminate\Queue\Failed\FailedJobProviderInterface, with the two changes this collection makes everywhere: a context, and an auth.Grant.

The Grant is not decoration. A failed job carries a customer's payload, and "list the failed jobs" is a read like any other -- so it takes a Grant and every implementation filters by auth.Tenant(g). A provider that answered across tenants would be the one query in the collection that leaks, and it would leak the arguments of every job every customer ever queued (RULE 17).

type FileFailedJobProvider

type FileFailedJobProvider struct {
	// contains filtered or unexported fields
}

FileFailedJobProvider keeps the failed jobs in a JSON file.

It answers Illuminate\Queue\Failed\FileFailedJobProvider. It exists for the deployment that has a queue and no database: a single process on one machine, draining a RESP queue, that still wants a dead letter list to survive a restart.

It holds the newest Limit failures and drops the rest, which is Laravel's behaviour and the right one for a file: a dead letter list that grows without bound on a disk nobody is watching is an outage waiting to be filed as a disk-full alert.

It is not for more than one process. The file is rewritten whole under a mutex this process holds, and two of them would lose each other's writes -- which is exactly the case DatabaseFailedJobProvider is for.

func NewFileFailedJobProvider

func NewFileFailedJobProvider(path string, limit int) *FileFailedJobProvider

NewFileFailedJobProvider returns the provider over path.

A limit of zero or less means a hundred, which is Laravel's default.

func (*FileFailedJobProvider) All

All is this tenant's failed jobs, newest first. It answers all().

func (*FileFailedJobProvider) Count

func (p *FileFailedJobProvider) Count(ctx context.Context, g auth.Grant, connectionName, queue string) (int, error)

Count is how many of this tenant's jobs have failed. It answers count().

func (*FileFailedJobProvider) Find

Find is one of this tenant's failed jobs. It answers find().

func (*FileFailedJobProvider) Flush

Flush removes this tenant's failed jobs older than age, or all of them when age is zero. It answers flush().

func (*FileFailedJobProvider) Forget

Forget removes one of this tenant's failed jobs. It answers forget().

func (*FileFailedJobProvider) IDs

func (p *FileFailedJobProvider) IDs(ctx context.Context, g auth.Grant, queue string) ([]string, error)

IDs is the identifiers of this tenant's failed jobs, newest first. It answers ids().

func (*FileFailedJobProvider) Log

Log records a job that gave up. It answers log().

func (*FileFailedJobProvider) Prune

func (p *FileFailedJobProvider) Prune(_ context.Context, g auth.Grant, before time.Time) (int, error)

Prune removes this tenant's failed jobs that failed before an instant, and returns how many went. It answers prune().

type NullFailedJobProvider

type NullFailedJobProvider struct{}

NullFailedJobProvider accepts every failure and keeps none of them.

It answers Illuminate\Queue\Failed\NullFailedJobProvider. It is what an application wires when the dead letter list lives somewhere else entirely -- an error tracker, a log pipeline -- and keeping a second copy in the database would be a table nobody reads.

It is a value and not a pointer, for the reason NullQueue is: a nil pointer that silently swallows every failure is a worse mistake than the one this type exists to make cheap.

func (NullFailedJobProvider) All

All is empty. It answers all().

func (NullFailedJobProvider) Count

Count is zero. It answers count().

func (NullFailedJobProvider) Find

Find never finds anything. It answers find().

func (NullFailedJobProvider) Flush

Flush has nothing to flush. It answers flush().

func (NullFailedJobProvider) Forget

Forget has nothing to forget. It answers forget(), which returns true in PHP -- the caller asked for the job to be gone, and it is.

func (NullFailedJobProvider) IDs

IDs is empty. It answers ids().

func (NullFailedJobProvider) Log

Log discards the failure and returns no id. It answers log().

type PrunableFailedJobProvider

type PrunableFailedJobProvider interface {
	// Prune removes the entries that failed before this instant, and returns
	// how many went.
	Prune(ctx context.Context, g auth.Grant, before time.Time) (int, error)
}

PrunableFailedJobProvider is a provider that can drop old entries.

It answers Illuminate\Queue\Failed\PrunableFailedJobProvider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL