[patches] [feedback_daemon 1/1] daemon: Introduce feedback daemon
Ilya Markov
imarkov at tarantool.org
Fri Mar 2 19:15:22 MSK 2018
From: Roman Proskin <opomuc at gmail.com>
* feedback daemon sends information about instance to the
specified host.
* Add new options to box.cfg:
- feedback_enabled - switch on/off daemon, default=true.
- feedback_host - host to which feedbacks are sent,
default="https://feedback.tarantool.io".
- feedback_interval - time interval in seconds of feedbacks
sending, default=3600.
* Add possibility to generate feedback file in json format with
function box.feedback.save
Relates #2762
branch: gh-2762-feedback-daemon
---
src/box/CMakeLists.txt | 1 +
src/box/lua/feedback_daemon.lua | 136 ++++++++++++++++++++++++++++++++++
src/box/lua/init.c | 2 +
src/box/lua/load_cfg.lua | 9 +++
src/box/lua/schema.lua | 14 ++++
src/errinj.h | 1 +
src/httpc.c | 3 +-
test/app-tap/init_script.result | 77 ++++++++++---------
test/box-tap/feedback_daemon.test.lua | 97 ++++++++++++++++++++++++
test/box/admin.result | 6 ++
test/box/cfg.result | 12 +++
test/box/errinj.result | 2 +
test/box/misc.result | 1 +
test/engine/iterator.result | 2 +-
test/engine/savepoint.result | 12 +--
15 files changed, 330 insertions(+), 45 deletions(-)
create mode 100644 src/box/lua/feedback_daemon.lua
create mode 100755 test/box-tap/feedback_daemon.test.lua
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index ad7f910..fb6602b 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -6,6 +6,7 @@ lua_source(lua_sources lua/schema.lua)
lua_source(lua_sources lua/tuple.lua)
lua_source(lua_sources lua/session.lua)
lua_source(lua_sources lua/checkpoint_daemon.lua)
+lua_source(lua_sources lua/feedback_daemon.lua)
lua_source(lua_sources lua/net_box.lua)
lua_source(lua_sources lua/upgrade.lua)
lua_source(lua_sources lua/console.lua)
diff --git a/src/box/lua/feedback_daemon.lua b/src/box/lua/feedback_daemon.lua
new file mode 100644
index 0000000..bfec2d0
--- /dev/null
+++ b/src/box/lua/feedback_daemon.lua
@@ -0,0 +1,136 @@
+-- feedback_daemon.lua (internal file)
+--
+local log = require('log')
+local json = require('json')
+local fiber = require('fiber')
+local http = require('http.client')
+
+local PREFIX = "feedback_daemon"
+
+local daemon = {
+ enabled = false,
+ interval = 0,
+ host = nil,
+ fiber = nil,
+ control = nil,
+ guard = nil,
+}
+
+local function get_fiber_id(f)
+ local fid = 0
+ if f ~= nil and f:status() ~= "dead" then
+ fid = f:id()
+ end
+ return fid
+end
+
+local function fill_in_feedback(feedback)
+ if box.info.status ~= "running" then
+ return nil, "not running"
+ end
+ feedback.tarantool_version = box.info.version
+ feedback.server_id = box.info.uuid
+ feedback.cluster_id = box.info.cluster.uuid
+ return feedback
+end
+
+local function send(self)
+ fiber.name(PREFIX, { truncate = true })
+ local header = { feedback_type = "version", feedback_version = 1 }
+
+ while true do
+ local feedback = fill_in_feedback(header)
+ local msg = self.control:get(self.interval)
+ if msg == "shutdown" then
+ break
+ elseif feedback ~= nil then
+ pcall(http.post, self.host, json.encode(feedback), {timeout=1})
+ end
+ end
+end
+
+local function guard_loop(daemon)
+ fiber.name(string.format("guard of %s", PREFIX), {truncate=true})
+
+ while true do
+ if get_fiber_id(daemon.fiber) == 0 then
+ daemon.fiber = fiber.create(send, daemon)
+ log.verbose("%s restarted", PREFIX)
+ end
+ fiber.sleep(daemon.interval)
+ end
+end
+
+-- these functions are used for test purposes only
+local function start(self)
+ self:stop()
+ if self.enabled then
+ self.control = fiber.channel()
+ self.guard = fiber.create(guard_loop, self)
+ end
+ log.verbose("%s started", PREFIX)
+end
+
+local function stop(self)
+ if (get_fiber_id(self.guard) ~= 0) then
+ self.guard:cancel()
+ --
+ -- this cycle may be replaced with fiber_join
+ -- so guard fiber may be set joinable
+ --
+ while self.guard:status() ~= 'dead' do
+ fiber.sleep(0.001)
+ end
+ self.guard = nil
+ end
+ if (get_fiber_id(self.fiber) ~= 0) then
+ self.control:put("shutdown")
+ while self.fiber:status() ~= 'dead' do
+ fiber.sleep(0.001)
+ end
+ self.fiber = nil
+ self.control = nil
+ end
+ log.verbose("%s stopped", PREFIX)
+end
+
+local function reload(self)
+ self:stop()
+ self:start()
+end
+
+setmetatable(daemon, {
+ __index = {
+ set_feedback_params = function()
+ daemon.enabled = box.cfg.feedback_enabled
+ daemon.host = box.cfg.feedback_host
+ daemon.interval = box.cfg.feedback_interval
+ reload(daemon)
+ return
+ end,
+ -- this function is used in saving feedback in file
+ generate_feedback = function()
+ return fill_in_feedback({ feedback_type = "version", feedback_version = 1 })
+ end,
+ start = function()
+ start(daemon)
+ end,
+ stop = function()
+ stop(daemon)
+ end,
+ reload = function()
+ reload(daemon)
+ end,
+ send_test = function()
+ if daemon.control ~= nil then
+ daemon.control:put("send")
+ end
+ end
+ }
+})
+
+if box.internal == nil then
+ box.internal = { [PREFIX] = daemon }
+else
+ box.internal[PREFIX] = daemon
+end
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 7547758..d4b5788 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -65,6 +65,7 @@ extern char session_lua[],
load_cfg_lua[],
xlog_lua[],
checkpoint_daemon_lua[],
+ feedback_daemon_lua[],
net_box_lua[],
upgrade_lua[],
console_lua[];
@@ -74,6 +75,7 @@ static const char *lua_sources[] = {
"box/tuple", tuple_lua,
"box/schema", schema_lua,
"box/checkpoint_daemon", checkpoint_daemon_lua,
+ "box/feedback_daemon", feedback_daemon_lua,
"box/upgrade", upgrade_lua,
"box/net_box", net_box_lua,
"box/console", console_lua,
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index d4f2128..89fd774 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -59,6 +59,9 @@ local default_cfg = {
replication_sync_lag = 10,
replication_connect_timeout = 4,
replication_connect_quorum = nil, -- connect all
+ feedback_enabled = true,
+ feedback_host = "https://feedback.tarantool.io",
+ feedback_interval = 3600,
}
-- types of available options
@@ -115,6 +118,9 @@ local template_cfg = {
replication_sync_lag = 'number',
replication_connect_timeout = 'number',
replication_connect_quorum = 'number',
+ feedback_enabled = 'boolean',
+ feedback_host = 'string',
+ feedback_interval = 'number',
}
local function normalize_uri(port)
@@ -175,6 +181,9 @@ local dynamic_cfg = {
checkpoint_count = private.cfg_set_checkpoint_count,
checkpoint_interval = private.checkpoint_daemon.set_checkpoint_interval,
worker_pool_threads = private.cfg_set_worker_pool_threads,
+ feedback_enabled = private.feedback_daemon.set_feedback_params,
+ feedback_host = private.feedback_daemon.set_feedback_params,
+ feedback_interval = private.feedback_daemon.set_feedback_params,
-- do nothing, affects new replicas, which query this value on start
wal_dir_rescan_delay = function() end,
custom_proc_title = function()
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 30c6bc6..8d2f11d 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -5,6 +5,8 @@ local msgpack = require('msgpack')
local msgpackffi = require('msgpackffi')
local fun = require('fun')
local log = require('log')
+local fio = require('fio')
+local json = require('json')
local session = box.session
local internal = require('box.internal')
local function setmap(table)
@@ -2234,4 +2236,16 @@ box.internal.schema.init = function()
box_sequence_init()
end
+box.feedback = {}
+box.feedback.save = function(file_name)
+ local feedback = json.encode(box.internal.feedback_daemon.generate_feedback())
+ local fh, err = fio.open(file_name, {'O_CREAT', 'O_RDWR', 'O_TRUNC'},
+ tonumber('0777', 8))
+ if not fh then
+ error(err)
+ end
+ fh:write(feedback)
+ fh:close()
+end
+
box.NULL = msgpack.NULL
diff --git a/src/errinj.h b/src/errinj.h
index 352a3c3..20383e0 100644
--- a/src/errinj.h
+++ b/src/errinj.h
@@ -107,6 +107,7 @@ struct errinj {
_(ERRINJ_RELAY_EXIT_DELAY, ERRINJ_DOUBLE, {.dparam = 0}) \
_(ERRINJ_VY_DELAY_PK_LOOKUP, ERRINJ_BOOL, {.bparam = false}) \
_(ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \
+ _(ERRINJ_HTTPC_EXECUTE, ERRINJ_BOOL, {.bparam = false}) \
ENUM0(errinj_id, ERRINJ_LIST);
extern struct errinj errinjs[];
diff --git a/src/httpc.c b/src/httpc.c
index cce7e51..633e688 100644
--- a/src/httpc.c
+++ b/src/httpc.c
@@ -35,6 +35,7 @@
#include <curl/curl.h>
#include "fiber.h"
+#include "errinj.h"
/**
* libcurl callback for CURLOPT_WRITEFUNCTION
@@ -318,7 +319,7 @@ httpc_execute(struct httpc_request *req, double timeout)
if (curl_execute(&req->curl_request, &env->curl_env, timeout) != CURLM_OK)
return -1;
-
+ ERROR_INJECT_RETURN(ERRINJ_HTTPC_EXECUTE);
long longval = 0;
switch (req->curl_request.code) {
case CURLE_OK:
diff --git a/test/app-tap/init_script.result b/test/app-tap/init_script.result
index 80153e3..ae01b48 100644
--- a/test/app-tap/init_script.result
+++ b/test/app-tap/init_script.result
@@ -7,43 +7,46 @@ box.cfg
2 checkpoint_count:2
3 checkpoint_interval:3600
4 coredump:false
-5 force_recovery:false
-6 hot_standby:false
-7 listen:port
-8 log:tarantool.log
-9 log_format:plain
-10 log_level:5
-11 log_nonblock:true
-12 memtx_dir:.
-13 memtx_max_tuple_size:1048576
-14 memtx_memory:107374182
-15 memtx_min_tuple_size:16
-16 pid_file:box.pid
-17 read_only:false
-18 readahead:16320
-19 replication_connect_timeout:4
-20 replication_sync_lag:10
-21 replication_timeout:1
-22 rows_per_wal:500000
-23 slab_alloc_factor:1.05
-24 too_long_threshold:0.5
-25 vinyl_bloom_fpr:0.05
-26 vinyl_cache:134217728
-27 vinyl_dir:.
-28 vinyl_max_tuple_size:1048576
-29 vinyl_memory:134217728
-30 vinyl_page_size:8192
-31 vinyl_range_size:1073741824
-32 vinyl_read_threads:1
-33 vinyl_run_count_per_level:2
-34 vinyl_run_size_ratio:3.5
-35 vinyl_timeout:60
-36 vinyl_write_threads:2
-37 wal_dir:.
-38 wal_dir_rescan_delay:2
-39 wal_max_size:268435456
-40 wal_mode:write
-41 worker_pool_threads:4
+5 feedback_enabled:true
+6 feedback_host:https://feedback.tarantool.io
+7 feedback_interval:3600
+8 force_recovery:false
+9 hot_standby:false
+10 listen:port
+11 log:tarantool.log
+12 log_format:plain
+13 log_level:5
+14 log_nonblock:true
+15 memtx_dir:.
+16 memtx_max_tuple_size:1048576
+17 memtx_memory:107374182
+18 memtx_min_tuple_size:16
+19 pid_file:box.pid
+20 read_only:false
+21 readahead:16320
+22 replication_connect_timeout:4
+23 replication_sync_lag:10
+24 replication_timeout:1
+25 rows_per_wal:500000
+26 slab_alloc_factor:1.05
+27 too_long_threshold:0.5
+28 vinyl_bloom_fpr:0.05
+29 vinyl_cache:134217728
+30 vinyl_dir:.
+31 vinyl_max_tuple_size:1048576
+32 vinyl_memory:134217728
+33 vinyl_page_size:8192
+34 vinyl_range_size:1073741824
+35 vinyl_read_threads:1
+36 vinyl_run_count_per_level:2
+37 vinyl_run_size_ratio:3.5
+38 vinyl_timeout:60
+39 vinyl_write_threads:2
+40 wal_dir:.
+41 wal_dir_rescan_delay:2
+42 wal_max_size:268435456
+43 wal_mode:write
+44 worker_pool_threads:4
--
-- Test insert from detached fiber
--
diff --git a/test/box-tap/feedback_daemon.test.lua b/test/box-tap/feedback_daemon.test.lua
new file mode 100755
index 0000000..079fdf1
--- /dev/null
+++ b/test/box-tap/feedback_daemon.test.lua
@@ -0,0 +1,97 @@
+#!/usr/bin/env tarantool
+
+-- Testing feedback module
+
+local tap = require('tap')
+local json = require('json')
+local fiber = require('fiber')
+local test = tap.test('feedback_daemon')
+
+test:plan(9)
+
+box.cfg{log = 'report.log', log_level = 6}
+
+local function self_decorator(self)
+ return function(handler)
+ return function(req) return handler(self, req) end
+ end
+end
+
+-- set up mock for feedback server
+local function get_feedback(self, req)
+ local body = req:read()
+ local ok, data = pcall(json.decode, body)
+ if ok then
+ self:put({ 'feedback', body })
+ end
+end
+
+local interval = 0.01
+
+box.cfg{
+ feedback_host = '0.0.0.0:4444/feedback',
+ feedback_interval = interval,
+}
+
+-- check it does not fail without server
+local daemon = box.internal.feedback_daemon
+daemon.start()
+daemon.send_test()
+local httpd = require('http.server').new('0.0.0.0', '4444')
+httpd:route(
+ { path = '/feedback', method = 'POST' },
+ self_decorator(box.space._schema)(get_feedback)
+)
+httpd:start()
+
+local function check(message)
+ while box.space._schema:get('feedback') == nil do fiber.sleep(0.001) end
+ local data = box.space._schema:get('feedback')
+ test:ok(data ~= nil, message)
+ box.space._schema:delete('feedback')
+end
+
+-- check if feedback has been sent and received
+daemon.reload()
+check("feedback received after reload")
+
+local errinj = box.error.injection
+errinj.set("ERRINJ_HTTPC", true)
+check('feedback received after errinj')
+errinj.set("ERRINJ_HTTPC", false)
+
+daemon.send_test()
+check("feedback received after explicit sending")
+
+box.cfg{feedback_enabled = false}
+daemon.send_test()
+while box.space._schema:get('feedback') ~= nil do fiber.sleep(0.001) end
+test:ok(box.space._schema:get('feedback') == nil, "no feedback after disabling")
+
+box.cfg{feedback_enabled = true}
+daemon.send_test()
+check("feedback after start")
+
+daemon.stop()
+daemon.send_test()
+while box.space._schema:get('feedback') ~= nil do fiber.sleep(0.001) end
+test:ok(box.space._schema:get('feedback') == nil, "no feedback after stop")
+
+daemon.start()
+daemon.send_test()
+check("feedback after start")
+
+box.feedback.save("feedback.json")
+daemon.send_test()
+while box.space._schema:get('feedback') == nil do fiber.sleep(0.001) end
+local data = box.space._schema:get('feedback')
+local fio = require("fio")
+local fh = fio.open("feedback.json")
+test:ok(fh, "file is created")
+local file_data = fh:read()
+test:is(file_data, data[2], "data is equal")
+fh:close()
+fio.unlink("feedback.json")
+
+test:check()
+os.exit(0)
diff --git a/test/box/admin.result b/test/box/admin.result
index 7a3e937..2969766 100644
--- a/test/box/admin.result
+++ b/test/box/admin.result
@@ -26,6 +26,12 @@ cfg_filter(box.cfg)
- 3600
- - coredump
- false
+ - - feedback_enabled
+ - true
+ - - feedback_host
+ - https://feedback.tarantool.io
+ - - feedback_interval
+ - 3600
- - force_recovery
- false
- - hot_standby
diff --git a/test/box/cfg.result b/test/box/cfg.result
index 67539cd..717fa31 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -22,6 +22,12 @@ cfg_filter(box.cfg)
- 3600
- - coredump
- false
+ - - feedback_enabled
+ - true
+ - - feedback_host
+ - https://feedback.tarantool.io
+ - - feedback_interval
+ - 3600
- - force_recovery
- false
- - hot_standby
@@ -111,6 +117,12 @@ cfg_filter(box.cfg)
- 3600
- - coredump
- false
+ - - feedback_enabled
+ - true
+ - - feedback_host
+ - https://feedback.tarantool.io
+ - - feedback_interval
+ - 3600
- - force_recovery
- false
- - hot_standby
diff --git a/test/box/errinj.result b/test/box/errinj.result
index 054045c..667c309 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -16,6 +16,8 @@ errinj.info()
state: 0
ERRINJ_WAL_WRITE:
state: false
+ ERRINJ_HTTPC_EXECUTE:
+ state: false
ERRINJ_VYRUN_DATA_READ:
state: false
ERRINJ_VY_SCHED_TIMEOUT:
diff --git a/test/box/misc.result b/test/box/misc.result
index cd3af7f..57717c4 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -63,6 +63,7 @@ t
- commit
- ctl
- error
+ - feedback
- index
- info
- internal
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 9cedc6a..0c4fcc9 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4215,7 +4215,7 @@ s:replace{35}
...
state, value = gen(param,state)
---
-- error: 'builtin/box/schema.lua:983: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:985: usage: next(param, state)'
...
value
---
diff --git a/test/engine/savepoint.result b/test/engine/savepoint.result
index 9d238ec..d440efa 100644
--- a/test/engine/savepoint.result
+++ b/test/engine/savepoint.result
@@ -14,7 +14,7 @@ s1 = box.savepoint()
...
box.rollback_to_savepoint(s1)
---
-- error: 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- error: 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
box.begin() s1 = box.savepoint()
---
@@ -323,27 +323,27 @@ test_run:cmd("setopt delimiter ''");
ok1, errmsg1
---
- false
-- 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
ok2, errmsg2
---
- false
-- 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
ok3, errmsg3
---
- false
-- 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
ok4, errmsg4
---
- false
-- 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
ok5, errmsg5
---
- false
-- 'builtin/box/schema.lua:298: Usage: box.rollback_to_savepoint(savepoint)'
+- 'builtin/box/schema.lua:300: Usage: box.rollback_to_savepoint(savepoint)'
...
s:select{}
---
--
2.7.4
More information about the Tarantool-patches
mailing list