0
# Message Visibility
1
2
Message visibility operations control how long messages remain invisible to other consumers after being received. This mechanism prevents multiple consumers from processing the same message simultaneously and allows for flexible processing time management.
3
4
## Change Message Visibility
5
6
### Change Single Message Visibility
7
8
Modify the visibility timeout for a specific message using its receipt handle.
9
10
```java { .api }
11
ChangeMessageVisibilityResult changeMessageVisibility(ChangeMessageVisibilityRequest request);
12
13
// Convenience method
14
ChangeMessageVisibilityResult changeMessageVisibility(String queueUrl, String receiptHandle, Integer visibilityTimeout);
15
16
class ChangeMessageVisibilityRequest extends AmazonWebServiceRequest {
17
ChangeMessageVisibilityRequest();
18
ChangeMessageVisibilityRequest(String queueUrl, String receiptHandle, Integer visibilityTimeout);
19
20
String getQueueUrl();
21
ChangeMessageVisibilityRequest withQueueUrl(String queueUrl);
22
23
String getReceiptHandle();
24
ChangeMessageVisibilityRequest withReceiptHandle(String receiptHandle);
25
26
Integer getVisibilityTimeout();
27
ChangeMessageVisibilityRequest withVisibilityTimeout(Integer visibilityTimeout);
28
}
29
30
class ChangeMessageVisibilityResult {
31
// Empty result class - success indicated by no exception
32
}
33
```
34
35
**Usage Example:**
36
37
```java
38
// Extend processing time for a message
39
ReceiveMessageResult receiveResult = client.receiveMessage(new ReceiveMessageRequest(queueUrl));
40
41
for (Message message : receiveResult.getMessages()) {
42
try {
43
// Start processing
44
System.out.println("Processing message: " + message.getMessageId());
45
46
// If processing will take longer, extend visibility timeout
47
client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
48
.withQueueUrl(queueUrl)
49
.withReceiptHandle(message.getReceiptHandle())
50
.withVisibilityTimeout(300)); // Extend to 5 minutes
51
52
// Continue with long-running processing
53
performLongRunningTask(message);
54
55
// Delete after successful processing
56
client.deleteMessage(queueUrl, message.getReceiptHandle());
57
58
} catch (Exception e) {
59
// Make message immediately visible for retry
60
client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
61
.withQueueUrl(queueUrl)
62
.withReceiptHandle(message.getReceiptHandle())
63
.withVisibilityTimeout(0)); // Make immediately visible
64
}
65
}
66
```
67
68
### Change Batch Message Visibility
69
70
Modify visibility timeout for up to 10 messages in a single API call.
71
72
```java { .api }
73
ChangeMessageVisibilityBatchResult changeMessageVisibilityBatch(ChangeMessageVisibilityBatchRequest request);
74
75
// Convenience method
76
ChangeMessageVisibilityBatchResult changeMessageVisibilityBatch(String queueUrl, List<ChangeMessageVisibilityBatchRequestEntry> entries);
77
78
class ChangeMessageVisibilityBatchRequest extends AmazonWebServiceRequest {
79
String getQueueUrl();
80
ChangeMessageVisibilityBatchRequest withQueueUrl(String queueUrl);
81
82
List<ChangeMessageVisibilityBatchRequestEntry> getEntries();
83
ChangeMessageVisibilityBatchRequest withEntries(List<ChangeMessageVisibilityBatchRequestEntry> entries);
84
ChangeMessageVisibilityBatchRequest withEntries(ChangeMessageVisibilityBatchRequestEntry... entries);
85
}
86
87
class ChangeMessageVisibilityBatchRequestEntry {
88
String getId();
89
ChangeMessageVisibilityBatchRequestEntry withId(String id);
90
91
String getReceiptHandle();
92
ChangeMessageVisibilityBatchRequestEntry withReceiptHandle(String receiptHandle);
93
94
Integer getVisibilityTimeout();
95
ChangeMessageVisibilityBatchRequestEntry withVisibilityTimeout(Integer visibilityTimeout);
96
}
97
98
class ChangeMessageVisibilityBatchResult {
99
List<ChangeMessageVisibilityBatchResultEntry> getSuccessful();
100
List<BatchResultErrorEntry> getFailed();
101
}
102
103
class ChangeMessageVisibilityBatchResultEntry {
104
String getId();
105
}
106
```
107
108
**Usage Example:**
109
110
```java
111
// Batch visibility timeout changes
112
ReceiveMessageResult receiveResult = client.receiveMessage(new ReceiveMessageRequest(queueUrl)
113
.withMaxNumberOfMessages(10));
114
115
List<ChangeMessageVisibilityBatchRequestEntry> visibilityEntries = new ArrayList<>();
116
117
for (Message message : receiveResult.getMessages()) {
118
// Determine new timeout based on message attributes or content
119
int newTimeout = determineProcessingTime(message);
120
121
visibilityEntries.add(new ChangeMessageVisibilityBatchRequestEntry()
122
.withId(message.getMessageId())
123
.withReceiptHandle(message.getReceiptHandle())
124
.withVisibilityTimeout(newTimeout));
125
}
126
127
if (!visibilityEntries.isEmpty()) {
128
ChangeMessageVisibilityBatchRequest batchRequest = new ChangeMessageVisibilityBatchRequest()
129
.withQueueUrl(queueUrl)
130
.withEntries(visibilityEntries);
131
132
ChangeMessageVisibilityBatchResult batchResult = client.changeMessageVisibilityBatch(batchRequest);
133
134
// Check results
135
System.out.println("Successfully changed visibility for " +
136
batchResult.getSuccessful().size() + " messages");
137
138
for (BatchResultErrorEntry error : batchResult.getFailed()) {
139
System.err.println("Failed to change visibility for " + error.getId() +
140
": " + error.getMessage());
141
}
142
}
143
```
144
145
## Visibility Timeout Patterns
146
147
### Progressive Timeout Extension
148
149
Gradually increase visibility timeout for messages that require more processing time:
150
151
```java
152
public class ProgressiveVisibilityManager {
153
private final AmazonSQS sqsClient;
154
private final String queueUrl;
155
private final Map<String, Integer> messageAttempts = new ConcurrentHashMap<>();
156
157
public ProgressiveVisibilityManager(AmazonSQS sqsClient, String queueUrl) {
158
this.sqsClient = sqsClient;
159
this.queueUrl = queueUrl;
160
}
161
162
public void extendVisibilityTimeout(Message message) {
163
String messageId = message.getMessageId();
164
int attempts = messageAttempts.getOrDefault(messageId, 0) + 1;
165
messageAttempts.put(messageId, attempts);
166
167
// Progressive timeout: 30s, 60s, 120s, 300s
168
int timeout = Math.min(30 * (1 << (attempts - 1)), 300);
169
170
try {
171
sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
172
.withQueueUrl(queueUrl)
173
.withReceiptHandle(message.getReceiptHandle())
174
.withVisibilityTimeout(timeout));
175
176
System.out.println("Extended visibility timeout to " + timeout +
177
" seconds for attempt " + attempts);
178
} catch (Exception e) {
179
System.err.println("Failed to extend visibility timeout: " + e.getMessage());
180
}
181
}
182
183
public void completeMessage(Message message) {
184
messageAttempts.remove(message.getMessageId());
185
186
try {
187
sqsClient.deleteMessage(queueUrl, message.getReceiptHandle());
188
} catch (Exception e) {
189
System.err.println("Failed to delete message: " + e.getMessage());
190
}
191
}
192
}
193
```
194
195
### Heartbeat Pattern
196
197
Periodically refresh visibility timeout for long-running processing:
198
199
```java
200
public class MessageHeartbeat {
201
private final AmazonSQS sqsClient;
202
private final String queueUrl;
203
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
204
205
public MessageHeartbeat(AmazonSQS sqsClient, String queueUrl) {
206
this.sqsClient = sqsClient;
207
this.queueUrl = queueUrl;
208
}
209
210
public CompletableFuture<Void> processWithHeartbeat(Message message,
211
Function<Message, Void> processor) {
212
213
// Schedule heartbeat every 30 seconds
214
ScheduledFuture<?> heartbeat = scheduler.scheduleAtFixedRate(() -> {
215
try {
216
sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
217
.withQueueUrl(queueUrl)
218
.withReceiptHandle(message.getReceiptHandle())
219
.withVisibilityTimeout(60)); // Keep alive for 1 minute
220
} catch (Exception e) {
221
System.err.println("Heartbeat failed: " + e.getMessage());
222
}
223
}, 30, 30, TimeUnit.SECONDS);
224
225
return CompletableFuture.runAsync(() -> {
226
try {
227
processor.apply(message);
228
229
// Processing completed successfully
230
sqsClient.deleteMessage(queueUrl, message.getReceiptHandle());
231
232
} catch (Exception e) {
233
// Make message immediately visible for retry
234
sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
235
.withQueueUrl(queueUrl)
236
.withReceiptHandle(message.getReceiptHandle())
237
.withVisibilityTimeout(0));
238
239
throw new RuntimeException("Processing failed", e);
240
} finally {
241
heartbeat.cancel(false);
242
}
243
});
244
}
245
}
246
```
247
248
### Immediate Retry Pattern
249
250
Make failed messages immediately available for reprocessing:
251
252
```java
253
public void processMessageWithImmediateRetry(Message message) {
254
try {
255
// Attempt processing
256
processMessage(message);
257
258
// Success - delete message
259
client.deleteMessage(queueUrl, message.getReceiptHandle());
260
261
} catch (RetryableException e) {
262
// Transient error - make immediately visible for retry
263
System.out.println("Transient error, making message immediately visible for retry");
264
265
client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
266
.withQueueUrl(queueUrl)
267
.withReceiptHandle(message.getReceiptHandle())
268
.withVisibilityTimeout(0));
269
270
} catch (NonRetryableException e) {
271
// Permanent error - delete message or send to DLQ
272
System.err.println("Permanent error, removing message from queue");
273
274
client.deleteMessage(queueUrl, message.getReceiptHandle());
275
}
276
}
277
```
278
279
## Visibility Timeout Best Practices
280
281
### Choosing Appropriate Timeouts
282
283
Select visibility timeouts based on processing requirements:
284
285
```java
286
public class VisibilityTimeoutCalculator {
287
288
public static int calculateTimeout(Message message) {
289
// Base timeout
290
int baseTimeout = 30; // 30 seconds
291
292
// Adjust based on message size
293
int messageSize = message.getBody().length();
294
if (messageSize > 100000) { // 100KB
295
baseTimeout += 60; // Add 1 minute for large messages
296
}
297
298
// Adjust based on message type
299
String messageType = getMessageAttribute(message, "MessageType");
300
switch (messageType) {
301
case "ImageProcessing":
302
return 300; // 5 minutes
303
case "DataExport":
304
return 900; // 15 minutes
305
case "EmailSend":
306
return 60; // 1 minute
307
default:
308
return baseTimeout;
309
}
310
}
311
312
private static String getMessageAttribute(Message message, String attributeName) {
313
MessageAttributeValue attribute = message.getMessageAttributes().get(attributeName);
314
return attribute != null ? attribute.getStringValue() : "";
315
}
316
}
317
```
318
319
### Error Handling
320
321
Handle visibility timeout errors appropriately:
322
323
```java
324
try {
325
client.changeMessageVisibility(request);
326
} catch (MessageNotInflightException e) {
327
// Message is no longer in-flight (may have been processed by another consumer)
328
System.err.println("Message not in-flight: " + e.getMessage());
329
} catch (ReceiptHandleIsInvalidException e) {
330
// Receipt handle is invalid or expired
331
System.err.println("Invalid receipt handle: " + e.getMessage());
332
} catch (InvalidAttributeValueException e) {
333
// Invalid visibility timeout value (must be 0-43200 seconds)
334
System.err.println("Invalid timeout value: " + e.getMessage());
335
}
336
337
// Batch operation error handling
338
ChangeMessageVisibilityBatchResult result = client.changeMessageVisibilityBatch(batchRequest);
339
340
for (BatchResultErrorEntry error : result.getFailed()) {
341
switch (error.getCode()) {
342
case "MessageNotInflight":
343
System.err.println("Message " + error.getId() + " not in-flight");
344
break;
345
case "ReceiptHandleIsInvalid":
346
System.err.println("Invalid receipt handle for " + error.getId());
347
break;
348
default:
349
System.err.println("Error for " + error.getId() + ": " + error.getMessage());
350
}
351
}
352
```
353
354
## Visibility Timeout Limits
355
356
### Timeout Constraints
357
358
Understanding visibility timeout limits and constraints:
359
360
```java
361
public class VisibilityTimeoutLimits {
362
// Minimum visibility timeout (0 = immediately visible)
363
public static final int MIN_VISIBILITY_TIMEOUT = 0;
364
365
// Maximum visibility timeout (12 hours)
366
public static final int MAX_VISIBILITY_TIMEOUT = 43200;
367
368
// Default queue visibility timeout range
369
public static final int DEFAULT_MIN_QUEUE_TIMEOUT = 0;
370
public static final int DEFAULT_MAX_QUEUE_TIMEOUT = 43200;
371
372
public static boolean isValidTimeout(int timeout) {
373
return timeout >= MIN_VISIBILITY_TIMEOUT && timeout <= MAX_VISIBILITY_TIMEOUT;
374
}
375
376
public static int clampTimeout(int timeout) {
377
return Math.max(MIN_VISIBILITY_TIMEOUT,
378
Math.min(MAX_VISIBILITY_TIMEOUT, timeout));
379
}
380
}
381
```
382
383
### Receipt Handle Validity
384
385
Understanding receipt handle lifecycle:
386
387
```java
388
public class ReceiptHandleManager {
389
// Receipt handles are valid only during message visibility period
390
// They become invalid when:
391
// 1. Message visibility timeout expires
392
// 2. Message is deleted
393
// 3. Message is received again by another consumer
394
395
public boolean isReceiptHandleValid(String receiptHandle) {
396
try {
397
// Attempt to change visibility with 0 timeout (no-op if valid)
398
client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
399
.withQueueUrl(queueUrl)
400
.withReceiptHandle(receiptHandle)
401
.withVisibilityTimeout(0));
402
return true;
403
} catch (ReceiptHandleIsInvalidException e) {
404
return false;
405
}
406
}
407
}
408
```