16
16
package software .amazon .awssdk .services .sqs .internal .batchmanager ;
17
17
18
18
import java .time .Duration ;
19
+ import java .util .Arrays ;
20
+ import java .util .List ;
21
+ import java .util .Map ;
19
22
import java .util .concurrent .CompletableFuture ;
20
23
import java .util .concurrent .TimeUnit ;
21
24
import java .util .concurrent .atomic .AtomicReference ;
25
+ import java .util .stream .Collectors ;
22
26
import software .amazon .awssdk .annotations .SdkInternalApi ;
23
27
import software .amazon .awssdk .services .sqs .SqsAsyncClient ;
24
28
import software .amazon .awssdk .services .sqs .model .GetQueueAttributesRequest ;
25
29
import software .amazon .awssdk .services .sqs .model .QueueAttributeName ;
26
30
import software .amazon .awssdk .services .sqs .model .ReceiveMessageRequest ;
27
31
import software .amazon .awssdk .utils .Validate ;
28
32
29
-
30
33
@ SdkInternalApi
31
- public class QueueAttributesManager {
34
+ public final class QueueAttributesManager {
32
35
36
+ private static final List <QueueAttributeName > QUEUE_ATTRIBUTE_NAMES =
37
+ Arrays .asList (QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS ,
38
+ QueueAttributeName .VISIBILITY_TIMEOUT );
33
39
private final SqsAsyncClient sqsClient ;
34
40
private final String queueUrl ;
35
- private final AtomicReference <CompletableFuture <Duration >> defaultWaitTimeSecondsFuture = new AtomicReference <>();
36
- private final AtomicReference <CompletableFuture <Duration >> visibilityTimeoutSecondsFuture = new AtomicReference <>();
37
- private final Duration minReceiveWaitTime ;
41
+ private final AtomicReference <CompletableFuture <Map <QueueAttributeName , String >>> queueAttributeMap = new AtomicReference <>();
38
42
39
- public QueueAttributesManager (SqsAsyncClient sqsClient , String queueUrl , Duration minReceiveWaitTime ) {
43
+ public QueueAttributesManager (SqsAsyncClient sqsClient , String queueUrl ) {
40
44
this .sqsClient = sqsClient ;
41
45
this .queueUrl = queueUrl ;
42
- this .minReceiveWaitTime = minReceiveWaitTime ;
43
46
}
44
47
45
48
/**
46
49
* Retrieves the received message timeout based on the provided request and queue attributes.
47
50
*
48
- * @param rq The receive message request
51
+ * @param rq The receive message request
52
+ * @param configuredWaitTime The configured minimum wait time
49
53
* @return CompletableFuture with the calculated receive message timeout in milliseconds
50
54
*/
51
- public CompletableFuture <Duration > getReceiveMessageTimeout (ReceiveMessageRequest rq ) {
52
- CompletableFuture <Duration > waitTimeFuture = defaultWaitTimeSecondsFuture .get ();
55
+ public CompletableFuture <Duration > getReceiveMessageTimeout (ReceiveMessageRequest rq , Duration configuredWaitTime ) {
56
+ Integer waitTimeSeconds = rq .waitTimeSeconds ();
57
+ if (waitTimeSeconds != null ) {
58
+ long waitTimeMillis = TimeUnit .SECONDS .toMillis (waitTimeSeconds );
59
+ return CompletableFuture .completedFuture (Duration .ofMillis (Math .max (configuredWaitTime .toMillis (), waitTimeMillis )));
60
+ }
61
+
62
+ CompletableFuture <Map <QueueAttributeName , String >> attributeFuture = getAttributeMap ();
63
+ CompletableFuture <Duration > resultFuture = attributeFuture .thenApply (attributes -> {
64
+ String waitTimeSecondsStr = attributes .get (QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS );
65
+ long waitTimeFromSqsMillis = TimeUnit .SECONDS .toMillis (Long .parseLong (waitTimeSecondsStr ));
66
+ return Duration .ofMillis (Math .max (configuredWaitTime .toMillis (), waitTimeFromSqsMillis ));
67
+ });
53
68
54
- if (waitTimeFuture == null ) {
55
- CompletableFuture <Duration > newWaitTimeFuture = new CompletableFuture <>();
56
- if (defaultWaitTimeSecondsFuture .compareAndSet (null , newWaitTimeFuture )) {
57
- fetchQueueWaitTime (newWaitTimeFuture );
69
+ resultFuture .whenComplete ((r , t ) -> {
70
+ if (resultFuture .isCancelled ()) {
71
+ attributeFuture .cancel (true );
58
72
}
59
- waitTimeFuture = defaultWaitTimeSecondsFuture .get ();
60
- }
73
+ });
61
74
62
- return waitTimeFuture . thenApply ( waitTime -> calculateWaitTime ( rq , waitTime )) ;
75
+ return resultFuture ;
63
76
}
64
77
65
78
/**
66
- * Fetches the queue wait time from SQS and completes the provided future with the result .
79
+ * Retrieves the visibility timeout for the queue .
67
80
*
68
- * @param newWaitTimeFuture The future to complete with the fetched wait time
81
+ * @return CompletableFuture with the visibility timeout in nanoseconds
69
82
*/
70
- private void fetchQueueWaitTime (CompletableFuture <Duration > newWaitTimeFuture ) {
71
- GetQueueAttributesRequest request = GetQueueAttributesRequest .builder ()
72
- .queueUrl (queueUrl )
73
- .attributeNames (
74
- QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS )
75
- .build ();
76
- sqsClient .getQueueAttributes (request )
77
- .thenApply (response -> {
78
- String messageWaitTime =
79
- Validate .notNull (response
80
- .attributes ()
81
- .get (QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS ),
82
- QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS +
83
- " attribute is null in sqs." );
84
-
85
- return Duration .ofSeconds (Integer .parseInt (messageWaitTime ));
86
- })
87
- .thenAccept (newWaitTimeFuture ::complete )
88
- .exceptionally (ex -> {
89
- newWaitTimeFuture .completeExceptionally (ex );
90
- return null ;
91
- });
92
- }
83
+ public CompletableFuture <Duration > getVisibilityTimeout () {
84
+ CompletableFuture <Map <QueueAttributeName , String >> attributeFuture = getAttributeMap ();
85
+ CompletableFuture <Duration > resultFuture = attributeFuture .thenApply (attributes -> {
86
+ String visibilityTimeoutStr = attributes .get (QueueAttributeName .VISIBILITY_TIMEOUT );
87
+ return Duration .ofSeconds (Integer .parseInt (visibilityTimeoutStr ));
88
+ });
89
+
90
+ resultFuture .whenComplete ((r , t ) -> {
91
+ if (resultFuture .isCancelled ()) {
92
+ attributeFuture .cancel (true );
93
+ }
94
+ });
93
95
94
- /**
95
- * Calculates the wait time for receiving a message, ensuring it meets the minimum wait time.
96
- *
97
- * @param rq The receive message request
98
- * @param defaultWaitTime The default wait time from the queue attributes
99
- * @return The calculated wait time in milliseconds
100
- */
101
- private Duration calculateWaitTime (ReceiveMessageRequest rq , Duration defaultWaitTime ) {
96
+ return resultFuture ;
102
97
103
- int waitTimeSeconds = (rq .waitTimeSeconds () != null ) ? rq .waitTimeSeconds () : (int ) defaultWaitTime .getSeconds ();
104
- return Duration .ofMillis (Math .max (minReceiveWaitTime .toMillis (),
105
- TimeUnit .MILLISECONDS .convert (waitTimeSeconds , TimeUnit .SECONDS )));
106
98
}
107
99
108
100
/**
109
- * Retrieves the visibility timeout for the queue .
101
+ * Retrieves the queue attributes based on the predefined attribute names .
110
102
*
111
- * @return CompletableFuture with the visibility timeout in nanoseconds
103
+ * @return CompletableFuture with the map of attribute names and their values.
112
104
*/
113
- public CompletableFuture <Duration > getVisibilityTimeout () {
114
- CompletableFuture <Duration > timeoutFuture = visibilityTimeoutSecondsFuture .get ();
115
-
116
- if (timeoutFuture == null ) {
117
- CompletableFuture <Duration > newTimeoutFuture = new CompletableFuture <>();
118
- if (visibilityTimeoutSecondsFuture .compareAndSet (null , newTimeoutFuture )) {
119
- fetchVisibilityTimeout (newTimeoutFuture );
105
+ private CompletableFuture <Map <QueueAttributeName , String >> getAttributeMap () {
106
+ CompletableFuture <Map <QueueAttributeName , String >> future = queueAttributeMap .get ();
107
+
108
+ if (future == null || future .isCompletedExceptionally ()) {
109
+ CompletableFuture <Map <QueueAttributeName , String >> newFuture = fetchQueueAttributes ();
110
+ if (queueAttributeMap .compareAndSet (future , newFuture )) {
111
+ newFuture .whenComplete ((r , t ) -> {
112
+ if (t != null ) {
113
+ queueAttributeMap .compareAndSet (newFuture , null );
114
+ }
115
+ });
116
+ return newFuture ;
120
117
}
121
- timeoutFuture = visibilityTimeoutSecondsFuture .get ();
118
+ return queueAttributeMap .get ();
122
119
}
123
120
124
- return timeoutFuture ;
121
+ return future ;
125
122
}
126
123
124
+
127
125
/**
128
- * Fetches the visibility timeout from SQS and completes the provided future with the result.
126
+ * Fetches the queue attributes from SQS and completes the provided future with the result.
129
127
*
130
- * @param newTimeoutFuture The future to complete with the fetched visibility timeout
128
+ * @return CompletableFuture with the map of attribute names and values.
131
129
*/
132
- private void fetchVisibilityTimeout ( CompletableFuture <Duration > newTimeoutFuture ) {
130
+ private CompletableFuture <Map < QueueAttributeName , String >> fetchQueueAttributes ( ) {
133
131
GetQueueAttributesRequest request = GetQueueAttributesRequest .builder ()
134
132
.queueUrl (queueUrl )
135
- .attributeNames (QueueAttributeName . VISIBILITY_TIMEOUT )
133
+ .attributeNames (QUEUE_ATTRIBUTE_NAMES )
136
134
.build ();
137
- sqsClient .getQueueAttributes (request )
138
- .thenApply (response -> {
139
- String visibilityTimeout =
140
- Validate .notNull (response
141
- .attributes ()
142
- .get (QueueAttributeName .VISIBILITY_TIMEOUT ),
143
- QueueAttributeName .VISIBILITY_TIMEOUT +
144
- " attribute is null in sqs." );
145
-
146
- return Duration .ofSeconds (Integer .parseInt (visibilityTimeout ));
147
- })
148
- .thenAccept (newTimeoutFuture ::complete )
149
- .exceptionally (ex -> {
150
- newTimeoutFuture .completeExceptionally (ex );
151
- return null ;
152
- });
135
+
136
+ CompletableFuture <Map <QueueAttributeName , String >> future =
137
+ sqsClient .getQueueAttributes (request )
138
+ .thenApply (response -> {
139
+ Map <QueueAttributeName , String > attributes = response .attributes ();
140
+ Validate .notNull (attributes .get (QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS ),
141
+ QueueAttributeName .RECEIVE_MESSAGE_WAIT_TIME_SECONDS
142
+ + " attribute is null in SQS." );
143
+ Validate .notNull (attributes .get (QueueAttributeName .VISIBILITY_TIMEOUT ),
144
+ QueueAttributeName .VISIBILITY_TIMEOUT + " attribute is null in SQS." );
145
+ return attributes .entrySet ().stream ()
146
+ .filter (entry -> QUEUE_ATTRIBUTE_NAMES .contains (entry .getKey ()))
147
+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ));
148
+ });
149
+
150
+ future .whenComplete ((r , t ) -> {
151
+ if (t != null ) {
152
+ queueAttributeMap .set (null ); // Reset the future on failure
153
+ }
154
+ });
155
+ return future ;
153
156
}
154
- }
157
+ }
0 commit comments