Skip to content

Commit f987bf1

Browse files
authored
Merge pull request #150 from github/meiji163/throttle-cluster
Throttle app by cluster
2 parents ca98a19 + e8c33cb commit f987bf1

File tree

4 files changed

+45
-20
lines changed

4 files changed

+45
-20
lines changed

doc/http.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Notes:
4949
- `/throttle-app/archive/ttl/30/ratio/1`: completely refuse `/check/archive/*` requests for a duration of `30` minutes
5050
- `/throttle-app/archive/ttl/30/ratio/0.9`: _mostly_ refuse `/check/archive/*` requests for a duration of `30` minutes. On average (random dice roll), `9` out of `10` requests (i.e. `90%`) will be denied, and one approved.
5151
- `/throttle-app/archive/ttl/30/ratio/0.5`: refuse `50%` of `/check/archive/*` requests for a duration of `30` minutes
52-
52+
5353
- `/throttle-app/<app-name>/ttl/<ttlMinutes>`:
5454

5555
- If app is already throttled, modify TTL portion only, without changing the ratio.
@@ -65,7 +65,8 @@ Notes:
6565

6666
Same as calling `/throttle-app/<app-name>/ttl/60/ratio/1`. Provided as convenience endpoint.
6767

68-
- `/unthrottle-app/<app-name>`: remove any imposed throttling constraint from given app. Example:
68+
- `/throttle-app` can take a query parameter `store_name` to throttle the app only on one store (i.e. MySQL cluster). For example `/throttle-app/archive?store_name=mycluster` refuses `/check/archive/mysql/mycluster` requests for `1` hour.
69+
6970

7071
`/unthrottle-app/archive` will re-allow the `archive` app to get valid response from `/check/archive/*` requests.
7172

pkg/http/api.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,21 @@ func (api *APIImpl) MetricsHealth(w http.ResponseWriter, r *http.Request, ps htt
235235

236236
// ThrottleApp forcibly marks given app as throttled. Future requests by this app may be denied.
237237
func (api *APIImpl) ThrottleApp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
238-
appName := ps.ByName("app")
238+
storeName := r.URL.Query().Get("store_name")
239+
var appName string
240+
if storeName != "" {
241+
// limit throttling to this store
242+
appName = fmt.Sprintf("%s/%s", ps.ByName("app"), storeName)
243+
} else {
244+
// default is throttle app globally
245+
appName = ps.ByName("app")
246+
}
247+
239248
var expireAt time.Time // default zero
240249
var ttlMinutes int64
241250
var ratio float64
242251
var err error
252+
243253
if ps.ByName("ttlMinutes") == "" {
244254
ttlMinutes = 0
245255
} else if ttlMinutes, err = strconv.ParseInt(ps.ByName("ttlMinutes"), 10, 64); err != nil {
@@ -263,12 +273,21 @@ response:
263273
api.respondGeneric(w, r, err)
264274
}
265275

266-
// ThrottleApp unthrottles given app.
276+
// UnthrottleApp unthrottles given app.
267277
func (api *APIImpl) UnthrottleApp(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
268278
appName := ps.ByName("app")
269-
err := api.consensusService.UnthrottleApp(appName)
270-
271-
api.respondGeneric(w, r, err)
279+
appWithStorePrefix := appName + "/"
280+
281+
for app := range api.consensusService.ThrottledAppsMap() {
282+
if app == appName || strings.HasPrefix(app, appWithStorePrefix) {
283+
err := api.consensusService.UnthrottleApp(app)
284+
if err != nil {
285+
api.respondGeneric(w, r, err)
286+
return
287+
}
288+
}
289+
}
290+
api.respondGeneric(w, r, nil)
272291
}
273292

274293
// ThrottledApps returns a snapshot of all currently throttled apps

pkg/throttle/check.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (check *ThrottlerCheck) checkAppMetricResult(appName string, storeType stri
4848
}
4949
}
5050
//
51-
metricResult, threshold := check.throttler.AppRequestMetricResult(appName, metricResultFunc, denyApp)
51+
metricResult, threshold := check.throttler.AppRequestMetricResult(appName, storeName, metricResultFunc, denyApp)
5252
if flags.OverrideThreshold > 0 {
5353
threshold = flags.OverrideThreshold
5454
}

pkg/throttle/throttler.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -527,16 +527,21 @@ func (throttler *Throttler) UnthrottleApp(appName string) {
527527
throttler.throttledApps.Delete(appName)
528528
}
529529

530-
func (throttler *Throttler) IsAppThrottled(appName string) bool {
531-
if object, found := throttler.throttledApps.Get(appName); found {
532-
appThrottle := object.(*base.AppThrottle)
533-
if appThrottle.ExpireAt.Before(time.Now()) {
534-
// throttling cleanup hasn't purged yet, but it is expired
535-
return false
536-
}
537-
// handle ratio
538-
if rand.Float64() < appThrottle.Ratio {
539-
return true
530+
func (throttler *Throttler) IsAppThrottled(appName, storeName string) bool {
531+
appWithStore := fmt.Sprintf("%s/%s", appName, storeName)
532+
keys := []string{appWithStore, appName}
533+
// check if app is throttled for this store or globally
534+
for _, key := range keys {
535+
if object, found := throttler.throttledApps.Get(key); found {
536+
appThrottle := object.(*base.AppThrottle)
537+
if appThrottle.ExpireAt.Before(time.Now()) {
538+
// throttling cleanup hasn't purged yet, but it is expired
539+
continue
540+
}
541+
// handle ratio
542+
if rand.Float64() < appThrottle.Ratio {
543+
return true
544+
}
540545
}
541546
}
542547
return false
@@ -589,11 +594,11 @@ func (throttler *Throttler) metricsHealthSnapshot() base.MetricHealthMap {
589594
return snapshot
590595
}
591596

592-
func (throttler *Throttler) AppRequestMetricResult(appName string, metricResultFunc base.MetricResultFunc, denyApp bool) (metricResult base.MetricResult, threshold float64) {
597+
func (throttler *Throttler) AppRequestMetricResult(appName string, storeName string, metricResultFunc base.MetricResultFunc, denyApp bool) (metricResult base.MetricResult, threshold float64) {
593598
if denyApp {
594599
return base.AppDeniedMetric, 0
595600
}
596-
if throttler.IsAppThrottled(appName) {
601+
if throttler.IsAppThrottled(appName, storeName) {
597602
return base.AppDeniedMetric, 0
598603
}
599604
return metricResultFunc()

0 commit comments

Comments
 (0)