feat: add independent platform modules and multi-provider storage
This commit is contained in:
62
queue/queue.go
Normal file
62
queue/queue.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Package queue defines durable, at-least-once work delivery. Handlers must be
|
||||
// idempotent: a crash after a business commit but before ACK can cause redelivery.
|
||||
// This is a message queue, not a workflow engine or a distributed transaction.
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrFull = errors.New("queue: capacity reached")
|
||||
var ErrConflict = errors.New("queue: message ID already has different content")
|
||||
var ErrLeaseLost = errors.New("queue: delivery ownership changed")
|
||||
|
||||
type Message struct {
|
||||
ID string `json:"id"` // Stable producer identity, saved before submitting.
|
||||
Name string `json:"name"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
type Receipt struct {
|
||||
ID string `json:"id"`
|
||||
BackendID string `json:"backendId"`
|
||||
}
|
||||
type Delivery struct {
|
||||
Message
|
||||
Attempt int64 `json:"attempt"`
|
||||
}
|
||||
type Handler func(context.Context, Delivery) error
|
||||
type Publisher interface {
|
||||
Publish(context.Context, Message) (Receipt, error)
|
||||
}
|
||||
type Consumer interface {
|
||||
Run(context.Context, Handler) error
|
||||
}
|
||||
|
||||
// Reject marks a known permanent failure; it is retained for operator review.
|
||||
// Ordinary errors are retried up to the consumer's configured attempt limit.
|
||||
type Rejection struct{ Code string }
|
||||
|
||||
func (e *Rejection) Error() string { return "queue: rejected: " + e.Code }
|
||||
func Reject(code string) error { return &Rejection{Code: code} }
|
||||
|
||||
func Validate(ctx context.Context, m Message, maxBytes int) error {
|
||||
if ctx == nil {
|
||||
return errors.New("queue: context required")
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range []string{m.ID, m.Name} {
|
||||
if v == "" || len(v) > 128 || strings.IndexFunc(v, func(r rune) bool { return r < 33 || r > 126 }) >= 0 {
|
||||
return errors.New("queue: ID and name must be 1..128 printable ASCII characters")
|
||||
}
|
||||
}
|
||||
if len(m.Payload) > maxBytes || !json.Valid(m.Payload) {
|
||||
return fmt.Errorf("queue: payload must be valid JSON within %d bytes", maxBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user