mirror of
https://github.com/hatchet-dev/hatchet.git
synced 2026-03-29 03:01:00 -05:00
Adds a queue that gets triggered whenever a cron is created, updated, or deleted that will automatically update the list of crons running in the ticker.
265 lines
6.0 KiB
Go
265 lines
6.0 KiB
Go
package msgqueue
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/hatchet-dev/hatchet/pkg/random"
|
|
)
|
|
|
|
type Queue interface {
|
|
// Name returns the name of the queue.
|
|
Name() string
|
|
|
|
// Durable returns true if this queue should survive task queue restarts.
|
|
Durable() bool
|
|
|
|
// AutoDeleted returns true if this queue should be deleted when the last consumer unsubscribes.
|
|
AutoDeleted() bool
|
|
|
|
// Exclusive returns true if this queue should only be accessed by the current connection.
|
|
Exclusive() bool
|
|
|
|
// FanoutExchangeKey returns which exchange the queue should be subscribed to. This is only currently relevant
|
|
// to tenant pub/sub queues.
|
|
//
|
|
// In RabbitMQ terminology, the existence of a subscriber key means that the queue is bound to a fanout
|
|
// exchange, and a new random queue is generated for each connection when connections are retried.
|
|
FanoutExchangeKey() string
|
|
|
|
// DLQ returns the queue's dead letter queue, if it exists.
|
|
DLQ() Queue
|
|
|
|
// IsDLQ returns true if the queue is a dead letter queue.
|
|
IsDLQ() bool
|
|
|
|
// We distinguish between a static DLQ or an automatic DLQ. An automatic DLQ will automatically retry messages
|
|
// in a loop with a 5-second backoff, and each subscription to the regular Queue will include a subscription
|
|
// to the DLQ.
|
|
IsAutoDLQ() bool
|
|
|
|
// IsExpirable refers to whether the queue itself is expirable
|
|
IsExpirable() bool
|
|
}
|
|
|
|
type staticQueue string
|
|
|
|
const (
|
|
TASK_PROCESSING_QUEUE staticQueue = "task_processing_queue_v2"
|
|
OLAP_QUEUE staticQueue = "olap_queue_v2"
|
|
DISPATCHER_DEAD_LETTER_QUEUE staticQueue = "dispatcher_dlq_v2"
|
|
TICKER_UPDATE_QUEUE staticQueue = "ticker_update_queue_v2"
|
|
)
|
|
|
|
func (s staticQueue) Name() string {
|
|
return string(s)
|
|
}
|
|
|
|
func (s staticQueue) Durable() bool {
|
|
return true
|
|
}
|
|
|
|
func (s staticQueue) AutoDeleted() bool {
|
|
return false
|
|
}
|
|
|
|
func (s staticQueue) Exclusive() bool {
|
|
return false
|
|
}
|
|
|
|
func (s staticQueue) FanoutExchangeKey() string {
|
|
return ""
|
|
}
|
|
|
|
func (s staticQueue) DLQ() Queue {
|
|
name := fmt.Sprintf("%s_dlq", s)
|
|
|
|
return dlq{
|
|
staticQueue: staticQueue(name),
|
|
isAutoDLQ: true,
|
|
}
|
|
}
|
|
|
|
func (s staticQueue) IsDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (s staticQueue) IsAutoDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (s staticQueue) IsExpirable() bool {
|
|
return false
|
|
}
|
|
|
|
type dlq struct {
|
|
staticQueue
|
|
|
|
isAutoDLQ bool
|
|
}
|
|
|
|
func (d dlq) IsDLQ() bool {
|
|
return true
|
|
}
|
|
|
|
func (d dlq) IsAutoDLQ() bool {
|
|
return d.isAutoDLQ
|
|
}
|
|
|
|
func (d dlq) DLQ() Queue {
|
|
return nil
|
|
}
|
|
|
|
func NewRandomStaticQueue() staticQueue {
|
|
randBytes, _ := random.Generate(8)
|
|
return staticQueue(fmt.Sprintf("random_static_queue_v2_%s", randBytes))
|
|
}
|
|
|
|
// dispatcherQueue is a type of queue which is durable and exclusive, but utilizes a per-queue TTL
|
|
// and per-message TTL + DLX to handle messages which are not consumed within a certain time period,
|
|
// for example if the dispatcher goes down.
|
|
type dispatcherQueue string
|
|
|
|
func (d dispatcherQueue) Name() string {
|
|
return string(d)
|
|
}
|
|
|
|
func (d dispatcherQueue) Durable() bool {
|
|
return true
|
|
}
|
|
|
|
func (d dispatcherQueue) AutoDeleted() bool {
|
|
return false
|
|
}
|
|
|
|
func (d dispatcherQueue) Exclusive() bool {
|
|
return true
|
|
}
|
|
|
|
func (d dispatcherQueue) FanoutExchangeKey() string {
|
|
return ""
|
|
}
|
|
|
|
func (d dispatcherQueue) DLQ() Queue {
|
|
return dlq{
|
|
staticQueue: DISPATCHER_DEAD_LETTER_QUEUE,
|
|
// we maintain a completely separate DLQ for dispatcher queues
|
|
isAutoDLQ: false,
|
|
}
|
|
}
|
|
|
|
func (d dispatcherQueue) IsDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (d dispatcherQueue) IsAutoDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (d dispatcherQueue) IsExpirable() bool {
|
|
return true
|
|
}
|
|
|
|
func QueueTypeFromDispatcherID(d uuid.UUID) dispatcherQueue {
|
|
return dispatcherQueue(d.String() + "_dispatcher_v1")
|
|
}
|
|
|
|
type consumerQueue string
|
|
|
|
func (s consumerQueue) Name() string {
|
|
return string(s)
|
|
}
|
|
|
|
func (n consumerQueue) Durable() bool {
|
|
return false
|
|
}
|
|
|
|
func (n consumerQueue) AutoDeleted() bool {
|
|
return true
|
|
}
|
|
|
|
func (n consumerQueue) Exclusive() bool {
|
|
return true
|
|
}
|
|
|
|
func (n consumerQueue) FanoutExchangeKey() string {
|
|
return ""
|
|
}
|
|
|
|
func (n consumerQueue) DLQ() Queue {
|
|
return nil
|
|
}
|
|
|
|
func (n consumerQueue) IsDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (n consumerQueue) IsAutoDLQ() bool {
|
|
return false
|
|
}
|
|
|
|
func (n consumerQueue) IsExpirable() bool {
|
|
// since exclusive and auto-deleted, it's not expirable
|
|
return false
|
|
}
|
|
|
|
const (
|
|
Scheduler = "scheduler"
|
|
)
|
|
|
|
func QueueTypeFromPartitionIDAndController(p, controller string) consumerQueue {
|
|
return consumerQueue(fmt.Sprintf("%s_%s_v1", p, controller))
|
|
}
|
|
|
|
type fanoutQueue struct {
|
|
consumerQueue
|
|
}
|
|
|
|
// The fanout exchange key for a consumer is the name of the consumer queue.
|
|
func (f fanoutQueue) FanoutExchangeKey() string {
|
|
return f.consumerQueue.Name()
|
|
}
|
|
|
|
func TenantEventConsumerQueue(t uuid.UUID) fanoutQueue {
|
|
// generate a unique queue name for the tenant
|
|
return fanoutQueue{
|
|
consumerQueue: consumerQueue(GetTenantExchangeName(t)),
|
|
}
|
|
}
|
|
|
|
func GetTenantExchangeName(t uuid.UUID) string {
|
|
return t.String() + "_v1"
|
|
}
|
|
|
|
type AckHook func(task *Message) error
|
|
|
|
type MessageQueue interface {
|
|
// Clone copies the message queue with a new instance.
|
|
Clone() (func() error, MessageQueue, error)
|
|
|
|
// SetQOS sets the quality of service for the message queue.
|
|
SetQOS(prefetchCount int)
|
|
|
|
// SendMessage sends a message to the message queue.
|
|
SendMessage(ctx context.Context, queue Queue, msg *Message) error
|
|
|
|
// Subscribe subscribes to the task queue. It returns a cleanup function that should be called when the
|
|
// subscription is no longer needed.
|
|
Subscribe(queue Queue, preAck AckHook, postAck AckHook) (func() error, error)
|
|
|
|
// RegisterTenant registers a new pub/sub mechanism for a tenant. This should be called when a
|
|
// new tenant is created. If this is not called, implementors should ensure that there's a check
|
|
// on the first message to a tenant to ensure that the tenant is registered, and store the tenant
|
|
// in an LRU cache which lives in-memory.
|
|
RegisterTenant(ctx context.Context, tenantId uuid.UUID) error
|
|
|
|
// IsReady returns true if the task queue is ready to accept tasks.
|
|
IsReady() bool
|
|
}
|
|
|
|
func NoOpHook(task *Message) error {
|
|
return nil
|
|
}
|