selected = policy(&serversList, &dnsq);
}
+ if (selected >= servers.size()) {
+ /* invalid offset, meaning that there is no server available */
+ return {};
+ }
+
selectedBackend = servers.at(selected).second;
}
}
setServerPolicyLua("splitsetup", splitSetup)
+A faster, FFI version is also available since 1.5.0:
+
+.. code-block:: lua
+
+ local ffi = require("ffi")
+ local C = ffi.C
+
+ local counter = 0
+ function luaffiroundrobin(servers_list, dq)
+ counter = counter + 1
+ return (counter % tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list)))
+ end
+ setServerPolicyLuaFFI("luaffiroundrobin", luaffiroundrobin)
+
+Note that this version returns the index (starting at 0) of the server to select,
+instead of returning the server itself. It was initially not possible to indicate
+that all servers were unavailable from these policies, but since 1.9.2 returning
+a value equal or greater than the number of servers will be interpreted as such.
+
For performance reasons, 1.6.0 introduced per-thread Lua FFI policies that are run in a lock-free per-thread Lua context instead of the global one.
This reduces contention between threads at the cost of preventing sharing data between threads for these policies. Since the policy needs to be recompiled
in the context of each thread instead of the global one, Lua code that returns a function should be passed to the function as a string instead of directly
.. versionadded:: 1.5.0
+ .. versionchanged:: 1.9.2
+ Returning a value equal or greater than the number of servers will be interpreted as all servers being unavailable.
+
Set server selection policy to one named ``name`` and provided by the FFI function ``function``.
:param string name: name for this policy
.. versionadded:: 1.6.0
+ .. versionchanged:: 1.9.2
+ Returning a value equal or greater than the number of servers will be interpreted as all servers being unavailable.
+
Set server selection policy to one named ``name`` and the Lua FFI function returned by the Lua code passed in ``code``.
The resulting policy will be executed in a lock-free per-thread context, instead of running in the global Lua context.
resetLuaContext();
}
+BOOST_AUTO_TEST_CASE(test_lua_ffi_no_server_available) {
+ DNSName name("powerdns.com.");
+ static const std::string policySetupStr = R"foo(
+ local ffi = require("ffi")
+ local C = ffi.C
+ local counter = 0
+ function ffipolicy(servers_list, dq)
+ local serversCount = tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list))
+ -- return clearly out of bounds value to indicate that no server can be used
+ return serversCount + 100
+ end
+
+ setServerPolicyLuaFFI("FFI policy", ffipolicy)
+ )foo";
+ resetLuaContext();
+ g_lua.lock()->executeCode(getLuaFFIWrappers());
+ g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy) {
+ g_policy.setState(ServerPolicy(name, policy));
+ });
+ g_lua.lock()->executeCode(policySetupStr);
+
+ {
+ ServerPolicy pol = g_policy.getCopy();
+ ServerPolicy::NumberedServerVector servers;
+ for (size_t idx = 1; idx <= 10; idx++) {
+ servers.push_back({ idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")) });
+ servers.at(idx - 1).second->setUp();
+ }
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
+
+ auto dq = getDQ(&name);
+ auto server = pol.getSelectedBackend(servers, dq);
+ BOOST_REQUIRE(server == nullptr);
+ }
+ resetLuaContext();
+}
+
BOOST_AUTO_TEST_CASE(test_lua_ffi_hashed) {
std::vector<DNSName> names;
names.reserve(1000);
self.assertEqual(self._responsesCounter['UDP Responder 2'], numberOfQueries - self._responsesCounter['UDP Responder'])
if 'TCP Responder 2' in self._responsesCounter:
self.assertEqual(self._responsesCounter['TCP Responder 2'], numberOfQueries - self._responsesCounter['TCP Responder'])
+
+class TestRoutingLuaFFILBNoServer(DNSDistTest):
+
+ _config_template = """
+ -- we want a ServFail answer when all servers are down
+ setServFailWhenNoServer(true)
+
+ local ffi = require("ffi")
+ local C = ffi.C
+ function luaffipolicy(servers_list, dq)
+ -- return a large value, outside of the number of servers, to indicate that
+ -- no server is available
+ return tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list)) + 100
+ end
+ setServerPolicyLuaFFI("luaffipolicy", luaffipolicy)
+
+ s1 = newServer{address="127.0.0.1:%s"}
+ s1:setDown()
+ """
+ _verboseMode = True
+
+ def testOurPolicy(self):
+ """
+ Routing: LuaFFI policy, all servers are down
+ """
+ name = 'lua-ffi-no-servers.routing.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.set_rcode(dns.rcode.SERVFAIL)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (_, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertEqual(expectedResponse, receivedResponse)