1 /* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include <gtest/gtest.h>
24 #include <string.h>
25
26 #define FRIEND_OF_GTID_SET class GroupTest_Group_containers_Test
27 #define FRIEND_OF_GROUP_CACHE class GroupTest_Group_containers_Test
28 #define FRIEND_OF_GROUP_LOG_STATE class GroupTest_Group_containers_Test
29 #define NON_DISABLED_UNITTEST_GTID
30
31 #include "binlog.h"
32 #include "my_thread.h"
33 #include "rpl_gtid.h"
34 #include "sql_class.h"
35
36 #define N_SIDS 16
37
38 #define ASSERT_OK(X) ASSERT_EQ(RETURN_STATUS_OK, X)
39 #define EXPECT_OK(X) EXPECT_EQ(RETURN_STATUS_OK, X)
40 #define EXPECT_NOK(X) EXPECT_NE(RETURN_STATUS_OK, X)
41
42 class GroupTest : public ::testing::Test {
43 public:
44 static const char *uuids[16];
45 rpl_sid sids[16];
46 unsigned int seed;
47
SetUp()48 void SetUp() {
49 seed = (unsigned int)time(nullptr);
50 printf("# seed = %u\n", seed);
51 srand(seed);
52 for (int i = 0; i < 16; i++) sids[i].parse(uuids[i]);
53
54 verbose = false;
55 errtext_stack_pos = 0;
56 errtext_stack[0] = 0;
57 append_errtext(__LINE__, "seed=%d", seed);
58 my_delete("sid-map-0", MYF(0));
59 my_delete("sid-map-1", MYF(0));
60 my_delete("sid-map-2", MYF(0));
61 }
62
TearDown()63 void TearDown() {
64 my_delete("sid-map-0", MYF(0));
65 my_delete("sid-map-1", MYF(0));
66 my_delete("sid-map-2", MYF(0));
67 }
68
69 /*
70 Test that different, equivalent ways to construct a Gtid_set give
71 the same resulting Gtid_set. This is used to test Gtid_set,
72 Sid_map, Group_cache, Group_log_state, and Owned_groups.
73
74 We will generate sets of groups in *stages*. Each stage is
75 divided into a number of *sub-stages* (the number of substages is
76 taken uniformly at random from the set 1, 2, ..., 200). In each
77 sub-stage, we randomly sample one sub-group from a fixed set of
78 groups. The fixed set of groups consists of groups from 16
79 different SIDs. For the Nth SID (1 <= N <= 16), the fixed set of
80 groups contains all GNOS from the closed interval [N, N - 1 + N *
81 N]. The stage consists of the set of groups from all the
82 sub-stages.
83 */
84
85 #define BEGIN_SUBSTAGE_LOOP(group_test, stage, do_errtext) \
86 (group_test)->push_errtext(); \
87 for (int substage_i = 0; substage_i < (stage)->n_substages; substage_i++) { \
88 Substage &substage = (stage)->substages[substage_i]; \
89 if (do_errtext) \
90 (group_test) \
91 ->append_errtext(__LINE__, "sidno=%d group=%s substage_i=%d", \
92 substage.sidno, substage.gtid_str, substage_i);
93 #define END_SUBSTAGE_LOOP(group_test) \
94 } \
95 group_test->pop_errtext()
96
97 /**
98 A substage, i.e., one of the randomly generated groups.
99 */
100 struct Substage {
101 rpl_sidno sidno;
102 rpl_gno gno;
103 const rpl_sid *sid;
104 char sid_str[binary_log::Uuid::TEXT_LENGTH + 1];
105 char gtid_str[binary_log::Uuid::TEXT_LENGTH + 1 + MAX_GNO_TEXT_LENGTH + 1];
106 bool is_first, is_last, is_auto;
107 #ifndef NO_DBUG
printGroupTest::Substage108 void print() const {
109 printf("%d/%s [first=%d last=%d auto=%d]", sidno, gtid_str, is_first,
110 is_last, is_auto);
111 }
112 #endif
113 };
114
115 /**
116 A stage, i.e., the sequence of randomly generated groups.
117 */
118 struct Stage {
119 class GroupTest *group_test;
120 Sid_map *sid_map;
121
122 // List of groups added in the present stage.
123 static const int MAX_SUBSTAGES = 200;
124 Substage substages[MAX_SUBSTAGES];
125 int n_substages;
126
127 // Set of groups added in the present stage.
128 Gtid_set set;
129 int str_len;
130 char *str;
131
132 // The subset of groups that can be added as automatic groups.
133 Gtid_set automatic_groups;
134 // The subset of groups that cannot be added as automatic groups.
135 Gtid_set non_automatic_groups;
136
StageGroupTest::Stage137 Stage(class GroupTest *gt, Sid_map *sm)
138 : group_test(gt),
139 sid_map(sm),
140 set(sm),
141 str_len(0),
142 str(nullptr),
143 automatic_groups(sm),
144 non_automatic_groups(sm) {
145 init(sm);
146 }
147
initGroupTest::Stage148 void init(Sid_map *sm) {
149 rpl_sidno max_sidno = sm->get_max_sidno();
150 ASSERT_OK(set.ensure_sidno(max_sidno));
151 ASSERT_OK(automatic_groups.ensure_sidno(max_sidno));
152 ASSERT_OK(non_automatic_groups.ensure_sidno(max_sidno));
153 }
154
~StageGroupTest::Stage155 ~Stage() { free(str); }
156
printGroupTest::Stage157 void print() const {
158 printf("%d substages = {\n", n_substages);
159 for (int i = 0; i < n_substages; i++) {
160 printf(" substage[%d]: ", i);
161 substages[i].print();
162 printf("\n");
163 }
164 printf("\n");
165 }
166
167 /**
168 Generate the the random groups that constitute a stage.
169
170 @param done_groups The set of all groups added in previous
171 stages.
172 @param other_sm Sid_map to which groups should be added.
173 */
new_stageGroupTest::Stage174 void new_stage(const Gtid_set *done_groups, Sid_map *other_sm) {
175 set.clear();
176 automatic_groups.clear();
177 non_automatic_groups.clear();
178
179 n_substages = 1 + (rand() % MAX_SUBSTAGES);
180 BEGIN_SUBSTAGE_LOOP(group_test, this, false) {
181 // generate random GTID
182 substage.sidno = 1 + (rand() % N_SIDS);
183 substage.gno = 1 + (rand() % (substage.sidno * substage.sidno));
184 // compute alternative forms
185 substage.sid = sid_map->sidno_to_sid(substage.sidno);
186 ASSERT_NE((rpl_sid *)nullptr, substage.sid) << group_test->errtext;
187 substage.sid->to_string(substage.sid_str);
188 substage.sid->to_string(substage.gtid_str);
189 substage.gtid_str[rpl_sid.TEXT_LENGTH] = ':';
190 format_gno(substage.gtid_str + rpl_sid.TEXT_LENGTH + 1, substage.gno);
191
192 ASSERT_LE(1, other_sm->add_permanent(substage.sid))
193 << group_test->errtext;
194
195 // check if this group could be added as an 'automatic' group
196 Gtid_set::Const_interval_iterator ivit(done_groups, substage.sidno);
197 const Gtid_set::Interval *iv = ivit.get();
198 substage.is_auto =
199 !set.contains_group(substage.sidno, substage.gno) &&
200 ((iv == nullptr || iv->start > 1) ? substage.gno == 1
201 : substage.gno == iv->end);
202
203 // check if this sub-group is the first in its group in this
204 // stage, and add it to the set
205 substage.is_first = !set.contains_group(substage.sidno, substage.gno);
206 if (substage.is_first) ASSERT_OK(set.add(substage.gtid_str));
207 }
208 END_SUBSTAGE_LOOP(group_test);
209
210 // Iterate backwards so that we can detect when a subgroup is
211 // the last subgroup of its group.
212 set.clear();
213 for (int substage_i = n_substages - 1; substage_i >= 0; substage_i--) {
214 Substage &substage = substages[substage_i];
215 substage.is_last = !set.contains_group(substage.sidno, substage.gno);
216 if (substage.is_last) ASSERT_OK(set.add(substage.gtid_str));
217 }
218
219 str_len = set.get_string_length();
220 str = (char *)realloc(str, str_len + 1);
221 set.to_string(str);
222 }
223 };
224
225 /*
226 We maintain a text that contains the state of the test. We print
227 this text when a test assertion fails. The text is updated each
228 iteration of each loop, so that we can easier track the exact
229 point in time when an error occurs. Since loops may be nested, we
230 maintain a stack of offsets in the error text: before a new loop
231 is entered, the position of the end of the string is pushed to the
232 stack and the text appended in each iteration is added to that
233 position.
234 */
235 char errtext[1000];
236 int errtext_stack[100];
237 int errtext_stack_pos;
238 bool verbose;
239
append_errtext(int line,const char * fmt,...)240 void append_errtext(int line, const char *fmt, ...)
241 MY_ATTRIBUTE((format(printf, 3, 4))) {
242 va_list argp;
243 va_start(argp, fmt);
244 vsprintf(errtext + errtext_stack[errtext_stack_pos], fmt, argp);
245 if (verbose) printf("@line %d: %s\n", line, errtext);
246 va_end(argp);
247 }
248
push_errtext()249 void push_errtext() {
250 int old_len = errtext_stack[errtext_stack_pos];
251 int len = old_len + strlen(errtext + old_len);
252 strcpy(errtext + len, " | ");
253 errtext_stack[++errtext_stack_pos] = len + 3;
254 }
255
pop_errtext()256 void pop_errtext() { errtext[errtext_stack[errtext_stack_pos--] - 3] = 0; }
257
group_subset(Gtid_set * sub,Gtid_set * super,bool outcome,int line,const char * desc)258 void group_subset(Gtid_set *sub, Gtid_set *super, bool outcome, int line,
259 const char *desc) {
260 append_errtext(line, "%s", desc);
261 // check using is_subset
262 EXPECT_EQ(outcome, sub->is_subset(super)) << errtext;
263 // check using set subtraction
264 enum_return_status status;
265 Gtid_set sub_minus_super(sub, &status);
266 ASSERT_OK(status) << errtext;
267 ASSERT_OK(sub_minus_super.remove(super)) << errtext;
268 ASSERT_EQ(outcome, sub_minus_super.is_empty()) << errtext;
269 }
270 };
271
272 const char *GroupTest::uuids[16] = {
273 "00000000-0000-0000-0000-000000000000",
274 "11111111-1111-1111-1111-111111111111",
275 "22222222-2222-2222-2222-222222222222",
276 "33333333-3333-3333-3333-333333333333",
277 "44444444-4444-4444-4444-444444444444",
278 "55555555-5555-5555-5555-555555555555",
279 "66666666-6666-6666-6666-666666666666",
280 "77777777-7777-7777-7777-777777777777",
281 "88888888-8888-8888-8888-888888888888",
282 "99999999-9999-9999-9999-999999999999",
283 "aaaaAAAA-aaaa-AAAA-aaaa-aAaAaAaAaaaa",
284 "bbbbBBBB-bbbb-BBBB-bbbb-bBbBbBbBbbbb",
285 "ccccCCCC-cccc-CCCC-cccc-cCcCcCcCcccc",
286 "ddddDDDD-dddd-DDDD-dddd-dDdDdDdDdddd",
287 "eeeeEEEE-eeee-EEEE-eeee-eEeEeEeEeeee",
288 "ffffFFFF-ffff-FFFF-ffff-fFfFfFfFffff",
289 };
290
TEST_F(GroupTest,Uuid)291 TEST_F(GroupTest, Uuid) {
292 Uuid u;
293 char buf[100];
294
295 // check that we get back the same UUID after parse + print
296 for (int i = 0; i < N_SIDS; i++) {
297 EXPECT_OK(u.parse(uuids[i])) << "i=" << i;
298 u.to_string(buf);
299 EXPECT_STRCASEEQ(uuids[i], buf) << "i=" << i;
300 }
301 // check error cases
302 EXPECT_OK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFFFf"));
303 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFFg"));
304 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFF"));
305 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-fff-fffffffffFFFF"));
306 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFF-"));
307 EXPECT_NOK(u.parse(" ffffFFFF-ffff-FFFF-ffff-ffffffffFFFF"));
308 EXPECT_NOK(u.parse("ffffFFFFfffff-FFFF-ffff-ffffffffFFFF"));
309 }
310
TEST_F(GroupTest,Sid_map)311 TEST_F(GroupTest, Sid_map) {
312 Checkable_rwlock lock;
313 Sid_map sm(&lock);
314
315 lock.rdlock();
316 ASSERT_OK(sm.open("sid-map-0"));
317
318 // Add a random SID until we have N_SID SIDs in the map.
319 while (sm.get_max_sidno() < N_SIDS)
320 ASSERT_LE(1, sm.add_permanent(&sids[rand() % N_SIDS])) << errtext;
321
322 // Check that all N_SID SIDs are in the map, and that
323 // get_sorted_sidno() has the correct order. This implies that no
324 // SID was added twice.
325 for (int i = 0; i < N_SIDS; i++) {
326 rpl_sidno sidno = sm.get_sorted_sidno(i);
327 const rpl_sid *sid;
328 char buf[100];
329 EXPECT_NE((rpl_sid *)nullptr, sid = sm.sidno_to_sid(sidno)) << errtext;
330 const int max_len = binary_log::Uuid::TEXT_LENGTH;
331 EXPECT_EQ(max_len, sid->to_string(buf)) << errtext;
332 EXPECT_STRCASEEQ(uuids[i], buf) << errtext;
333 EXPECT_EQ(sidno, sm.sid_to_sidno(sid)) << errtext;
334 }
335 lock.unlock();
336 lock.assert_no_lock();
337 }
338
TEST_F(GroupTest,Group_containers)339 TEST_F(GroupTest, Group_containers) {
340 /*
341 In this test, we maintain 298 Gtid_sets. We add groups to these
342 Gtid_sets in stages, as described above. We add the groups to
343 each of the 298 Gtid_sets in different ways, as described below.
344 At the end of each stage, we check that all the 298 resulting
345 Gtid_sets are mutually equal.
346
347 We add groups in the two ways:
348
349 A. Test Gtid_sets and Sid_maps. We vary two parameters:
350
351 Parameter 1: vary the way that groups are added:
352 0. Add one group at a time, using add(sidno, gno).
353 1. Add one group at a time, using add(text).
354 2. Add all new groups at once, using add(gs_new).
355 3. add all new groups at once, using add(gs_new.to_string()).
356 4. Maintain a string that contains the concatenation of all
357 gs_new.to_string(). in each stage, we set gs[4] to a new
358 Gtid_set created from this string.
359
360 Parameter 2: vary the Sid_map object:
361 0. Use a Sid_map that has all the SIDs in order.
362 1. Use a Sid_map where SIDs are added in the order they appear.
363
364 We vary these parameters in all combinations; thus we construct
365 10 Gtid_sets.
366 */
367 enum enum_sets_method {
368 METHOD_SIDNO_GNO = 0,
369 METHOD_GROUP_TEXT,
370 METHOD_GTID_SET,
371 METHOD_GTID_SET_TEXT,
372 METHOD_ALL_TEXTS_CONCATENATED,
373 MAX_METHOD
374 };
375 enum enum_sets_sid_map { SID_MAP_0 = 0, SID_MAP_1, MAX_SID_MAP };
376 const int N_COMBINATIONS_SETS = MAX_METHOD * MAX_SID_MAP;
377 /*
378 B. Test Group_cache, Group_log_state, and Owned_groups. All
379 sub-groups for the stage are added to the Group_cache, the
380 Group_cache is flushed to the Group_log_state, and the
381 Gtid_set is extracted from the Group_log_state. We vary the
382 following parameters.
383
384 Parameter 1: type of statement:
385 0. Transactional replayed statement: add all groups to the
386 transaction group cache (which is flushed to a
387 Group_log_state at the end of the stage). Set
388 GTID_NEXT_LIST to the list of all groups in the stage.
389 1. Non-transactional replayed statement: add all groups to the
390 stmt group cache (which is flushed to the Group_log_state
391 at the end of each sub-stage). Set GTID_NEXT_LIST = NULL.
392 2. Randomize: for each sub-stage, choose 0 or 1 with 50%
393 chance. Set GTID_NEXT_LIST to the list of all groups in
394 the stage.
395 3. Automatic groups: add all groups to the stmt group cache,
396 but make the group automatic if possible, i.e., if the SID
397 and GNO are unlogged and there is no smaller unlogged GNO
398 for this SID. Set GTID_NEXT_LIST = NULL.
399
400 Parameter 2: ended or non-ended sub-groups:
401 0. All sub-groups are unended (except automatic sub-groups).
402 1. For each group, the last sub-group of the group in the
403 stage is ended. Don't add groups that are already ended in the
404 Group_log_state.
405 2. For each group in the stage, choose 0 or 1 with 50% chance.
406
407 Parameter 3: empty or normal sub-group:
408 0. Generate only normal (and possibly automatic) sub-groups.
409 1. Generate only empty (and possibly automatic) sub-groups.
410 2. Generate only empty (and possibly automatic) sub-groups.
411 Add the sub-groups implicitly: do not call
412 add_empty_subgroup(); instead rely on
413 gtid_before_flush_trx_cache() to add empty subgroups.
414 3. Choose 0 or 1 with 33% chance.
415
416 Parameter 4: insert anonymous sub-groups or not:
417 0. Do not generate anonymous sub-groups.
418 1. Generate an anomous sub-group before each sub-group with
419 50% chance and an anonymous group after each sub-group with
420 50% chance.
421
422 We vary these parameters in all combinations; thus we construct
423 4*3*4*2=96 Gtid_sets.
424 */
425 enum enum_caches_type {
426 TYPE_TRX = 0,
427 TYPE_NONTRX,
428 TYPE_RANDOMIZE,
429 TYPE_AUTO,
430 MAX_TYPE
431 };
432 enum enum_caches_end { END_OFF = 0, END_ON, END_RANDOMIZE, MAX_END };
433 enum enum_caches_empty {
434 EMPTY_OFF = 0,
435 EMPTY_ON,
436 EMPTY_IMPLICIT,
437 EMPTY_RANDOMIZE,
438 MAX_EMPTY
439 };
440 enum enum_caches_anon { ANON_OFF = 0, ANON_ON, MAX_ANON };
441 const int N_COMBINATIONS_CACHES = MAX_TYPE * MAX_END * MAX_EMPTY * MAX_ANON;
442 const int N_COMBINATIONS = N_COMBINATIONS_SETS + N_COMBINATIONS_CACHES;
443
444 // Auxiliary macros to loop through all combinations of parameters.
445 #define BEGIN_LOOP_A \
446 push_errtext(); \
447 for (int method_i = 0, combination_i = 0; method_i < MAX_METHOD; \
448 method_i++) { \
449 for (int sid_map_i = 0; sid_map_i < MAX_SID_MAP; \
450 sid_map_i++, combination_i++) { \
451 Gtid_set >id_set MY_ATTRIBUTE((unused)) = \
452 containers[combination_i]->gtid_set; \
453 Sid_map *&sid_map MY_ATTRIBUTE((unused)) = sid_maps[sid_map_i]; \
454 append_errtext(__LINE__, "sid_map_i=%d method_i=%d combination_i=%d", \
455 sid_map_i, method_i, combination_i);
456
457 #define END_LOOP_A \
458 } \
459 } \
460 pop_errtext()
461
462 #define BEGIN_LOOP_B \
463 push_errtext(); \
464 for (int type_i = 0, combination_i = N_COMBINATIONS_SETS; type_i < MAX_TYPE; \
465 type_i++) { \
466 for (int end_i = 0; end_i < MAX_END; end_i++) { \
467 for (int empty_i = 0; empty_i < MAX_EMPTY; empty_i++) { \
468 for (int anon_i = 0; anon_i < MAX_ANON; anon_i++, combination_i++) { \
469 Gtid_set >id_set MY_ATTRIBUTE((unused)) = \
470 containers[combination_i]->gtid_set; \
471 Group_cache &stmt_cache MY_ATTRIBUTE((unused)) = \
472 containers[combination_i]->stmt_cache; \
473 Group_cache &trx_cache MY_ATTRIBUTE((unused)) = \
474 containers[combination_i]->trx_cache; \
475 Group_log_state &group_log_state MY_ATTRIBUTE((unused)) = \
476 containers[combination_i]->group_log_state; \
477 append_errtext(__LINE__, \
478 "type_i=%d end_i=%d empty_i=%d " \
479 "anon_i=%d combination_i=%d", \
480 type_i, end_i, empty_i, anon_i, combination_i); \
481 // verbose= (combination_i == 108); /*todo*/
482
483 #define END_LOOP_B \
484 } \
485 } \
486 } \
487 } \
488 pop_errtext()
489
490 // Do not generate warnings (because that causes segfault when done
491 // from a unittest).
492 global_system_variables.log_error_verbosity = 1;
493
494 mysql_bin_log.server_uuid_sidno = 1;
495
496 // Create Sid_maps.
497 Checkable_rwlock &lock = mysql_bin_log.sid_lock;
498 Sid_map **sid_maps = new Sid_map *[2];
499 sid_maps[0] = &mysql_bin_log.sid_map;
500 sid_maps[1] = new Sid_map(&lock);
501
502 lock.rdlock();
503 ASSERT_OK(sid_maps[0]->open("sid-map-1"));
504 ASSERT_OK(sid_maps[1]->open("sid-map-2"));
505 /*
506 Make sid_maps[0] and sid_maps[1] different: sid_maps[0] is
507 generated in order; sid_maps[1] is generated in the order that
508 SIDS are inserted in the Gtid_set.
509 */
510 for (int i = 0; i < N_SIDS; i++)
511 ASSERT_LE(1, sid_maps[0]->add_permanent(&sids[i])) << errtext;
512
513 // Create list of container objects. These are the objects that we
514 // test.
515 struct Containers {
516 Gtid_set gtid_set;
517 Group_cache stmt_cache;
518 Group_cache trx_cache;
519 Group_log_state group_log_state;
520 Containers(Checkable_rwlock *lock, Sid_map *sm)
521 : gtid_set(sm), group_log_state(lock, sm) {
522 init();
523 }
524 void init() { ASSERT_OK(group_log_state.ensure_sidno()); };
525 };
526 Containers **containers = new Containers *[N_COMBINATIONS];
527 BEGIN_LOOP_A { containers[combination_i] = new Containers(&lock, sid_map); }
528 END_LOOP_A;
529 BEGIN_LOOP_B {
530 containers[combination_i] = new Containers(&lock, sid_maps[0]);
531 }
532 END_LOOP_B;
533
534 /*
535 Construct a Gtid_set that contains the set of all groups from
536 which we sample.
537 */
538 static char all_groups_str[100 * 100];
539 char *s = all_groups_str;
540 s += sprintf(s, "%s:1", uuids[0]);
541 for (rpl_sidno sidno = 2; sidno <= N_SIDS; sidno++)
542 s += sprintf(s, ",\n%s:1-%d", uuids[sidno - 1], sidno * sidno);
543 enum_return_status status;
544 Gtid_set all_groups(sid_maps[0], all_groups_str, &status);
545 ASSERT_OK(status) << errtext;
546
547 // The set of groups that were added in some previous stage.
548 Gtid_set done_groups(sid_maps[0]);
549 ASSERT_OK(done_groups.ensure_sidno(sid_maps[0]->get_max_sidno()));
550
551 /*
552 Iterate through stages. In each stage, create the "stage group
553 set" by generating up to 200 subgroups. Add this stage group set
554 to each of the group sets in different ways. Stop when the union
555 of all stage group sets is equal to the full set from which we
556 took the samples.
557 */
558 char *done_str = nullptr;
559 int done_str_len = 0;
560 Stage stage(this, sid_maps[0]);
561 int stage_i = 0;
562
563 /*
564 We need a THD object only to read THD::variables.gtid_next,
565 THD::variables.gtid_end, THD::variables.gtid_next_list,
566 THD::thread_id, THD::server_status. We don't want to invoke the
567 THD constructor because that would require setting up mutexes,
568 etc. Hence we use malloc instead of new.
569 */
570 THD *thd = (THD *)malloc(sizeof(THD));
571 ASSERT_NE((THD *)nullptr, thd) << errtext;
572 Gtid_specification *gtid_next = &thd->variables.gtid_next;
573 thd->set_new_thread_id();
574 gtid_next->type = Gtid_specification::AUTOMATIC;
575 bool >id_end = thd->variables.gtid_end;
576 bool >id_commit = thd->variables.gtid_commit;
577 thd->server_status = 0;
578 thd->system_thread = NON_SYSTEM_THREAD;
579 thd->variables.gtid_next_list.gtid_set = &stage.set;
580
581 push_errtext();
582 while (!all_groups.equals(&done_groups)) {
583 stage_i++;
584 append_errtext(__LINE__, "stage_i=%d", stage_i);
585 stage.new_stage(&done_groups, sid_maps[1]);
586
587 if (verbose) {
588 printf("======== stage %d ========\n", stage_i);
589 stage.print();
590 }
591
592 // Create a string that contains all previous stage.str,
593 // concatenated.
594 done_str = (char *)realloc(done_str, done_str_len + 1 + stage.str_len + 1);
595 ASSERT_NE((char *)nullptr, done_str) << errtext;
596 done_str_len += sprintf(done_str + done_str_len, ",%s", stage.str);
597
598 // Add groups to Gtid_sets.
599 BEGIN_LOOP_A {
600 switch (method_i) {
601 case METHOD_SIDNO_GNO:
602 BEGIN_SUBSTAGE_LOOP(this, &stage, true) {
603 rpl_sidno sidno_1 = sid_map->sid_to_sidno(substage.sid);
604 ASSERT_LE(1, sidno_1) << errtext;
605 ASSERT_OK(gtid_set.ensure_sidno(sidno_1));
606 ASSERT_OK(gtid_set._add(sidno_1, substage.gno));
607 }
608 END_SUBSTAGE_LOOP(this);
609 break;
610 case METHOD_GROUP_TEXT:
611 BEGIN_SUBSTAGE_LOOP(this, &stage, true) {
612 ASSERT_OK(gtid_set.add(substage.gtid_str));
613 }
614 END_SUBSTAGE_LOOP(this);
615 break;
616 case METHOD_GTID_SET:
617 ASSERT_OK(gtid_set.add(&stage.set)) << errtext;
618 break;
619 case METHOD_GTID_SET_TEXT:
620 ASSERT_OK(gtid_set.add(stage.str)) << errtext;
621 break;
622 case METHOD_ALL_TEXTS_CONCATENATED:
623 gtid_set.clear();
624 ASSERT_OK(gtid_set.add(done_str)) << errtext;
625 case MAX_METHOD:
626 break;
627 }
628 }
629 END_LOOP_A;
630
631 // Add groups to Group_caches.
632 BEGIN_LOOP_B {
633 if (verbose) {
634 printf("======== stage=%d combination=%d ========\n", stage_i,
635 combination_i);
636 #ifndef DBUG_OFF
637 printf("group log state:\n");
638 group_log_state.print();
639 printf("trx cache:\n");
640 trx_cache.print(sid_maps[0]);
641 printf("stmt cache:\n");
642 stmt_cache.print(sid_maps[0]);
643 #endif // ifdef DBUG_OFF
644 }
645
646 Gtid_set ended_groups(sid_maps[0]);
647 bool trx_contains_logged_subgroup = false;
648 bool stmt_contains_logged_subgroup = false;
649 BEGIN_SUBSTAGE_LOOP(this, &stage, true) {
650 int type_j;
651 if (type_i == TYPE_RANDOMIZE)
652 type_j = rand() % 2;
653 else if (type_i == TYPE_AUTO && !substage.is_auto)
654 type_j = TYPE_NONTRX;
655 else
656 type_j = type_i;
657 int end_j;
658 if (substage.is_first &&
659 ((end_i == END_RANDOMIZE && (rand() % 2)) || end_i == END_ON)) {
660 ASSERT_OK(ended_groups.ensure_sidno(substage.sidno));
661 ASSERT_OK(ended_groups._add(substage.sidno, substage.gno));
662 }
663 end_j = substage.is_last &&
664 ended_groups.contains_group(substage.sidno, substage.gno);
665
666 /*
667 In EMPTY_RANDOMIZE mode, we have to determine once *per
668 group* (not substage) if we use EMPTY_END or not. So we
669 determine this for the first subgroup of the group, and then
670 we memoize which groups use EMPTY_END using the Gtid_set
671 empty_end.
672 */
673 int empty_j;
674 if (empty_i == EMPTY_RANDOMIZE)
675 empty_j = rand() % 3;
676 else
677 empty_j = empty_i;
678 int anon_j1, anon_j2;
679 if (type_j != TYPE_TRX || anon_i == ANON_OFF)
680 anon_j1 = anon_j2 = ANON_OFF;
681 else {
682 anon_j1 = rand() % 2;
683 anon_j2 = rand() % 2;
684 }
685 if (verbose)
686 printf("type_j=%d end_j=%d empty_j=%d anon_j1=%d anon_j2=%d\n",
687 type_j, end_j, empty_j, anon_j1, anon_j2);
688
689 thd->variables.gtid_next_list.is_non_null =
690 (type_i == TYPE_NONTRX || type_i == TYPE_AUTO) ? 0 : 1;
691 gtid_commit = (substage_i == stage.n_substages - 1) ||
692 !thd->variables.gtid_next_list.is_non_null;
693
694 if (type_j == TYPE_AUTO) {
695 gtid_next->type = Gtid_specification::AUTOMATIC;
696 gtid_next->group.sidno = substage.sidno;
697 gtid_next->group.gno = 0;
698 gtid_end = false;
699 lock.unlock();
700 lock.assert_no_lock();
701 gtid_before_statement(thd, &lock, &group_log_state, &stmt_cache,
702 &trx_cache);
703 lock.rdlock();
704 stmt_cache.add_logged_subgroup(thd, 20 + rand() % 100 /*binlog_len*/);
705 stmt_contains_logged_subgroup = true;
706 } else {
707 Group_cache &cache = type_j == TYPE_TRX ? trx_cache : stmt_cache;
708
709 if (anon_j1) {
710 gtid_next->type = Gtid_specification::ANONYMOUS;
711 gtid_next->group.sidno = 0;
712 gtid_next->group.gno = 0;
713 gtid_end = false;
714 lock.unlock();
715 lock.assert_no_lock();
716 gtid_before_statement(thd, &lock, &group_log_state, &stmt_cache,
717 &trx_cache);
718 lock.rdlock();
719 cache.add_logged_subgroup(thd, 20 + rand() % 100 /*binlog_len*/);
720 trx_contains_logged_subgroup = true;
721 }
722
723 gtid_next->type = Gtid_specification::GTID;
724 gtid_next->group.sidno = substage.sidno;
725 gtid_next->group.gno = substage.gno;
726 gtid_end = (end_j == END_ON) ? true : false;
727 lock.unlock();
728 lock.assert_no_lock();
729 gtid_before_statement(thd, &lock, &group_log_state, &stmt_cache,
730 &trx_cache);
731 lock.rdlock();
732 if (!group_log_state.is_ended(substage.sidno, substage.gno)) {
733 switch (empty_j) {
734 case EMPTY_OFF:
735 cache.add_logged_subgroup(thd,
736 20 + rand() % 100 /*binlog_len*/);
737 if (type_j == TYPE_TRX)
738 trx_contains_logged_subgroup = true;
739 else
740 stmt_contains_logged_subgroup = true;
741 break;
742 case EMPTY_ON:
743 cache.add_empty_subgroup(substage.sidno, substage.gno,
744 end_j ? true : false);
745 break;
746 case EMPTY_IMPLICIT:
747 break; // do nothing
748 default:
749 assert(0);
750 }
751 }
752
753 if (anon_j2) {
754 gtid_next->type = Gtid_specification::ANONYMOUS;
755 gtid_next->group.sidno = 0;
756 gtid_next->group.gno = 0;
757 gtid_end = false;
758 lock.unlock();
759 lock.assert_no_lock();
760 gtid_before_statement(thd, &lock, &group_log_state, &stmt_cache,
761 &trx_cache);
762 lock.rdlock();
763 cache.add_logged_subgroup(thd, 20 + rand() % 100 /*binlog_len*/);
764 trx_contains_logged_subgroup = true;
765 }
766 }
767
768 #ifndef DBUG_OFF
769 if (verbose) {
770 printf("stmt_cache:\n");
771 stmt_cache.print(sid_maps[0]);
772 }
773 #endif // ifndef DBUG_OFF
774 if (!stmt_cache.is_empty())
775 gtid_flush_group_cache(
776 thd, &lock, &group_log_state, nullptr /*group log*/, &stmt_cache,
777 &trx_cache, 1 /*binlog_no*/, 1 /*binlog_pos*/,
778 stmt_contains_logged_subgroup ? 20 + rand() % 99 : -1
779 /*offset_after_last_statement*/);
780 stmt_contains_logged_subgroup = false;
781 gtid_before_flush_trx_cache(thd, &lock, &group_log_state, &trx_cache);
782 if (gtid_commit) {
783 // simulate gtid_after_flush_trx_cache() but don't
784 // execute a COMMIT statement
785 thd->variables.gtid_has_ongoing_super_group = 0;
786
787 #ifndef DBUG_OFF
788 if (verbose) {
789 printf("trx_cache:\n");
790 trx_cache.print(sid_maps[0]);
791 printf(
792 "trx_cache.is_empty=%d n_subgroups=%d "
793 "trx_contains_logged_subgroup=%d\n",
794 trx_cache.is_empty(), trx_cache.get_n_subgroups(),
795 trx_contains_logged_subgroup);
796 }
797 #endif // ifndef DBUG_OFF
798
799 if (!trx_cache.is_empty())
800 gtid_flush_group_cache(
801 thd, &lock, &group_log_state, nullptr /*group log*/, &trx_cache,
802 &trx_cache, 1 /*binlog_no*/, 1 /*binlog_pos*/,
803 trx_contains_logged_subgroup ? 20 + rand() % 99 : -1
804 /*offset_after_last_statement*/);
805 trx_contains_logged_subgroup = false;
806 }
807 }
808 END_SUBSTAGE_LOOP(this);
809
810 gtid_set.clear();
811 ASSERT_OK(group_log_state.owned_groups.get_partial_groups(>id_set));
812 ASSERT_OK(gtid_set.add(&group_log_state.ended_groups));
813 }
814 END_LOOP_B;
815
816 // add stage.set to done_groups
817 Gtid_set old_done_groups(&done_groups, &status);
818 ASSERT_OK(status);
819 ASSERT_OK(done_groups.add(&stage.set));
820
821 // check the Gtid_set::remove and Gtid_set::is_subset functions
822 Gtid_set diff(&done_groups, &status);
823 ASSERT_OK(status);
824 ASSERT_OK(diff.remove(&old_done_groups));
825 Gtid_set not_new(&stage.set, &status);
826 ASSERT_OK(status);
827 ASSERT_OK(not_new.remove(&diff));
828
829 #define GROUP_SUBSET(gs1, gs2, outcome) \
830 group_subset(&gs1, &gs2, outcome, __LINE__, #gs1 " <= " #gs2);
831 push_errtext();
832 GROUP_SUBSET(not_new, not_new, true);
833 GROUP_SUBSET(not_new, diff, not_new.is_empty());
834 GROUP_SUBSET(not_new, stage.set, true);
835 GROUP_SUBSET(not_new, done_groups, true);
836 GROUP_SUBSET(not_new, old_done_groups, true);
837
838 GROUP_SUBSET(diff, not_new, diff.is_empty());
839 GROUP_SUBSET(diff, diff, true);
840 GROUP_SUBSET(diff, stage.set, true);
841 GROUP_SUBSET(diff, done_groups, true);
842 GROUP_SUBSET(diff, old_done_groups, diff.is_empty());
843
844 GROUP_SUBSET(stage.set, not_new, diff.is_empty());
845 GROUP_SUBSET(stage.set, diff, not_new.is_empty());
846 GROUP_SUBSET(stage.set, stage.set, true);
847 GROUP_SUBSET(stage.set, done_groups, true);
848 GROUP_SUBSET(stage.set, old_done_groups, diff.is_empty());
849
850 // GROUP_SUBSET(done_groups, not_new, ???);
851 GROUP_SUBSET(done_groups, diff, old_done_groups.is_empty());
852 GROUP_SUBSET(done_groups, stage.set, done_groups.equals(&stage.set));
853 GROUP_SUBSET(done_groups, done_groups, true);
854 GROUP_SUBSET(done_groups, old_done_groups, diff.is_empty());
855
856 GROUP_SUBSET(old_done_groups, not_new, old_done_groups.equals(¬_new));
857 GROUP_SUBSET(old_done_groups, diff, old_done_groups.is_empty());
858 // GROUP_SUBSET(old_done_groups, stage.set, ???);
859 GROUP_SUBSET(old_done_groups, done_groups, true);
860 GROUP_SUBSET(old_done_groups, old_done_groups, true);
861 pop_errtext();
862
863 /*
864 Verify that all group sets are equal. We test both a.equals(b)
865 and b.equals(a) and a.equals(a), because we want to verify that
866 Gtid_set::equals is correct too. We compare both the sets
867 using Gtid_set::equals, and the output of to_string() using
868 EXPECT_STREQ.
869 */
870 BEGIN_LOOP_A {
871 char *buf1 = new char[gtid_set.get_string_length() + 1];
872 gtid_set.to_string(buf1);
873 for (int i = 0; i < N_COMBINATIONS_SETS; i++) {
874 Gtid_set >id_set_2 = containers[i]->gtid_set;
875 if (combination_i < i) {
876 char *buf2 = new char[gtid_set_2.get_string_length() + 1];
877 gtid_set_2.to_string(buf2);
878 EXPECT_STREQ(buf1, buf2) << errtext << " i=" << i;
879 delete buf2;
880 }
881 EXPECT_EQ(true, gtid_set.equals(>id_set_2)) << errtext << " i=" << i;
882 }
883 delete buf1;
884 }
885 END_LOOP_A;
886 BEGIN_LOOP_B {
887 EXPECT_EQ(true, containers[combination_i]->gtid_set.equals(&done_groups))
888 << errtext;
889 }
890 END_LOOP_B;
891 }
892 pop_errtext();
893
894 // Finally, verify that the string representations of
895 // done_groups is as expected
896 static char buf[100 * 100];
897 done_groups.to_string(buf);
898 EXPECT_STRCASEEQ(all_groups_str, buf) << errtext;
899 lock.unlock();
900 lock.assert_no_lock();
901
902 // Clean up.
903 free(done_str);
904 for (int i = 0; i < N_COMBINATIONS; i++) delete containers[i];
905 delete containers;
906 delete sid_maps[1];
907 delete sid_maps;
908 free(thd);
909
910 mysql_bin_log.sid_lock.assert_no_lock();
911 }
912