]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdistdist/test-dnsdist-connections-cache.cc
dnsdist: Split the list of downstream connections in two, active and idle
[thirdparty/pdns.git] / pdns / dnsdistdist / test-dnsdist-connections-cache.cc
CommitLineData
baa91396
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22#define BOOST_TEST_DYN_LINK
23#define BOOST_TEST_NO_MAIN
24
25#include <boost/test/unit_test.hpp>
26
27#include "dnsdist-tcp-downstream.hh"
28
29class MockupConnection
30{
31public:
54a9a226 32 MockupConnection(const std::shared_ptr<DownstreamState>& ds, std::unique_ptr<FDMultiplexer>&, const struct timeval&, std::string&&): d_ds(ds)
baa91396
RG
33 {
34 }
35
36 bool canBeReused() const
37 {
38 return d_reusable;
39 }
40
41 bool isUsable() const
42 {
43 return d_usable;
44 }
45
46 bool willBeReusable(bool) const
47 {
48 return d_reusable;
49 }
50
51 void setReused()
52 {
53 }
54
55 struct timeval getLastDataReceivedTime() const
56 {
57 return d_lastDataReceivedTime;
58 }
59
60 bool isIdle() const
61 {
62 return d_idle;
63 }
64
65 void stopIO()
66 {
67 }
68
54a9a226
RG
69 std::shared_ptr<DownstreamState> getDS() const
70 {
71 return d_ds;
72 }
73
74 std::shared_ptr<DownstreamState> d_ds;
b8352ee0
RG
75 struct timeval d_lastDataReceivedTime
76 {
77 0, 0
78 };
baa91396
RG
79 bool d_reusable{true};
80 bool d_usable{true};
81 bool d_idle{false};
82};
83
84BOOST_AUTO_TEST_SUITE(test_dnsdist_connections_cache)
85
86BOOST_AUTO_TEST_CASE(test_ConnectionsCache)
87{
88 DownstreamConnectionsManager<MockupConnection> manager;
54a9a226 89 const size_t maxIdleConnPerDownstream = 5;
baa91396
RG
90 const uint16_t cleanupInterval = 1;
91 const uint16_t maxIdleTime = 5;
54a9a226 92 manager.setMaxIdleConnectionsPerDownstream(maxIdleConnPerDownstream);
baa91396
RG
93 manager.setCleanupInterval(cleanupInterval);
94 manager.setMaxIdleTime(maxIdleTime);
95
96 auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent());
97 auto downstream1 = std::make_shared<DownstreamState>(ComboAddress("192.0.2.1"));
98 auto downstream2 = std::make_shared<DownstreamState>(ComboAddress("192.0.2.2"));
99 struct timeval now;
100 gettimeofday(&now, nullptr);
101
102 auto conn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
103 BOOST_REQUIRE(conn != nullptr);
104 BOOST_CHECK_EQUAL(manager.count(), 1U);
54a9a226
RG
105 BOOST_CHECK_EQUAL(manager.getActiveCount(), 1U);
106 BOOST_CHECK_EQUAL(manager.getIdleCount(), 0U);
baa91396
RG
107
108 /* since the connection can be reused, we should get the same one */
109 {
110 auto conn1 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
111 BOOST_CHECK(conn.get() == conn1.get());
112 BOOST_CHECK_EQUAL(manager.count(), 1U);
54a9a226 113 BOOST_CHECK_EQUAL(manager.getActiveCount(), 1U);
baa91396
RG
114 }
115
116 /* if we mark it non-usable, we should get a new one */
117 conn->d_usable = false;
118 auto conn2 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
119 BOOST_CHECK(conn.get() != conn2.get());
120 BOOST_CHECK_EQUAL(manager.count(), 2U);
54a9a226 121 BOOST_CHECK_EQUAL(manager.getActiveCount(), 2U);
baa91396
RG
122
123 /* since the second connection can be reused, we should get it */
124 {
125 auto conn3 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
126 BOOST_CHECK(conn3.get() == conn2.get());
127 BOOST_CHECK_EQUAL(manager.count(), 2U);
54a9a226 128 BOOST_CHECK_EQUAL(manager.getActiveCount(), 2U);
baa91396
RG
129 }
130
131 /* different downstream so different connection */
132 auto differentConn = manager.getConnectionToDownstream(mplexer, downstream2, now, std::string());
133 BOOST_REQUIRE(differentConn != nullptr);
134 BOOST_CHECK(differentConn.get() != conn.get());
135 BOOST_CHECK(differentConn.get() != conn2.get());
136 BOOST_CHECK_EQUAL(manager.count(), 3U);
54a9a226 137 BOOST_CHECK_EQUAL(manager.getActiveCount(), 3U);
baa91396
RG
138 {
139 /* but we should be able to reuse it */
140 auto sameConn = manager.getConnectionToDownstream(mplexer, downstream2, now, std::string());
141 BOOST_CHECK(sameConn.get() == differentConn.get());
142 BOOST_CHECK_EQUAL(manager.count(), 3U);
54a9a226 143 BOOST_CHECK_EQUAL(manager.getActiveCount(), 3U);
baa91396
RG
144 }
145
146 struct timeval later = now;
147 later.tv_sec += cleanupInterval + 1;
148
149 /* mark the second connection as no longer usable */
150 conn2->d_usable = false;
151 /* first one as well but still fresh so it will not get checked */
152 conn->d_usable = true;
153 conn->d_lastDataReceivedTime = later;
154 /* third one is usable but idle for too long */
155 differentConn->d_idle = true;
156 differentConn->d_lastDataReceivedTime = later;
157 differentConn->d_lastDataReceivedTime.tv_sec -= (maxIdleTime + 1);
158
159 /* we should not do an actual cleanup attempt since the last cleanup was done recently */
160 manager.cleanupClosedConnections(now);
161 BOOST_CHECK_EQUAL(manager.count(), 3U);
162
163 manager.cleanupClosedConnections(later);
164 BOOST_CHECK_EQUAL(manager.count(), 1U);
165
166 /* mark the remaining conn as non-usable, to get new ones */
167 conn->d_usable = false;
168 conn->d_lastDataReceivedTime.tv_sec = 0;
169
b8352ee0 170 std::vector<std::shared_ptr<MockupConnection>> conns = {conn};
54a9a226 171 while (conns.size() < maxIdleConnPerDownstream) {
baa91396
RG
172 auto newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
173 newConn->d_usable = false;
174 conns.push_back(newConn);
175 BOOST_CHECK_EQUAL(manager.count(), conns.size());
176 }
177
54a9a226 178 /* if we add a new one, the oldest should NOT get expunged because they are all active ones! */
baa91396 179 auto newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
54a9a226 180 BOOST_CHECK_GT(manager.count(), maxIdleConnPerDownstream);
baa91396
RG
181
182 {
183 /* mark all connections as not usable anymore */
184 for (auto& c : conns) {
185 c->d_usable = false;
186 }
187
188 /* except the last one */
189 newConn->d_usable = true;
190
54a9a226 191 BOOST_CHECK_EQUAL(manager.count(), conns.size() + 1);
baa91396
RG
192 later.tv_sec += cleanupInterval + 1;
193 manager.cleanupClosedConnections(later);
194 BOOST_CHECK_EQUAL(manager.count(), 1U);
195 }
196
197 conns.clear();
198 auto cleared = manager.clear();
199 BOOST_CHECK_EQUAL(cleared, 1U);
54a9a226
RG
200
201 /* add 10 actives connections */
202 while (conns.size() < 10) {
203 newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
204 newConn->d_usable = false;
205 conns.push_back(newConn);
206 BOOST_CHECK_EQUAL(manager.count(), conns.size());
207 BOOST_CHECK_EQUAL(manager.getActiveCount(), conns.size());
208 }
209 /* now we mark them as idle */
210 for (auto& c : conns) {
211 /* use a different shared_ptr to make sure that the comparison is done on the actual raw pointer */
212 auto shared = c;
213 shared->d_idle = true;
214 BOOST_CHECK(manager.moveToIdle(shared));
215 }
216 BOOST_CHECK_EQUAL(manager.count(), maxIdleConnPerDownstream);
217 BOOST_CHECK_EQUAL(manager.getActiveCount(), 0U);
218 BOOST_CHECK_EQUAL(manager.getIdleCount(), maxIdleConnPerDownstream);
baa91396
RG
219}
220
221BOOST_AUTO_TEST_SUITE_END();