Files
SQS-service/app/gosqs/get_queue_attributes.go
T
Repinoid fd8ffc329d P1.1: per-queue блокировки вместо глобального лока на операции с очередью
Все операции с содержимым очереди (send/receive/delete/visibility/purge/attributes/
tags/admin/gauge/tick) переведены на per-queue RWMutex (QueueStore.GetLocked/
GetRLocked + queue.LockMu). Глобальный SyncQueues-лок остался только для map и
неизменяемых полей. DLQ-переносы берут лок DLQ отдельно (порядок main→DLQ).
marshalQueueMeta больше не мутирует очередь (DTO-снапшот). Устранён дедлок
create_queue (setQueueAttributesV1 вызывается вне глобального лока). Добавлены
конкурентные тесты; go test -race зелёный. Снимает боттлнек: синхронный Redis-I/O
больше не держит глобальный лок.
2026-10-06 18:58:16 +03:00

118 lines
4.5 KiB
Go

// Изменено: 2026-04-11 — убрана зависимость от copystructure (лишняя аллокация)
// GetQueueAttributesV1 — возвращает атрибуты очереди тенанта.
package gosqs
import (
"fmt"
"net/http"
"strconv"
"strings"
"shared-sqs/app/interfaces"
"shared-sqs/app/models"
"shared-sqs/app/utils"
log "github.com/sirupsen/logrus"
)
func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
requestBody := models.NewGetQueueAttributesRequest()
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
if !ok {
log.Error("Invalid Request - GetQueueAttributesV1")
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
}
if requestBody.QueueUrl == "" {
log.Error("Missing QueueUrl - GetQueueAttributesV1")
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
}
t := getTenantFromContext(req)
if t == nil {
return utils.CreateErrorResponseV1("InvalidClientTokenId", true)
}
// Определяем набор запрошенных атрибутов (или All)
requestedAttributes := func() map[string]bool {
attrs := map[string]bool{}
if len(requestBody.AttributeNames) == 0 {
return map[string]bool{"All": true}
}
for _, attr := range requestBody.AttributeNames {
if "All" == attr {
return map[string]bool{"All": true}
}
attrs[attr] = true
}
return attrs
}()
// Фильтруем атрибуты без deep copy — простая проверка через map lookup
_, wantAll := requestedAttributes["All"]
shouldInclude := func(attr string) bool {
if wantAll {
return true
}
_, ok := requestedAttributes[attr]
return ok
}
uriSegments := strings.Split(requestBody.QueueUrl, "/")
queueName := uriSegments[len(uriSegments)-1]
key := tenantQueueKey(t.AccessKey, queueName)
log.Infof("Get Queue Attributes: %s (tenant: %s)", queueName, t.ID)
queueAttributes := make([]models.Attribute, 0)
queue := models.SyncQueues.GetRLocked(key)
if queue == nil {
log.Errorf("Get Queue Attributes: %s queue does not exist for tenant %s", queueName, t.ID)
return utils.CreateErrorResponseV1("QueueNotFound", true)
}
defer queue.RUnlockMu()
if shouldInclude("DelaySeconds") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "DelaySeconds", Value: strconv.Itoa(queue.DelaySeconds)})
}
if shouldInclude("MaximumMessageSize") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "MaximumMessageSize", Value: strconv.Itoa(queue.MaximumMessageSize)})
}
if shouldInclude("MessageRetentionPeriod") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "MessageRetentionPeriod", Value: strconv.Itoa(queue.MessageRetentionPeriod)})
}
if shouldInclude("ReceiveMessageWaitTimeSeconds") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "ReceiveMessageWaitTimeSeconds", Value: strconv.Itoa(queue.ReceiveMessageWaitTimeSeconds)})
}
if shouldInclude("VisibilityTimeout") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "VisibilityTimeout", Value: strconv.Itoa(queue.VisibilityTimeout)})
}
if shouldInclude("ApproximateNumberOfMessages") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "ApproximateNumberOfMessages", Value: strconv.Itoa(len(queue.Messages))})
}
if shouldInclude("ApproximateNumberOfMessagesNotVisible") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "ApproximateNumberOfMessagesNotVisible", Value: strconv.Itoa(numberOfHiddenMessagesInQueue(queue))})
}
if shouldInclude("CreatedTimestamp") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "CreatedTimestamp", Value: "0000000000"})
}
if shouldInclude("LastModifiedTimestamp") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "LastModifiedTimestamp", Value: "0000000000"})
}
if shouldInclude("QueueArn") {
queueAttributes = append(queueAttributes, models.Attribute{Name: "QueueArn", Value: queue.Arn})
}
if shouldInclude("RedrivePolicy") && queue.DeadLetterQueue != nil {
queueAttributes = append(queueAttributes, models.Attribute{
Name: "RedrivePolicy",
Value: fmt.Sprintf(`{"maxReceiveCount":"%d", "deadLetterTargetArn":"%s"}`, queue.MaxReceiveCount, queue.DeadLetterQueue.Arn),
})
}
respStruct := models.GetQueueAttributesResponse{
Xmlns: models.BaseXmlns,
Result: models.GetQueueAttributesResult{Attrs: queueAttributes},
Metadata: models.BaseResponseMetadata,
}
return http.StatusOK, respStruct
}