]>
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 | */ | |
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 | ||
29 | class MockupConnection | |
30 | { | |
31 | public: | |
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 | ||
84 | BOOST_AUTO_TEST_SUITE(test_dnsdist_connections_cache) | |
85 | ||
86 | BOOST_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 | ||
221 | BOOST_AUTO_TEST_SUITE_END(); |