azure-service-bus-go/deadletter.go

162 строки
4.7 KiB
Go

package servicebus
import (
"context"
"sync"
"github.com/devigned/tab"
)
type (
// Closer provides the ability to close an entity
Closer interface {
Close(ctx context.Context) error
}
// ReceiveOner provides the ability to receive and handle events
ReceiveOner interface {
Closer
ReceiveOne(ctx context.Context, handler Handler) error
}
// DeadLetterBuilder provides the ability to create a new receiver addressed to a given entity's dead letter queue.
DeadLetterBuilder interface {
NewDeadLetterReceiver(ctx context.Context, opts ...ReceiverOption) (ReceiveOner, error)
}
// TransferDeadLetterBuilder provides the ability to create a new receiver addressed to a given entity's transfer
// dead letter queue.
TransferDeadLetterBuilder interface {
NewTransferDeadLetterReceiver(ctx context.Context, opts ...ReceiverOption) (ReceiveOner, error)
}
// DeadLetter represents a dead letter queue in Azure Service Bus.
//
// Azure Service Bus queues, topics and subscriptions provide a secondary sub-queue, called a dead-letter queue
// (DLQ). The dead-letter queue does not need to be explicitly created and cannot be deleted or otherwise managed
// independent of the main entity.
//
// The purpose of the dead-letter queue is to hold messages that cannot be delivered to any receiver, or messages
// that could not be processed. Messages can then be removed from the DLQ and inspected. An application might, with
// help of an operator, correct issues and resubmit the message, log the fact that there was an error, and take
// corrective action.
//
// From an API and protocol perspective, the DLQ is mostly similar to any other queue, except that messages can only
// be submitted via the dead-letter operation of the parent entity. In addition, time-to-live is not observed, and
// you can't dead-letter a message from a DLQ. The dead-letter queue fully supports peek-lock delivery and
// transactional operations.
//
// Note that there is no automatic cleanup of the DLQ. Messages remain in the DLQ until you explicitly retrieve
// them from the DLQ and call Complete() on the dead-letter message.
DeadLetter struct {
builder DeadLetterBuilder
rMu sync.Mutex
receiver ReceiveOner
}
// TransferDeadLetter represents a transfer dead letter queue in Azure Service Bus.
//
// Messages will be sent to the transfer dead-letter queue under the following conditions:
// - A message passes through more than 3 queues or topics that are chained together.
// - The destination queue or topic is disabled or deleted.
// - The destination queue or topic exceeds the maximum entity size.
TransferDeadLetter struct {
builder TransferDeadLetterBuilder
rMu sync.Mutex
receiver ReceiveOner
}
)
// NewDeadLetter constructs an instance of DeadLetter which represents a dead letter queue in Azure Service Bus
func NewDeadLetter(builder DeadLetterBuilder) *DeadLetter {
return &DeadLetter{
builder: builder,
}
}
// ReceiveOne will receive one message from the dead letter queue
func (dl *DeadLetter) ReceiveOne(ctx context.Context, handler Handler) error {
if err := dl.ensureReceiver(ctx); err != nil {
return err
}
return dl.receiver.ReceiveOne(ctx, handler)
}
// Close the underlying connection to Service Bus
func (dl *DeadLetter) Close(ctx context.Context) error {
dl.rMu.Lock()
defer dl.rMu.Unlock()
if dl.receiver == nil {
return nil
}
if err := dl.receiver.Close(ctx); err != nil {
tab.For(ctx).Error(err)
return err
}
return nil
}
func (dl *DeadLetter) ensureReceiver(ctx context.Context) error {
dl.rMu.Lock()
defer dl.rMu.Unlock()
r, err := dl.builder.NewDeadLetterReceiver(ctx)
if err != nil {
return err
}
dl.receiver = r
return nil
}
// NewTransferDeadLetter constructs an instance of DeadLetter which represents a transfer dead letter queue in
// Azure Service Bus
func NewTransferDeadLetter(builder TransferDeadLetterBuilder) *TransferDeadLetter {
return &TransferDeadLetter{
builder: builder,
}
}
// ReceiveOne will receive one message from the dead letter queue
func (dl *TransferDeadLetter) ReceiveOne(ctx context.Context, handler Handler) error {
if err := dl.ensureReceiver(ctx); err != nil {
return err
}
return dl.receiver.ReceiveOne(ctx, handler)
}
// Close the underlying connection to Service Bus
func (dl *TransferDeadLetter) Close(ctx context.Context) error {
dl.rMu.Lock()
defer dl.rMu.Unlock()
if dl.receiver == nil {
return nil
}
if err := dl.receiver.Close(ctx); err != nil {
tab.For(ctx).Error(err)
return err
}
return nil
}
func (dl *TransferDeadLetter) ensureReceiver(ctx context.Context) error {
dl.rMu.Lock()
defer dl.rMu.Unlock()
r, err := dl.builder.NewTransferDeadLetterReceiver(ctx)
if err != nil {
return err
}
dl.receiver = r
return nil
}