FEAT: Discovery Video Service Subscription
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// Package services holds this service's AWS clients. Discovery reaches exactly
|
||||
// one AWS API — the SQS queue subscribed to the catalogue topic — so unlike
|
||||
// cms there is no S3 or MediaConvert here, and nothing database-related.
|
||||
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 serve a catalogue that silently stops growing.
|
||||
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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user