Amazon SQS
The AWS SDKs and the AWS CLI, with a custom endpoint. Standard and FIFO queues, dead-letter queues and message move tasks.
Endpoint and credentials
| Endpoint | https://sqs.powerqueue.link |
|---|---|
| Access key id | your tenant identifier: <your-identifier> |
| Secret access key | anything (signatures are not checked, but SDKs insist on one) |
| Region | anything; it appears in queue ARNs |
| Queue URLs | https://sqs.powerqueue.link/<your-identifier>/<queue-name> |
| Queue ARNs | arn:aws:sqs:<region>:<your-identifier>:<queue-name> |
The identifier takes the place of the AWS account id in URLs and ARNs. Recent AWS SDKs and the AWS CLI also read the endpoint from the environment variable AWS_ENDPOINT_URL_SQS, so existing code can be pointed here without changes:
export AWS_ENDPOINT_URL_SQS=https://sqs.powerqueue.link
export AWS_ACCESS_KEY_ID=<your-identifier>
export AWS_SECRET_ACCESS_KEY=anything
export AWS_DEFAULT_REGION=us-east-1
Python (boto3)
pip install boto3:
import boto3
sqs = boto3.client(
"sqs",
endpoint_url="https://sqs.powerqueue.link",
region_name="us-east-1",
aws_access_key_id="<your-identifier>",
aws_secret_access_key="anything",
)
url = sqs.create_queue(QueueName="orders", Attributes={"VisibilityTimeout": "30"})["QueueUrl"]
sqs.send_message(QueueUrl=url, MessageBody='{"id": 1}')
sqs.send_message_batch(
QueueUrl=url,
Entries=[{"Id": str(i), "MessageBody": f'{{"id": {i}}}'} for i in range(2, 5)],
)
while True:
messages = sqs.receive_message(QueueUrl=url, MaxNumberOfMessages=10, WaitTimeSeconds=5).get("Messages", [])
if not messages:
break
for message in messages:
print(message["Body"])
sqs.delete_message(QueueUrl=url, ReceiptHandle=message["ReceiptHandle"])
JavaScript (AWS SDK v3)
npm install @aws-sdk/client-sqs, then run as an ES module:
import {
SQSClient, GetQueueUrlCommand, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand,
} from "@aws-sdk/client-sqs";
const sqs = new SQSClient({
endpoint: "https://sqs.powerqueue.link",
region: "us-east-1",
credentials: { accessKeyId: "<your-identifier>", secretAccessKey: "anything" },
});
const { QueueUrl } = await sqs.send(new GetQueueUrlCommand({ QueueName: "orders" }));
await sqs.send(new SendMessageCommand({ QueueUrl, MessageBody: "hello" }));
const { Messages = [] } = await sqs.send(
new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 10, WaitTimeSeconds: 5 }),
);
for (const message of Messages) {
console.log(message.Body);
await sqs.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: message.ReceiptHandle }));
}
Go (AWS SDK for Go v2)
go get github.com/aws/aws-sdk-go-v2/config github.com/aws/aws-sdk-go-v2/credentials github.com/aws/aws-sdk-go-v2/service/sqs:
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sqs"
)
func main() {
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("<your-identifier>", "anything", "")),
)
if err != nil {
log.Fatal(err)
}
client := sqs.NewFromConfig(cfg, func(o *sqs.Options) {
o.BaseEndpoint = aws.String("https://sqs.powerqueue.link")
})
queue, err := client.GetQueueUrl(ctx, &sqs.GetQueueUrlInput{QueueName: aws.String("orders")})
if err != nil {
log.Fatal(err)
}
if _, err := client.SendMessage(ctx, &sqs.SendMessageInput{QueueUrl: queue.QueueUrl, MessageBody: aws.String("hello")}); err != nil {
log.Fatal(err)
}
received, err := client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
QueueUrl: queue.QueueUrl, MaxNumberOfMessages: 10, WaitTimeSeconds: 5,
})
if err != nil {
log.Fatal(err)
}
for _, message := range received.Messages {
fmt.Println(*message.Body)
if _, err := client.DeleteMessage(ctx, &sqs.DeleteMessageInput{QueueUrl: queue.QueueUrl, ReceiptHandle: message.ReceiptHandle}); err != nil {
log.Fatal(err)
}
}
}
AWS CLI
With the four environment variables above set:
aws sqs create-queue --queue-name orders
aws sqs send-message --queue-url https://sqs.powerqueue.link/<your-identifier>/orders --message-body hello
aws sqs receive-message --queue-url https://sqs.powerqueue.link/<your-identifier>/orders --wait-time-seconds 5
aws sqs list-queues
Or pass --endpoint-url https://sqs.powerqueue.link on each command instead of AWS_ENDPOINT_URL_SQS.
Supported operations
All 23 SQS actions, over both the AWS JSON protocol (current SDKs) and the older Query protocol:
- Queues:
CreateQueue,GetQueueUrl,ListQueues,DeleteQueue,GetQueueAttributes,SetQueueAttributes,PurgeQueue,TagQueue,UntagQueue,ListQueueTags. - Messages:
SendMessage,SendMessageBatch,ReceiveMessage(long polling up to 20 seconds, up to 10 messages),DeleteMessage,DeleteMessageBatch,ChangeMessageVisibility,ChangeMessageVisibilityBatch. - Dead-letter queues:
RedrivePolicyandRedriveAllowPolicyattributes,ListDeadLetterSourceQueues,StartMessageMoveTask,ListMessageMoveTasks,CancelMessageMoveTask. - Permissions:
AddPermission,RemovePermission(stored and echoed back; see below). - FIFO queues (names ending in
.fifo): message groups, deduplication ids, content-based deduplication, and the high-throughput settingsDeduplicationScopeandFifoThroughputLimit. - Presigned URLs for the Query protocol.
Differences from Amazon SQS
- Unknown queue names are created on use:
GetQueueUrl,SendMessage,SendMessageBatchandReceiveMessageon a missing queue create a standard queue (a FIFO queue for a.fifoname) instead of answeringQueueDoesNotExist. Such queues are deleted after an hour empty and unused, unless you callCreateQueuefor them. - New queues default to a
MessageRetentionPeriodof 3,600 seconds (1 hour, the most allowed) and aMaximumMessageSizeof 262,144 bytes (the most allowed), not AWS's 4 days and 1 MiB. - Permissions, queue policies and KMS or SSE settings are stored and returned, but have no effect: the identifier is the only access control.
- Any operation not listed is refused with
UnsupportedOperation.
Messages from other interfaces
A body sent through another interface that is not valid SQS text arrives base64-encoded with the attribute x-pq-body-encoding = base64; a typed Service Bus body arrives as JSON with x-pq-body-encoding = json. To send binary for the other interfaces, send its base64 with x-pq-body-encoding = base64: the bytes are stored. AMQP 0-9-1 headers and Service Bus properties arrive as message attributes with their types; an AMQP table or array header as JSON of type String.json, a decimal as Number. Both digests (MD5OfMessageBody, MD5OfMessageAttributes) cover what you receive, markers included.
The full rules, with examples: Using one queue from several interfaces.
Errors
| Situation | Error code | HTTP |
|---|---|---|
| Access key id is not a tenant identifier, or the request is unsigned | InvalidClientTokenId, with the identifier help text | 403 |
| Too many new tenants from your address, or the service is full | RequestThrottled | 403 |
| More than 100 requests a second (after a burst of 200) | RequestThrottled (botocore retries it as throttling) | 403 |
| Body plus attributes over 256 KiB | InvalidParameterValue | 400 |
A 101st queue, by CreateQueue or by use | OverLimit | 403 |
| Queue holding 1,000 messages, or a tenant holding 16 MiB of messages | OverLimit (a batch entry fails with OverLimit) | 403 |
| Attributes, tags and policies of a tenant's queues, with its other settings, growing past 1 MiB together | OverLimit | 403 |
MessageRetentionPeriod over 3,600 seconds, or MaximumMessageSize over 262,144 bytes | InvalidAttributeValue | 400 |
| Another tenant's queue URL | QueueDoesNotExist | 400 |
| Another tenant's ARN in a redrive policy or move task | InvalidParameterValue | 400 |