]>
Commit | Line | Data |
---|---|---|
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 | |
33 | class MockupConnection | |
34 | { | |
35 | public: | |
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 | ||
93 | BOOST_AUTO_TEST_SUITE(test_dnsdist_connections_cache) | |
94 | ||
95 | BOOST_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 | ||
242 | BOOST_AUTO_TEST_SUITE_END(); |