#ifndef RPL_PARALLEL_H
#define RPL_PARALLEL_H
#include "log_event.h"
struct rpl_parallel;
struct rpl_parallel_entry;
struct rpl_parallel_thread_pool;
extern struct rpl_parallel_thread_pool pool_bkp_for_pfs;
class Relay_log_info;
struct inuse_relaylog;
/*
Structure used to keep track of the parallel replication of a batch of
event-groups that group-committed together on the master.
It is used to ensure that every event group in one batch has reached the
commit stage before the next batch starts executing.
Note the lifetime of this structure:
- It is allocated when the first event in a new batch of group commits
is queued, from the free list rpl_parallel_entry::gco_free_list.
- The gco for the batch currently being queued is owned by
rpl_parallel_entry::current_gco. The gco for a previous batch that has
been fully queued is owned by the gco->prev_gco pointer of the gco for
the following batch.
- The worker thread waits on gco->COND_group_commit_orderer for
rpl_parallel_entry::count_committing_event_groups to reach wait_count
before starting; the first waiter links the gco into the next_gco
pointer of the gco of the previous batch for signalling.
- When an event group reaches the commit stage, it signals the
COND_group_commit_orderer if its gco->next_gco pointer is non-NULL and
rpl_parallel_entry::count_committing_event_groups has reached
gco->next_gco->wait_count.
- The gco lives until all its event groups have completed their commit.
This is detected by rpl_parallel_entry::last_committed_sub_id being
greater than or equal gco->last_sub_id. Once this happens, the gco is
freed. Note that since update of last_committed_sub_id can happen
out-of-order, the thread that frees a given gco can be for any later
event group, not necessarily an event group from the gco being freed.
*/
struct group_commit_orderer {
/* Wakeup condition, used with rpl_parallel_entry::LOCK_parallel_entry. */
mysql_cond_t COND_group_commit_orderer;
uint64 wait_count;
group_commit_orderer *prev_gco;
group_commit_orderer *next_gco;
/*
The sub_id of last event group in the previous GCO.
Only valid if prev_gco != NULL.
*/
uint64 prior_sub_id;
/*
The sub_id of the last event group in this GCO. Only valid when next_gco
is non-NULL.
*/
uint64 last_sub_id;
/*
This flag is set when this GCO has been installed into the next_gco pointer
of the previous GCO.
*/
bool installed;
enum force_switch_bits
{
/*
This flag is set for a GCO in which we have event groups with multiple
different commit_id values from the master. This happens when we
optimistically try to execute in parallel transactions not known to be
conflict-free.
When this flag is set, in case of DDL we need to start a new GCO
regardless of current commit_id, as DDL is not safe to
speculatively apply in parallel with prior event groups.
*/
MULTI_BATCH= 1,
/*
This flag is set for a GCO that contains DDL. If set, it forces
a switch to a new GCO upon seeing a new commit_id, as DDL is not
safe to speculatively replicate in parallel with subsequent
transactions.
*/
FORCE_SWITCH= 2
};
uint8 flags;
#ifndef DBUG_OFF
/*
Flag set when the GCO has been freed and entered the free list, to catch
(in debug) errors in the complex lifetime of this object.
*/
bool gc_done;
#endif
};
struct rpl_parallel_thread {
bool delay_start;
bool running;
bool stop;
bool pause_for_ftwrl;
mysql_mutex_t LOCK_rpl_thread;
mysql_cond_t COND_rpl_thread;
mysql_cond_t COND_rpl_thread_queue;
mysql_cond_t COND_rpl_thread_stop;
struct rpl_parallel_thread *next; /* For free list. */
struct rpl_parallel_thread_pool *pool;
THD *thd;
/*
Who owns the thread, if any (it's a pointer into the
rpl_parallel_entry::rpl_threads array.
*/
struct rpl_parallel_thread **current_owner;
/* The rpl_parallel_entry of the owner. */
rpl_parallel_entry *current_entry;
struct queued_event {
queued_event *next;
/*
queued_event can hold either an event to be executed, or just a binlog
position to be updated without any associated event.
*/
enum queued_event_t {
QUEUED_EVENT,
QUEUED_POS_UPDATE,
QUEUED_MASTER_RESTART
} typ;
union {
Log_event *ev; /* QUEUED_EVENT */
rpl_parallel_entry *entry_for_queued; /* QUEUED_POS_UPDATE and
QUEUED_MASTER_RESTART */
};
rpl_group_info *rgi;
inuse_relaylog *ir;
ulonglong future_event_relay_log_pos;
char event_relay_log_name[FN_REFLEN];
char future_event_master_log_name[FN_REFLEN];
ulonglong event_relay_log_pos;
my_off_t future_event_master_log_pos;
size_t event_size;
} *event_queue, *last_in_queue;
uint64 queued_size;
/* These free lists are protected by LOCK_rpl_thread. */
queued_event *qev_free_list;
rpl_group_info *rgi_free_list;
group_commit_orderer *gco_free_list;
/*
These free lists are local to the thread, so need not be protected by any
lock. They are moved to the global free lists in batches in the function
batch_free(), to reduce LOCK_rpl_thread contention.
The lists are not NULL-terminated (as we do not need to traverse them).
Instead, if they are non-NULL, the loc_XXX_last_ptr_ptr points to the
`next' pointer of the last element, which is used to link into the front
of the global freelists.
*/
queued_event *loc_qev_list, **loc_qev_last_ptr_ptr;
size_t loc_qev_size;
uint64 qev_free_pending;
rpl_group_info *loc_rgi_list, **loc_rgi_last_ptr_ptr;
group_commit_orderer *loc_gco_list, **loc_gco_last_ptr_ptr;
/* These keep track of batch update of inuse_relaylog refcounts. */
inuse_relaylog *accumulated_ir_last;
uint64 accumulated_ir_count;
char channel_name[MAX_CONNECTION_NAME];
uint channel_name_length;
rpl_gtid last_seen_gtid;
int last_error_number;
char last_error_message[MAX_SLAVE_ERRMSG];
ulonglong last_error_timestamp;
ulonglong worker_idle_time;
ulong last_trans_retry_count;
ulonglong start_time;
void start_time_tracker()
{
start_time= microsecond_interval_timer();
}
ulonglong compute_time_lapsed()
{
return (ulonglong)((microsecond_interval_timer() - start_time) / 1000000.0);
}
void add_to_worker_idle_time_and_reset()
{
worker_idle_time+= compute_time_lapsed();
start_time=0;
}
ulonglong get_worker_idle_time()
{
if (start_time)
return (worker_idle_time + compute_time_lapsed());
else
return worker_idle_time;
}
void enqueue(queued_event *qev)
{
if (last_in_queue)
last_in_queue->next= qev;
else
event_queue= qev;
last_in_queue= qev;
queued_size+= qev->event_size;
}
void dequeue1(queued_event *list)
{
DBUG_ASSERT(list == event_queue);
event_queue= last_in_queue= NULL;
}
void dequeue2(size_t dequeue_size)
{
queued_size-= dequeue_size;
}
queued_event *get_qev_common(Log_event *ev, ulonglong event_size);
queued_event *get_qev(Log_event *ev, ulonglong event_size,
Relay_log_info *rli);
queued_event *retry_get_qev(Log_event *ev, queued_event *orig_qev,
const char *relay_log_name,
ulonglong event_pos, ulonglong event_size);
/*
Put a qev on the local free list, to be later released to the global free
list by batch_free().
*/
void loc_free_qev(queued_event *qev);
/*
Release an rgi immediately to the global free list. Requires holding the
LOCK_rpl_thread mutex.
*/
void free_qev(queued_event *qev);
rpl_group_info *get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev,
rpl_parallel_entry *e, ulonglong event_size);
/*
Put an gco on the local free list, to be later released to the global free
list by batch_free().
*/
void loc_free_rgi(rpl_group_info *rgi);
/*
Release an rgi immediately to the global free list. Requires holding the
LOCK_rpl_thread mutex.
*/
void free_rgi(rpl_group_info *rgi);
group_commit_orderer *get_gco(uint64 wait_count, group_commit_orderer *prev,
uint64 first_sub_id);
/*
Put a gco on the local free list, to be later released to the global free
list by batch_free().
*/
void loc_free_gco(group_commit_orderer *gco);
/*
Move all local free lists to the global ones. Requires holding
LOCK_rpl_thread.
*/
void batch_free();
/* Update inuse_relaylog refcounts with what we have accumulated so far. */
void inuse_relaylog_refcount_update();
rpl_parallel_thread();
};
struct pool_bkp_for_pfs{
uint32 count;
bool inited, is_valid;
struct rpl_parallel_thread **rpl_thread_arr;
void init(uint32 thd_count)
{
DBUG_ASSERT(thd_count);
rpl_thread_arr= (rpl_parallel_thread **)
my_malloc(PSI_INSTRUMENT_ME,
thd_count * sizeof(rpl_parallel_thread*),
MYF(MY_WME | MY_ZEROFILL));
for (uint i=0; i