Can SQS scale up to 1,000,000 queues for a single account? - amazon-web-services

I need a messaging service that allows me to create a channel for each user in order to facilitate real-time notifications. If I have somewhere between 100,000 and 1 million users, does it make sense to create an SQS queue for each of these users?
According to the SQS pricing documentation it would only cost $0.40 to create 1 million queues, but would I run into scaling problems?
Also, is there a way to set an expiration date on a queue? If a user deletes their account, then their queue no longer needs to exist.

Creating queues is not an issue here. Polling or even long polling the queue is going to be really expensive for you. In order to process real-time notifications, you need to poll every queue, 1M of them for lets say every 5 seconds.
Based on SQS Pricing, Price per 1 Million Requests after free tier is $0.00000040 per request.
That means you will be calling the ReceiveMessage API for about:
1000000 queues * 17280 (1 day in seconds / 5 seconds) = 17280000000 times.
Which is about $6912.00 per day for the worst case scenarios.
You need to architect the solution in a better way.

"a channel for each user in order to facilitate real-time notifications" - you don't need a dedicated queue per user for this - you can do this with one main messaging queue, and depending on your traffic patterns, probably a few overflow queues to deal with ultra-high-traffic users.
"One queue" you say? "How on earth will that scale to 1M users?"
The number of users doesn't matter. What matters is that your message consumption can keep up with message production (incoming messages per second). If you can do that, it'll seem like realtime to your users.
Message consumption can scale to as high as you're willing to spend - just spawn up a thread to handle each incoming message (use a thread pool!)
of course, you'll need to limit each host to X processing threads, based on how many it can handle (hence 'as high as you're willing')
the overflow queues are to keep costs under control - if you're scaled to handle 10K messages per second, you don't want a user to come along and send you 1M messages per second, knocking out your service and the rest of your customers - throttle them to some reasonable limit, and process the rest of those messages at a lower priority.
"But... millions." - Yes. SQS can handle a lot. And a single multi-tenant queue will scale much better in terms of architecture and cost than multiple single-tenant channels ever will.

Most AWS resource volumes are limited, and while I'm not finding any account limits on numbers of queues, I may have missed it or it may just not be published. I definitely wouldn't be excited about the queue per notification destination architecture you're pitching here, if my co-workers brought it to me. I would be concerned about the cost of putting the same notification to all the listeners' queues, and then reading them back out.
What you're describing sounds more like pub sub. Or, if you want better delivery guarantees, maybe a stream like kinesis or kafka. I've also heard of folks using Redis to implement this kind of thing.

Could you potentially design a queue consumer that pauses after a certain period of idle time to prevent unnecessary API calls?
Something like:
const AWS = require('aws-sdk');
// Set the AWS region
AWS.config.update({ region: 'YOUR_REGION' });
// Set the parameters for the consumer
const IDLE_TIMEOUT = 300; // Stop Polling after 300 seconds of idle time
const POLLING_INTERVAL = 10; // Poll the queue every 10 seconds
// Create an SQS client
const sqs = new AWS.SQS();
// Set a flag to control the Polling loop
let isPolling = false;
function startPolling() {
isPolling = true;
}
function stopPolling() {
isPolling = false;
}
async function processMessage(message) {
// Do something with the message here
console.log(message);
}
async function pollQueue() {
// Set the idle timer to 0
let idleTimer = 0;
while (isPolling) {
// Check if the idle timer has reached the timeout
if (idleTimer > IDLE_TIMEOUT) {
// Stop Polling
stopPolling();
break;
}
// Poll the queue for messages
const params = {
QueueUrl: 'YOUR_QUEUE_URL',
MaxNumberOfMessages: 10,
WaitTimeSeconds: POLLING_INTERVAL,
};
const data = await sqs.receiveMessage(params).promise();
// Get the messages from the response
const messages = data.Messages || [];
// Process the messages
for (const message of messages) {
await processMessage(message);
// Delete the message from the queue
const deleteParams = {
QueueUrl: 'YOUR_QUEUE_URL',
ReceiptHandle: message.ReceiptHandle,
};
await sqs.deleteMessage(deleteParams).promise();
// Reset the idle timer
idleTimer = 0;
}
// Increment the idle timer
idleTimer += POLLING_INTERVAL;
// Sleep for the Polling interval
await new Promise((resolve) => setTimeout(resolve, POLLING_INTERVAL * 1000));
}
}
// Start the Polling loop when the startPolling function is called
startPolling();
pollQueue();
This way you could only activate the consumer after some process begins, and avoid constant polling of the queue when the service is inactive.

