feat: add destroyBackendTimer to stop reporting metrics (#58)

diff --git a/README.md b/README.md
index 1ec2a4e..2145bfd 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,7 @@
 
 ## Nginx APIs
 - **startTimer**, `require("skywalking.client"):startBackendTimer("http://127.0.0.1:8080")`. Start the backend timer. This timer register the metadata and report traces to the backend.
+- **destroyBackendTimer**, `require("skywalking.client"):destroyBackendTimer()`. Stop the timer created by `startBackendTimer`, and clean unreported data.
 - **start**, `require("skywalking.tracer"):start("upstream service", correlation)`. Begin the tracing before the upstream begin. The custom data (table type) can be injected as the second parameter, and then they will be propagated to the downstream service.
 - **finish**, `require("skywalking.tracer"):finish()`. Finish the tracing for this HTTP request.
 - **prepareForReport**, `require("skywalking.tracer"):prepareForReport()`. Prepare the finished segment for further report.
diff --git a/examples/nginx.conf b/examples/nginx.conf
index db613a2..980cbac 100644
--- a/examples/nginx.conf
+++ b/examples/nginx.conf
@@ -40,6 +40,9 @@
 
         require("skywalking.util").set_randomseed()
         require("skywalking.client"):startBackendTimer("http://127.0.0.1:8080")
+
+        -- Any time you want to stop reporting metrics, call `destroyBackendTimer`
+        -- require("skywalking.client"):destroyBackendTimer()
     }
 
     server {
diff --git a/lib/skywalking/client.lua b/lib/skywalking/client.lua
index dccc9de..9efaa43 100644
--- a/lib/skywalking/client.lua
+++ b/lib/skywalking/client.lua
@@ -14,10 +14,15 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
+local Const = require('skywalking.constants')
+
 
 local SEGMENT_BATCH_COUNT = 100
 
-local Client = {}
+local Client = {
+    -- expose the delay for test
+    backendTimerDelay = 3 -- in seconds 
+}
 
 -- Tracing timer reports instance properties report, keeps alive and sends traces
 -- After report instance properties successfully, it sends keep alive packages.
@@ -25,7 +30,6 @@
     local metadata_buffer = ngx.shared.tracing_buffer
 
     -- The codes of timer setup is following the OpenResty timer doc
-    local delay = 3  -- in seconds
     local new_timer = ngx.timer.at
     local check
 
@@ -34,7 +38,7 @@
     local ERR = ngx.ERR
 
     check = function(premature)
-        if not premature then
+        if not premature and not self.stopped then
             local instancePropertiesSubmitted = metadata_buffer:get('instancePropertiesSubmitted')
             if (instancePropertiesSubmitted == nil or instancePropertiesSubmitted == false) then
                 self:reportServiceInstance(metadata_buffer, backend_http_uri)
@@ -45,7 +49,7 @@
             self:reportTraces(metadata_buffer, backend_http_uri)
 
             -- do the health check
-            local ok, err = new_timer(delay, check)
+            local ok, err = new_timer(self.backendTimerDelay, check)
             if not ok then
                 log(ERR, "failed to create timer: ", err)
                 return
@@ -54,7 +58,7 @@
     end
 
     if 0 == ngx.worker.id() then
-        local ok, err = new_timer(delay, check)
+        local ok, err = new_timer(self.backendTimerDelay, check)
         if not ok then
             log(ERR, "failed to create timer: ", err)
             return
@@ -62,6 +66,19 @@
     end
 end
 
+-- Stop the tracing report timer and clean unreported data
+function Client:destoryBackendTimer()
+    self.stopped = true
+
+    local metadata_buffer = ngx.shared.tracing_buffer
+    local ok, err = metadata_buffer:delete(Const.segment_queue)
+    if not ok then
+        return nil, err
+    end
+    
+    return true
+end
+
 function Client:reportServiceInstance(metadata_buffer, backend_http_uri)
     local log = ngx.log
     local DEBUG = ngx.DEBUG
@@ -170,7 +187,7 @@
     local ERR = ngx.ERR
 
     local queue = ngx.shared.tracing_buffer
-    local segment = queue:rpop('segment')
+    local segment = queue:rpop(Const.segment_queue)
     local segmentTransform = ''
 
     local count = 0
@@ -183,7 +200,7 @@
         end
 
         segmentTransform = segmentTransform .. segment
-        segment = queue:rpop('segment')
+        segment = queue:rpop(Const.segment_queue)
         count = count + 1
 
         if count >= SEGMENT_BATCH_COUNT then
diff --git a/lib/skywalking/constants.lua b/lib/skywalking/constants.lua
new file mode 100644
index 0000000..ae0655c
--- /dev/null
+++ b/lib/skywalking/constants.lua
@@ -0,0 +1,22 @@
+--
+-- 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 _M = {
+    segment_queue = "segment",
+}
+
+
+return _M
diff --git a/lib/skywalking/tracer.lua b/lib/skywalking/tracer.lua
index edd7d0d..167b397 100644
--- a/lib/skywalking/tracer.lua
+++ b/lib/skywalking/tracer.lua
@@ -19,6 +19,7 @@
 local Layer = require('skywalking.span_layer')
 local Segment = require('skywalking.segment')
 local Util = require('skywalking.util')
+local Const = require('skywalking.constants')
 local json = require('cjson.safe')
 
 local metadata_shdict = ngx.shared.tracing_buffer
@@ -112,7 +113,7 @@
     end
     ngx.log(ngx.DEBUG, 'segment = ', segmentJson)
 
-    local length, err = metadata_shdict:lpush('segment', segmentJson)
+    local length, err = metadata_shdict:lpush(Const.segment_queue, segmentJson)
     if not length then
         ngx.log(ngx.ERR, "failed to push segment: ", err)
         return
diff --git a/t/client.t b/t/client.t
new file mode 100644
index 0000000..1321f46
--- /dev/null
+++ b/t/client.t
@@ -0,0 +1,100 @@
+use Test::Nginx::Socket 'no_plan';
+
+use Cwd qw(cwd);
+my $pwd = cwd();
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (!$block->no_error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+
+    my $http_config = $block->http_config // '';
+    my $default_http_config = <<_EOC_;
+    lua_package_path "$pwd/lib/?.lua;;";
+    lua_shared_dict tracing_buffer 100m;
+    $http_config
+_EOC_
+
+    $block->set_value("http_config", $default_http_config);
+
+    my $config = $block->config;
+    my $default_config = <<_EOC_;
+    $config
+    location = /v3/management/reportProperties {
+        content_by_lua_block {
+            ngx.req.read_body()
+            local data = ngx.req.get_body_data()
+            if data then
+                data = require("cjson.safe").decode(data)
+                ngx.log(ngx.WARN, "language: ", data.properties[1].value)
+            end
+        }
+    }
+
+    location = /v3/management/keepAlive {
+        content_by_lua_block {
+            ngx.log(ngx.WARN, "Go keepAlive")
+            ngx.exit(200)
+        }
+    }
+_EOC_
+
+    $block->set_value("config", $default_config);
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: start backend timer
+--- config
+    location /t {
+        content_by_lua_block {
+            local client = require("skywalking.client")
+            client.backendTimerDelay = 0.01
+            client:startBackendTimer("http://127.0.0.1:" .. ngx.var.server_port)
+            ngx.sleep(0.02)
+            ngx.say('ok')
+        }
+    }
+--- response_body
+ok
+--- error_log
+language: lua
+Go keepAlive
+
+
+
+=== TEST 2: destory backend timer
+--- config
+    location /t {
+        content_by_lua_block {
+            local client = require("skywalking.client")
+            client.backendTimerDelay = 0.01
+            client:startBackendTimer("http://127.0.0.1:" .. ngx.var.server_port)
+            local ok, err = client:destoryBackendTimer()
+            ngx.sleep(0.02)
+            if not err then
+                ngx.say(ok)
+            else
+                ngx.say(err)
+            end
+        }
+    }
+--- response_body
+true
+--- no_error_log
+language: lua
+Go keepAlive