18
18
import io .awspring .cloud .sqs .listener .source .PollingMessageSource ;
19
19
import java .time .Duration ;
20
20
import java .util .concurrent .atomic .AtomicBoolean ;
21
- import java .util .concurrent .atomic .AtomicInteger ;
22
21
import java .util .concurrent .atomic .AtomicReference ;
23
22
import org .slf4j .Logger ;
24
23
import org .slf4j .LoggerFactory ;
25
- import org .springframework .util .Assert ;
26
24
27
25
/**
28
26
* Non-blocking {@link BackPressureHandler} implementation that uses a switch between high and low throughput modes.
29
27
* <p>
30
28
* <strong>Throughput modes</strong>
31
29
* <ul>
32
- * <li>In low-throughput mode, a single batch can be requested at a time. The number of permits that will be delivered
33
- * is adjusted so that the number of in flight messages will not exceed the batch size.</li>
30
+ * <li>In low-throughput mode, a single batch can be requested at a time.</li>
34
31
* <li>In high-throughput mode, multiple batches can be requested at a time. The number of permits that will be
35
- * delivered is adjusted so that the number of in flight messages will not exceed the maximum number of concurrent
36
- * messages. Note that for a single poll the maximum number of permits that will be delivered will not exceed the batch
37
- * size.</li>
32
+ * delivered is the requested amount.</li>
38
33
* </ul>
39
34
* <p>
40
35
* <strong>Throughput mode switch:</strong> The initial throughput mode is the low-throughput mode. If some messages are
47
42
*
48
43
* @see PollingMessageSource
49
44
*/
50
- public class ThroughputBackPressureHandler implements BatchAwareBackPressureHandler , IdentifiableContainerComponent {
45
+ public class ThroughputBackPressureHandler implements BackPressureHandler , IdentifiableContainerComponent {
51
46
52
47
private static final Logger logger = LoggerFactory .getLogger (ThroughputBackPressureHandler .class );
53
48
54
- private final int batchSize ;
55
- private final int maxConcurrentMessages ;
56
-
57
49
private final AtomicReference <CurrentThroughputMode > currentThroughputMode = new AtomicReference <>(
58
50
CurrentThroughputMode .LOW );
59
51
60
- private final AtomicInteger inFlightRequests = new AtomicInteger ( 0 );
52
+ private final AtomicBoolean occupied = new AtomicBoolean ( false );
61
53
62
54
private final AtomicBoolean drained = new AtomicBoolean (false );
63
55
64
56
private String id = getClass ().getSimpleName ();
65
57
66
58
private ThroughputBackPressureHandler (Builder builder ) {
67
- this .batchSize = builder .batchSize ;
68
- this .maxConcurrentMessages = builder .maxConcurrentMessages ;
69
- logger .debug ("ThroughputBackPressureHandler created with batchSize {}" , this .batchSize );
59
+ logger .debug ("ThroughputBackPressureHandler created" );
70
60
}
71
61
72
62
public static Builder builder () {
@@ -83,35 +73,21 @@ public String getId() {
83
73
return this .id ;
84
74
}
85
75
86
- @ Override
87
- public int requestBatch () throws InterruptedException {
88
- return request (this .batchSize );
89
- }
90
-
91
76
@ Override
92
77
public int request (int amount ) throws InterruptedException {
93
78
if (drained .get ()) {
94
79
return 0 ;
95
80
}
96
- int amountCappedAtBatchSize = Math .min (amount , this .batchSize );
97
- int permits ;
98
- int inFlight = inFlightRequests .get ();
99
- if (CurrentThroughputMode .LOW == this .currentThroughputMode .get ()) {
100
- // In low-throughput mode, we only acquire one batch at a time,
101
- // so we need to limit the available permits to the batchSize - inFlight messages.
102
- permits = Math .max (0 , Math .min (amountCappedAtBatchSize , this .batchSize - inFlight ));
103
- logger .debug ("[{}] Acquired {} permits (low-throughput mode), requested: {}, in flight: {}" , this .id ,
104
- permits , amount , inFlight );
81
+ CurrentThroughputMode throughputMode = this .currentThroughputMode .get ();
82
+ if (throughputMode == CurrentThroughputMode .LOW && this .occupied .get ()) {
83
+ logger .debug ("[{}] No permits acquired because a batch already being processed in low throughput mode" ,
84
+ this .id );
85
+ return 0 ;
105
86
}
106
87
else {
107
- // In high-throughput mode, we can acquire more permits than the batch size,
108
- // but we need to limit the available permits to the maxConcurrentMessages - inFlight messages.
109
- permits = Math .max (0 , Math .min (amountCappedAtBatchSize , this .maxConcurrentMessages - inFlight ));
110
- logger .debug ("[{}] Acquired {} permits (high-throughput mode), requested: {}, in flight: {}" , this .id ,
111
- permits , amount , inFlight );
88
+ logger .debug ("[{}] Acquired {} permits ({} mode)" , this .id , amount , throughputMode );
89
+ return amount ;
112
90
}
113
- inFlightRequests .addAndGet (permits );
114
- return permits ;
115
91
}
116
92
117
93
@ Override
@@ -120,7 +96,6 @@ public void release(int amount, ReleaseReason reason) {
120
96
return ;
121
97
}
122
98
logger .debug ("[{}] Releasing {} permits ({})" , this .id , amount , reason );
123
- inFlightRequests .addAndGet (-amount );
124
99
switch (reason ) {
125
100
case NONE_FETCHED -> updateThroughputMode (CurrentThroughputMode .HIGH , CurrentThroughputMode .LOW );
126
101
case PARTIAL_FETCH -> updateThroughputMode (CurrentThroughputMode .LOW , CurrentThroughputMode .HIGH );
@@ -153,25 +128,7 @@ private enum CurrentThroughputMode {
153
128
154
129
public static class Builder {
155
130
156
- private int batchSize ;
157
- private int maxConcurrentMessages ;
158
-
159
- public Builder batchSize (int batchSize ) {
160
- this .batchSize = batchSize ;
161
- return this ;
162
- }
163
-
164
- public Builder totalPermits (int maxConcurrentMessages ) {
165
- this .maxConcurrentMessages = maxConcurrentMessages ;
166
- return this ;
167
- }
168
-
169
131
public ThroughputBackPressureHandler build () {
170
- Assert .notNull (this .batchSize , "Missing batchSize configuration" );
171
- Assert .isTrue (this .batchSize > 0 , "batch size must be greater than 0" );
172
- Assert .notNull (this .maxConcurrentMessages , "Missing maxConcurrentMessages configuration" );
173
- Assert .notNull (this .maxConcurrentMessages >= this .batchSize ,
174
- "maxConcurrentMessages must be greater than or equal to batchSize" );
175
132
return new ThroughputBackPressureHandler (this );
176
133
}
177
134
}
0 commit comments