Related

SQS Lambda Trigger polling rate

I'm trying to understand how SQS Lambda Triggers works when polling for messages from the Queue.
Criteria
I'm trying to make sure that not more than 3 messages are processed within a period of 1 second.
Idea
My idea is to set the trigger BatchSize to 3 and setting the ReceiveMessageWaitTimeSeconds of the queue to 1 second. Am I thinking about this correctly?
Edit:
I did some digging and looks like I can set a concurrency limit on my Lambda. If I set my Lambda concurrency limit to one that ensures only one batch of message gets processed at a time. If my lambda runs for a second, then the next batch of messages gets processed at least a second later. The gotcha here is long-polling auto scales the number of asychronous polling on the queue based on message volume. This means, the lambdas can potentailly throttle when a large number of messages comes in. When the lambdas throttle, the message goes back to the queue until it eventually goes into the DLQ.
ReceiveMessageWaitTimeSeconds is used for long polling. It is the length of time, in seconds, for which a ReceiveMessage action waits for messages to arrive (docs). Long polling does not mean that your client will wait for the full length of the time set. If you have it set to one second, but in the queue we already have enough messages, your client will consume them instantaneously and will try to consume again as soon as processing is completed.
If you want to consume certain number of messages at certain rate, you have do this on your application (for example consumes messages on a scheduled basis). SQS by itself does not provide any kind of rate limiting similar to what you would want to accomplish.

Event hub Send event to random partitions but exactly one partition

I have event hub publisher but it is duplicating messages across random partitions multiple times . I want parallel messages for huge number of messages coming in which should go into random but exactly in one partition from where the consumer should get the data .
How do I do that . This is causing the message to be duplicated .
EventHubProducerClientOptions producerClientOptions = new EventHubProducerClientOptions
{
RetryOptions = new EventHubsRetryOptions
{
Mode = EventHubsRetryMode.Exponential,
MaximumRetries = 30,
TryTimeout = TimeSpan.FromSeconds(5),
Delay = TimeSpan.FromSeconds(10),
MaximumDelay = TimeSpan.FromSeconds(15),
}
};
using EventDataBatch eventBatch = await producerClient.CreateBatchAsync();
// Add events to the batch. An event is a represented by a collection of bytes and metadata.
eventBatch.TryAdd(eventMessage);
string logInfo = $"[PUBLISHED - [{EventId}]] =======> {message}";
logger.LogInformation(logInfo);
// Use the producer client to send the batch of events to the event hub
await producerClient.SendAsync(eventBatch);
Your code sample is publishing your batch to the Event Hubs gateway, where events will be routed to a partition. For a successful publish operation, each event will be sent to one partition only.
"Successful" is the key in that phrase. You're configuring your retry policy with a TryTimeout of 5 seconds and allowing 30 retries. The duplication that you're seeing is most likely caused by your publish request timing out due to the very short interval, being successfully received by the service, but leaving the service unable to acknowledge success. This will cause the client to consider the operation a failure and retry.
By default, the TryTimeout interval is 60 seconds. I'm not sure why you've chosen to restrict the timeout to such a small value, but I'd strongly advise considering changes. Respectfully, unless you've done profiling and measuring to prove that you need to make changes, I'd advise using the default values for retries in their entirety.

MassTransit SQS polling frequency

