MySQL supports a Debug Sync Facility, which - in spite of the
"debug" in its name - is completely independent from the DBUG
facility (except that it uses DBUG to trace its operation, if DBUG
is also configured in the server). The documentation here is
derived from comments in the
With a properly configured server (see Section 24.6.2, “Debug Sync Activation/Deactivation”), this facility allows placement of synchronization points in the server code by using the DEBUG_SYNC macro:
open_tables(...) DEBUG_SYNC(thd, "after_open_tables"); lock_tables(...)
When activated, a synchronization point can
Emit a signal and/or
Wait for a signal
An event identified by a name that a signal thread uses to notify the wait thread that waits on this event. When the signal thread notifies the wait thread, the signal name is copied into global list and the wait thread is signalled to wake up and proceed with further processing.
- emit a signal
Signal thread wakes up wait thread or multiple wait threads that shall wait for the signal identified by a signal name. This signal thread copies the signal name into a global list and broadcasts the event which wakes the threads that wait for this event.
- wait for a signal
Wait on a event indentified by the signal name until the signal thread signals the event.
By default, all synchronization points are inactive. They do nothing (except burn a couple of CPU cycles for checking if they are active).
A synchronization point becomes active when an action is requested for it. To do so, assign a value to the DEBUG_SYNC system variable:
SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
This activates the synchronization point named 'after_open_tables'. The activation requests the synchronization point to emit the signal 'opened' and wait for another thread to emit the signal 'flushed' when the thread's execution runs through the synchronization point.
For every synchronization point there can be one action per thread only. Every thread can request multiple actions, but only one per synchronization point. In other words, a thread can activate multiple synchronization points.
Here is an example how to activate and use the synchronization points:
--connection conn1 SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed'; send INSERT INTO t1 VALUES(1); --connection conn2 SET DEBUG_SYNC= 'now WAIT_FOR opened'; SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed'; FLUSH TABLE t1;
When conn1 runs through the INSERT statement, it hits the synchronization point 'after_open_tables'. It notices that it is active and executes its action. It emits the signal 'opened' and waits for another thread to emit the signal 'flushed'.
conn2 waits immediately at the special synchronization point 'now' for another thread to emit the 'opened' signal.
If conn1 signals 'opened' before conn2 reaches 'now', conn2 will find the 'opened' signal. The wait thread shall not wait in this case.
When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets conn1 awake and clears the 'flushed' signal from the global list. In case the 'flushed' signal is to be notified to multiple wait threads, an attribute NO_CLEAR_EVENT need to be specified with the WAIT_FOR in addition to signal the name as:
SET DEBUG_SYNC= 'WAIT_FOR flushed NO_CLEAR_EVENT';
It is up to the user to ensure once when all the wait threads have processed the 'flushed' signal to clear/deactivate the signal using the RESET action of DEBUG_SYNC accordingly.
Normally the activation of a synchronization point is cleared when it has been executed. Sometimes it is necessary to keep the synchronization point active for another execution. You can add an execute count to the action:
SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
This sets the synchronization point's activation counter to 3. Each execution decrements the counter. After the third execution the synchronization point becomes inactive.
One of the primary goals of this facility is to eliminate sleeps from the test suite. In most cases it should be possible to rewrite test cases so that they do not need to sleep. (Note that Debug Sync can synchronize only multiple threads within a single process. It cannot synchronize multiple processes.) However, to support test development, and as a last resort, synchronization point waiting times out. There is a default timeout, but it can be overridden:
SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
TIMEOUT 0 is special: If the signal is not present, the wait times out immediately.
If a wait timeout occurs (even on TIMEOUT 0), a warning is generated so that it shows up in the test result.
You can throw an error message and kill the query when a synchronization point is hit a certain number of times:
SET DEBUG_SYNC= 'name HIT_LIMIT 3';
Or combine it with signal and/or wait:
SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
Here the first two hits emit the signal, the third hit returns the error message and kills the query.
For cases where you are not sure that an action is taken and thus cleared in any case, you can forcibly clear (deactivate) a synchronization point:
SET DEBUG_SYNC= 'name CLEAR';
If you want to clear all actions and clear the global signal, use:
SET DEBUG_SYNC= 'RESET';
This is the only way to reset the global signal to an empty string.
For testing of the facility itself you can execute a synchronization point just as if it had been hit:
SET DEBUG_SYNC= 'name TEST';