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