Fix some bugs and problems in gossip (#3527)

* Allow configuration of debugOutput to be performed

* don't send to own IP or update own data

* Use same socket to send and receive. Avoid problems in many opened and closed sockets to send

* Add callback for REMOVEed hosts

* Send broascast messages if seedList is empty

* Adapt yeelink to new luacheck rules

* Fix building of luac.cross for win to win2019 and VS 2019
This commit is contained in:
Gregor Hartmann 2022-07-10 17:01:21 +02:00 committed by Marcel Stör
parent d0ddcc6dac
commit f5ad76acb3
6 changed files with 57 additions and 34 deletions

View File

@ -58,7 +58,7 @@ jobs:
build_luac_cross_win:
runs-on: windows-latest
runs-on: windows-2019
steps:
- uses: actions/checkout@v2

2
.gitignore vendored
View File

@ -28,6 +28,8 @@ uz_zip
# ignore VS Code files
.vscode/**
# ignore VS files
.vs/**
# ignore IDEA files
.idea

View File

@ -68,7 +68,7 @@ gossip.setConfig(config)
Sets the configuration for gossip. The available options are:
`seedList` : the list of seeds gossip will start with; this will be updated as new nodes are discovered. Note that it's enough for all nodes to start with the same IP in the seedList, as once they have one seed in common, the data will propagate
`seedList` : the list of seeds gossip will start with; this will be updated as new nodes are discovered. Note that it's enough for all nodes to start with the same IP in the seedList, as once they have one seed in common, the data will propagate. If the seedList is empty a broadcast is sent, so this can be used for automatic discovery of nodes.
`roundInterval`: interval in milliseconds at which gossip will pick a random node from the seed list and send a `SYN` request

View File

@ -15,11 +15,7 @@ end
utils.debug = function(message)
if gossip.config.debug then
if gossip.config.debugOutput then
gossip.config.debugOutput(message);
else
print(message);
end
gossip.config.debugOutput(message);
end
end
@ -105,15 +101,23 @@ state.start = function()
return;
end
-- sending to own IP makes no sense
for index, value in ipairs(gossip.config.seedList) do
if value == gossip.ip then
table.remove(gossip.config.seedList, index)
utils.debug('removing own ip from seed list')
end
end
gossip.networkState[gossip.ip] = {};
local localState = gossip.networkState[gossip.ip];
localState.revision = state.setRev();
localState.heartbeat = tmr.time();
localState.state = constants.nodeState.UP;
gossip.inboundSocket = net.createUDPSocket();
gossip.inboundSocket:listen(gossip.config.comPort);
gossip.inboundSocket:on('receive', network.receiveData);
gossip.socket = net.createUDPSocket();
gossip.socket:listen(gossip.config.comPort);
gossip.socket:on('receive', network.receiveData);
gossip.started = true;
@ -128,16 +132,25 @@ end
state.tickNodeState = function(ip)
if gossip.networkState[ip] then
local nodeState = gossip.networkState[ip].state;
local oldNodeState = nodeState;
if nodeState < constants.nodeState.REMOVE then
nodeState = nodeState + constants.nodeState.TICK;
gossip.networkState[ip].state = nodeState;
end
if oldNodeState == constants.nodeState.DOWN then
if gossip.updateCallback then gossip.updateCallback(gossip.networkState[ip]); end
end
end
end
-- Network
network.broadcastIp = "255.255.255.255"
network.pushGossip = function(data, ip)
if not gossip.started then
utils.debug('Gossip not started.');
return;
end
gossip.networkState[gossip.ip].data = data;
network.sendSyn(nil, ip);
end
@ -145,10 +158,12 @@ end
network.updateNetworkState = function(updateData)
if gossip.updateCallback then gossip.updateCallback(updateData); end
for ip, data in pairs(updateData) do
if not utils.contains(gossip.config.seedList, ip) then
if not utils.contains(gossip.config.seedList, ip) and ip ~= network.broadcastIp and ip ~= gossip.ip then
table.insert(gossip.config.seedList, ip);
end
gossip.networkState[ip] = data;
if ip ~= gossip.ip then
gossip.networkState[ip] = data;
end
end
end
@ -170,17 +185,16 @@ network.pickRandomNode = function()
return gossip.config.seedList[randomListPick];
end
utils.debug(
'Seedlist is empty. Please provide one or wait for node to be contacted.');
return nil;
'Seedlist is empty. Using broadcast IP '..network.broadcastIp..' to discover.');
return network.broadcastIp;
end
network.sendData = function(ip, data, sendType)
local outboundSocket = net.createUDPSocket();
data.type = sendType;
local dataToSend = sjson.encode(data);
data.type = nil;
outboundSocket:send(gossip.config.comPort, ip, dataToSend);
outboundSocket:close();
data.type = sendType
local dataToSend = sjson.encode(data)
data.type = nil
gossip.socket:send(gossip.config.comPort, ip, dataToSend)
utils.debug("Sent "..#dataToSend.." bytes")
end
network.receiveSyn = function(ip, synData)
@ -235,7 +249,8 @@ constants.defaultConfig = {
seedList = {},
roundInterval = 15000,
comPort = 5000,
debug = false
debug = false,
debugOutput = print
};
constants.comparisonFields = {'revision', 'heartbeat', 'state'};

View File

@ -24,6 +24,7 @@ file.putcontents = dummy
local Ip_1 = '192.168.0.1';
local Ip_2 = '192.168.0.2';
local Ip_own = '192.168.0.3';
-- test runner
@ -186,20 +187,19 @@ function Test.utils_getMinus()
state = constants.nodeState.SUSPECT;
};
--local diff1 = utils.getMinus(data1, data2);
local diff1 = utils.getMinus(data1, data2);
local diff2 = utils.getMinus(data2, data1);
--assert(diff1[Ip_1] ~= nil and diff1[Ip_2] == nil);
assert(diff1[Ip_1] ~= nil and diff1[Ip_2] == nil);
assert(diff2[Ip_1] == nil and diff2[Ip_2] ~= nil);
end
-- state
function Test.state_setRev()
gossip.ip = Ip_1;
gossip.networkState[Ip_1] = {};
gossip.networkState[Ip_1].revision = -1;
gossip.ip = Ip_own;
gossip.networkState[Ip_own] = {};
gossip.networkState[Ip_own].revision = -1;
assert(state.setRev() == 0, 'Revision not initialized to 0.');
end
@ -223,6 +223,7 @@ end
-- network
function Test.network_updateNetworkState_no_callback()
gossip.ip = Ip_own;
local updateData = {}
updateData[Ip_1] = {
revision = 1,
@ -232,7 +233,12 @@ function Test.network_updateNetworkState_no_callback()
updateData[Ip_2] = {
revision = 1,
heartbeat = 700,
state = constants.nodeState.UP
state = constants.nodeState.DOWN
};
updateData[Ip_own] = {
revision = 1,
heartbeat = 800,
state = constants.nodeState.DOWN
};
network.updateNetworkState(updateData);
-- send duplicate data

View File

@ -54,10 +54,10 @@ end)
-- apikey must be -> string <-
-- e.g. xxx.init(00000,00000,"123j12b3jkb12k4b23bv54i2b5b3o4")
--========================================
function M.init(_device, _sensor, _apikey)
device = tostring(_device)
sensor = tostring(_sensor)
apikey = _apikey
function M.init(device_, sensor_, apikey_)
device = tostring(device_)
sensor = tostring(sensor_)
apikey = apikey_
if dns == "0.0.0.0" then
tmr.create():alarm(5000,tmr.ALARM_AUTO,function ()
if dns == "0.0.0.0" then
@ -90,9 +90,9 @@ end
--
--e.g. xxx.update(233.333)
--============================================================
function M.update(_datapoint)
function M.update(datapoint_)
datapoint = tostring(_datapoint)
datapoint = tostring(datapoint_)
sk:on("connection", function()