Skip to content
This repository was archived by the owner on Mar 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions localstack-core/localstack/services/sqs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@

# the default maximum message size in SQS
DEFAULT_MAXIMUM_MESSAGE_SIZE = 1048576
APPROXIMATE_DYNAMIC_ATTRIBUTES = [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe just call it DYNAMIC_ATTRIBUTES?

# these attributes are not set once, but calculated dynamically, therefore we do not to store them
# internally with the other attributes, and only include them in the necessary responses.
QueueAttributeName.ApproximateNumberOfMessages,
QueueAttributeName.ApproximateNumberOfMessagesDelayed,
QueueAttributeName.ApproximateNumberOfMessagesNotVisible,
]
INTERNAL_QUEUE_ATTRIBUTES = [
# these attributes cannot be changed by set_queue_attributes and should
# therefore be ignored when comparing queue attributes for create_queue
Expand Down
38 changes: 15 additions & 23 deletions localstack-core/localstack/services/sqs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
TagMap,
)
from localstack.services.sqs import constants as sqs_constants
from localstack.services.sqs.constants import APPROXIMATE_DYNAMIC_ATTRIBUTES
from localstack.services.sqs.exceptions import (
InvalidAttributeValue,
InvalidParameterValueException,
Expand All @@ -43,7 +44,7 @@
)
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
from localstack.utils.aws.arns import get_partition
from localstack.utils.strings import long_uid
from localstack.utils.strings import camel_to_snake_case, long_uid
from localstack.utils.time import now
from localstack.utils.urls import localstack_host

Expand Down Expand Up @@ -346,15 +347,6 @@ def shutdown(self):

def default_attributes(self) -> QueueAttributeMap:
return {
QueueAttributeName.ApproximateNumberOfMessages: lambda: str(
self.approx_number_of_messages
),
QueueAttributeName.ApproximateNumberOfMessagesNotVisible: lambda: str(
self.approx_number_of_messages_not_visible
),
QueueAttributeName.ApproximateNumberOfMessagesDelayed: lambda: str(
self.approx_number_of_messages_delayed
),
QueueAttributeName.CreatedTimestamp: str(now()),
QueueAttributeName.DelaySeconds: "0",
QueueAttributeName.LastModifiedTimestamp: str(now()),
Expand Down Expand Up @@ -473,15 +465,15 @@ def maximum_message_size(self) -> int:
return int(self.attributes[QueueAttributeName.MaximumMessageSize])

@property
def approx_number_of_messages(self) -> int:
def approximate_number_of_messages(self) -> int:
raise NotImplementedError

@property
def approx_number_of_messages_not_visible(self) -> int:
def approximate_number_of_messages_not_visible(self) -> int:
return len(self.inflight)

@property
def approx_number_of_messages_delayed(self) -> int:
def approximate_number_of_messages_delayed(self) -> int:
return len(self.delayed)

def validate_receipt_handle(self, receipt_handle: str):
Expand Down Expand Up @@ -724,7 +716,7 @@ def get_queue_attributes(self, attribute_names: AttributeNameList = None) -> dic
return {}

if QueueAttributeName.All in attribute_names:
attribute_names = self.attributes.keys()
attribute_names = list(self.attributes.keys()) + APPROXIMATE_DYNAMIC_ATTRIBUTES

result: dict[QueueAttributeName, str] = {}

Expand All @@ -734,13 +726,13 @@ def get_queue_attributes(self, attribute_names: AttributeNameList = None) -> dic
except AttributeError:
raise InvalidAttributeName(f"Unknown Attribute {attr}.")

value = self.attributes.get(attr)
if callable(value):
func = value
value = func()
if value is not None:
result[attr] = value
elif value == "False" or value == "True":
if attr in APPROXIMATE_DYNAMIC_ATTRIBUTES:
# The approximate_* attributes are calculated on the spot when accessed.
# We have a @property for each of those which calculates the value.
value = str(getattr(self, camel_to_snake_case(attr)))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i would prefer a less magical approach and just have a match/case block. this way we know where the properties are being access:

something like

        result: dict[QueueAttributeName, str] = {}

        for attr in attribute_names:
            try:
                getattr(QueueAttributeName, attr)
            except AttributeError:
                raise InvalidAttributeName(f"Unknown Attribute {attr}.")
            
            value = None
            match attr:
                case QueueAttributeName.ApproximateNumberOfMessages:
                    value = self.approx_number_of_messages
                case QueueAttributeName.ApproximateNumberOfMessagesDelayed:
                    value = ...
                case _:
                    value = self.attributes.get(attr)

            if value == "False" or value == "True":
                result[attr] = value.lower()
            elif value is not None:
                result[attr] = value
        return result

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, and yes, it is definitely simpler this way. I guess I couldn't resist the magical approach 😃

else:
value = self.attributes.get(attr)
if value == "False" or value == "True":
result[attr] = value.lower()
elif value is not None:
result[attr] = value
Expand Down Expand Up @@ -799,7 +791,7 @@ def clear(self):
self.visible.queue.clear()

@property
def approx_number_of_messages(self):
def approximate_number_of_messages(self):
return self.visible.qsize()

def shutdown(self):
Expand Down Expand Up @@ -1025,7 +1017,7 @@ def __init__(self, name: str, region: str, account_id: str, attributes=None, tag
self.deduplication_scope = self.attributes[QueueAttributeName.DeduplicationScope]

@property
def approx_number_of_messages(self):
def approximate_number_of_messages(self):
n = 0
for message_group in self.message_groups.values():
n += len(message_group.messages)
Expand Down
10 changes: 5 additions & 5 deletions localstack-core/localstack/services/sqs/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,21 +321,21 @@ def publish_approximate_cloudwatch_metrics(self):
SqsMetricBatchData(
QueueName=queue.name,
MetricName="ApproximateNumberOfMessagesVisible",
Value=queue.approx_number_of_messages,
Value=queue.approximate_number_of_messages,
)
)
batch_data.append(
SqsMetricBatchData(
QueueName=queue.name,
MetricName="ApproximateNumberOfMessagesNotVisible",
Value=queue.approx_number_of_messages_not_visible,
Value=queue.approximate_number_of_messages_not_visible,
)
)
batch_data.append(
SqsMetricBatchData(
QueueName=queue.name,
MetricName="ApproximateNumberOfMessagesDelayed",
Value=queue.approx_number_of_messages_delayed,
Value=queue.approximate_number_of_messages_delayed,
)
)

Expand Down Expand Up @@ -465,7 +465,7 @@ def submit(self, move_task: MessageMoveTask):
try:
source_queue = self._get_queue_by_arn(move_task.source_arn)
move_task.approximate_number_of_messages_to_move = (
source_queue.approx_number_of_messages
source_queue.approximate_number_of_messages
)
move_task.approximate_number_of_messages_moved = 0
move_task.mark_started()
Expand Down Expand Up @@ -1130,7 +1130,7 @@ def receive_message(
num = override
elif num == -1:
# backdoor to get all messages
num = queue.approx_number_of_messages
num = queue.approximate_number_of_messages
elif (
num < 1 or num > MAX_NUMBER_OF_MESSAGES
) and not SQS_DISABLE_MAX_NUMBER_OF_MESSAGE_LIMIT:
Expand Down
Loading