Given an event that calls a stored procedure, what's the best practice to ensure that only one instance of the procedure is running at a time? Specifically in cases where the procedure may sometimes take longer to run than it takes for the event to tick over.
Let's take the following fabricated example, an event that takes 1 second to tick over and a procedure that takes 5 seconds to execute:
DELIMITER ;;
CREATE PROCEDURE `P_wait`()
BEGIN
SELECT SLEEP(5);
END;;
DELIMITER ;
DROP EVENT IF EXISTS `E_wait`;
DELIMITER ;;
CREATE EVENT `E_wait`
ON SCHEDULE
EVERY 1 SECOND
DO
BEGIN
CALL `P_wait`(); //proc_call
END;;
DELIMITER ;
As you would expect, when the event is running you will see 5 instances of SLEEP() in the PROCESSLIST:
mysql> SHOW PROCESSLIST;
+-------+------+-----------+-------+---------+------+-------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-------+------+-----------+-------+---------+------+-------------+------------------+
| 27045 | root | localhost | temp | Query | 0 | NULL | SHOW PROCESSLIST |
| 27069 | root | localhost | temp | Connect | 4 | User sleep | SELECT SLEEP(5) |
| 27070 | root | localhost | temp | Connect | 3 | User sleep | SELECT SLEEP(5) |
| 27072 | root | localhost | temp | Connect | 2 | User sleep | SELECT SLEEP(5) |
| 27073 | root | localhost | temp | Connect | 1 | User sleep | SELECT SLEEP(5) |
| 27074 | root | localhost | temp | Connect | 0 | User sleep | SELECT SLEEP(5) |
+-------+------+-----------+-------+---------+------+-------------+------------------+
While it's not the case in this example, you can see how if the procedure was blocking (like if it contained a transaction with a SELECT ... FOR UPDATE Statement) this would quickly escalate into problems.
What's the best way of ensuring that when E_wait ticks over each second, that if an instance of P_wait is still running, it won't get called again? I only ever want at most one instance of P_wait running at a time. What's the Best Practice here?
Basically what would I need to put in place of /*PROCEDURE_NOT_RUNNING*/ if I modified the Event as follows:
...
BEGIN
IF /*PROCEDURE_NOT_RUNNING*/ THEN
CALL wait_test(); //proc_call
END IF;
END;;
...
E_wait at the beginning of P_wait and re-enabling it at the end) is not an option, since that could result in the event not running at all if the server restarts while it is disabled.To run the above event, and only ever see at most one instance of SELECT SLEEP(5) in SHOW PROCESSLIST.
It has been suggested to use some of the built-in lock functions for this problem. I will give that a shot and will update if I've managed to solve the problem.
EDIT:
I achieved the desired result after modifying the Event to include a lock:
DROP EVENT IF EXISTS `E_wait`;
DELIMITER ;;
CREATE EVENT `E_wait`
ON SCHEDULE
EVERY 1 SECOND
DO
BEGIN
SELECT GET_LOCK('temp.E_wait', 0) INTO @got_lock;
IF @got_lock = 1 THEN
CALL `P_wait`();
SELECT RELEASE_LOCK('temp.E_wait') INTO @discard;
END IF;
END;;
DELIMITER ;
The answer to this stackoverflow question helped.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With