MySQL 8.4.0
Source Code Documentation
rpl_rli_pdb.h
Go to the documentation of this file.
1/* Copyright (c) 2011, 2024, Oracle and/or its affiliates.
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 designed to work 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 either included with
13 the program or referenced in the documentation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24#ifndef RPL_RLI_PDB_H
25#define RPL_RLI_PDB_H
26
27#include <stdarg.h>
28#include <sys/types.h>
29#include <time.h>
30#include <atomic>
31#include <tuple>
32
33#include "my_bitmap.h"
34#include "my_compiler.h"
35#include "my_dbug.h"
36#include "my_inttypes.h"
37#include "my_io.h"
38#include "my_psi_config.h"
44#include "mysql/my_loglevel.h"
46#include "prealloced_array.h" // Prealloced_array
47#include "sql/log_event.h" // Format_description_log_event
48#include "sql/rpl_gtid.h"
49#include "sql/rpl_mta_submode.h" // enum_mts_parallel_type
50#include "sql/rpl_replica.h" // MTS_WORKER_UNDEF
51#include "sql/rpl_rli.h" // Relay_log_info
52#include "sql/sql_class.h"
54
56class Slave_worker;
57struct TABLE;
58
59#ifndef NDEBUG
60extern ulong w_rr;
61#endif
62/**
63 Legends running throughout the module:
64
65 C - Coordinator
66 CP - checkpoint
67 W - Worker
68
69 B-event event that Begins a group (a transaction)
70 T-event event that Terminates a group (a transaction)
71*/
72
73/* Assigned Partition Hash (APH) entry */
75 uint db_len;
76 const char *db;
78 /*
79 The number of transaction pending on this database.
80 This should only be modified under the lock slave_worker_hash_lock.
81 */
82 long usage;
83 /*
84 The list of temp tables belonging to @ db database is
85 attached to an assigned @c worker to become its thd->temporary_tables.
86 The list is updated with every ddl incl CREATE, DROP.
87 It is removed from the entry and merged to the coordinator's
88 thd->temporary_tables in case of events: slave stops, APH oversize.
89 */
91
92 /* todo: relax concurrency to mimic record-level locking.
93 That is to augmenting the entry with mutex/cond pair
94 pthread_mutex_t
95 pthread_cond_t
96 timestamp updated_at; */
97};
98
101Slave_worker *map_db_to_worker(const char *dbname, Relay_log_info *rli,
102 db_worker_hash_entry **ptr_entry,
103 bool need_temp_tables, Slave_worker *w);
105 Slave_worker_array *workers,
106 Log_event *ev);
107
108#define SLAVE_INIT_DBS_IN_GROUP 4 // initial allocation for CGEP dynarray
109
111 Slave_job_group() = default;
112
113 /*
114 We need a custom copy constructor and assign operator because std::atomic<T>
115 is not copy-constructible.
116 */
122 worker_id(other.worker_id),
123 worker(other.worker),
131 done(other.done.load()),
132 shifted(other.shifted),
133 ts(other.ts),
134#ifndef NDEBUG
135 notified(other.notified),
136#endif
140 }
141
147 worker_id = other.worker_id;
148 worker = other.worker;
149 total_seqno = other.total_seqno;
156 done.store(other.done.load());
157 shifted = other.shifted;
158 ts = other.ts;
159#ifndef NDEBUG
160 notified = other.notified;
161#endif
165 return *this;
166 }
167
168 char *group_master_log_name; // (actually redundant)
169 /*
170 T-event lop_pos filled by Worker for CheckPoint (CP)
171 */
173
174 /*
175 When relay-log name changes allocates and fill in a new name of relay-log,
176 otherwise it fills in NULL.
177 Coordinator keeps track of each Worker has been notified on the updating
178 to make sure the routine runs once per change.
179
180 W checks the value at commit and memorizes a not-NULL.
181 Freeing unless NULL is left to Coordinator at CP.
182 */
183 char *group_relay_log_name; // The value is last seen relay-log
188
189 my_off_t master_log_pos; // B-event log_pos
190 /* checkpoint coord are reset by periodical and special (Rotate event) CP:s */
192 my_off_t checkpoint_log_pos; // T-event lop_pos filled by W for CheckPoint
195 checkpoint_relay_log_pos; // T-event lop_pos filled by W for CheckPoint
197 std::atomic<int32> done; // Flag raised by W, read and reset by Coordinator
198 ulong shifted; // shift the last CP bitmap at receiving a new CP
199 time_t ts; // Group's timestamp to update Seconds_behind_source
200#ifndef NDEBUG
201 bool notified{false}; // to debug group_master_log_name change notification
202#endif
203 /* Clock-based scheduler requirement: */
204 longlong last_committed; // commit parent timestamp
205 longlong sequence_number; // transaction's logical timestamp
206 /*
207 After Coordinator has seen a new FD event, it sets this member to
208 point to the new event, once per worker. Coordinator does so
209 when it schedules a first group following the FD event to a worker.
210 It checks Slave_worker::fd_change_notified flag to decide whether
211 to do this or not.
212 When the worker executes the group, it replaces its currently
213 active FD by the new FD once it takes on the group first event. It
214 checks this member and resets it after the FD replacement is done.
215
216 The member is kind of lock-free. It's updated by Coordinator and
217 read by Worker without holding any mutex. That's still safe thanks
218 to Slave_worker::jobs_lock that works as synchronizer, Worker
219 can't read any stale info.
220 The member is updated by Coordinator when it decides which Worker
221 an event following a new FD is to be scheduled.
222 After Coordinator has chosen a Worker, it queues the event to it
223 with necessarily taking Slave_worker::jobs_lock. The Worker grabs
224 the mutex lock later at pulling the event from the queue and
225 releases the lock before to read from this member.
226
227 This sequence of actions shows the write operation always precedes
228 the read one, and ensures no stale FD info is passed to the
229 Worker.
230 */
232 /*
233 Coordinator fills the struct with defaults and options at starting of
234 a group distribution.
235 */
239 group_master_log_name = nullptr; // todo: remove
240 group_relay_log_name = nullptr;
242 total_seqno = seqno;
243 checkpoint_log_name = nullptr;
247 checkpoint_seqno = (uint)-1;
248 done = 0;
249 ts = 0;
250#ifndef NDEBUG
251 notified = false;
252#endif
255 new_fd_event = nullptr;
256 }
257};
258
259/**
260 The class defines a type of queue with a predefined max capacity that is
261 implemented using the circular memory buffer.
262 That is items of the queue are accessed as indexed elements of
263 the array buffer in a way that when the index value reaches
264 a max value it wraps around to point to the first buffer element.
265*/
266template <typename Element_type>
268 public:
270 /**
271 The capacity and maximum length of the queue in terms of element.
272 */
273 size_t capacity;
274 /**
275 Its value modulo `capacity` is index of the element where the next element
276 will be enqueued. It's entry+length. It may be bigger than capacity, but
277 will be smaller than 2*capacity.
278 */
279 size_t avail;
280 /**
281 The head index of the queue. It is an index of next element that will be
282 dequeued. It is less than capacity, so it is an actual index (in contrast
283 to `avail`), don't need to be calculated modulo `capacity`.
284 */
285 size_t entry;
286 /**
287 Actual length. It can be read while not protected by any mutex.
288 */
289 std::atomic<size_t> len;
291
294 capacity(max),
295 avail(0),
296 entry(0),
297 len(0),
298 inited_queue(false) {
299 if (!m_Q.reserve(capacity)) inited_queue = true;
300 m_Q.resize(capacity);
301 }
304
305 /**
306 Content of the being dequeued item is copied to the arg-pointer
307 location.
308
309 @param [out] item A pointer to the being dequeued item.
310 @return true if an element was returned, false if the queue was empty.
311 */
312 bool de_queue(Element_type *item);
313 /**
314 Similar to de_queue but extracting happens from the tail side.
315
316 @param [out] item A pointer to the being dequeued item.
317 @return true if an element was returned, false if the queue was empty.
318 */
319 bool de_tail(Element_type *item);
320
321 /**
322 return the index where the arg item locates
323 or an error encoded as a value `circular_buffer_queue::error_result`.
324 */
325 size_t en_queue(Element_type *item);
326 /**
327 return the value of @c data member of the head of the queue.
328 */
329 Element_type *head_queue() {
330 if (empty()) return nullptr;
331 return &m_Q[entry];
332 }
333
334 /* index is within the valid range */
335 bool in(size_t i) {
336 return (avail >= capacity) ? (entry <= i || i < avail - capacity)
337 : (entry <= i && i < avail);
338 }
339 size_t get_length() const { return len.load(std::memory_order_relaxed); }
340 bool empty() const { return get_length() == 0; }
341 bool full() const { return get_length() == capacity; }
342
343 static constexpr size_t error_result = std::numeric_limits<size_t>::max();
344};
345
346/**
347 Group Assigned Queue whose first element identifies first gap
348 in committed sequence. The head of the queue is therefore next to
349 the low-water-mark.
350*/
351class Slave_committed_queue : public circular_buffer_queue<Slave_job_group> {
352 public:
353 bool inited;
354
355 /* master's Rot-ev exec */
356 void update_current_binlog(const char *post_rotate);
357
358 /*
359 The last checkpoint time Low-Water-Mark
360 */
362
363 /* last time processed indexes for each worker */
365
366 /* the being assigned group index in GAQ */
368
369 Slave_committed_queue(size_t max, uint n);
370
372 if (inited) {
374 free_dynamic_items(); // free possibly left allocated strings in GAQ list
375 }
376 }
377
378#ifndef NDEBUG
379 bool count_done(Relay_log_info *rli);
380#endif
381
382 /* Checkpoint routine refreshes the queue */
384 /* Method is for slave shutdown time cleanup */
385 void free_dynamic_items();
386 /*
387 returns a pointer to Slave_job_group struct instance as indexed by arg
388 in the circular buffer dyn-array
389 */
391 assert(ind < capacity);
392 return &m_Q[ind];
393 }
394
395 /**
396 Assigns @c assigned_group_index to an index of enqueued item
397 and returns it.
398 */
399 size_t en_queue(Slave_job_group *item) {
400 return assigned_group_index =
402 }
403
404 /**
405 Dequeue from head.
406
407 @param [out] item A pointer to the being dequeued item.
408 @return true if an element was returned, false if the queue was empty.
409 */
412 }
413
414 /**
415 Similar to de_queue() but removing an item from the tail side.
416
417 @param [out] item A pointer to the being dequeued item.
418 @return true if an element was returned, false if the queue was empty.
419 */
422 }
423
424 size_t find_lwm(Slave_job_group **, size_t);
425};
426
427/**
428 @return the index where the arg item has been located
429 or an error encoded as a value
430 `circular_buffer_queue::error_result`.
431*/
432template <typename Element_type>
434 if (full()) {
435 return error_result;
436 }
437
438 const auto ret = (avail++) % capacity;
439 m_Q[ret] = *item;
440 len++;
441 assert(len == avail - entry);
442 assert(entry < avail);
443
444 return ret;
445}
446
447/**
448 Dequeue from head.
449
450 @param [out] item A pointer to the being dequeued item.
451 @return true if an element was returned, false if the queue was empty.
452*/
453template <typename Element_type>
455 if (empty()) {
456 return false;
457 }
458 *item = m_Q[entry++];
459 len--;
460 assert(len == avail - entry);
461 assert(entry <= avail);
462
463 // The start of the queue just have returned to the first index. Normalize
464 // indexes so they are small again.
465 if (entry == capacity) {
466 entry = 0;
467 avail -= capacity;
468 assert(avail < capacity);
469 assert(avail == len);
470 }
471 return true;
472}
473
474template <typename Element_type>
476 if (empty()) {
477 return false;
478 }
479
480 assert(avail > entry);
481 *item = m_Q[(--avail) % capacity];
482 len--;
483 assert(len == avail - entry);
484 return true;
485}
486
487class Slave_jobs_queue : public circular_buffer_queue<Slave_job_item> {
488 public:
490 /*
491 Coordinator marks with true, Worker signals back at queue back to
492 available
493 */
496};
497
499 public:
502 PSI_mutex_key *param_key_info_run_lock,
503 PSI_mutex_key *param_key_info_data_lock,
504 PSI_mutex_key *param_key_info_sleep_lock,
505 PSI_mutex_key *param_key_info_thd_lock,
506 PSI_mutex_key *param_key_info_data_cond,
507 PSI_mutex_key *param_key_info_start_cond,
508 PSI_mutex_key *param_key_info_stop_cond,
509 PSI_mutex_key *param_key_info_sleep_cond,
510#endif
511 uint param_id, const char *param_channel);
512
513 ~Slave_worker() override;
514
515 Slave_jobs_queue jobs; // assignment queue containing events to execute
516 mysql_mutex_t jobs_lock; // mutex for the jobs queue
517 mysql_cond_t jobs_cond; // condition variable for the jobs queue
518 Relay_log_info *c_rli; // pointer to Coordinator's rli
519
521 curr_group_exec_parts; // Current Group Executed Partitions
522
523#ifndef NDEBUG
524 bool curr_group_seen_sequence_number; // is set to true about starts_group()
525#endif
526 ulong id; // numeric identifier of the Worker
527
528 /*
529 Worker runtime statistics
530 */
531 // the index in GAQ of the last processed group by this Worker
532 volatile ulong last_group_done_index;
534 last_groups_assigned_index; // index of previous group assigned to worker
535 ulong wq_empty_waits; // how many times got idle
536 ulong events_done; // how many events (statements) processed
537 ulong groups_done; // how many groups (transactions) processed
538 std::atomic<int> curr_jobs; // number of active assignments
539 // number of partitions allocated to the worker at point in time
541 // symmetric to rli->mts_end_group_sets_max_dbs
543
544 volatile bool relay_log_change_notified; // Coord sets and resets, W can read
545 volatile bool checkpoint_notified; // Coord sets and resets, W can read
546 volatile bool
547 master_log_change_notified; // Coord sets and resets, W can read
548 /*
549 The variable serves to Coordinator as a memo to itself
550 to notify a Worker about the fact that a new FD has been read.
551 Normally, the value is true, to mean the Worker is notified.
552 When Coordinator reads a new FD it changes the value to false.
553 When Coordinator schedules to a Worker the first event following the new FD,
554 it propagates the new FD to the Worker through
555 Slave_job_group::new_fd_event. Afterwards Coordinator returns the value back
556 to the regular true, to denote things done. Worker will adapt to the new FD
557 once it takes on a first event of the marked group.
558 */
560 ulong bitmap_shifted; // shift the last bitmap at receiving new CP
561 // WQ current excess above the overrun level
563 /*
564 number of events starting from which Worker queue is regarded as
565 close to full. The number of the excessive events yields a weight factor
566 to compute Coordinator's nap.
567 */
569 /*
570 reverse to overrun: the number of events below which Worker is
571 considered under-running
572 */
574 /*
575 Total of increments done to rli->mts_wq_excess_cnt on behalf of this worker.
576 When WQ length is dropped below overrun the counter is reset.
577 */
579 /*
580 Coordinates of the last CheckPoint (CP) this Worker has
581 acknowledged; part of is persistent data
582 */
587 MY_BITMAP group_executed; // bitmap describes groups executed after last CP
588 MY_BITMAP group_shifted; // temporary bitmap to compute group_executed
589 ulong
590 worker_checkpoint_seqno; // the most significant ON bit in group_executed
591 /* Initial value of FD-for-execution version until it's gets known. */
596 ERROR_LEAVING = 2, // is set by Worker
597 STOP = 3, // is set by Coordinator upon receiving STOP
599 4 // is set by worker upon completing job when STOP REPLICA is issued
600 };
601
602 /*
603 This function is used to make a copy of the worker object before we
604 destroy it on STOP REPLICA. This new object is then used to report the
605 worker status until next START REPLICA following which the new worker
606 objects will be used.
607 */
609 THD *worker_thd, const Error &last_error,
610 Gtid_monitoring_info *monitoring_info_arg);
611
612 /*
613 The running status is guarded by jobs_lock mutex that a writer
614 Coordinator or Worker itself needs to hold when write a new value.
615 */
617 /*
618 exit_incremented indicates whether worker has contributed to max updated
619 index. By default it is set to false. When the worker contributes for the
620 first time this variable is set to true.
621 */
623
624 int init_worker(Relay_log_info *, ulong);
625 int rli_init_info(bool);
626 int flush_info(bool force = false);
627 static size_t get_number_worker_fields();
628 /**
629 Sets bits for columns that are allowed to be `NULL`.
630
631 @param nullable_fields the bitmap to hold the nullable fields.
632 */
633 static void set_nullable_fields(MY_BITMAP *nullable_fields);
635 const char *get_master_log_name();
637 ulonglong set_master_log_pos(ulong val) { return master_log_pos = val; }
638 bool commit_positions(Log_event *evt, Slave_job_group *ptr_g, bool force);
639 /**
640 The method is a wrapper to provide uniform interface with STS and is
641 to be called from Relay_log_info and Slave_worker pre_commit() methods.
642 */
643 bool commit_positions() override {
644 assert(current_event);
645
646 return commit_positions(
649 }
650 /**
651 See the comments for STS version of this method.
652 */
653 void post_commit(bool on_rollback) override {
654 if (on_rollback) {
655 if (is_transactional())
658 } else if (!is_transactional())
661 true);
662 }
663 /*
664 When commit fails clear bitmap for executed worker group. Revert back the
665 positions to the old positions that existed before commit using the
666 checkpoint.
667
668 @param Slave_job_group a pointer to Slave_job_group struct instance which
669 holds group master log pos, group relay log pos and checkpoint positions.
670 */
672 bool reset_recovery_info();
673 /**
674 The method runs at Worker initialization, at runtime when
675 Coordinator supplied a new FD event for execution context, and at
676 the Worker pool shutdown.
677 Similarly to the Coordinator's
678 Relay_log_info::set_rli_description_event() the possibly existing
679 old FD is destroyed, carefully; each worker decrements
680 Format_description_log_event::atomic_usage_counter and when it is made
681 zero the destructor runs.
682 Unlike to Coordinator's role, the usage counter of the new FD is *not*
683 incremented, see @c Log_event::get_slave_worker() where and why it's done
684 there.
685
686 Notice, the method is run as well by Coordinator per each Worker at MTS
687 shutdown time.
688
689 Todo: consider to merge logics of the method with that of
690 Relay_log_info class.
691
692 @param fdle pointer to a new Format_description_log_event
693
694 @return 1 if an error was encountered, 0 otherwise.
695 */
698
699 if (fdle) {
700 /*
701 When the master rotates its binary log, set gtid_next to
702 NOT_YET_DETERMINED. This tells the slave thread that:
703
704 - If a Gtid_log_event is read subsequently, gtid_next will be set to the
705 given GTID (this is done in gtid_pre_statement_checks()).
706
707 - If a statement is executed before any Gtid_log_event, then gtid_next
708 is set to anonymous (this is done in Gtid_log_event::do_apply_event().
709
710 It is important to not set GTID_NEXT=NOT_YET_DETERMINED in the middle of
711 a transaction. If that would happen when GTID_MODE=ON, the next
712 statement would fail because it implicitly sets GTID_NEXT=ANONYMOUS,
713 which is disallowed when GTID_MODE=ON. So then there would be no way to
714 end the transaction; any attempt to do so would result in this error.
715
716 There are three possible states when reaching this execution flow point
717 (see further below for a more detailed explanation on each):
718
719 - **No active transaction, and not in a group**: set `gtid_next` to
720 `NOT_YET_DETERMINED`.
721
722 - **No active transaction, and in a group**: do nothing regarding
723 `gtid_next`.
724
725 - **An active transaction exists**: impossible to set `gtid_next` and no
726 reason to process the `Format_description` event so, trigger an error.
727
728 For the sake of correctness, let's defined the meaning of having a
729 transaction "active" or "in a group".
730
731 A transaction is "active" if either BEGIN was executed or autocommit=0
732 and a DML statement was executed (@see
733 THD::in_active_multi_stmt_transaction).
734
735 A transaction is "in a group" if it is applied by the replication
736 applier, and the relay log position is between Gtid_log_event and the
737 committing event (@see Relay_log_info::is_in_group).
738
739 The three different states explained further:
740
741 **No active transaction, and not in a group**: It is normal to have
742 gtid_next=automatic/undefined and have a Format_description_log_event in
743 this condition. We are outside transaction context and should set
744 gtid_next to not_yet_determined.
745
746 **No active transaction, and in a group**: Having
747 gtid_next=automatic/undefined in a group is impossible if master is 5.7
748 or later, because the group always starts with a Gtid_log_event or an
749 Anonymous_gtid_log_event, which will set gtid_next to anonymous or
750 gtid. But it is possible to have gtid_next=undefined when replicating
751 from a 5.6 master with gtid_mode=off, because it does not generate any
752 such event. And then, it is possible to have no active transaction in a
753 group if the master has logged a DDL as a User_var_log_event followed by
754 a Query_log_event. The User_var_log_event will start a group, but not
755 start an active transaction or change gtid_next. In this case, it is
756 possible that a Format_description_log_event occurs, if the group
757 (transaction) is broken on two relay logs, so that User_var_log_event
758 appears at the end of one relay log and Query_log_event at the beginning
759 of the next one. In such cases, we should not set gtid_next.
760
761 **An active transaction exists**: It is possible to have
762 gtid_next=automatic/undefined in an active transaction, only if
763 gtid_next=automatic, which is only possible in a client connection using
764 gtid_next=automatic. In this scenario, there is no reason to execute a
765 Format_description_log_event. So we generate an error.
766 */
767 if (info_thd->variables.gtid_next.is_automatic() ||
768 info_thd->variables.gtid_next.is_undefined()) {
769 bool in_active_multi_stmt =
771
772 if (!is_in_group() && !in_active_multi_stmt) {
773 DBUG_PRINT("info",
774 ("Setting gtid_next.type to NOT_YET_DETERMINED_GTID"));
775 info_thd->variables.gtid_next.set_not_yet_determined();
776 } else if (in_active_multi_stmt) {
777 my_error(ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION, MYF(0),
778 "gtid_next");
779 return 1;
780 }
781 }
784 }
787
789 /* The being deleted by Worker FD can't be the latest one */
791
793 }
794 }
796
797 return 0;
798 }
799
801 inline void set_gaq_index(ulong val) {
802 if (gaq_index == c_rli->gaq->capacity) gaq_index = val;
803 }
804
806
807 /**
808 Make the necessary changes to both the `Slave_worker` and current
809 `Log_event` objects, before retrying to apply the transaction.
810
811 Since the event is going to be re-read from the relay-log file, there
812 may be actions needed to be taken to reset the state both of `this`
813 instance, as well as of the current `Log_event` being processed.
814
815 @param event The `Log_event` object currently being processed.
816 */
818
819 /**
820 Checks if the transaction can be retried, and if not, reports an error.
821
822 @param[in] thd The THD object of current thread.
823
824 @returns std::tuple<bool, bool, uint> where each element has
825 following meaning:
826
827 first element of tuple is function return value and determines:
828 false if the transaction should be retried
829 true if the transaction should not be retried
830
831 second element of tuple determines:
832 the function will set the value to true, in case the retry
833 should be "silent". Silent means that the caller should not
834 report it in performance_schema tables, write to the error log,
835 or sleep. Currently, silent is used by NDB only.
836
837 third element of tuple determines:
838 If the caller should report any other error than that stored in
839 thd->get_stmt_da()->mysql_errno(), then this function will store
840 that error in this third element of the tuple.
841
842 */
843 std::tuple<bool, bool, uint> check_and_report_end_of_retries(THD *thd);
844
845 /**
846 It is called after an error happens. It checks if that is an temporary
847 error and if the transaction should be retried. Then it will retry the
848 transaction if it is allowed. Retry policy and logic is similar to
849 single-threaded slave.
850
851 @param[in] start_relay_number The extension number of the relay log which
852 includes the first event of the transaction.
853 @param[in] start_relay_pos The offset of the transaction's first event.
854
855 @param[in] end_relay_number The extension number of the relay log which
856 includes the last event it should retry.
857 @param[in] end_relay_pos The offset of the last event it should retry.
858
859 @retval false if transaction succeeds (possibly after a number of retries)
860 @retval true if transaction fails
861 */
862 bool retry_transaction(uint start_relay_number, my_off_t start_relay_pos,
863 uint end_relay_number, my_off_t end_relay_pos);
864
865 bool set_info_search_keys(Rpl_info_handler *to) override;
866
867 /**
868 Get coordinator's RLI. Especially used get the rli from
869 a slave thread, like this: thd->rli_slave->get_c_rli();
870 thd could be a SQL thread or a worker thread.
871 */
872 Relay_log_info *get_c_rli() override { return c_rli; }
873
874 /**
875 return an extension "for channel channel_name"
876 for error messages per channel
877 */
878 const char *get_for_channel_str(bool upper_case = false) const override;
879
882 return ptr_g->sequence_number;
883 }
884
885 /**
886 Return true if replica-preserve-commit-order is enabled and an
887 earlier transaction is waiting for a row-level lock held by this
888 transaction.
889 */
891
892 /**
893 Called when replica-preserve-commit-order is enabled, by the worker
894 processing an earlier transaction that waits on a row-level lock
895 held by this worker's transaction.
896 */
898
899 /**
900 @return either the master server version as extracted from the last
901 installed Format_description_log_event, or when it was not
902 installed then the slave own server version.
903 */
908 }
909
910 protected:
911 void do_report(loglevel level, int err_code, const char *msg,
912 va_list v_args) const override
913 MY_ATTRIBUTE((format(printf, 4, 0)));
914
915 void do_report(loglevel level, int err_code,
916 const Gtid_specification *gtid_next, const char *msg,
917 va_list v_args) const override
918 MY_ATTRIBUTE((format(printf, 5, 0)));
919
920 private:
921 ulong gaq_index; // GAQ index of the current assignment
922 ulonglong master_log_pos; // event's cached log_pos for possible error report
923 void end_info();
924 bool read_info(Rpl_info_handler *from) override;
925 bool write_info(Rpl_info_handler *to) override;
926 std::atomic<bool> m_commit_order_deadlock;
927
928 /// This flag indicates whether positions were already modified during the
929 /// event processing, if yes, positions are not updated in the
930 /// slave_worker_ends_group function
932
935 bool worker_sleep(ulong seconds);
936 bool read_and_apply_events(uint start_relay_number, my_off_t start_relay_pos,
937 uint end_relay_number, my_off_t end_relay_pos);
939
940 public:
941 /**
942 Set the flag the signals a deadlock to false
943 */
945
946 /**
947 Returns an array with the expected column numbers of the primary key
948 fields of the table repository.
949 */
950 static const uint *get_table_pk_field_indexes();
951 /**
952 Returns the index of the Channel_name field of the table repository.
953 */
954 static uint get_channel_field_index();
955};
956
959 Slave_job_item *job_item);
960
963// Auxiliary function
965
967 Relay_log_info *rli);
968
969inline Slave_worker *get_thd_worker(const THD *thd) {
970 return static_cast<Slave_worker *>(thd->rli_slave);
971}
972
974
975#endif
Contains the classes representing events occurring in the replication stream.
Class representing an error.
Definition: error.h:48
For binlog version 4.
Definition: log_event.h:1525
std::atomic< int32 > atomic_usage_counter
Definition: log_event.h:1543
Stores information to monitor a transaction during the different replication stages.
Definition: rpl_gtid.h:1412
This is the abstract base class for binary log events.
Definition: log_event.h:538
ulong mts_group_idx
Index in rli->gaq array to indicate a group that this event is purging.
Definition: log_event.h:693
Definition: rpl_rli.h:203
Format_description_log_event * get_rli_description_event() const
Return the current Format_description_log_event.
Definition: rpl_rli.h:1765
bool is_in_group() const
A group is defined as the entire range of events that constitute a transaction or auto-committed stat...
Definition: rpl_rli.h:1537
Log_event * current_event
Reference to being applied event.
Definition: rpl_rli.h:2043
Slave_committed_queue * gaq
Definition: rpl_rli.h:1204
ulong adapt_to_master_version_updown(ulong master_version, ulong current_version)
The method compares two supplied versions and carries out down- or up- grade customization of executi...
Definition: rpl_rli.cc:2622
Format_description_log_event * rli_description_event
Definition: rpl_rli.h:1841
Definition: rpl_info_handler.h:58
bool is_transactional() const
Definition: rpl_info.h:107
THD * info_thd
Definition: rpl_info.h:78
Group Assigned Queue whose first element identifies first gap in committed sequence.
Definition: rpl_rli_pdb.h:351
void free_dynamic_items()
Method should be executed at slave system stop to cleanup dynamically allocated items that remained a...
Definition: rpl_rli_pdb.cc:1518
bool de_queue(Slave_job_group *item)
Dequeue from head.
Definition: rpl_rli_pdb.h:410
Slave_committed_queue(size_t max, uint n)
Definition: rpl_rli_pdb.cc:1317
bool inited
Definition: rpl_rli_pdb.h:353
bool count_done(Relay_log_info *rli)
Definition: rpl_rli_pdb.cc:1335
ulong assigned_group_index
Definition: rpl_rli_pdb.h:367
bool de_tail(Slave_job_group *item)
Similar to de_queue() but removing an item from the tail side.
Definition: rpl_rli_pdb.h:420
size_t move_queue_head(Slave_worker_array *ws)
The queue is processed from the head item by item to purge items representing committed groups.
Definition: rpl_rli_pdb.cc:1379
Prealloced_array< ulonglong, 1 > last_done
Definition: rpl_rli_pdb.h:364
Slave_job_group * get_job_group(size_t ind)
Definition: rpl_rli_pdb.h:390
~Slave_committed_queue()
Definition: rpl_rli_pdb.h:371
void update_current_binlog(const char *post_rotate)
size_t find_lwm(Slave_job_group **, size_t)
Finds low-water mark of committed jobs in GAQ.
Definition: rpl_rli_pdb.cc:1481
size_t en_queue(Slave_job_group *item)
Assigns assigned_group_index to an index of enqueued item and returns it.
Definition: rpl_rli_pdb.h:399
Slave_job_group lwm
Definition: rpl_rli_pdb.h:361
Definition: rpl_rli_pdb.h:487
Slave_jobs_queue()
Definition: rpl_rli_pdb.h:489
bool overfill
Definition: rpl_rli_pdb.h:494
ulonglong waited_overfill
Definition: rpl_rli_pdb.h:495
Error const & last_error() const
Definition: rpl_reporting.h:143
Definition: rpl_rli_pdb.h:498
ulonglong get_master_log_pos()
Definition: rpl_rli_pdb.h:636
ulong underrun_level
Definition: rpl_rli_pdb.h:573
char checkpoint_relay_log_name[FN_REFLEN]
Definition: rpl_rli_pdb.h:583
Prealloced_array< db_worker_hash_entry *, SLAVE_INIT_DBS_IN_GROUP > curr_group_exec_parts
Definition: rpl_rli_pdb.h:521
ulong excess_cnt
Definition: rpl_rli_pdb.h:578
bool curr_group_seen_sequence_number
Definition: rpl_rli_pdb.h:524
bool read_and_apply_events(uint start_relay_number, my_off_t start_relay_pos, uint end_relay_number, my_off_t end_relay_pos)
Read events from relay logs and apply them.
Definition: rpl_rli_pdb.cc:1962
void reset_gaq_index()
Definition: rpl_rli_pdb.h:800
ulong overrun_level
Definition: rpl_rli_pdb.h:568
bool commit_positions() override
The method is a wrapper to provide uniform interface with STS and is to be called from Relay_log_info...
Definition: rpl_rli_pdb.h:643
ulong bitmap_shifted
Definition: rpl_rli_pdb.h:560
longlong sequence_number()
Definition: rpl_rli_pdb.h:880
void reset_commit_order_deadlock()
Set the flag the signals a deadlock to false.
Definition: rpl_rli_pdb.cc:1786
int flush_info(bool force=false)
Definition: rpl_rli_pdb.cc:478
int set_rli_description_event(Format_description_log_event *fdle) override
The method runs at Worker initialization, at runtime when Coordinator supplied a new FD event for exe...
Definition: rpl_rli_pdb.h:696
ulonglong checkpoint_master_log_pos
Definition: rpl_rli_pdb.h:586
Slave_jobs_queue jobs
Definition: rpl_rli_pdb.h:515
volatile bool relay_log_change_notified
Definition: rpl_rli_pdb.h:544
MY_BITMAP group_shifted
Definition: rpl_rli_pdb.h:588
ulong worker_checkpoint_seqno
Definition: rpl_rli_pdb.h:590
bool worker_sleep(ulong seconds)
Sleep for a given amount of seconds or until killed.
Definition: rpl_rli_pdb.cc:1764
void copy_values_for_PFS(ulong worker_id, en_running_state running_status, THD *worker_thd, const Error &last_error, Gtid_monitoring_info *monitoring_info_arg)
Definition: rpl_rli_pdb.cc:561
std::tuple< bool, bool, uint > check_and_report_end_of_retries(THD *thd)
Checks if the transaction can be retried, and if not, reports an error.
Definition: rpl_rli_pdb.cc:1811
ulong gaq_index
Definition: rpl_rli_pdb.h:921
void post_commit(bool on_rollback) override
See the comments for STS version of this method.
Definition: rpl_rli_pdb.h:653
void do_report(loglevel level, int err_code, const char *msg, va_list v_args) const override
Definition: rpl_rli_pdb.cc:1536
const char * get_for_channel_str(bool upper_case=false) const override
return an extension "for channel channel_name" for error messages per channel
Definition: rpl_rli_pdb.cc:2645
void report_commit_order_deadlock()
Called when replica-preserve-commit-order is enabled, by the worker processing an earlier transaction...
Definition: rpl_rli_pdb.cc:1794
Slave_worker(const Slave_worker &info)
std::atomic< int > curr_jobs
Definition: rpl_rli_pdb.h:538
Slave_worker(Relay_log_info *rli, PSI_mutex_key *param_key_info_run_lock, PSI_mutex_key *param_key_info_data_lock, PSI_mutex_key *param_key_info_sleep_lock, PSI_mutex_key *param_key_info_thd_lock, PSI_mutex_key *param_key_info_data_cond, PSI_mutex_key *param_key_info_start_cond, PSI_mutex_key *param_key_info_stop_cond, PSI_mutex_key *param_key_info_sleep_cond, uint param_id, const char *param_channel)
Definition: rpl_rli_pdb.cc:246
bool write_info(Rpl_info_handler *to) override
Definition: rpl_rli_pdb.cc:583
int init_worker(Relay_log_info *, ulong)
Method is executed by Coordinator at Worker startup time to initialize members parly with values supp...
Definition: rpl_rli_pdb.cc:318
static uint get_channel_field_index()
Returns the index of the Channel_name field of the table repository.
Definition: rpl_rli_pdb.cc:2653
bool read_info(Rpl_info_handler *from) override
Definition: rpl_rli_pdb.cc:509
void assign_partition_db(Log_event *ev)
Definition: rpl_rli_pdb.cc:2080
const char * get_master_log_name()
Definition: rpl_rli_pdb.cc:633
static void set_nullable_fields(MY_BITMAP *nullable_fields)
Sets bits for columns that are allowed to be NULL.
Definition: rpl_rli_pdb.cc:627
bool set_info_search_keys(Rpl_info_handler *to) override
To search in the slave repositories, each slave info object (mi, rli or worker) should use a primary ...
Definition: rpl_rli_pdb.cc:572
ulong server_version
Definition: rpl_rli_pdb.h:592
ulong id
Definition: rpl_rli_pdb.h:526
void prepare_for_retry(Log_event &event)
Make the necessary changes to both the Slave_worker and current Log_event objects,...
Definition: rpl_rli_pdb.cc:1800
Slave_worker & operator=(const Slave_worker &info)
ulonglong master_log_pos
Definition: rpl_rli_pdb.h:922
int slave_worker_exec_event(Log_event *ev)
MTS worker main routine.
Definition: rpl_rli_pdb.cc:1673
ulong get_master_server_version()
Definition: rpl_rli_pdb.h:904
bool end_group_sets_max_dbs
Definition: rpl_rli_pdb.h:542
ulong events_done
Definition: rpl_rli_pdb.h:536
bool exit_incremented
Definition: rpl_rli_pdb.h:622
volatile ulong last_group_done_index
Definition: rpl_rli_pdb.h:532
Relay_log_info * c_rli
Definition: rpl_rli_pdb.h:518
bool found_commit_order_deadlock()
Return true if replica-preserve-commit-order is enabled and an earlier transaction is waiting for a r...
Definition: rpl_rli_pdb.cc:1790
Relay_log_info * get_c_rli() override
Get coordinator's RLI.
Definition: rpl_rli_pdb.h:872
bool reset_recovery_info()
Clean up a part of Worker info table that is regarded in in gaps collecting at recovery.
Definition: rpl_rli_pdb.cc:614
mysql_mutex_t jobs_lock
Definition: rpl_rli_pdb.h:516
en_running_state
Definition: rpl_rli_pdb.h:593
@ NOT_RUNNING
Definition: rpl_rli_pdb.h:594
@ STOP_ACCEPTED
Definition: rpl_rli_pdb.h:598
@ RUNNING
Definition: rpl_rli_pdb.h:595
@ STOP
Definition: rpl_rli_pdb.h:597
@ ERROR_LEAVING
Definition: rpl_rli_pdb.h:596
void rollback_positions(Slave_job_group *ptr_g)
Definition: rpl_rli_pdb.cc:721
long wq_overrun_cnt
Definition: rpl_rli_pdb.h:562
static const uint * get_table_pk_field_indexes()
Returns an array with the expected column numbers of the primary key fields of the table repository.
Definition: rpl_rli_pdb.cc:2649
void slave_worker_ends_group(Log_event *, int)
Deallocation routine to cancel out few effects of map_db_to_worker().
Definition: rpl_rli_pdb.cc:1145
ulonglong set_master_log_pos(ulong val)
Definition: rpl_rli_pdb.h:637
bool m_flag_positions_committed
This flag indicates whether positions were already modified during the event processing,...
Definition: rpl_rli_pdb.h:931
std::atomic< bool > m_commit_order_deadlock
Definition: rpl_rli_pdb.h:926
bool retry_transaction(uint start_relay_number, my_off_t start_relay_pos, uint end_relay_number, my_off_t end_relay_pos)
It is called after an error happens.
Definition: rpl_rli_pdb.cc:1868
long usage_partition
Definition: rpl_rli_pdb.h:540
void set_gaq_index(ulong val)
Definition: rpl_rli_pdb.h:801
char checkpoint_master_log_name[FN_REFLEN]
Definition: rpl_rli_pdb.h:585
ulonglong last_groups_assigned_index
Definition: rpl_rli_pdb.h:534
void end_info()
Definition: rpl_rli_pdb.cc:464
en_running_state volatile running_status
Definition: rpl_rli_pdb.h:616
ulong wq_empty_waits
Definition: rpl_rli_pdb.h:535
ulonglong checkpoint_relay_log_pos
Definition: rpl_rli_pdb.h:584
bool fd_change_notified
Definition: rpl_rli_pdb.h:559
volatile bool checkpoint_notified
Definition: rpl_rli_pdb.h:545
ulong groups_done
Definition: rpl_rli_pdb.h:537
mysql_cond_t jobs_cond
Definition: rpl_rli_pdb.h:517
static size_t get_number_worker_fields()
Definition: rpl_rli_pdb.cc:623
~Slave_worker() override
Definition: rpl_rli_pdb.cc:288
MY_BITMAP group_executed
Definition: rpl_rli_pdb.h:587
volatile bool master_log_change_notified
Definition: rpl_rli_pdb.h:547
int rli_init_info(bool)
A part of Slave worker initializer that provides a minimum context for MTS recovery.
Definition: rpl_rli_pdb.cc:417
For each client connection we create a separate thread with THD serving as a thread/connection descri...
Definition: sql_lexer_thd.h:36
Relay_log_info * rli_slave
Definition: sql_class.h:1077
System_variables variables
Definition: sql_lexer_thd.h:64
bool in_active_multi_stmt_transaction() const
true if the session is in a multi-statement transaction mode (
Definition: sql_class.h:3229
The class defines a type of queue with a predefined max capacity that is implemented using the circul...
Definition: rpl_rli_pdb.h:267
bool in(size_t i)
Definition: rpl_rli_pdb.h:335
bool empty() const
Definition: rpl_rli_pdb.h:340
static constexpr size_t error_result
Definition: rpl_rli_pdb.h:343
std::atomic< size_t > len
Actual length.
Definition: rpl_rli_pdb.h:289
size_t en_queue(Element_type *item)
return the index where the arg item locates or an error encoded as a value circular_buffer_queue::err...
Definition: rpl_rli_pdb.h:433
size_t capacity
The capacity and maximum length of the queue in terms of element.
Definition: rpl_rli_pdb.h:273
size_t get_length() const
Definition: rpl_rli_pdb.h:339
circular_buffer_queue()
Definition: rpl_rli_pdb.h:302
Element_type * head_queue()
return the value of data member of the head of the queue.
Definition: rpl_rli_pdb.h:329
size_t avail
Its value modulo capacity is index of the element where the next element will be enqueued.
Definition: rpl_rli_pdb.h:279
circular_buffer_queue(size_t max)
Definition: rpl_rli_pdb.h:292
bool de_queue(Element_type *item)
Content of the being dequeued item is copied to the arg-pointer location.
Definition: rpl_rli_pdb.h:454
size_t entry
The head index of the queue.
Definition: rpl_rli_pdb.h:285
bool full() const
Definition: rpl_rli_pdb.h:341
bool inited_queue
Definition: rpl_rli_pdb.h:290
Prealloced_array< Element_type, 1 > m_Q
Definition: rpl_rli_pdb.h:269
bool de_tail(Element_type *item)
Similar to de_queue but extracting happens from the tail side.
Definition: rpl_rli_pdb.h:475
~circular_buffer_queue()=default
unsigned long get_product_version() const
This method is used to find out the version of server that originated the current FD instance.
Definition: control_events.cpp:167
const int64_t SEQ_UNINIT
Uninitialized timestamp value (for either last committed or sequence number).
Definition: binlog_event.h:158
void my_error(int nr, myf MyFlags,...)
Fill in and print a previously registered error message.
Definition: my_error.cc:216
#define MTS_WORKER_UNDEF
Definition: rpl_replica.h:96
unsigned int PSI_mutex_key
Instrumented mutex key.
Definition: psi_mutex_bits.h:52
static constexpr unsigned PSI_INSTRUMENT_ME
Definition: psi_bits.h:43
Binary log event definitions.
Header for compiler-dependent features.
#define DBUG_PRINT(keyword, arglist)
Definition: my_dbug.h:181
#define DBUG_TRACE
Definition: my_dbug.h:146
Some integer typedefs for easier portability.
unsigned long long int ulonglong
Definition: my_inttypes.h:56
ulonglong my_off_t
Definition: my_inttypes.h:72
long long int longlong
Definition: my_inttypes.h:55
#define MYF(v)
Definition: my_inttypes.h:97
Common #defines and includes for file and socket I/O.
#define FN_REFLEN
Definition: my_io.h:83
Definition of the global "loglevel" enumeration.
loglevel
Definition: my_loglevel.h:41
void my_free(void *ptr)
Frees the memory pointed by the ptr.
Definition: my_memory.cc:81
Defines various enable/disable and HAVE_ macros related to the performance schema instrumentation sys...
#define HAVE_PSI_INTERFACE
Definition: my_psi_config.h:39
Instrumentation helpers for conditions.
ABI for instrumented mutexes.
struct MasterPos master_pos
bool load(THD *, const dd::String_type &fname, dd::String_type *buf)
Read an sdi file from disk and store in a buffer.
Definition: sdi_file.cc:308
bool empty(const Histogram &histogram)
Return true if 'histogram' was built on an empty table.
Definition: histogram.h:693
Performance schema instrumentation interface.
Instrumentation helpers for mutexes.
required string event
Definition: replication_group_member_actions.proto:32
enum_mts_parallel_type
Definition: rpl_mta_submode.h:47
TABLE * mts_move_temp_tables_to_thd(THD *, TABLE *)
Relocation of the list of temporary tables to thd->temporary_tables.
Definition: rpl_rli_pdb.cc:819
Slave_worker * get_least_occupied_worker(Relay_log_info *rli, Slave_worker_array *workers, Log_event *ev)
Get the least occupied worker.
Definition: rpl_rli_pdb.cc:1129
bool set_max_updated_index_on_stop(Slave_worker *worker, Slave_job_item *job_item)
This function is called by both coordinator and workers.
Definition: rpl_rli_pdb.cc:172
bool init_hash_workers(Relay_log_info *rli)
Definition: rpl_rli_pdb.cc:752
void destroy_hash_workers(Relay_log_info *)
Definition: rpl_rli_pdb.cc:763
Slave_worker * get_thd_worker(const THD *thd)
Definition: rpl_rli_pdb.h:969
ulong w_rr
Definition: rpl_rli_pdb.cc:81
int slave_worker_exec_job_group(Slave_worker *w, Relay_log_info *rli)
apply one job group.
Definition: rpl_rli_pdb.cc:2449
TABLE * mts_move_temp_table_to_entry(TABLE *, THD *, db_worker_hash_entry *)
Relocating temporary table reference into entry's table list head.
Definition: rpl_rli_pdb.cc:785
bool handle_slave_worker_stop(Slave_worker *worker, Slave_job_item *job_item)
This function is called by both coordinator and workers.
Definition: rpl_rli_pdb.cc:108
Slave_worker * map_db_to_worker(const char *dbname, Relay_log_info *rli, db_worker_hash_entry **ptr_entry, bool need_temp_tables, Slave_worker *w)
The function produces a reference to the struct of a Worker that has been or will be engaged to proce...
Definition: rpl_rli_pdb.cc:914
bool append_item_to_jobs(slave_job_item *job_item, Slave_worker *w, Relay_log_info *rli)
Coordinator enqueues a job item into a Worker private queue.
Definition: rpl_rli_pdb.cc:2105
This struct represents a specification of a GTID for a statement to be executed: either "AUTOMATIC",...
Definition: rpl_gtid.h:3977
Definition: my_bitmap.h:43
Definition: rpl_rli_pdb.h:110
Slave_job_group()=default
my_off_t master_log_pos
Definition: rpl_rli_pdb.h:189
my_off_t checkpoint_log_pos
Definition: rpl_rli_pdb.h:192
Format_description_log_event * new_fd_event
Definition: rpl_rli_pdb.h:231
Slave_job_group(const Slave_job_group &other)
Definition: rpl_rli_pdb.h:117
bool notified
Definition: rpl_rli_pdb.h:201
time_t ts
Definition: rpl_rli_pdb.h:199
longlong last_committed
Definition: rpl_rli_pdb.h:204
std::atomic< int32 > done
Definition: rpl_rli_pdb.h:197
Slave_job_group & operator=(const Slave_job_group &other)
Definition: rpl_rli_pdb.h:142
Slave_worker * worker
Definition: rpl_rli_pdb.h:186
my_off_t checkpoint_relay_log_pos
Definition: rpl_rli_pdb.h:195
ulong worker_id
Definition: rpl_rli_pdb.h:185
uint checkpoint_seqno
Definition: rpl_rli_pdb.h:191
void reset(my_off_t master_pos, ulonglong seqno)
Definition: rpl_rli_pdb.h:236
my_off_t group_master_log_pos
Definition: rpl_rli_pdb.h:172
char * group_master_log_name
Definition: rpl_rli_pdb.h:168
char * checkpoint_log_name
Definition: rpl_rli_pdb.h:193
ulong shifted
Definition: rpl_rli_pdb.h:198
longlong sequence_number
Definition: rpl_rli_pdb.h:205
char * checkpoint_relay_log_name
Definition: rpl_rli_pdb.h:196
char * group_relay_log_name
Definition: rpl_rli_pdb.h:183
ulonglong total_seqno
Definition: rpl_rli_pdb.h:187
my_off_t group_relay_log_pos
Definition: rpl_rli_pdb.h:184
Definition: table.h:1405
Definition: completion_hash.h:35
Legends running throughout the module:
Definition: rpl_rli_pdb.h:74
TABLE *volatile temporary_tables
Definition: rpl_rli_pdb.h:90
uint db_len
Definition: rpl_rli_pdb.h:75
long usage
Definition: rpl_rli_pdb.h:82
const char * db
Definition: rpl_rli_pdb.h:76
Slave_worker * worker
Definition: rpl_rli_pdb.h:77
An instrumented cond structure.
Definition: mysql_cond_bits.h:50
An instrumented mutex structure.
Definition: mysql_mutex_bits.h:50
Definition: rpl_rli.h:82
double seconds()
Definition: task.cc:310
Include file for Sun RPC to compile out of the box.
int n
Definition: xcom_base.cc:509