]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdistdist/test-dnsdist-connections-cache.cc
Merge pull request #14041 from rgacogne/ddist-fix-crash-tcp-downstream
[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 */
1c2d079d 22#ifndef BOOST_TEST_DYN_LINK
baa91396 23#define BOOST_TEST_DYN_LINK
1c2d079d
FM
24#endif
25
baa91396
RG
26#define BOOST_TEST_NO_MAIN
27
28#include <boost/test/unit_test.hpp>
29
30#include "dnsdist-tcp-downstream.hh"
8a2dd7db 31#include "dnsdist-downstream-connection.hh"
baa91396
RG
32
33class MockupConnection
34{
35public:
7112953b
RG
36 MockupConnection(const std::shared_ptr<DownstreamState>& ds, std::unique_ptr<FDMultiplexer>&, const struct timeval&, std::string&&) :
37 d_ds(ds)
baa91396
RG
38 {
39 }
40
41 bool canBeReused() const
42 {
43 return d_reusable;
44 }
45
46 bool isUsable() const
47 {
48 return d_usable;
49 }
50
51 bool willBeReusable(bool) const
52 {
53 return d_reusable;
54 }
55
56 void setReused()
57 {
58 }
59
60 struct timeval getLastDataReceivedTime() const
61 {
62 return d_lastDataReceivedTime;
63 }
64
65 bool isIdle() const
66 {
67 return d_idle;
68 }
69
70 void stopIO()
71 {
72 }
73
3594ea13 74 void release(bool removeFomCache)
5c7fb70a
RG
75 {
76 }
77
54a9a226
RG
78 std::shared_ptr<DownstreamState> getDS() const
79 {
80 return d_ds;
81 }
82
83 std::shared_ptr<DownstreamState> d_ds;
b8352ee0
RG
84 struct timeval d_lastDataReceivedTime
85 {
86 0, 0
87 };
baa91396
RG
88 bool d_reusable{true};
89 bool d_usable{true};
90 bool d_idle{false};
91};
92
93BOOST_AUTO_TEST_SUITE(test_dnsdist_connections_cache)
94
95BOOST_AUTO_TEST_CASE(test_ConnectionsCache)
96{
97 DownstreamConnectionsManager<MockupConnection> manager;
54a9a226 98 const size_t maxIdleConnPerDownstream = 5;
baa91396
RG
99 const uint16_t cleanupInterval = 1;
100 const uint16_t maxIdleTime = 5;
54a9a226 101 manager.setMaxIdleConnectionsPerDownstream(maxIdleConnPerDownstream);
baa91396
RG
102 manager.setCleanupInterval(cleanupInterval);
103 manager.setMaxIdleTime(maxIdleTime);
104
105 auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent());
106 auto downstream1 = std::make_shared<DownstreamState>(ComboAddress("192.0.2.1"));
107 auto downstream2 = std::make_shared<DownstreamState>(ComboAddress("192.0.2.2"));
108 struct timeval now;
109 gettimeofday(&now, nullptr);
110
111 auto conn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
112 BOOST_REQUIRE(conn != nullptr);
113 BOOST_CHECK_EQUAL(manager.count(), 1U);
54a9a226
RG
114 BOOST_CHECK_EQUAL(manager.getActiveCount(), 1U);
115 BOOST_CHECK_EQUAL(manager.getIdleCount(), 0U);
baa91396
RG
116
117 /* since the connection can be reused, we should get the same one */
118 {
119 auto conn1 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
120 BOOST_CHECK(conn.get() == conn1.get());
121 BOOST_CHECK_EQUAL(manager.count(), 1U);
54a9a226 122 BOOST_CHECK_EQUAL(manager.getActiveCount(), 1U);
baa91396
RG
123 }
124
125 /* if we mark it non-usable, we should get a new one */
126 conn->d_usable = false;
127 auto conn2 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
128 BOOST_CHECK(conn.get() != conn2.get());
129 BOOST_CHECK_EQUAL(manager.count(), 2U);
54a9a226 130 BOOST_CHECK_EQUAL(manager.getActiveCount(), 2U);
baa91396
RG
131
132 /* since the second connection can be reused, we should get it */
133 {
134 auto conn3 = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
135 BOOST_CHECK(conn3.get() == conn2.get());
136 BOOST_CHECK_EQUAL(manager.count(), 2U);
54a9a226 137 BOOST_CHECK_EQUAL(manager.getActiveCount(), 2U);
baa91396
RG
138 }
139
140 /* different downstream so different connection */
141 auto differentConn = manager.getConnectionToDownstream(mplexer, downstream2, now, std::string());
142 BOOST_REQUIRE(differentConn != nullptr);
143 BOOST_CHECK(differentConn.get() != conn.get());
144 BOOST_CHECK(differentConn.get() != conn2.get());
145 BOOST_CHECK_EQUAL(manager.count(), 3U);
54a9a226 146 BOOST_CHECK_EQUAL(manager.getActiveCount(), 3U);
baa91396
RG
147 {
148 /* but we should be able to reuse it */
149 auto sameConn = manager.getConnectionToDownstream(mplexer, downstream2, now, std::string());
150 BOOST_CHECK(sameConn.get() == differentConn.get());
151 BOOST_CHECK_EQUAL(manager.count(), 3U);
54a9a226 152 BOOST_CHECK_EQUAL(manager.getActiveCount(), 3U);
baa91396
RG
153 }
154
155 struct timeval later = now;
156 later.tv_sec += cleanupInterval + 1;
157
158 /* mark the second connection as no longer usable */
159 conn2->d_usable = false;
160 /* first one as well but still fresh so it will not get checked */
161 conn->d_usable = true;
162 conn->d_lastDataReceivedTime = later;
163 /* third one is usable but idle for too long */
164 differentConn->d_idle = true;
165 differentConn->d_lastDataReceivedTime = later;
166 differentConn->d_lastDataReceivedTime.tv_sec -= (maxIdleTime + 1);
167
168 /* we should not do an actual cleanup attempt since the last cleanup was done recently */
169 manager.cleanupClosedConnections(now);
170 BOOST_CHECK_EQUAL(manager.count(), 3U);
171
172 manager.cleanupClosedConnections(later);
173 BOOST_CHECK_EQUAL(manager.count(), 1U);
174
175 /* mark the remaining conn as non-usable, to get new ones */
176 conn->d_usable = false;
177 conn->d_lastDataReceivedTime.tv_sec = 0;
178
b8352ee0 179 std::vector<std::shared_ptr<MockupConnection>> conns = {conn};
54a9a226 180 while (conns.size() < maxIdleConnPerDownstream) {
baa91396
RG
181 auto newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
182 newConn->d_usable = false;
183 conns.push_back(newConn);
184 BOOST_CHECK_EQUAL(manager.count(), conns.size());
185 }
186
54a9a226 187 /* if we add a new one, the oldest should NOT get expunged because they are all active ones! */
baa91396 188 auto newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
54a9a226 189 BOOST_CHECK_GT(manager.count(), maxIdleConnPerDownstream);
baa91396
RG
190
191 {
192 /* mark all connections as not usable anymore */
193 for (auto& c : conns) {
194 c->d_usable = false;
195 }
196
197 /* except the last one */
198 newConn->d_usable = true;
199
54a9a226 200 BOOST_CHECK_EQUAL(manager.count(), conns.size() + 1);
baa91396
RG
201 later.tv_sec += cleanupInterval + 1;
202 manager.cleanupClosedConnections(later);
203 BOOST_CHECK_EQUAL(manager.count(), 1U);
204 }
205
206 conns.clear();
207 auto cleared = manager.clear();
208 BOOST_CHECK_EQUAL(cleared, 1U);
54a9a226
RG
209
210 /* add 10 actives connections */
211 while (conns.size() < 10) {
212 newConn = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
213 newConn->d_usable = false;
214 conns.push_back(newConn);
215 BOOST_CHECK_EQUAL(manager.count(), conns.size());
216 BOOST_CHECK_EQUAL(manager.getActiveCount(), conns.size());
217 }
218 /* now we mark them as idle */
219 for (auto& c : conns) {
220 /* use a different shared_ptr to make sure that the comparison is done on the actual raw pointer */
221 auto shared = c;
222 shared->d_idle = true;
223 BOOST_CHECK(manager.moveToIdle(shared));
224 }
225 BOOST_CHECK_EQUAL(manager.count(), maxIdleConnPerDownstream);
226 BOOST_CHECK_EQUAL(manager.getActiveCount(), 0U);
227 BOOST_CHECK_EQUAL(manager.getIdleCount(), maxIdleConnPerDownstream);
7da062ca
RG
228
229 {
230 /* if we ask for a connection, one of these should become active and no longer idle */
231 /* but first we need to mark them as usable again */
232 for (const auto& c : conns) {
233 c->d_usable = true;
234 }
235 auto got = manager.getConnectionToDownstream(mplexer, downstream1, now, std::string());
236 BOOST_CHECK_EQUAL(manager.count(), maxIdleConnPerDownstream);
237 BOOST_CHECK_EQUAL(manager.getActiveCount(), 1U);
238 BOOST_CHECK_EQUAL(manager.getIdleCount(), maxIdleConnPerDownstream - 1U);
239 }
baa91396
RG
240}
241
242BOOST_AUTO_TEST_SUITE_END();