81 lines
2.5 KiB
Go
81 lines
2.5 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
|
)
|
|
|
|
var SQSClient SQS
|
|
|
|
// QueueMessage is one message taken off the queue. ReceiptHandle is what
|
|
// identifies it for deletion — it belongs to this delivery, not to the
|
|
// message, so it cannot be held across receives.
|
|
type QueueMessage struct {
|
|
Body string
|
|
ReceiptHandle string
|
|
}
|
|
|
|
type SQS interface {
|
|
ReceiveMessages(ctx context.Context) ([]QueueMessage, error)
|
|
DeleteMessage(ctx context.Context, receiptHandle string) error
|
|
}
|
|
|
|
type SQSConcrete struct {
|
|
SQSClient *sqs.Client
|
|
QueueURL string
|
|
}
|
|
|
|
// receiveWaitSeconds turns every receive into a long poll: the call parks on
|
|
// the server until a message arrives or this elapses, so an event is picked up
|
|
// within milliseconds of being published while an idle consumer costs one
|
|
// request every twenty seconds rather than spinning. 20 is the AWS maximum.
|
|
const receiveWaitSeconds = 20
|
|
|
|
func (svc SQSConcrete) ReceiveMessages(ctx context.Context) ([]QueueMessage, error) {
|
|
output, err := svc.SQSClient.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
|
|
QueueUrl: &svc.QueueURL,
|
|
MaxNumberOfMessages: 10,
|
|
WaitTimeSeconds: receiveWaitSeconds,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
messages := make([]QueueMessage, 0, len(output.Messages))
|
|
for _, message := range output.Messages {
|
|
if message.Body == nil || message.ReceiptHandle == nil {
|
|
continue
|
|
}
|
|
messages = append(messages, QueueMessage{
|
|
Body: *message.Body,
|
|
ReceiptHandle: *message.ReceiptHandle,
|
|
})
|
|
}
|
|
|
|
return messages, nil
|
|
}
|
|
|
|
// DeleteMessage acknowledges a message. Until this is called the message is
|
|
// merely invisible, and it returns to the queue when its visibility timeout
|
|
// expires — which is how a handler that failed gets another attempt.
|
|
func (svc SQSConcrete) DeleteMessage(ctx context.Context, receiptHandle string) error {
|
|
_, err := svc.SQSClient.DeleteMessage(ctx, &sqs.DeleteMessageInput{
|
|
QueueUrl: &svc.QueueURL,
|
|
ReceiptHandle: &receiptHandle,
|
|
})
|
|
return err
|
|
}
|
|
|
|
// AssertSuccessfulConnection verifies the queue is reachable and readable with
|
|
// the configured credentials. It panics on failure, since a service whose
|
|
// consumer cannot start would silently leave every video stuck "processing".
|
|
func (svc SQSConcrete) AssertSuccessfulConnection(ctx context.Context) {
|
|
if _, err := svc.SQSClient.GetQueueAttributes(ctx, &sqs.GetQueueAttributesInput{
|
|
QueueUrl: &svc.QueueURL,
|
|
}); err != nil {
|
|
panic(fmt.Errorf("sqs: cannot connect to queue %q: %w", svc.QueueURL, err))
|
|
}
|
|
}
|