[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-12 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r503378670



##
File path: t/plugin/api-breaker.t
##
@@ -464,15 +518,15 @@ passed
 
 
 
-=== TEST 17: max_breaker_seconds = 35
+=== TEST 18: add plugin, max_breaker_seconds = 17

Review comment:
   
![image](https://user-images.githubusercontent.com/6814606/95765173-0faced00-0ce4-11eb-8dc7-03eb60d880a3.png)
   





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-12 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r503172641



##
File path: t/plugin/api-breaker.t
##
@@ -464,15 +518,15 @@ passed
 
 
 
-=== TEST 17: max_breaker_seconds = 35
+=== TEST 18: add plugin, max_breaker_seconds = 17

Review comment:
   ![Uploading image.png…]()
   

##
File path: t/plugin/api-breaker.t
##
@@ -384,41 +390,89 @@ passed
 
 
 
-=== TEST 15: max_breaker_seconds = 20
+=== TEST 12: default value
+--- request
+GET /api_breaker?code=500
+--- error_code: 500
+--- no_error_log
+[error]
+
+
+
+=== TEST 13: trigger default value of unhealthy.http_statuses breaker
+--- request eval
+["GET /api_breaker?code=200", "GET /api_breaker?code=500", "GET 
/api_breaker?code=503", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500", "GET /api_breaker?code=500"]
+--- error_code eval
+[200, 500, 503, 500, 500, 502]
+--- no_error_log
+[error]
+
+
+
+=== TEST 14: trigger timeout 2 second
+--- config
+location /sleep1 {
+proxy_pass "http://127.0.0.1:1980/sleep1;;
+}
+--- request eval
+["GET /api_breaker?code=500", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500", "GET /api_breaker?code=200", "GET /sleep1", "GET 
/sleep1", "GET /sleep1", "GET /api_breaker?code=200", "GET 
/api_breaker?code=200", "GET /api_breaker?code=200", "GET 
/api_breaker?code=200","GET /api_breaker?code=200"]
+--- error_code eval
+[500, 500, 500, 502, 200, 200, 200, 200, 200, 200, 200,200]
+--- no_error_log
+[error]
+
+
+
+=== TEST 15: trigger timeout again 4 second
+--- config
+location /sleep1 {

Review comment:
   ditto

##
File path: t/plugin/api-breaker.t
##
@@ -384,41 +390,89 @@ passed
 
 
 
-=== TEST 15: max_breaker_seconds = 20
+=== TEST 12: default value
+--- request
+GET /api_breaker?code=500
+--- error_code: 500
+--- no_error_log
+[error]
+
+
+
+=== TEST 13: trigger default value of unhealthy.http_statuses breaker
+--- request eval
+["GET /api_breaker?code=200", "GET /api_breaker?code=500", "GET 
/api_breaker?code=503", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500", "GET /api_breaker?code=500"]
+--- error_code eval
+[200, 500, 503, 500, 500, 502]
+--- no_error_log
+[error]
+
+
+
+=== TEST 14: trigger timeout 2 second
+--- config
+location /sleep1 {

Review comment:
   do we really need it? please confirm!
   
   You are writing test cases, if the case is incorrect, it means your work is 
meaningless.





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-10 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502797834



##
File path: t/plugin/api-breaker.t
##
@@ -0,0 +1,358 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+location /t {
+content_by_lua_block {
+local plugin = require("apisix.plugins.api-breaker")
+local ok, err = plugin.check_schema({
+unhealthy_response_code = 502,
+unhealthy = {
+http_statuses = {500},
+failures = 1,
+},
+healthy = {
+http_statuses = {200},
+successes = 1,
+},
+})
+if not ok then
+ngx.say(err)
+end
+
+ngx.say("done")
+}
+}
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: default http_statuses
+--- config
+location /t {
+content_by_lua_block {
+local plugin = require("apisix.plugins.api-breaker")
+local ok, err = plugin.check_schema({
+unhealthy_response_code = 502,
+unhealthy = {
+failures = 1,
+},
+healthy = {
+successes = 1,
+},
+})
+if not ok then
+ngx.say(err)
+end
+
+ngx.say("done")
+}
+}
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: add plugin
+--- config
+location /t {
+content_by_lua_block {
+local t = require("lib.test_admin").test
+local code, body = t('/apisix/admin/routes/1',
+ngx.HTTP_PUT,
+[[{
+"plugins": {
+"api-breaker": {
+"unhealthy_response_code": 502,
+"unhealthy": {
+"http_statuses": [500, 503],
+"failures": 3
+},
+"healthy": {
+"http_statuses": [200, 206],
+"successes": 3
+}
+}
+},
+"upstream": {
+"nodes": {
+"127.0.0.1:1980": 1
+},
+"type": "roundrobin"
+},
+"uri": "/api_breaker"
+}]]
+)
+
+if code >= 300 then
+ngx.status = code
+end
+ngx.say(body)
+}
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: trigger breaker
+--- request eval
+["GET /api_breaker?code=200", "GET /api_breaker?code=500", "GET 
/api_breaker?code=503", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500", "GET /api_breaker?code=500"]
+--- error_code eval
+[200, 500, 503, 500, 502, 502]
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: trigger reset status
+--- request eval
+["GET /api_breaker?code=500", "GET /api_breaker?code=500", "GET 
/api_breaker?code=200", "GET /api_breaker?code=200", "GET 
/api_breaker?code=200", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500"]
+--- error_code eval
+[500, 500, 200, 200, 200, 500, 500]
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: trigger del healthy numeration
+--- request eval
+["GET /api_breaker?code=500", "GET /api_breaker?code=200", "GET 
/api_breaker?code=500", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500", "GET /api_breaker?code=500", "GET 
/api_breaker?code=500"]
+--- error_code eval
+[500, 200, 500, 500, 502, 502, 502]
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: add plugin with default config value
+--- config
+location /t {
+content_by_lua_block {
+  

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-10 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502780370



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,221 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 300 -- TODO: user can config
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("failed to get ngx.shared dict when load plugin " .. plugin_name)
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,

Review comment:
   I think it can be larger

##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,221 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 300 -- TODO: user can config
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("failed to get ngx.shared dict when load plugin " .. plugin_name)
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+healthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,

Review 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502743561



##
File path: t/plugin/api-breaker.t
##
@@ -0,0 +1,219 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();

Review comment:
   why we need this useless line?

##
File path: t/plugin/api-breaker.t
##
@@ -0,0 +1,219 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+run_tests;

Review comment:
   need a blank line before this line





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502743189



##
File path: doc/plugins/api-breaker.md
##
@@ -0,0 +1,106 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+About the breaker timeout logic; the current version does not open the 
relevant configuration items to the user, the code logic automatically 
**triggers the unhealthy state **incrementation of the number of operations.

Review comment:
   I think we should use `:` here

##
File path: doc/zh-cn/plugins/api-breaker.md
##
@@ -0,0 +1,107 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。
+
+关于熔断超时逻辑;当前版本没有开放相关配置项给用户,由代码逻辑自动按**触发不健康状态**的次数递增运算。
+
+比如:上流服务第一次返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断2秒**。然后,过2秒以后,上流服务再次连续返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断4秒**。依次类推,最大达到300秒不再增加。

Review comment:
   show more step information ^_^
   help the user to understand it

##
File path: doc/zh-cn/plugins/api-breaker.md
##
@@ -0,0 +1,107 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。
+
+关于熔断超时逻辑;当前版本没有开放相关配置项给用户,由代码逻辑自动按**触发不健康状态**的次数递增运算。
+
+比如:上流服务第一次返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断2秒**。然后,过2秒以后,上流服务再次连续返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断4秒**。依次类推,最大达到300秒不再增加。
+
+
+## 属性列表
+
+| 名称| 类型   | 必选项 | 默认值 | 有效值  | 描述 
|
+| --- | -- | -- | -- | 
--- |  |
+| unhealthy_response_code | integer| 必须   | 无 | [200, ..., 
600] | 不健康返回错误码 |
+| unhealthy.http_statuses | array[integer] | 可选   | {500}  | [500, ..., 
599] | 不健康时候的状态码   |
+| unhealthy.failures  | integer| 可选   | 1  | >=1   
  | 触发不健康状态的连续错误请求次数 |
+| healthy.http_statuses   | array[integer] | 可选   | {200, 206} | [200, ..., 
499] | 健康时候的状态码 |
+| successes.successes | integer| 可选   | 1  | >=1   
  | 触发健康状态的连续正常请求次数   |
+
+## 启用方式
+
+这是一个示例,在指定的路由上启用`api-breaker`插件:
+
+```shell
+curl "http://127.0.0.1:9080/apisix/admin/routes/5; -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+   {
+  "plugins": {
+  "api-breaker": {
+  "unhealthy_response_code": 502,
+  "unhealthy": {
+  "http_statuses": [500, 503],
+  "failures": 3
+  },
+  "healthy": {
+  "http_statuses": [200],
+  "successes": 1
+  }
+  }
+  },
+  "uri": "/get",
+  "host": "127.0.0.1",
+  "upstream_id": 50
+  }'
+```
+
+## 测试插件
+
+```shell
+$ curl -i -X POST "http://127.0.0.1:9080/get;
+HTTP/1.1 502 Bad Gateway
+Content-Type: application/octet-stream
+Connection: keep-alive
+Server: APISIX/1.5
+
+... ...
+```
+
+> 然后。像上面的配置,如果你的上流服务返回500,连续3次。客户端将会收到502(unhealthy_response_code)应答。
+
+
+## 禁用插件
+
+当想禁用`api-breaker`插件时,非常简单,只需要在插件配置中删除相应的 json 配置,无需重启服务,即可立即生效:
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+"uri": "/*",

Review comment:
   it should be the same as 
https://github.com/apache/apisix/pull/2339/files#diff-9eaeaaf55eabada0eba200cbda40c43eR69

##
File path: doc/zh-cn/plugins/api-breaker.md
##
@@ -0,0 +1,107 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。

Review comment:
   need one space between Chinese and English

##
File path: doc/zh-cn/plugins/api-breaker.md
##
@@ -0,0 +1,107 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。
+
+关于熔断超时逻辑;当前版本没有开放相关配置项给用户,由代码逻辑自动按**触发不健康状态**的次数递增运算。
+
+比如:上流服务第一次返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断2秒**。然后,过2秒以后,上流服务再次连续返回`unhealthy.http_statuses`状态达到`unhealthy.failures`次时,**熔断4秒**。依次类推,最大达到300秒不再增加。
+
+
+## 属性列表
+
+| 名称| 类型   | 必选项 | 默认值 | 有效值  | 描述 
|
+| 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502731996



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,208 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 600
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("get ngx.shared dict error.")
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+},
+},
+healthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+required = {"unhealthy_response_code", "unhealthy", "healthy"},
+}
+
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function is_healthy(healthy_status, upstream_statu)
+for _, healthy in ipairs(healthy_status) do
+if healthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function healthy_cache_key(ctx)
+return "healthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_cache_key(ctx)
+return "unhealthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_lastime_cache_key(ctx)
+return "unhealthy-lastime" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local _M = {
+version = 0.1,
+name = plugin_name,
+priority = 1005,
+schema = schema,
+}
+
+
+function _M.check_schema(conf)
+local ok, err = core.schema.check(schema, conf)
+if not ok then
+return false, err
+end
+
+return true
+end
+
+
+function _M.access(conf, ctx)
+local unhealthy_val, err = shared_buffer:get(unhealthy_cache_key(ctx))
+if err then
+core.log.error("ngx.shared get error", err)
+end
+
+local unhealthy_lastime, err = 
shared_buffer:get(unhealthy_lastime_cache_key(ctx))
+if err then
+core.log.error("ngx.shared get error", err)
+end
+
+if unhealthy_val and unhealthy_lastime then
+local ride = math.ceil(unhealthy_val / conf.unhealthy.failures)
+if ride < 1 then
+ride = 1
+end
+
+if unhealthy_lastime + 2^ride >= ngx.time() then
+return conf.unhealthy_response_code
+end
+end
+end
+
+
+function _M.header_filter(conf, ctx)
+local unhealthy_status = conf.unhealthy.http_statuses
+local healthy_status = conf.healthy.http_statuses
+
+local unhealthy_key = unhealthy_cache_key(ctx)
+local healthy_key = healthy_cache_key(ctx)
+
+local upstream_statu = core.response.get_upstream_status(ctx)
+
+if is_unhealthy(unhealthy_status, upstream_statu) then
+local newval, err = shared_buffer:incr(unhealthy_key, 1, 0, 
DEFAULT_EXPTIME)
+if err 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502731483



##
File path: doc/plugins/api-breaker.md
##
@@ -0,0 +1,102 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+## Attributes
+
+| Name  | Type  | Requirement | Default | Valid  | 
Description |
+| - | - | --- | --- | -- | 
--- |
+| unhealthy_response_code   | integer | required |  | [200, 
..., 600] | return error code when unhealthy |
+| unhealthy.http_statuses | array[integer] | optional | {500}  | [500, 
..., 599] | Status codes when unhealthy |
+| unhealthy.failures  | integer| optional | 1  | >=1   
  | Number of consecutive error requests that triggered an unhealthy state |
+| healthy.http_statuses   | array[integer] | optional | {200, 206} | [200, 
..., 499] | Status codes when healthy |
+| successes.successes | integer| optional | 1  | >=1   
  | Number of consecutive normal requests that trigger health status |
+
+## How To Enable
+
+Here's an example, enable the `api-breaker` plugin on the specified route:
+
+```shell
+curl "http://127.0.0.1:9080/apisix/admin/routes/5; -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+   {
+  "plugins": {
+  "api-breaker": {
+  "unhealthy_response_code": 502,
+  "unhealthy": {
+  "http_statuses": [500, 503],
+  "failures": 3
+  },
+  "healthy": {
+  "http_statuses": [200],
+  "successes": 1
+  }
+  }
+  },
+ "uri": "/get",
+  "host": "127.0.0.1",
+  "upstream_id": 50
+  }'
+```
+
+## Test Plugin
+
+```shell

Review comment:
   Just show an example, make the user learn this plugin easier.





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502731314



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,214 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 300
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("failed to get ngx.shared dict when load plugin " .. plugin_name)
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+healthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+}
+},
+required = {"unhealthy_response_code", "unhealthy", "healthy"},
+}
+
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function is_healthy(healthy_status, upstream_statu)
+for _, healthy in ipairs(healthy_status) do
+if healthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function healthy_cache_key(ctx)
+return "healthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_cache_key(ctx)
+return "unhealthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_lastime_cache_key(ctx)
+return "unhealthy-lastime" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local _M = {
+version = 0.1,
+name = plugin_name,
+priority = 1005,
+schema = schema,
+}
+
+
+function _M.check_schema(conf)
+local ok, err = core.schema.check(schema, conf)
+if not ok then
+return false, err
+end
+
+return true
+end
+
+
+function _M.access(conf, ctx)
+local unhealthy_val, err = shared_buffer:get(unhealthy_cache_key(ctx))
+if err then
+core.log.error("failed to get unhealthy_cache_key in ngx.shared:", err)
+end
+
+local unhealthy_lastime, err = 
shared_buffer:get(unhealthy_lastime_cache_key(ctx))
+if err then
+core.log.error("failed to get unhealthy_lastime_cache_key in 
ngx.shared: ", err)
+end
+
+if unhealthy_val and unhealthy_lastime then
+local ride = math.ceil(unhealthy_val / conf.unhealthy.failures)
+if ride < 1 then
+ride = 1
+end
+
+-- The maximum intercept request is 5 minutes(DEFAULT_EXPTIME),
+-- and then the upstream service will be retry.
+if unhealthy_lastime + 2^ride >= ngx.time() then
+return conf.unhealthy_response_code
+end
+end
+end
+
+
+function _M.header_filter(conf, ctx)

Review 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502718278



##
File path: doc/plugins/api-breaker.md
##
@@ -0,0 +1,102 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+## Attributes
+
+| Name  | Type  | Requirement | Default | Valid  | 
Description |
+| - | - | --- | --- | -- | 
--- |
+| unhealthy_response_code   | integer | required |  | [200, 
..., 600] | return error code when unhealthy |
+| unhealthy.http_statuses | array[integer] | optional | {500}  | [500, 
..., 599] | Status codes when unhealthy |
+| unhealthy.failures  | integer| optional | 1  | >=1   
  | Number of consecutive error requests that triggered an unhealthy state |
+| healthy.http_statuses   | array[integer] | optional | {200, 206} | [200, 
..., 499] | Status codes when healthy |
+| successes.successes | integer| optional | 1  | >=1   
  | Number of consecutive normal requests that trigger health status |
+
+## How To Enable
+
+Here's an example, enable the `api-breaker` plugin on the specified route:
+
+```shell
+curl "http://127.0.0.1:9080/apisix/admin/routes/5; -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+   {
+  "plugins": {
+  "api-breaker": {
+  "unhealthy_response_code": 502,
+  "unhealthy": {
+  "http_statuses": [500, 503],
+  "failures": 3
+  },
+  "healthy": {
+  "http_statuses": [200],
+  "successes": 1
+  }
+  }
+  },
+ "uri": "/get",

Review comment:
   bad indentation

##
File path: doc/plugins/api-breaker.md
##
@@ -0,0 +1,102 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+## Attributes
+
+| Name  | Type  | Requirement | Default | Valid  | 
Description |
+| - | - | --- | --- | -- | 
--- |
+| unhealthy_response_code   | integer | required |  | [200, 
..., 600] | return error code when unhealthy |
+| unhealthy.http_statuses | array[integer] | optional | {500}  | [500, 
..., 599] | Status codes when unhealthy |
+| unhealthy.failures  | integer| optional | 1  | >=1   
  | Number of consecutive error requests that triggered an unhealthy state |
+| healthy.http_statuses   | array[integer] | optional | {200, 206} | [200, 
..., 499] | Status codes when healthy |
+| successes.successes | integer| optional | 1  | >=1   
  | Number of consecutive normal requests that trigger health status |
+
+## How To Enable
+
+Here's an example, enable the `api-breaker` plugin on the specified route:
+
+```shell
+curl "http://127.0.0.1:9080/apisix/admin/routes/5; -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+   {
+  "plugins": {
+  "api-breaker": {
+  "unhealthy_response_code": 502,
+  "unhealthy": {
+  "http_statuses": [500, 503],
+  "failures": 3
+  },
+  "healthy": {
+  "http_statuses": [200],
+  "successes": 1
+  }
+  }
+  },
+ "uri": "/get",
+  "host": "127.0.0.1",
+  "upstream_id": 50
+  }'
+```
+
+## Test Plugin
+
+```shell

Review comment:
   we should provide two examples, a healthy response and an unhealthy 
response.

##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,214 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502259561



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,214 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 300
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("failed to get ngx.shared dict when load plugin " .. plugin_name)
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+healthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+}
+},
+required = {"unhealthy_response_code", "unhealthy", "healthy"},
+}
+
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function is_healthy(healthy_status, upstream_statu)
+for _, healthy in ipairs(healthy_status) do
+if healthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function healthy_cache_key(ctx)
+return "healthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_cache_key(ctx)
+return "unhealthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_lastime_cache_key(ctx)
+return "unhealthy-lastime" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local _M = {
+version = 0.1,
+name = plugin_name,
+priority = 1005,
+schema = schema,
+}
+
+
+function _M.check_schema(conf)
+local ok, err = core.schema.check(schema, conf)
+if not ok then
+return false, err
+end
+
+return true
+end
+
+
+function _M.access(conf, ctx)
+local unhealthy_val, err = shared_buffer:get(unhealthy_cache_key(ctx))
+if err then
+core.log.error("failed to get unhealthy_cache_key in ngx.shared:", err)
+end
+
+local unhealthy_lastime, err = 
shared_buffer:get(unhealthy_lastime_cache_key(ctx))
+if err then
+core.log.error("failed to get unhealthy_lastime_cache_key in 
ngx.shared: ", err)
+end
+
+if unhealthy_val and unhealthy_lastime then
+local ride = math.ceil(unhealthy_val / conf.unhealthy.failures)
+if ride < 1 then
+ride = 1
+end
+
+-- The maximum intercept request is 5 minutes(DEFAULT_EXPTIME),
+-- and then the upstream service will be retry.
+if unhealthy_lastime + 2^ride >= ngx.time() then
+return conf.unhealthy_response_code
+end
+end
+end
+
+
+function _M.header_filter(conf, ctx)

Review 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-09 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r501548363



##
File path: conf/config-default-dev.yaml
##
@@ -0,0 +1,203 @@
+#

Review comment:
   I do not think we need `conf/config-default-dev.yaml` for CI.
   
https://github.com/apache/apisix/pull/2339/checks?check_run_id=1223679517#step:6:310
   
   it seems that you only need to fix the test case `t/admin/plugins.t` .
   
   ```
   Test Summary Report
   ---
   t/admin/plugins.t   (Wstat: 256 Tests: 15 Failed: 1)
 Failed test:  2
 Non-zero exit status: 1

##
File path: doc/plugins/api-blocker.md
##
@@ -0,0 +1,102 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+## Attributes
+
+| Name  | Type  | Requirement | Default | Valid  | 
Description |
+| - | - | --- | --- | -- | 
--- |
+| unhealthy_response_code   | integer | required |  | [200, 
..., 600] | return error code when unhealthy |
+| unhealthy.http_statuses | array[integer] | required | {500}  | [500, 
..., 599] | Status codes when unhealthy |
+| unhealthy.failures  | integer| required | 1  | >=1   
  | Number of consecutive error requests that triggered an unhealthy state |
+| healthy.http_statuses   | array[integer] | required | {200, 206} | [200, 
..., 499] | Status codes when healthy |

Review comment:
   doc is wrong. It is not a "required" field, it should be an "optional" 
field. Please fix other similar issues.
   
   
https://github.com/apache/apisix/pull/2339/files#diff-a3aa73270ae0e95e30630eb2b5d825b1R73

##
File path: doc/zh-cn/plugins/api-blocker.md
##
@@ -0,0 +1,102 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。

Review comment:
   need one space between Chinese and English.

##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,212 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 600
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("get ngx.shared dict error.")
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+healthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-08 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r502129630



##
File path: doc/plugins/api-blocker.md
##
@@ -0,0 +1,102 @@
+
+
+- [中文](../zh-cn/plugins/api-blocker.md)
+
+# Summary
+
+- [**Name**](#name)
+- [**Attributes**](#attributes)
+- [**How To Enable**](#how-to-enable)
+- [**Test Plugin**](#test-plugin)
+- [**Disable Plugin**](#disable-plugin)
+
+## Name
+
+The plugin implements API fuse functionality to help us protect our upstream 
business services.
+
+## Attributes
+
+| Name  | Type  | Requirement | Default | Valid  | 
Description |
+| - | - | --- | --- | -- | 
--- |
+| unhealthy_response_code   | integer | required |  | [200, 
..., 600] | return error code when unhealthy |
+| unhealthy.http_statuses | array[integer] | required | {500}  | [500, 
..., 599] | Status codes when unhealthy |
+| unhealthy.failures  | integer| required | 1  | >=1   
  | Number of consecutive error requests that triggered an unhealthy state |
+| healthy.http_statuses   | array[integer] | required | {200, 206} | [200, 
..., 499] | Status codes when healthy |

Review comment:
   doc is wrong. It is not a "required" field, it should be an "optional" 
field. Please fix other similar issues.
   
   
https://github.com/apache/apisix/pull/2339/files#diff-a3aa73270ae0e95e30630eb2b5d825b1R73

##
File path: doc/zh-cn/plugins/api-blocker.md
##
@@ -0,0 +1,102 @@
+
+
+- [English](../../plugins/api-blocker.md)
+
+# 目录
+
+- [**定义**](#定义)
+- [**属性列表**](#属性列表)
+- [**启用方式**](#启用方式)
+- [**测试插件**](#测试插件)
+- [**禁用插件**](#禁用插件)
+
+## 定义
+
+该插件实现API熔断功能,帮助我们保护上游业务服务。

Review comment:
   need one space between Chinese and English.

##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,212 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 600
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("get ngx.shared dict error.")
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+healthy = {
+type = "object",
+properties = {
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+}
+},
+required = {"unhealthy_response_code", "unhealthy", "healthy"},
+}
+
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-08 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r501548363



##
File path: conf/config-default-dev.yaml
##
@@ -0,0 +1,203 @@
+#

Review comment:
   I do not think we need `conf/config-default-dev.yaml` for CI.
   
https://github.com/apache/apisix/pull/2339/checks?check_run_id=1223679517#step:6:310
   
   it seems that you only need to fix the test case `t/admin/plugins.t` .
   
   ```
   Test Summary Report
   ---
   t/admin/plugins.t   (Wstat: 256 Tests: 15 Failed: 1)
 Failed test:  2
 Non-zero exit status: 1





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-10-06 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r500282768



##
File path: t/plugin/api-breaker.t
##
@@ -0,0 +1,219 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+location /t {
+content_by_lua_block {
+local plugin = require("apisix.plugins.api-breaker")
+local ok, err = plugin.check_schema({
+unhealthy_response_code = 502,
+unhealthy = {
+http_statuses = {500},
+failures = 1,
+},
+healthy = {
+http_statuses = {200},
+successes = 1,
+},
+})
+if not ok then
+ngx.say(err)
+end
+
+ngx.say("done")
+}
+}
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+=== TEST 2: default http_statuses
+--- config
+location /t {
+content_by_lua_block {
+local plugin = require("apisix.plugins.api-breaker")
+local ok, err = plugin.check_schema({
+unhealthy_response_code = 502,
+unhealthy = {
+failures = 1,
+},
+healthy = {
+successes = 1,
+},
+})
+if not ok then
+ngx.say(err)
+end
+
+ngx.say("done")
+}
+}
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+=== TEST 3: add plugin
+--- config
+location /t {
+content_by_lua_block {
+local t = require("lib.test_admin").test
+local code, body = t('/apisix/admin/routes/1',
+ngx.HTTP_PUT,
+[[{
+"plugins": {
+"api-breaker": {
+"unhealthy_response_code": 502,
+"unhealthy": {
+"http_statuses": [500, 503],
+"failures": 3
+},
+"healthy": {
+"http_statuses": [200, 206],
+"successes": 3
+}
+}
+},
+"upstream": {
+"nodes": {
+"127.0.0.1:1988": 1
+},
+"type": "roundrobin"
+},
+"uri": "/hello"
+}]]
+)
+
+if code >= 300 then
+ngx.status = code
+end
+ngx.say(body)
+}
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+=== TEST 4: trigger breaker
+--- request eval
+["GET /hello?r=200", "GET /hello?r=500", "GET /hello?r=503", "GET 
/hello?r=500", "GET /hello?r=500", "GET /hello?r=500"]
+--- error_code eval
+[200, 500, 503, 500, 502, 502]
+--- no_error_log
+[error]
+
+
+=== TEST 5: trigger reset status
+--- request eval
+["GET /hello?r=500", "GET /hello?r=500", "GET /hello?r=200", "GET 
/hello?r=200", "GET /hello?r=200", "GET /hello?r=500", "GET /hello?r=500"]
+--- error_code eval
+[500, 500, 200, 200, 200, 500, 500]
+--- no_error_log
+[error]
+
+
+=== TEST 6: trigger del healthy numeration
+--- request eval
+["GET /hello?r=500", "GET /hello?r=200", "GET /hello?r=500", "GET 
/hello?r=500", "GET /hello?r=500", "GET /hello?r=500", "GET /hello?r=500"]
+--- error_code eval
+[500, 200, 500, 500, 502, 502, 502]
+--- no_error_log
+[error]
+
+
+=== TEST 7: add plugin with default config value
+--- config
+location /t {
+content_by_lua_block {
+local t = require("lib.test_admin").test
+local code, body = t('/apisix/admin/routes/1',
+ngx.HTTP_PUT,
+[[{
+  

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-30 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r497266741



##
File path: t/APISIX.pm
##
@@ -260,6 +261,17 @@ _EOC_
 require("apisix").http_init_worker()
 }
 
+# only for test

Review comment:
   we need to delete those conf in `t/APISIX.PM`





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-30 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r497260469



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,180 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local ngx = ngx
+local math = math
+local os = os
+local ipairs   = ipairs
+local core = require("apisix.core")
+local plugin_name = "api-breaker"
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+
+local schema = {
+type = "object",
+properties = {
+response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}

Review comment:
   @liuhengloveyou your schema is wrong. you can use the common tools to 
check it first.
   
   
![image](https://user-images.githubusercontent.com/6814606/94648532-50405a00-0325-11eb-851d-e30a05ecba52.png)
   





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-29 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r497253268



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,208 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local plugin_name = "api-breaker"
+local ngx = ngx
+local math = math
+local ipairs = ipairs
+local error = error
+local core = require("apisix.core")
+
+local DEFAULT_EXPTIME = 600
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+if not shared_buffer then
+error("get ngx.shared dict error.")
+end
+
+
+local schema = {
+type = "object",
+properties = {
+unhealthy_response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+},
+},
+healthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+required = {"unhealthy_response_code", "unhealthy", "healthy"},
+}
+
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function is_healthy(healthy_status, upstream_statu)
+for _, healthy in ipairs(healthy_status) do
+if healthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function healthy_cache_key(ctx)
+return "healthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_cache_key(ctx)
+return "unhealthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_lastime_cache_key(ctx)
+return "unhealthy-lastime" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local _M = {
+version = 0.1,
+name = plugin_name,
+priority = 1005,
+schema = schema,
+}
+
+
+function _M.check_schema(conf)
+local ok, err = core.schema.check(schema, conf)
+if not ok then
+return false, err
+end
+
+return true
+end
+
+
+function _M.access(conf, ctx)
+local unhealthy_val, err = shared_buffer:get(unhealthy_cache_key(ctx))
+if err then
+core.log.error("ngx.shared get error", err)
+end
+
+local unhealthy_lastime, err = 
shared_buffer:get(unhealthy_lastime_cache_key(ctx))
+if err then
+core.log.error("ngx.shared get error", err)
+end
+
+if unhealthy_val and unhealthy_lastime then
+local ride = math.ceil(unhealthy_val / conf.unhealthy.failures)
+if ride < 1 then
+ride = 1
+end
+
+if unhealthy_lastime + 2^ride >= ngx.time() then
+return conf.unhealthy_response_code
+end
+end
+end
+
+
+function _M.header_filter(conf, ctx)
+local unhealthy_status = conf.unhealthy.http_statuses
+local healthy_status = conf.healthy.http_statuses
+
+local unhealthy_key = unhealthy_cache_key(ctx)
+local healthy_key = healthy_cache_key(ctx)
+
+local upstream_statu = core.response.get_upstream_status(ctx)
+
+if is_unhealthy(unhealthy_status, upstream_statu) then
+local newval, err = shared_buffer:incr(unhealthy_key, 1, 0, 
DEFAULT_EXPTIME)
+if err 

[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-29 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r497252952



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,180 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local ngx = ngx
+local math = math
+local os = os
+local ipairs   = ipairs
+local core = require("apisix.core")
+local plugin_name = "api-breaker"
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+
+local schema = {
+type = "object",
+properties = {
+response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}

Review comment:
   let me confirm this bug. if I have any news, I'll reply here





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-28 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r496351550



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,180 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local ngx = ngx
+local math = math
+local os = os
+local ipairs   = ipairs
+local core = require("apisix.core")
+local plugin_name = "api-breaker"
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]
+
+local schema = {
+type = "object",
+properties = {
+response_code = {
+type = "integer",
+minimum = 200,
+maximum = 599,
+},
+unhealthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 500,
+maximum = 599,
+},
+uniqueItems = true,
+default = {500}
+},
+failures = {
+type = "integer",
+minimum = 1,
+default = 1,
+},
+},
+healthy = {
+type = "object",
+http_statuses = {
+type = "array",
+minItems = 1,
+items = {
+type = "integer",
+minimum = 200,
+maximum = 499,
+},
+uniqueItems = true,
+default = {200, 206}
+},
+successes = {
+type = "integer",
+minimum = 1,
+default = 1,
+}
+}
+},
+required = {"response_code", "unhealthy", "healthy"},
+}
+
+local function is_unhealthy(unhealthy_status, upstream_statu)
+for _, unhealthy in ipairs(unhealthy_status) do
+if unhealthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function is_healthy(healthy_status, upstream_statu)
+for _, healthy in ipairs(healthy_status) do
+if healthy == upstream_statu then
+return true
+end
+end
+
+return false
+end
+
+
+local function healthy_cache_key(ctx)
+return "healthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_cache_key(ctx)
+return "unhealthy-" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local function unhealthy_lastime_cache_key(ctx)
+return "unhealthy-lastime" .. core.request.get_host(ctx) .. ctx.var.uri
+end
+
+
+local _M = {
+version = 0.1,
+name = plugin_name,
+priority = 1000,
+schema = schema,
+}
+
+
+function _M.check_schema(conf)
+local ok, err = core.schema.check(schema, conf)
+if not ok then
+return false, err
+end
+
+return true
+end
+
+
+function _M.access(conf, ctx)
+local unhealthy_val, _ = shared_buffer:get(unhealthy_cache_key(ctx))
+local unhealthy_lastime, _ = 
shared_buffer:get(unhealthy_lastime_cache_key(ctx))
+
+if unhealthy_val and unhealthy_lastime then
+local ride = math.ceil(unhealthy_val / conf.unhealthy.failures)
+if ride < 1 then
+ride = 1
+end
+
+if unhealthy_lastime + 2^ride >= os.time() then

Review comment:
   `os.time()` is slow. `ngx.time()`





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org




[GitHub] [apisix] membphis commented on a change in pull request #2339: feature: breaker request by api

2020-09-28 Thread GitBox


membphis commented on a change in pull request #2339:
URL: https://github.com/apache/apisix/pull/2339#discussion_r496349323



##
File path: apisix/plugins/api-breaker.lua
##
@@ -0,0 +1,180 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local ngx = ngx
+local math = math
+local os = os
+local ipairs   = ipairs
+local core = require("apisix.core")
+local plugin_name = "api-breaker"
+
+local shared_buffer = ngx.shared['plugin-'.. plugin_name]

Review comment:
   what will happen if the user did not set this `shdict` ?





This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org