or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-operations.mdbuffered-client.mdclient-management.mddead-letter-queues.mdindex.mdmessage-operations.mdmessage-visibility.mdqueue-operations.mdqueue-permissions.mdqueue-tagging.md

message-visibility.mddocs/

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

```