The documentation about Consumers says this MassTransit embraces The Hollywood Principle, which states, "Don't call us, we'll call you." But with Amazons SQS at least, that is not at all true. MassTransit does poll the Amazon SQS, but just hides that from a consumer app. I found this by looking at the SQS queue I had created => monitoring tab and checking at "Number Of Empty Receives". I found I had quickly racked up thousands of calls, far from ideal as they can be chargeable.
The AWS solution is to adjust the polling frequency of the queue with a property "Receive Message Wait Time", which means you have something called Long Polling. While I can manually set that up in AWS, I'd rather do that when I configure my consumer instead. The AWS SDK suggests an attribute called "ReceiveMessageWaitTimeSeconds" but I can't seem to set that. How can I set the polling frequency in MassTransit? I can see WaitTimeSeconds in a few places in the configuration but none of this documented at all.
After a bit of playing, this code seems to do what I want but I'm not sure it is correct. Is there a better way?
var busControl = Bus.Factory.CreateUsingAmazonSqs(cfg =>
{
cfg.Host("eu-west-2", h =>
{
h.AccessKey("************");
h.SecretKey("***SECRET***");
});
//Specify a SQS message queue name
cfg.ReceiveEndpoint("my-queue", e =>
{
// create a consumer to read message from q
e.Consumer<EventConsumer>();
e.WaitTimeSeconds = 18;
//etc
You can set WaitTimeSeconds to a longer value to decrease the polling frequency. There is no push from SQS, it always consists of an API call.
As for the Hollywood principle, that's referring to MassTransit, not the transport interactions required by the broker.

Subscribing to AWS SQS Messages

I have large number of messages in AWS SQS Queue. These messages will be pushed to it constantly by other source. There are no proper dynamic on how often those messages will be pushed to queue. Currently, I keep polling SQS every second and checking if there are any messages available in there. Is there any better way of handling this, like receiving notification from SQS or SNS that some messages are available so that I only request SQS when I needed instead of constant polling?
The way to do what you want is to use long polling - rather than constantly poll every second, you open a request that stays open until it either times out or a message comes into the queue. Take a look at the documentation for ReceiveMessageRequest
ReceiveMessageRequest req = new ReceiveMessageRequest()
.withWaitTimeSeconds(Integer.valueOf(20)); // set long poll timeout to 20 sec
// set other properties on the request as well
ReceiveMessageResult result = amazonSQS.receiveMessage(req);
A common usage pattern for this is to have a background thread running the long poll and pushing the results into an internal queue (such as LinkedBlockingQueue or an ExecutorService) for a worker thread to read from.
PS. Don't forget to call deleteMessage once you're done processing the result so you don't end up receiving it again.
You can also use the worker functionality in AWS Elastic Beanstalk. It allows you to build a worker to process each message, and when you use Elastic Beanstalk to deploy it to an EC2 instance, you can define it as subscribed to a specific queue. Then each message will be POST to the worker, without your need to call receive-message on it from the queue.
It makes your system wiring much easier, as you can also have auto scaling rules that will allow you to spawn multiple workers to handle more messages in time of peak load, and scale down back to a single worker, when the load is low. It will also delete the message automatically, if you respond with OK from your worker.
See more information about it here: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html
You could also have a look at Shoryuken and the property delay:
delay: 25 # The delay in seconds to pause a queue when it's empty
But being honest we use delay: 0 here, the cost of SQS is inexpensive:
First 1 million Amazon SQS Requests per month are free
$0.50 per 1 million Amazon SQS Requests per month thereafter ($0.00000050 per SQS Request)
A single request can have from 1 to 10 messages, up to a maximum total payload of 256KB.
Each 64KB ‘chunk’ of payload is billed as 1 request. For example, a single API call with a 256KB payload will be billed as four requests.
You will probably spend less than 10 dollars monthly polling messages every second 24x7 in a single host.
One of the advantages of Shoryuken is that it fetches in batch, so it saves some money compared with a fetch per message solutions.

Efficient method to read messages from sqs without polling consecutively

I am very new to AWS SQS queues and I am currently playing around with python and boto.
Now I am able to read messages from SQS by polling consecutively.
The script is as follows:
while 1:
m = q.read(wait_time_seconds=10)
if m:
print m
How do I make this script constantly listen for new additions to the queue without using while loop?
Is there a way to write a Python consumer for SQS that doesn't have to poll periodically for new messages?
Not really... that's how SQS works. If a message arrives during the wait, it will be returned almost immediately.
This is not the inefficient operation that it seems like.
If you increase your timeout to the max allowed 20 seconds, then, worst case, you will generate no more than about 3 x 60 x 24 x 30 = 129,600 "empty" polls per month... × $0.00000050 per poll = $0.0648. (The first 1,000,000 requests are billed at $0.)
Note that during the timeout, if a new message arrives, it will return almost immediately, not wait the full 20 sec.