@@ -5,52 +5,66 @@ import (
5
5
"net/http"
6
6
"strconv"
7
7
"time"
8
-
9
- "go.uber.org/zap"
10
8
)
11
9
12
- // MonitorRateLimitHeaders monitors the rate limit headers (X-RateLimit-Remaining and Retry-After)
13
- // in the HTTP response and adjusts concurrency accordingly.
14
- // If X-RateLimit-Remaining is below a threshold or Retry-After is specified, decrease concurrency.
15
- // If neither condition is met, consider scaling up if concurrency is below the maximum limit.
16
- // - Threshold for X-RateLimit-Remaining: 10
17
- // - Maximum concurrency: MaxConcurrency
18
- func (ch * ConcurrencyHandler ) MonitorRateLimitHeaders (resp * http.Response ) {
19
- // Extract X-RateLimit-Remaining and Retry-After headers from the response
10
+ // EvaluateAndAdjustConcurrency evaluates the response from the server and adjusts the concurrency level accordingly.
11
+ func (ch * ConcurrencyHandler ) EvaluateAndAdjustConcurrency (resp * http.Response , responseTime time.Duration ) {
12
+ // Call monitoring functions
13
+ rateLimitFeedback := ch .MonitorRateLimitHeaders (resp )
14
+ responseCodeFeedback := ch .MonitorServerResponseCodes (resp )
15
+ responseTimeFeedback := ch .MonitorResponseTimeVariability (responseTime )
16
+
17
+ // Determine overall action based on feedback
18
+ suggestions := []int {rateLimitFeedback , responseCodeFeedback , responseTimeFeedback }
19
+ scaleDownCount := 0
20
+ scaleUpCount := 0
21
+
22
+ for _ , suggestion := range suggestions {
23
+ switch suggestion {
24
+ case - 1 :
25
+ scaleDownCount ++
26
+ case 1 :
27
+ scaleUpCount ++
28
+ }
29
+ }
30
+
31
+ // Decide on scaling action
32
+ if scaleDownCount > scaleUpCount {
33
+ ch .ScaleDown ()
34
+ } else if scaleUpCount > scaleDownCount {
35
+ ch .ScaleUp ()
36
+ }
37
+ }
38
+
39
+ // MonitorRateLimitHeaders monitors the rate limit headers in the response and suggests a concurrency adjustment.
40
+ func (ch * ConcurrencyHandler ) MonitorRateLimitHeaders (resp * http.Response ) int {
20
41
remaining := resp .Header .Get ("X-RateLimit-Remaining" )
21
42
retryAfter := resp .Header .Get ("Retry-After" )
43
+ suggestion := 0
22
44
23
45
if remaining != "" {
24
46
remainingValue , err := strconv .Atoi (remaining )
25
47
if err == nil && remainingValue < 10 {
26
- // Decrease concurrency if X-RateLimit-Remaining is below the threshold
27
- if len (ch .sem ) > MinConcurrency {
28
- newSize := len (ch .sem ) - 1
29
- ch .logger .Info ("Reducing concurrency due to low X-RateLimit-Remaining" , zap .Int ("NewSize" , newSize ))
30
- ch .ResizeSemaphore (newSize )
31
- }
48
+ // Suggest decrease concurrency if X-RateLimit-Remaining is below the threshold
49
+ suggestion = - 1
32
50
}
33
51
}
34
52
35
53
if retryAfter != "" {
36
- // Decrease concurrency if Retry-After is specified
37
- if len (ch .sem ) > MinConcurrency {
38
- newSize := len (ch .sem ) - 1
39
- ch .logger .Info ("Reducing concurrency due to Retry-After header" , zap .Int ("NewSize" , newSize ))
40
- ch .ResizeSemaphore (newSize )
41
- }
54
+ // Suggest decrease concurrency if Retry-After is specified
55
+ suggestion = - 1
42
56
} else {
43
- // Scale up if concurrency is below the maximum limit
44
- if len (ch .sem ) < MaxConcurrency {
45
- newSize := len (ch .sem ) + 1
46
- ch .logger .Info ("Increasing concurrency" , zap .Int ("NewSize" , newSize ))
47
- ch .ResizeSemaphore (newSize )
57
+ // Suggest increase concurrency if currently below maximum limit and no other decrease suggestion has been made
58
+ if len (ch .sem ) < MaxConcurrency && suggestion == 0 {
59
+ suggestion = 1
48
60
}
49
61
}
62
+
63
+ return suggestion
50
64
}
51
65
52
- // MonitorServerResponseCodes monitors server response codes and adjusts concurrency accordingly .
53
- func (ch * ConcurrencyHandler ) MonitorServerResponseCodes (resp * http.Response ) {
66
+ // MonitorServerResponseCodes monitors the response status codes and suggests a concurrency adjustment .
67
+ func (ch * ConcurrencyHandler ) MonitorServerResponseCodes (resp * http.Response ) int {
54
68
statusCode := resp .StatusCode
55
69
56
70
// Lock the metrics to ensure thread safety
@@ -63,7 +77,6 @@ func (ch *ConcurrencyHandler) MonitorServerResponseCodes(resp *http.Response) {
63
77
ch .Metrics .TotalRateLimitErrors ++
64
78
case statusCode >= 400 && statusCode < 500 :
65
79
// Assuming 4xx errors as client errors
66
- // Increase the TotalRetries count to indicate a client error
67
80
ch .Metrics .TotalRetries ++
68
81
}
69
82
@@ -75,23 +88,19 @@ func (ch *ConcurrencyHandler) MonitorServerResponseCodes(resp *http.Response) {
75
88
// Set the new error rate in the metrics
76
89
ch .Metrics .ResponseCodeMetrics .ErrorRate = errorRate
77
90
78
- // Check if the error rate exceeds the threshold and adjust concurrency accordingly
79
- if errorRate > ErrorRateThreshold && len (ch .sem ) > MinConcurrency {
80
- // Decrease concurrency
81
- newSize := len (ch .sem ) - 1
82
- ch .logger .Info ("Reducing request concurrency due to high error rate" , zap .Int ("NewSize" , newSize ))
83
- ch .ResizeSemaphore (newSize )
91
+ // Determine action based on the error rate
92
+ if errorRate > ErrorRateThreshold {
93
+ // Suggest decrease concurrency
94
+ return - 1
84
95
} else if errorRate <= ErrorRateThreshold && len (ch .sem ) < MaxConcurrency {
85
- // Scale up if error rate is below the threshold and concurrency is below the maximum limit
86
- newSize := len (ch .sem ) + 1
87
- ch .logger .Info ("Increasing request concurrency due to low error rate" , zap .Int ("NewSize" , newSize ))
88
- ch .ResizeSemaphore (newSize )
96
+ // Suggest increase concurrency if there is capacity
97
+ return 1
89
98
}
99
+ return 0
90
100
}
91
101
92
- // MonitorResponseTimeVariability calculates the standard deviation of response times
93
- // and uses moving averages to smooth out fluctuations, adjusting concurrency accordingly.
94
- func (ch * ConcurrencyHandler ) MonitorResponseTimeVariability (responseTime time.Duration ) {
102
+ // MonitorResponseTimeVariability monitors the response time variability and suggests a concurrency adjustment.
103
+ func (ch * ConcurrencyHandler ) MonitorResponseTimeVariability (responseTime time.Duration ) int {
95
104
ch .Metrics .Lock .Lock ()
96
105
defer ch .Metrics .Lock .Unlock ()
97
106
@@ -110,17 +119,15 @@ func (ch *ConcurrencyHandler) MonitorResponseTimeVariability(responseTime time.D
110
119
// Calculate standard deviation of response times
111
120
stdDev := math .Sqrt (ch .Metrics .ResponseTimeVariability .Variance )
112
121
113
- // Adjust concurrency based on response time variability
114
- if stdDev > ch .Metrics .ResponseTimeVariability .StdDevThreshold && len (ch .sem ) > MinConcurrency {
115
- newSize := len (ch .sem ) - 1
116
- ch .logger .Info ("Reducing request concurrency due to high response time variability" , zap .Int ("NewSize" , newSize ))
117
- ch .ResizeSemaphore (newSize )
122
+ // Determine action based on standard deviation
123
+ if stdDev > ch .Metrics .ResponseTimeVariability .StdDevThreshold {
124
+ // Suggest decrease concurrency
125
+ return - 1
118
126
} else if stdDev <= ch .Metrics .ResponseTimeVariability .StdDevThreshold && len (ch .sem ) < MaxConcurrency {
119
- // Scale up if response time variability is below the threshold and concurrency is below the maximum limit
120
- newSize := len (ch .sem ) + 1
121
- ch .logger .Info ("Increasing request concurrency due to low response time variability" , zap .Int ("NewSize" , newSize ))
122
- ch .ResizeSemaphore (newSize )
127
+ // Suggest increase concurrency if there is capacity
128
+ return 1
123
129
}
130
+ return 0
124
131
}
125
132
126
133
// calculateVariance calculates the variance of response times.
@@ -134,35 +141,3 @@ func (ch *ConcurrencyHandler) calculateVariance(averageResponseTime time.Duratio
134
141
ch .Metrics .ResponseTimeVariability .Variance = variance
135
142
return variance
136
143
}
137
-
138
- // MonitorNetworkLatency measures Time to First Byte (TTFB) and monitors network throughput,
139
- // adjusting concurrency based on changes in network latency and throughput.
140
- func (ch * ConcurrencyHandler ) MonitorNetworkLatency (ttfb time.Duration , throughput float64 ) {
141
- ch .Metrics .Lock .Lock ()
142
- defer ch .Metrics .Lock .Unlock ()
143
-
144
- // Calculate the TTFB moving average
145
- ch .Metrics .TTFB .Lock .Lock ()
146
- defer ch .Metrics .TTFB .Lock .Unlock ()
147
- ch .Metrics .TTFB .Total += ttfb
148
- ch .Metrics .TTFB .Count ++
149
- ttfbMovingAverage := ch .Metrics .TTFB .Total / time .Duration (ch .Metrics .TTFB .Count )
150
-
151
- // Calculate the throughput moving average
152
- ch .Metrics .Throughput .Lock .Lock ()
153
- defer ch .Metrics .Throughput .Lock .Unlock ()
154
- ch .Metrics .Throughput .Total += throughput
155
- ch .Metrics .Throughput .Count ++
156
- throughputMovingAverage := ch .Metrics .Throughput .Total / float64 (ch .Metrics .Throughput .Count )
157
-
158
- // Adjust concurrency based on TTFB and throughput moving averages
159
- if ttfbMovingAverage > MaxAcceptableTTFB && len (ch .sem ) > MinConcurrency {
160
- newSize := len (ch .sem ) - 1
161
- ch .logger .Info ("Reducing request concurrency due to high TTFB" , zap .Int ("NewSize" , newSize ))
162
- ch .ResizeSemaphore (newSize )
163
- } else if throughputMovingAverage > MaxAcceptableThroughput && len (ch .sem ) < MaxConcurrency {
164
- newSize := len (ch .sem ) + 1
165
- ch .logger .Info ("Increasing request concurrency due to high throughput" , zap .Int ("NewSize" , newSize ))
166
- ch .ResizeSemaphore (newSize )
167
- }
168
- }
0 commit comments