MySQL 26.7.0
Source Code Documentation
rpl_replica_commit_order_manager.h
Go to the documentation of this file.
1/* Copyright (c) 2014, 2026, 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_REPLICA_COMMIT_ORDER_MANAGER
25#define RPL_REPLICA_COMMIT_ORDER_MANAGER
26#include <stddef.h>
27#include <memory>
28#include <vector>
29
30#include "my_dbug.h"
31#include "my_inttypes.h"
34#include "sql/changestreams/apply/commit_order_queue.h" // Commit_order_queue
35#include "sql/changestreams/apply/parallel_worker_context.h" // Parallel_worker_context
36#include "sql/rpl_rli_pdb.h" // get_thd_worker
37
38class THD;
40
41/**
42 On a replica and only on a replica, this class is responsible for
43 committing the applied transactions in the same order as was observed on
44 the source.
45
46 The key components of the commit order management are:
47 - This class, that wraps the commit order management, allowing for API
48 clients to schedule workers for committing, make workers wait for their
49 turn to commit, finish up a scheduled worker task and allow for others
50 to progress.
51 - A commit order queue of type `cs::apply::Commit_order_queue` that holds
52 the sequence by which worker threads should commit and the committing
53 order state for each of the scheduled workers.
54 - The MDL infra-structure which allows for: one worker to wait for
55 another to finish when transactions need to be committed in order;
56 detect deadlocks involving workers waiting on each other for their turn
57 to commit and non-worker threads waiting on meta-data locks held by
58 worker threads.
59
60 The worker thread progress stages relevant to the commit order management
61 are:
62 - REGISTERED: the worker thread as been added to the commit order queue
63 by the coordinator and is allowed to start applying the transaction.
64 - FINISHED APPLYING: the worker thread just finished applying the
65 transaction and checks if it needs to wait for a preceding worker to
66 finish committing.
67 - REQUESTED GRANT: the worker thread waits on the MDL graph for the
68 preceding worker to finish committing.
69 - WAITED: the worker thread finished waiting (either is the first in the
70 commit order queue or has just been grantted permission to continue).
71 - RELEASE NEXT: the worker thread removes itself from the commit order
72 queue, checks if there is any worker waiting on the commit order and
73 releases such worker iff is the preceding worker for the waiting
74 worker.
75 - FINISHED: the worker marks itself as available to take on another
76 transaction to apply.
77
78 The progress of the worker within the stages:
79
80 +-------------------------+
81 | |
82 v |
83 [REGISTERED] |
84 | |
85 v |
86 [FINISHED APPLYING] |
87 | |
88 Worker is |
89 first in the queue? |
90 / \ |
91 yes / \ no |
92 / v |
93 \ [REQUESTED GRANT] |
94 \ / |
95 \ / |
96 \ / |
97 | |
98 v |
99 [WAITED] |
100 | |
101 v |
102 [RELEASE NEXT] |
103 | |
104 v |
105 [FINISHED] |
106 | |
107 +-------------------------+
108
109 Lock-free structures and atomic access to variables are used to manage
110 the commit order queue and to keep the worker stage transitions. This
111 means that there is no atomicity in regards to changes performed in the
112 queue or in the MDL graph within a given stage. Hence, stages maybe
113 skipped and sequentially scheduled worker threads may overlap in the
114 same stage.
115
116 In the context of the following tables, let W1 be a worker that is
117 scheduled to commit before some other worker W2.
118
119 The behavior of W2 (rows) towards W1 (columns) in regards to
120 thread synchronization, based on the stage of each thread:
121+------------+-----------------------------------------------------------------+
122| \ W1 | REGISTERED | FINISHED | REQUESTED | WAITED | RELEASE | FINISHED |
123| W2 \ | | APPLYING | GRANT | | NEXT | |
124+------------+------------+----------+-----------+--------+---------+----------+
125| REGISTERED | | | | | | |
126+------------+------------+----------+-----------+--------+---------+----------+
127| FIN. APPL. | | | | | | |
128+------------+------------+----------+-----------+--------+---------+----------+
129| REQ. GRANT | WAIT | WAIT | WAIT | WAIT | WAIT | |
130+------------+------------+----------+-----------+--------+---------+----------+
131| WAITED | | | | | | |
132+------------+------------+----------+-----------+--------+---------+----------+
133| REL. NEXT | | | | | WAIT | |
134+------------+------------+----------+-----------+--------+---------+----------+
135| FINISHED | | | | | | |
136+------------------------------------------------------------------------------+
137
138 The W2 wait when both worker threads are in the RELEASE NEXT stage
139 happens in the case W2 never entered the REQUESTED GRANT stage. This case
140 may happen if W1 being in RELEASE NEXT removes itself from the queue
141 before W2 enters FINISHED APPLYING and then W2 reaches the RELEASE NEXT
142 stage before W1 exits it:
143
144 [W1] [W2]
145
146 stage = RELEASE NEXT stage = REGISTERED
147 | |
148 v |
149 queue.pop() v
150 | stage = FINISHED_APPLYING
151 | |
152 v v
153 next_worker.stage queue.front() == W2
154 == FINISHED_APPLYING |
155 | |
156 | v
157 | stage = WAITED
158 | |
159 | v
160 | stage = RELEASE NEXT
161 | |
162 v v
163 next_worker.release() queue.pop()
164
165 The commit order queue includes mechanisms that block the popping until
166 the preceding worker finishes the releasing operation. This wait will
167 only be active for the amount of time that takes for W1 to change the
168 values of the MDL graph structures needed to release W2, which is a very
169 small amount of cycles.
170
171 The behavior of W1 (rows) towards W2 (columns)in regards to thread
172 synchronization, based on the stage of each thread:
173+------------+-----------------------------------------------------------------+
174| \ W2 | REGISTERED | FINISHED | REQUESTED | WAITED | RELEASE | FINISHED |
175| W1 \ | | APPLYING | GRANT | | NEXT | |
176+------------+------------+----------+-----------+--------+---------+----------+
177| REGISTERED | | | | | | |
178+------------+------------+----------+-----------+--------+---------+----------+
179| FIN. APPL. | | | | | | |
180+------------+------------+----------+-----------+--------+---------+----------+
181| REQ. GRANT | | | | | | |
182+------------+------------+----------+-----------+--------+---------+----------+
183| WAITED | | | | | | |
184+------------+------------+----------+-----------+--------+---------+----------+
185| REL. NEXT | | GRANT | GRANT | | | |
186+------------+------------+----------+-----------+--------+---------+----------+
187| FINISHED | | | | | | |
188+------------------------------------------------------------------------------+
189
190 The W1 grant to W2 may happen when W2 is either in the FINISHED APPLYING
191 or REQUESTED GRANT stages. W1 must also signal the grant when W2 is in
192 FINISHED APPLYING because W1 has no way to determine if W2 has already
193 evaluated the first element of the queue or not, that is, W1 can't
194 determine if W2 will proceed to the REQUESTED GRANT or to the WAITED
195 stage. Therefore, W1 will signal in both cases.
196
197 */
199 public:
201 Commit_order_manager(uint32 worker_numbers);
202 // Copy logic is not available
205
206 // Copy logic is not available
208
209 std::size_t pool_size() const;
210
211 /**
212 Initializes the MDL context for a given worker in the commit order queue.
213
214 @param worker_id Sequence id of the worker
215 @param thd Session pointer
216 */
217 void init_worker_context(std::size_t worker_id, THD *thd);
218
219 /**
220 Register the worker into commit order queue when coordinator dispatches a
221 transaction to the worker.
222
223 @param[in] worker The worker which the transaction will be dispatched to.
224 */
226
227 private:
228 /**
229 Determines if the worker passed as a parameter must wait on the MDL graph
230 for other workers to commit and, if it must, will wait for it's turn to
231 commit.
232
233 @param worker The worker to determine the commit waiting status for.
234
235 @return false if the worker is ready to commit, true if not.
236 */
238
239 /**
240 Wait for its turn to commit or unregister.
241
242 @param[in] worker The worker which is executing the transaction.
243
244 @retval false All previous transactions succeed, so this transaction can
245 go ahead and commit.
246 @retval true One or more previous transactions rollback, so this
247 transaction should rollback.
248 */
249 bool wait(Parallel_worker_context *worker);
250
251 /**
252 Unregister the thread from the commit order queue and signal
253 the next thread to awake.
254
255 @param[in] worker The worker which is executing the transaction.
256 */
258
259 /**
260 Unregister the transaction from the commit order queue and signal the next
261 one to go ahead.
262
263 @param[in] worker The worker which is executing the transaction.
264 */
265 void finish(Parallel_worker_context *worker);
266
267 /**
268 Reset server_status value of the commit group.
269
270 @param[in] first_thd The first thread of the commit group that needs
271 server_status to be updated.
272 */
273 void reset_server_status(THD *first_thd);
274
275 /**
276 Get rollback status.
277
278 @retval true Transactions in the queue should rollback.
279 @retval false Transactions in the queue shouldn't rollback.
280 */
281 bool get_rollback_status();
282
283 /**
284 Set rollback status to true.
285 */
286 void set_rollback_status();
287
288 /**
289 Unset rollback status to false.
290 */
292
294
295 std::atomic<bool> m_rollback_trx;
296
297 /* It stores order commit order information of all workers. */
299
300 /**
301 Flush record of transactions for all the waiting threads and then
302 awake them from their wait. It also calls gtid_state->update_commit_group()
303 which updates both the THD and the Gtid_state for whole commit group to
304 reflect that the transaction set of transactions has ended.
305
306 @param[in] worker The worker which is executing the transaction.
307 */
309
310 public:
311 /**
312 Determines if the worker holding the commit order wait ticket
313 `wait_for_commit is in deadlock with the MDL context encapsulated in
314 the visitor parameter.
315
316 @param wait_for_commit The wait ticket being held by the worker thread.
317 @param gvisitor The MDL graph visitor to check for deadlocks against.
318
319 @return true if a deadlock has been found and false otherwise.
320 */
321 bool visit_lock_graph(Commit_order_lock_graph &wait_for_commit,
323
324 /**
325 Check if order commit deadlock happens.
326
327 Worker1(trx1) Worker2(trx2)
328 ============= =============
329 ... ...
330 Engine acquires lock A
331 ... Engine acquires lock A(waiting for
332 trx1 to release it.
333 COMMIT(waiting for
334 trx2 to commit first).
335
336 Currently, there are two corner cases can cause the deadlock.
337 - Case 1
338 CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT, INDEX(c2)) ENGINE = InnoDB;
339 INSERT INTO t1 VALUES(1, NULL),(2, 2), (3, NULL), (4, 4), (5, NULL), (6,
340 6)
341
342 INSERT INTO t1 VALUES(7, NULL);
343 DELETE FROM t1 WHERE c2 <= 3;
344
345 - Case 2
346 ANALYZE TABLE t1;
347 INSERT INTO t2 SELECT * FROM mysql.innodb_table_stats
348
349 Since this is not a real lock deadlock, it could not be handled by engine.
350 slave need to handle it separately.
351 Worker1(trx1) Worker2(trx2)
352 ============= =============
353 ... ...
354 Engine acquires lock A
355 ... Engine acquires lock A.
356 1. found trx1 is holding the lock.
357 2. report the lock wait to server code by
358 calling thd_report_row_lock_wait().
359 Then this function is called to check
360 if it causes a order commit deadlock.
361 Report the deadlock to worker1.
362 3. waiting for trx1 to release it.
363 COMMIT(waiting for
364 trx2 to commit first).
365 Found the deadlock flag set
366 by worker2 and then
367 return with ER_LOCK_DEADLOCK.
368
369 Rollback the transaction
370 Get lock A and go ahead.
371 ...
372 Retry the transaction
373
374 To conclude, The transaction A which is waiting for transaction B to commit
375 and is holding a lock which is required by transaction B will be rolled
376 back and try again later.
377
378 @param[in] thd_self The THD object of self session which is acquiring
379 a lock hold by another session.
380 @param[in] thd_wait_for The THD object of a session which is holding
381 a lock being acquired by current session.
382 */
383 static void check_and_report_deadlock(THD *thd_self, THD *thd_wait_for);
384
385 /**
386 Wait for its turn to commit or unregister.
387
388 @param[in] thd The THD object of current thread.
389
390 @retval false All previous transactions succeed, so this transaction can
391 go ahead and commit.
392 @retval true The transaction is marked to rollback.
393 */
394 static bool wait(THD *thd);
395
396 /**
397 Wait for its turn to unregister and signal the next one to go ahead. In case
398 error happens while processing transaction, notify the following transaction
399 to rollback.
400
401 @param[in] thd The THD object of current thread.
402 @param[in] error If true failure in transaction execution
403 */
404 static void wait_and_finish(THD *thd, bool error);
405
406 /**
407 Get transaction rollback status.
408
409 @param[in] thd The THD object of current thread.
410
411 @retval true Current transaction should rollback.
412 @retval false Current transaction shouldn't rollback.
413 */
414 static bool get_rollback_status(THD *thd);
415
416 /**
417 Unregister the thread from the commit order queue and signal
418 the next thread to awake.
419
420 @param[in] thd The THD object of current thread.
421 */
422 static void finish_one(THD *thd);
423
424 /**
425 Determines whether current thread needs to wait for its turn to commit and
426 unregister from the commit order queue. The sql commands ALTER TABLE, DROP
427 TABLE, DROP DB, OPTIMIZE TABLE, ANALYZE TABLE and REPAIR TABLE are allowed
428 to wait for its turn to commit and unregister from the commit order queue as
429 exception in MYSQL_BIN_LOG::ordered_commit(), as these transactions have
430 multiple commits and so not determined if the call is ending transaction.
431
432 @param[in] thd The THD object of current thread.
433
434 @retval true Allow thread to wait for it turn
435 @retval false Do not allow thread to wait for it turn
436 */
438};
439
440/**
441 MDL subgraph inspector class to be used as a ticket to wait on by worker
442 threads. Each worker will create its own instance of this class and will use
443 its own THD MDL_context to search for deadlocks.
444 */
446 public:
447 /**
448 Constructor for the class.
449
450 @param ctx The worker THD MDL context object.
451 @param mngr The Commit_order_manager instance associated with the current
452 channel's Relay_log_info object.
453 @param worker_id The identifier of the worker targeted by this object.
454
455 */
457 uint32 worker_id);
458 /**
459 Default destructor.
460 */
461 virtual ~Commit_order_lock_graph() override = default;
462
463 /**
464 Retrieves the MDL context object associated with the underlying worker.
465
466 @return A pointer to the MDL context associated with the underlying worker
467 thread.
468 */
469 MDL_context *get_ctx() const;
470 /**
471 Retrieves the identifier for the underlying worker thread.
472
473 @return The identifier for the underlying worker thread.
474 */
475 uint32 get_worker_id() const;
476 /**
477 Determines if the underlying worker is in deadlock with the MDL context
478 encapsulated in the visitor parameter.
479
480 @param dvisitor The MDL graph visitor to check for deadlocks against.
481
482 @return true if a deadlock was found and false otherwise,
483 */
484 bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor) override;
485 /**
486 Retrieves the deadlock weight to be used to replace a visitor victim's, when
487 more than one deadlock is found.
488 */
489 uint get_deadlock_weight() const override;
490
491 private:
492 /** The MDL context object associated with the underlying worker. */
494 /**
495 The Commit_order_manager instance associated with the underlying worker
496 channel's Relay_log_info object.
497 */
499 /** The identifier for the underlying worker thread. */
501};
502
503/**
504 Determines whether current thread shall run the procedure here
505 to check whether it waits for its turn (and when its turn comes
506 unregister from the commit order queue).
507
508 The sql commands ALTER TABLE, ANALYZE TABLE, DROP DB, DROP EVENT,
509 DROP FUNCTION, DROP PROCEDURE, DROP TRIGGER, DROP TABLE, DROP VIEW,
510 OPTIMIZE TABLE and REPAIR TABLE shall run this procedure here, as
511 an exception, because these transactions have multiple intermediate
512 commits. Therefore cannot predetermine when the last commit is
513 done.
514
515 @param[in] thd The THD object of current thread.
516
517 @retval false Commit_order_manager object is not initialized
518 @retval true Commit_order_manager object is initialized
519*/
520bool has_commit_order_manager(const THD *thd);
521
522#endif /*RPL_REPLICA_COMMIT_ORDER_MANAGER*/
MDL subgraph inspector class to be used as a ticket to wait on by worker threads.
Definition: rpl_replica_commit_order_manager.h:445
uint get_deadlock_weight() const override
Retrieves the deadlock weight to be used to replace a visitor victim's, when more than one deadlock i...
Definition: rpl_replica_commit_order_manager.cc:721
virtual ~Commit_order_lock_graph() override=default
Default destructor.
MDL_context * get_ctx() const
Retrieves the MDL context object associated with the underlying worker.
Definition: rpl_replica_commit_order_manager.cc:709
Commit_order_lock_graph(MDL_context &ctx, Commit_order_manager &mngr, uint32 worker_id)
Constructor for the class.
Definition: rpl_replica_commit_order_manager.cc:704
bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor) override
Determines if the underlying worker is in deadlock with the MDL context encapsulated in the visitor p...
Definition: rpl_replica_commit_order_manager.cc:715
uint32 get_worker_id() const
Retrieves the identifier for the underlying worker thread.
Definition: rpl_replica_commit_order_manager.cc:711
uint32 m_worker_id
The identifier for the underlying worker thread.
Definition: rpl_replica_commit_order_manager.h:500
MDL_context & m_ctx
The MDL context object associated with the underlying worker.
Definition: rpl_replica_commit_order_manager.h:493
Commit_order_manager & m_mngr
The Commit_order_manager instance associated with the underlying worker channel's Relay_log_info obje...
Definition: rpl_replica_commit_order_manager.h:498
On a replica and only on a replica, this class is responsible for committing the applied transactions...
Definition: rpl_replica_commit_order_manager.h:198
bool wait(Parallel_worker_context *worker)
Wait for its turn to commit or unregister.
Definition: rpl_replica_commit_order_manager.cc:297
Commit_order_manager(uint32 worker_numbers)
Definition: rpl_replica_commit_order_manager.cc:52
static void check_and_report_deadlock(THD *thd_self, THD *thd_wait_for)
Check if order commit deadlock happens.
Definition: rpl_replica_commit_order_manager.cc:502
static void wait_and_finish(THD *thd, bool error)
Wait for its turn to unregister and signal the next one to go ahead.
Definition: rpl_replica_commit_order_manager.cc:559
bool get_rollback_status()
Get rollback status.
Definition: rpl_replica_commit_order_manager.cc:590
static bool wait_for_its_turn_before_flush_stage(THD *thd)
Determines whether current thread needs to wait for its turn to commit and unregister from the commit...
Definition: rpl_replica_commit_order_manager.cc:684
void unset_rollback_status()
Unset rollback status to false.
Definition: rpl_replica_commit_order_manager.cc:596
cs::apply::Commit_order_queue m_workers
Definition: rpl_replica_commit_order_manager.h:298
void report_deadlock(Parallel_worker_context *worker)
Definition: rpl_replica_commit_order_manager.cc:526
Commit_order_manager(const Commit_order_manager &)=delete
bool wait_on_graph(Parallel_worker_context *worker)
Determines if the worker passed as a parameter must wait on the MDL graph for other workers to commit...
Definition: rpl_replica_commit_order_manager.cc:80
void register_trx(Parallel_worker_context *worker)
Register the worker into commit order queue when coordinator dispatches a transaction to the worker.
Definition: rpl_replica_commit_order_manager.cc:66
bool visit_lock_graph(Commit_order_lock_graph &wait_for_commit, MDL_wait_for_graph_visitor &gvisitor)
Determines if the worker holding the commit order wait ticket `wait_for_commit is in deadlock with th...
Definition: rpl_replica_commit_order_manager.cc:630
std::size_t pool_size() const
Definition: rpl_replica_commit_order_manager.cc:59
void flush_engine_and_signal_threads(Parallel_worker_context *worker)
Flush record of transactions for all the waiting threads and then awake them from their wait.
Definition: rpl_replica_commit_order_manager.cc:356
std::atomic< bool > m_rollback_trx
Definition: rpl_replica_commit_order_manager.h:295
void finish_one(Parallel_worker_context *worker)
Unregister the thread from the commit order queue and signal the next thread to awake.
Definition: rpl_replica_commit_order_manager.cc:419
Commit_order_manager & operator=(const Commit_order_manager &)=delete
void set_rollback_status()
Set rollback status to true.
Definition: rpl_replica_commit_order_manager.cc:594
void init_worker_context(std::size_t worker_id, THD *thd)
Initializes the MDL context for a given worker in the commit order queue.
Definition: rpl_replica_commit_order_manager.cc:61
void reset_server_status(THD *first_thd)
Reset server_status value of the commit group.
Definition: rpl_replica_commit_order_manager.cc:411
void finish(Parallel_worker_context *worker)
Unregister the transaction from the commit order queue and signal the next one to go ahead.
Definition: rpl_replica_commit_order_manager.cc:468
Context of the owner of metadata locks.
Definition: mdl.h:1415
An abstract class for inspection of a connected subgraph of the wait-for graph.
Definition: mdl.h:923
Abstract class representing an edge in the waiters graph to be traversed by deadlock detection algori...
Definition: mdl.h:949
For each client connection we create a separate thread with THD serving as a thread/connection descri...
Definition: sql_lexer_thd.h:36
Queue to maintain the ordered sequence of workers waiting for commit.
Definition: commit_order_queue.h:55
Class representing the interface for parallel worker context.
Definition: parallel_worker_context.h:47
Some integer typedefs for easier portability.
uint32_t uint32
Definition: my_inttypes.h:67
Instrumentation helpers for conditions.
ABI for instrumented mutexes.
Commit_order_manager::Parallel_worker_context Parallel_worker_context
Definition: rpl_replica_commit_order_manager.cc:50
bool has_commit_order_manager(const THD *thd)
Determines whether current thread shall run the procedure here to check whether it waits for its turn...
Definition: rpl_replica_commit_order_manager.cc:679