1 /*
2 * Copyright (C) 2018-2019 Codership Oy <info@codership.com>
3 *
4 * This file is part of wsrep-lib.
5 *
6 * Wsrep-lib is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Wsrep-lib is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with wsrep-lib. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "mock_server_state.hpp"
21
22 #include <boost/test/unit_test.hpp>
23
24 namespace
25 {
26 struct server_fixture_base
27 {
server_fixture_base__anon01800a9c0111::server_fixture_base28 server_fixture_base()
29 : server_service(ss)
30 , ss("s1",
31 wsrep::server_state::rm_sync, server_service)
32 , cc(ss,
33 wsrep::client_id(1),
34 wsrep::client_state::m_high_priority)
35 , hps(ss, &cc, false)
36 , ws_handle(wsrep::transaction_id(1), (void*)1)
37 , ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(1)),
38 wsrep::stid(wsrep::id("1"), wsrep::transaction_id(1),
39 wsrep::client_id(1)),
40 wsrep::seqno(0),
41 wsrep::provider::flag::start_transaction |
42 wsrep::provider::flag::commit)
43 , cluster_id("1")
44 , bootstrap_view()
45 , second_view()
46 , third_view()
47 {
48 wsrep::gtid state_id(cluster_id, wsrep::seqno(0));
49 std::vector<wsrep::view::member> members;
50 members.push_back(wsrep::view::member(
51 wsrep::id("s1"), "s1", ""));
52 bootstrap_view = wsrep::view(state_id,
53 wsrep::seqno(1),
54 wsrep::view::primary,
55 0, // capabilities
56 0, // own index
57 1, // protocol version
58 members);
59
60 members.push_back(wsrep::view::member(
61 wsrep::id("s2"), "s2", ""));
62 second_view = wsrep::view(wsrep::gtid(cluster_id, wsrep::seqno(1)),
63 wsrep::seqno(2),
64 wsrep::view::primary,
65 0, // capabilities
66 1, // own index
67 1, // protocol version
68 members);
69
70 members.push_back(wsrep::view::member(
71 wsrep::id("s3"), "s3", ""));
72
73 third_view = wsrep::view(wsrep::gtid(cluster_id, wsrep::seqno(2)),
74 wsrep::seqno(3),
75 wsrep::view::primary,
76 0, // capabilities
77 1, // own index
78 1, // protocol version
79 members);
80
81 cc.open(cc.id());
82 BOOST_REQUIRE(cc.before_command() == 0);
83 }
84 wsrep::mock_server_service server_service;
85 wsrep::mock_server_state ss;
86 wsrep::mock_client cc;
87 wsrep::mock_high_priority_service hps;
88 wsrep::ws_handle ws_handle;
89 wsrep::ws_meta ws_meta;
90 wsrep::id cluster_id;
91 wsrep::view bootstrap_view;
92 wsrep::view second_view;
93 wsrep::view third_view;
94
connect_in_view__anon01800a9c0111::server_fixture_base95 void connect_in_view(const wsrep::view& view)
96 {
97 BOOST_REQUIRE(ss.connect("cluster", "local", "0", false) == 0);
98 ss.on_connect(view);
99 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
100 }
101
prepare_for_sst__anon01800a9c0111::server_fixture_base102 void prepare_for_sst()
103 {
104 ss.prepare_for_sst();
105 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joiner);
106 }
107
final_view__anon01800a9c0111::server_fixture_base108 void final_view()
109 {
110 BOOST_REQUIRE(ss.state() != wsrep::server_state::s_disconnected);
111 wsrep::view view(wsrep::gtid(), // state_id
112 wsrep::seqno::undefined(), // view seqno
113 wsrep::view::disconnected, // status
114 0, // capabilities
115 -1, // own_index
116 0, // protocol ver
117 std::vector<wsrep::view::member>() // members
118 );
119 ss.on_view(view, &hps);
120 }
121
disconnect__anon01800a9c0111::server_fixture_base122 void disconnect()
123 {
124 BOOST_REQUIRE(ss.state() != wsrep::server_state::s_disconnecting);
125 ss.disconnect();
126 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
127 final_view();
128 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
129 }
130
131 };
132
133 struct applying_server_fixture : server_fixture_base
134 {
applying_server_fixture__anon01800a9c0111::applying_server_fixture135 applying_server_fixture()
136 : server_fixture_base()
137 {
138 ss.mock_connect();
139 }
140 };
141
142 struct sst_first_server_fixture : server_fixture_base
143 {
sst_first_server_fixture__anon01800a9c0111::sst_first_server_fixture144 sst_first_server_fixture()
145 : server_fixture_base()
146 {
147 server_service.sst_before_init_ = true;
148 }
149
sst_received_action__anon01800a9c0111::sst_first_server_fixture150 void sst_received_action()
151 {
152 server_service.sync_point_enabled_ = "on_view_wait_initialized";
153 server_service.sync_point_action_ = server_service.spa_initialize;
154 }
155
initialization_failure_action__anon01800a9c0111::sst_first_server_fixture156 void initialization_failure_action()
157 {
158 server_service.sync_point_enabled_ = "on_view_wait_initialized";
159 server_service.sync_point_action_ =
160 server_service.spa_initialize_error;
161 }
162
clear_sync_point_action__anon01800a9c0111::sst_first_server_fixture163 void clear_sync_point_action()
164 {
165 server_service.sync_point_enabled_ = "";
166 server_service.sync_point_action_ = server_service.spa_none;
167 }
168
169 // Helper method to bootstrap the server with bootstrap view
bootstrap__anon01800a9c0111::sst_first_server_fixture170 void bootstrap()
171 {
172 connect_in_view(bootstrap_view);
173
174 sst_received_action();
175 ss.on_view(bootstrap_view, &hps);
176 clear_sync_point_action();
177 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
178 ss.on_sync();
179 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
180 }
181
182 };
183
184 struct init_first_server_fixture : server_fixture_base
185 {
init_first_server_fixture__anon01800a9c0111::init_first_server_fixture186 init_first_server_fixture()
187 : server_fixture_base()
188 {
189 server_service.sst_before_init_ = false;
190 }
191
192 // Helper method to bootstrap the server with bootstrap view
bootstrap__anon01800a9c0111::init_first_server_fixture193 void bootstrap()
194 {
195 ss.initialized();
196 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_initialized);
197 BOOST_REQUIRE(ss.connect("cluster", "local", "0", false) == 0);
198 ss.on_connect(bootstrap_view);
199 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
200 ss.on_view(bootstrap_view, &hps);
201 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
202 ss.on_sync();
203 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
204 }
205 };
206
207 // Helper to pass to BOOST_REQUIRE_EXCEPTION. Always returns true.
exception_check(const wsrep::runtime_error &)208 bool exception_check(const wsrep::runtime_error&) { return true; }
209 }
210
211 // Test on_apply() method for 1pc
BOOST_FIXTURE_TEST_CASE(server_state_applying_1pc,applying_server_fixture)212 BOOST_FIXTURE_TEST_CASE(server_state_applying_1pc,
213 applying_server_fixture)
214 {
215 char buf[1] = { 1 };
216 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
217 wsrep::const_buffer(buf, 1)) == 0);
218 const wsrep::transaction& txc(cc.transaction());
219 // ::abort();
220 BOOST_REQUIRE_MESSAGE(
221 txc.state() == wsrep::transaction::s_committed,
222 "Transaction state " << txc.state() << " not committed");
223 }
224
225 // Test on_apply() method for 2pc
BOOST_FIXTURE_TEST_CASE(server_state_applying_2pc,applying_server_fixture)226 BOOST_FIXTURE_TEST_CASE(server_state_applying_2pc,
227 applying_server_fixture)
228 {
229 hps.do_2pc_ = true;
230 char buf[1] = { 1 };
231 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
232 wsrep::const_buffer(buf, 1)) == 0);
233 const wsrep::transaction& txc(cc.transaction());
234 BOOST_REQUIRE(txc.state() == wsrep::transaction::s_committed);
235 }
236
237 // Test on_apply() method for 1pc transaction which
238 // fails applying and rolls back
BOOST_FIXTURE_TEST_CASE(server_state_applying_1pc_rollback,applying_server_fixture)239 BOOST_FIXTURE_TEST_CASE(server_state_applying_1pc_rollback,
240 applying_server_fixture)
241 {
242 /* make sure default success result is flipped to error_fatal */
243 ss.provider().commit_order_leave_result_ = wsrep::provider::success;
244 hps.fail_next_applying_ = true;
245 char buf[1] = { 1 };
246 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
247 wsrep::const_buffer(buf, 1)) == 1);
248 const wsrep::transaction& txc(cc.transaction());
249 BOOST_REQUIRE(txc.state() == wsrep::transaction::s_aborted);
250 }
251
252 // Test on_apply() method for 2pc transaction which
253 // fails applying and rolls back
BOOST_FIXTURE_TEST_CASE(server_state_applying_2pc_rollback,applying_server_fixture)254 BOOST_FIXTURE_TEST_CASE(server_state_applying_2pc_rollback,
255 applying_server_fixture)
256 {
257 /* make sure default success result is flipped to error_fatal */
258 ss.provider().commit_order_leave_result_ = wsrep::provider::success;
259 hps.do_2pc_ = true;
260 hps.fail_next_applying_ = true;
261 char buf[1] = { 1 };
262 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
263 wsrep::const_buffer(buf, 1)) == 1);
264 const wsrep::transaction& txc(cc.transaction());
265 BOOST_REQUIRE(txc.state() == wsrep::transaction::s_aborted);
266 }
267
BOOST_FIXTURE_TEST_CASE(server_state_streaming,applying_server_fixture)268 BOOST_FIXTURE_TEST_CASE(server_state_streaming, applying_server_fixture)
269 {
270 ws_meta = wsrep::ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(1)),
271 wsrep::stid(wsrep::id("1"),
272 wsrep::transaction_id(1),
273 wsrep::client_id(1)),
274 wsrep::seqno(0),
275 wsrep::provider::flag::start_transaction);
276 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
277 wsrep::const_buffer("1", 1)) == 0);
278 BOOST_REQUIRE(ss.find_streaming_applier(
279 ws_meta.server_id(), ws_meta.transaction_id()));
280 ws_meta = wsrep::ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(2)),
281 wsrep::stid(wsrep::id("1"),
282 wsrep::transaction_id(1),
283 wsrep::client_id(1)),
284 wsrep::seqno(1),
285 0);
286 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
287 wsrep::const_buffer("1", 1)) == 0);
288 ws_meta = wsrep::ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(2)),
289 wsrep::stid(wsrep::id("1"),
290 wsrep::transaction_id(1),
291 wsrep::client_id(1)),
292 wsrep::seqno(1),
293 wsrep::provider::flag::commit);
294 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, ws_meta,
295 wsrep::const_buffer("1", 1)) == 0);
296 BOOST_REQUIRE(ss.find_streaming_applier(
297 ws_meta.server_id(), ws_meta.transaction_id()) == 0);
298 }
299
300
BOOST_AUTO_TEST_CASE(server_state_state_strings)301 BOOST_AUTO_TEST_CASE(server_state_state_strings)
302 {
303 BOOST_REQUIRE(wsrep::to_string(
304 wsrep::server_state::s_disconnected) == "disconnected");
305 BOOST_REQUIRE(wsrep::to_string(
306 wsrep::server_state::s_initializing) == "initializing");
307 BOOST_REQUIRE(wsrep::to_string(
308 wsrep::server_state::s_initialized) == "initialized");
309 BOOST_REQUIRE(wsrep::to_string(
310 wsrep::server_state::s_connected) == "connected");
311 BOOST_REQUIRE(wsrep::to_string(
312 wsrep::server_state::s_joiner) == "joiner");
313 BOOST_REQUIRE(wsrep::to_string(
314 wsrep::server_state::s_joined) == "joined");
315 BOOST_REQUIRE(wsrep::to_string(
316 wsrep::server_state::s_donor) == "donor");
317 BOOST_REQUIRE(wsrep::to_string(
318 wsrep::server_state::s_synced) == "synced");
319 BOOST_REQUIRE(wsrep::to_string(
320 wsrep::server_state::s_disconnecting) == "disconnecting");
321 }
322
323 ///////////////////////////////////////////////////////////////////////////////
324 // Test cases for SST first //
325 ///////////////////////////////////////////////////////////////////////////////
326
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_boostrap,sst_first_server_fixture)327 BOOST_FIXTURE_TEST_CASE(server_state_sst_first_boostrap,
328 sst_first_server_fixture)
329 {
330 bootstrap();
331 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
332 }
333
334
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_join_with_sst,sst_first_server_fixture)335 BOOST_FIXTURE_TEST_CASE(server_state_sst_first_join_with_sst,
336 sst_first_server_fixture)
337 {
338 connect_in_view(second_view);
339 prepare_for_sst();
340 sst_received_action();
341 // Mock server service get_view() gets view from logged_view_.
342 // Get_view() is called from sst_received(). This emulates the
343 // case where SST contains the view in which SST happens.
344 server_service.logged_view(second_view);
345 server_service.position(wsrep::gtid(cluster_id, wsrep::seqno(2)));
346 ss.sst_received(cc, 0);
347 clear_sync_point_action();
348 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
349 ss.on_sync();
350 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
351 }
352
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_join_with_ist,sst_first_server_fixture)353 BOOST_FIXTURE_TEST_CASE(server_state_sst_first_join_with_ist,
354 sst_first_server_fixture)
355 {
356 connect_in_view(second_view);
357 // Mock server service get_view() gets view from logged_view_.
358 // Get_view() is called from sst_received(). This emulates the
359 // case where the view is stored in stable storage.
360 server_service.logged_view(second_view);
361 sst_received_action();
362 ss.on_view(second_view, &hps);
363 clear_sync_point_action();
364 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
365 ss.on_view(third_view, &hps);
366 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
367 ss.on_sync();
368 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
369 }
370
371
372 // Cycle from synced state to disconnected and back to synced. Server
373 // storage engines remain initialized.
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_synced_disconnected_synced_no_sst,sst_first_server_fixture)374 BOOST_FIXTURE_TEST_CASE(
375 server_state_sst_first_synced_disconnected_synced_no_sst,
376 sst_first_server_fixture)
377 {
378 bootstrap();
379 ss.disconnect();
380 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
381 final_view();
382 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
383
384 // Connect back as a sole member in the cluster
385 BOOST_REQUIRE(ss.connect("cluster", "local", "0", false) == 0);
386 // @todo: s_connecting state would be good to have
387 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
388 // Server state must keep the initialized state
389 BOOST_REQUIRE(ss.is_initialized() == true);
390 std::vector<wsrep::view::member> members;
391 members.push_back(wsrep::view::member(wsrep::id("s1"), "name", ""));
392 wsrep::view view(wsrep::gtid(cluster_id, wsrep::seqno(1)),
393 wsrep::seqno(2),
394 wsrep::view::primary,
395 0, // capabilities
396 0, // own index
397 1, // protocol version
398 members);
399 ss.on_connect(view);
400 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
401 // As storage engines have been initialized, there should not be
402 // any reason to wait for initialization. State should jump directly
403 // to s_joined after handling the view.
404 ss.on_view(view, &hps);
405 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
406 ss.on_sync();
407 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
408 }
409
410 //
411 // Error after connecting to cluster. This scenario may happen if SST
412 // request preparation fails.
413 //
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_error_on_connect,sst_first_server_fixture)414 BOOST_FIXTURE_TEST_CASE(
415 server_state_sst_first_error_on_connect,
416 sst_first_server_fixture)
417 {
418 connect_in_view(second_view);
419 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
420 disconnect();
421 }
422
423 // Error during SST.
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_error_on_joiner,sst_first_server_fixture)424 BOOST_FIXTURE_TEST_CASE(
425 server_state_sst_first_error_on_joiner,
426 sst_first_server_fixture)
427 {
428 connect_in_view(second_view);
429 ss.prepare_for_sst();
430 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joiner);
431 server_service.position(wsrep::gtid::undefined());
432 ss.sst_received(cc, 1);
433 disconnect();
434 }
435
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_error_on_initializing,sst_first_server_fixture)436 BOOST_FIXTURE_TEST_CASE(
437 server_state_sst_first_error_on_initializing,
438 sst_first_server_fixture)
439 {
440 connect_in_view(second_view);
441 ss.prepare_for_sst();
442 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joiner);
443 initialization_failure_action();
444 server_service.position(wsrep::gtid(second_view.state_id()));
445 BOOST_REQUIRE_EXCEPTION(ss.sst_received(cc, 0),
446 wsrep::runtime_error, exception_check);
447 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_initializing);
448 BOOST_REQUIRE_EXCEPTION(ss.on_view(second_view, &hps),
449 wsrep::runtime_error, exception_check);
450 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_initializing);
451 disconnect();
452 }
453
454 // Error or shutdown happens during catchup phase after receiving
455 // SST successfully.
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_error_on_joined,sst_first_server_fixture)456 BOOST_FIXTURE_TEST_CASE(
457 server_state_sst_first_error_on_joined,
458 sst_first_server_fixture)
459 {
460 connect_in_view(second_view);
461 ss.prepare_for_sst();
462 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joiner);
463 sst_received_action();
464 // Mock server service get_view() gets view from logged_view_.
465 // Get_view() is called from sst_received(). This emulates the
466 // case where SST contains the view in which SST happens.
467 server_service.logged_view(second_view);
468 server_service.position(wsrep::gtid(cluster_id, wsrep::seqno(2)));
469 ss.sst_received(cc, 0);
470 clear_sync_point_action();
471 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
472 disconnect();
473 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
474 }
475
476 // Error or shutdown happens when donating a snapshot.
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_error_on_donor,sst_first_server_fixture)477 BOOST_FIXTURE_TEST_CASE(
478 server_state_sst_first_error_on_donor,
479 sst_first_server_fixture)
480 {
481 bootstrap();
482 ss.start_sst("", wsrep::gtid(cluster_id, wsrep::seqno(2)), false);
483 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_donor);
484 disconnect();
485 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
486 }
487
488 ///////////////////////////////////////////////////////////////////////////////
489 // Test cases for init first //
490 ///////////////////////////////////////////////////////////////////////////////
491
BOOST_FIXTURE_TEST_CASE(server_state_init_first_boostrap,init_first_server_fixture)492 BOOST_FIXTURE_TEST_CASE(server_state_init_first_boostrap,
493 init_first_server_fixture)
494 {
495 bootstrap();
496 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
497 }
498
BOOST_FIXTURE_TEST_CASE(server_state_init_first_join_with_sst,init_first_server_fixture)499 BOOST_FIXTURE_TEST_CASE(server_state_init_first_join_with_sst,
500 init_first_server_fixture)
501 {
502 ss.initialized();
503 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_initialized);
504 connect_in_view(second_view);
505 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
506 prepare_for_sst();
507 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joiner);
508 server_service.logged_view(second_view);
509 server_service.position(wsrep::gtid(cluster_id, wsrep::seqno(2)));
510 ss.sst_received(cc, 0);
511 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
512 ss.on_sync();
513 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
514 }
515
BOOST_FIXTURE_TEST_CASE(server_state_init_first_join_with_ist,init_first_server_fixture)516 BOOST_FIXTURE_TEST_CASE(server_state_init_first_join_with_ist,
517 init_first_server_fixture)
518 {
519 ss.initialized();
520 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_initialized);
521 connect_in_view(second_view);
522 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
523 server_service.logged_view(second_view);
524 ss.on_view(second_view, &hps);
525 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
526 ss.on_view(third_view, &hps);
527 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
528 ss.on_sync();
529 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
530 }
531
532
533 // Cycle from synced state to disconnected and back to synced. Server
534 // storage engines remain initialized.
BOOST_FIXTURE_TEST_CASE(server_state_init_first_synced_disconnected_synced_no_sst,init_first_server_fixture)535 BOOST_FIXTURE_TEST_CASE(
536 server_state_init_first_synced_disconnected_synced_no_sst,
537 init_first_server_fixture)
538 {
539 bootstrap();
540 ss.disconnect();
541 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
542 final_view();
543 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
544
545 // Connect back as a sole member in the cluster
546 BOOST_REQUIRE(ss.connect("cluster", "local", "0", false) == 0);
547 // @todo: s_connecting state would be good to have
548 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
549 // Server state must keep the initialized state
550 BOOST_REQUIRE(ss.is_initialized() == true);
551 std::vector<wsrep::view::member> members;
552 members.push_back(wsrep::view::member(wsrep::id("s1"), "name", ""));
553 wsrep::view view(wsrep::gtid(cluster_id, wsrep::seqno(1)),
554 wsrep::seqno(2),
555 wsrep::view::primary,
556 0, // capabilities
557 0, // own index
558 1, // protocol version
559 members);
560 ss.on_connect(view);
561 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_connected);
562 // As storage engines have been initialized, there should not be
563 // any reason to wait for initialization. State should jump directly
564 // to s_joined after handling the view.
565 ss.on_view(view, &hps);
566 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
567 ss.on_sync();
568 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
569 }
570
571 /////////////////////////////////////////////////////////////////////////////
572 // Donor state transitions //
573 /////////////////////////////////////////////////////////////////////////////
574
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_donate_success,sst_first_server_fixture)575 BOOST_FIXTURE_TEST_CASE(
576 server_state_sst_first_donate_success,
577 sst_first_server_fixture)
578 {
579 bootstrap();
580 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
581 ss.start_sst("", wsrep::gtid(cluster_id, wsrep::seqno(2)), false);
582 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_donor);
583 ss.sst_sent(wsrep::gtid(cluster_id, wsrep::seqno(2)), 0);
584 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
585 ss.on_sync();
586 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
587 }
588
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_donate_error,sst_first_server_fixture)589 BOOST_FIXTURE_TEST_CASE(
590 server_state_sst_first_donate_error,
591 sst_first_server_fixture)
592 {
593 bootstrap();
594 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
595 ss.start_sst("", wsrep::gtid(cluster_id, wsrep::seqno(2)), false);
596 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_donor);
597 ss.sst_sent(wsrep::gtid(cluster_id, wsrep::seqno(2)), 1);
598 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_joined);
599 ss.on_sync();
600 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
601 }
602
603 /////////////////////////////////////////////////////////////////////////////
604 // Pause/Resume and Desync/Resync //
605 /////////////////////////////////////////////////////////////////////////////
606
BOOST_FIXTURE_TEST_CASE(server_state_sst_first_desync_and_pause_resync_and_resume,sst_first_server_fixture)607 BOOST_FIXTURE_TEST_CASE(
608 server_state_sst_first_desync_and_pause_resync_and_resume,
609 sst_first_server_fixture)
610 {
611 bootstrap();
612 ss.desync_and_pause();
613 // @todo: Should we have here different state than synced
614 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
615 ss.resume_and_resync();
616 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_synced);
617 }
618
619 /////////////////////////////////////////////////////////////////////////////
620 // Disconnect //
621 /////////////////////////////////////////////////////////////////////////////
622
BOOST_FIXTURE_TEST_CASE(server_state_disconnect,sst_first_server_fixture)623 BOOST_FIXTURE_TEST_CASE(
624 server_state_disconnect,
625 sst_first_server_fixture)
626 {
627 bootstrap();
628 ss.disconnect();
629 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
630 final_view();
631 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
632 }
633
634 // This test case verifies that the disconnect can be initiated
635 // concurrently by several callers. This may happen in failure situations
636 // where provider shutdown causes cascading failures and the failing operations
637 // try to disconnect the provider.
BOOST_FIXTURE_TEST_CASE(server_state_disconnect_twice,sst_first_server_fixture)638 BOOST_FIXTURE_TEST_CASE(
639 server_state_disconnect_twice,
640 sst_first_server_fixture)
641 {
642 bootstrap();
643 ss.disconnect();
644 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
645 ss.disconnect();
646 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnecting);
647 final_view();
648 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
649 ss.disconnect();
650 BOOST_REQUIRE(ss.state() == wsrep::server_state::s_disconnected);
651 }
652
653 /////////////////////////////////////////////////////////////////////////////
654 // Orphaned SR //
655 /////////////////////////////////////////////////////////////////////////////
656
657 // Test the behavior of server_state::close_orphaned_sr_transactions().
658 // In this test we check the scenario where we initially have 3 nodes in
659 // the cluster (s1, s2, s3), and this server_state delivers one streaming
660 // fragment from s2 and s3 each, followed by view changes:
661 //
662 // view 1: primary (s1, s2, s3)
663 // view 2: primary (s1, s2)
664 // view 3: non-primary (s1)
665 // view 4: non-primary (s1, s3)
666 // view 5: primary (s1, s2, s3)
667 //
668 // We expect that on view 2, transaction originated from s3 is considered
669 // orphaned, so it should be rolled back.
670 // Transaction from s2 should never be considered orphaned in this scenario,
671 // we expect it to survive until the end of the test. That's because
672 // transactions are rolled back in primary views only, and because s2
673 // is member of all primary views in this scenario.
BOOST_FIXTURE_TEST_CASE(server_state_close_orphaned_transactions,sst_first_server_fixture)674 BOOST_FIXTURE_TEST_CASE(server_state_close_orphaned_transactions,
675 sst_first_server_fixture)
676 {
677 connect_in_view(third_view);
678 server_service.logged_view(third_view);
679 sst_received_action();
680 ss.on_view(third_view, &hps);
681
682 // initially we have members (s1, s2, s3)
683 std::vector<wsrep::view::member> members(ss.current_view().members());
684
685 // apply a fragment coming from s2
686 wsrep::ws_meta meta_s2(wsrep::gtid(wsrep::id("s2"), wsrep::seqno(1)),
687 wsrep::stid(wsrep::id("s2"),
688 wsrep::transaction_id(1),
689 wsrep::client_id(1)),
690 wsrep::seqno(1),
691 wsrep::provider::flag::start_transaction);
692
693 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_s2,
694 wsrep::const_buffer("1", 1)) == 0);
695 BOOST_REQUIRE(ss.find_streaming_applier(
696 meta_s2.server_id(), meta_s2.transaction_id()));
697
698 // apply a fragment coming from s3
699 wsrep::ws_meta meta_s3(wsrep::gtid(wsrep::id("s3"), wsrep::seqno(2)),
700 wsrep::stid(wsrep::id("s3"),
701 wsrep::transaction_id(1),
702 wsrep::client_id(1)),
703 wsrep::seqno(2),
704 wsrep::provider::flag::start_transaction);
705
706 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_s3,
707 wsrep::const_buffer("1", 1)) == 0);
708 BOOST_REQUIRE(ss.find_streaming_applier(
709 meta_s3.server_id(), meta_s3.transaction_id()));
710
711 // s3 drops out of the cluster, deliver primary view (s1, s2)
712 wsrep::view::member s3(members.back());
713 members.pop_back();
714 ss.on_view(wsrep::view(ss.current_view().state_id(),
715 ss.current_view().view_seqno() + 1,
716 wsrep::view::primary,
717 0, // capabilities
718 0, // own index
719 1, // protocol version
720 members), &hps);
721
722 // transaction from s2 is still present
723 BOOST_REQUIRE(ss.find_streaming_applier(
724 meta_s2.server_id(), meta_s2.transaction_id()));
725 // transaction from s3 is gone
726 BOOST_REQUIRE(not ss.find_streaming_applier(
727 meta_s3.server_id(), meta_s3.transaction_id()));
728
729 // s2 drops out of the cluster, deliver non-primary view (s1)
730 wsrep::view::member s2(members.back());
731 members.pop_back();
732 ss.on_view(wsrep::view(ss.current_view().state_id(),
733 ss.current_view().view_seqno(),
734 wsrep::view::non_primary,
735 0, // capabilities
736 0, // own index
737 1, // protocol version
738 members), &hps);
739
740 // no streaming appliers are closed on non-primary view,
741 // so transaction from s2 is still present
742 BOOST_REQUIRE(ss.find_streaming_applier(
743 meta_s2.server_id(), meta_s2.transaction_id()));
744
745 // s3 comes back, deliver non-primary view (s1, s3)
746 members.push_back(s3);
747 ss.on_view(wsrep::view(ss.current_view().state_id(),
748 ss.current_view().view_seqno() + 1,
749 wsrep::view::non_primary,
750 0, // capabilities
751 0, // own index
752 1, // protocol version
753 members), &hps);
754
755 // transaction s2 is still present after non-primary view
756 BOOST_REQUIRE(ss.find_streaming_applier(
757 meta_s2.server_id(), meta_s2.transaction_id()));
758
759 // s2 comes back, deliver primary-view (s1, s2, s3)
760 members.push_back(s2);
761 ss.on_view(wsrep::view(ss.current_view().state_id(),
762 ss.current_view().view_seqno() + 1,
763 wsrep::view::primary,
764 0, // capabilities
765 0, // own index
766 1, // protocol version
767 members), &hps);
768
769 // finally, transaction from s2 is still present (part of primary view)
770 // and transaction from s3 is gone
771 BOOST_REQUIRE(ss.find_streaming_applier(
772 meta_s2.server_id(), meta_s2.transaction_id()));
773 BOOST_REQUIRE(not ss.find_streaming_applier(
774 meta_s3.server_id(), meta_s3.transaction_id()));
775
776 // cleanup
777 wsrep::ws_meta meta_commit_s2(wsrep::gtid(wsrep::id("s2"), wsrep::seqno(3)),
778 wsrep::stid(wsrep::id("s2"),
779 wsrep::transaction_id(1),
780 wsrep::client_id(1)),
781 wsrep::seqno(3),
782 wsrep::provider::flag::commit);
783
784 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_commit_s2,
785 wsrep::const_buffer("1", 1)) == 0);
786
787 BOOST_REQUIRE(not ss.find_streaming_applier(
788 meta_commit_s2.server_id(), meta_commit_s2.transaction_id()));
789 }
790
791
792 // Test the case where two consecutive primary views with the
793 // same members are delivered (provider may do so).
794 // Expect SR transactions to be rolled back on equal consecutive views
BOOST_FIXTURE_TEST_CASE(server_state_equal_consecutive_views,sst_first_server_fixture)795 BOOST_FIXTURE_TEST_CASE(server_state_equal_consecutive_views,
796 sst_first_server_fixture)
797 {
798 connect_in_view(third_view);
799 server_service.logged_view(third_view);
800 sst_received_action();
801 ss.on_view(third_view, &hps);
802
803 // apply a fragment coming from s2
804 wsrep::ws_meta meta_s2(wsrep::gtid(wsrep::id("s2"), wsrep::seqno(1)),
805 wsrep::stid(wsrep::id("s2"),
806 wsrep::transaction_id(1),
807 wsrep::client_id(1)),
808 wsrep::seqno(1),
809 wsrep::provider::flag::start_transaction);
810
811 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_s2,
812 wsrep::const_buffer("1", 1)) == 0);
813 BOOST_REQUIRE(ss.find_streaming_applier(
814 meta_s2.server_id(), meta_s2.transaction_id()));
815
816 // apply a fragment coming from s3
817 wsrep::ws_meta meta_s3(wsrep::gtid(wsrep::id("s3"), wsrep::seqno(2)),
818 wsrep::stid(wsrep::id("s3"),
819 wsrep::transaction_id(1),
820 wsrep::client_id(1)),
821 wsrep::seqno(2),
822 wsrep::provider::flag::start_transaction);
823
824 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_s3,
825 wsrep::const_buffer("1", 1)) == 0);
826 BOOST_REQUIRE(ss.find_streaming_applier(
827 meta_s3.server_id(), meta_s3.transaction_id()));
828
829 // deliver primary view with the same members (s1, s2, s3)
830 ss.on_view(wsrep::view(ss.current_view().state_id(),
831 ss.current_view().view_seqno() + 1,
832 wsrep::view::primary,
833 0, // capabilities
834 0, // own index
835 1, // protocol version
836 ss.current_view().members()), &hps);
837
838 // transaction from s2 and s3 are gone
839 BOOST_REQUIRE(not ss.find_streaming_applier(
840 meta_s2.server_id(), meta_s2.transaction_id()));
841 BOOST_REQUIRE(not ss.find_streaming_applier(
842 meta_s3.server_id(), meta_s3.transaction_id()));
843 }
844
845 // Verify that prepared XA transactions are not rolled back
846 // by close_orphaned_transactions()
BOOST_FIXTURE_TEST_CASE(server_state_xa_not_orphaned,sst_first_server_fixture)847 BOOST_FIXTURE_TEST_CASE(server_state_xa_not_orphaned,
848 sst_first_server_fixture)
849 {
850 connect_in_view(third_view);
851 server_service.logged_view(third_view);
852 sst_received_action();
853 ss.on_view(third_view, &hps);
854
855 // initially we have members (s1, s2, s3)
856 std::vector<wsrep::view::member> members(ss.current_view().members());
857
858
859 wsrep::ws_meta meta_s3(wsrep::gtid(wsrep::id("s3"), wsrep::seqno(1)),
860 wsrep::stid(wsrep::id("s3"),
861 wsrep::transaction_id(1),
862 wsrep::client_id(1)),
863 wsrep::seqno(1),
864 wsrep::provider::flag::start_transaction |
865 wsrep::provider::flag::prepare);
866
867 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_s3,
868 wsrep::const_buffer("1", 1)) == 0);
869 BOOST_REQUIRE(ss.find_streaming_applier(
870 meta_s3.server_id(), meta_s3.transaction_id()));
871
872
873 // s3 drops out of the cluster, deliver primary view (s1, s2)
874 wsrep::view::member s3(members.back());
875 members.pop_back();
876 ss.on_view(wsrep::view(ss.current_view().state_id(),
877 ss.current_view().view_seqno() + 1,
878 wsrep::view::primary,
879 0, // capabilities
880 0, // own index
881 1, // protocol version
882 members), &hps);
883
884 // transaction from s3 is still present
885 BOOST_REQUIRE(ss.find_streaming_applier(
886 meta_s3.server_id(), meta_s3.transaction_id()));
887
888 // s3 comes back, deliver primary view (s1, s2, s3)
889 members.push_back(s3);
890 ss.on_view(wsrep::view(ss.current_view().state_id(),
891 ss.current_view().view_seqno() + 1,
892 wsrep::view::primary,
893 0, // capabilities
894 0, // own index
895 1, // protocol version
896 members), &hps);
897
898 // transaction from s3 is still present
899 BOOST_REQUIRE(ss.find_streaming_applier(
900 meta_s3.server_id(), meta_s3.transaction_id()));
901
902 // cleanup
903 wsrep::ws_meta meta_commit_s3(wsrep::gtid(wsrep::id("s3"), wsrep::seqno(3)),
904 wsrep::stid(wsrep::id("s3"),
905 wsrep::transaction_id(1),
906 wsrep::client_id(1)),
907 wsrep::seqno(3),
908 wsrep::provider::flag::commit);
909
910 BOOST_REQUIRE(ss.on_apply(hps, ws_handle, meta_commit_s3,
911 wsrep::const_buffer("1", 1)) == 0);
912 BOOST_REQUIRE(not ss.find_streaming_applier(
913 meta_commit_s3.server_id(), meta_commit_s3.transaction_id()));
914 }
915