powerful, but with clear limits
MySQL ships a built-in mechanism called the Event Scheduler for running recurring SQL statements directly inside the database server, with no external cron daemon involved. This article walks through the CREATE EVENT syntax and the available scheduling options, draws a clear line between the Event Scheduler and Magento's own cron system plus OS-level cron, and explains how to monitor failed events before they stay silently disabled.
Table of Contents
- 1. What the MySQL Event Scheduler is and how to enable it
- 2. CREATE EVENT syntax and scheduling options at a glance
- 3. Practical example: a recurring cleanup job for sessions and logs
- 4. How this differs from Magento cron and application cron: when DB-internal events make sense
- 5. When events are NOT the right choice
- 6. Monitoring: information_schema.EVENTS and SHOW EVENTS
- 7. Error handling: what happens when an event fails
- 8. The Event Scheduler in replication environments and during failover
- 9. Security considerations: DEFINER and best practices for production use
- 10. Summary
- 11. FAQ
1. What the MySQL Event Scheduler is and how to enable it
The Event Scheduler is an internal thread in MySQL that runs scheduled tasks, called events, at their configured times, with no need for an external cron daemon or an application outside the database. The actual execution logic of every event is an arbitrary SQL statement or a compound statement block, written much like a stored procedure, and it runs with the full privileges of its definer.
By default the Event Scheduler is disabled and needs to be turned on through the system variable event_scheduler, and for that setting to survive restarts it should additionally be placed in the configuration file. With the scheduler disabled, defined events still exist in the metadata but simply never run, which is a common, easy-to-overlook pitfall after a server rebuild.
-- Enable the scheduler at runtime
SET GLOBAL event_scheduler = ON;
-- Anchor it permanently in the configuration
-- /etc/mysql/mysql.conf.d/mysqld.cnf
-- [mysqld]
-- event_scheduler = ON
-- Check the status
SHOW VARIABLES LIKE 'event_scheduler';
2. CREATE EVENT syntax and scheduling options at a glance
A recurring event is defined through ON SCHEDULE EVERY with an interval in seconds, minutes, hours, days, or weeks, optionally supplemented with a start and end time via STARTS and ENDS. For a one-off task that runs at a fixed point in time, ON SCHEDULE AT with a concrete timestamp is the right choice instead, useful for something like a one-time migration follow-up job.
The ON COMPLETION PRESERVE clause controls what happens after the last run of a one-off event: without it, the event is automatically removed from the system once it completes; with it, the definition stays intact and can be inspected afterward through ALTER EVENT or reactivated for another run.
-- Recurring event, hourly starting now
CREATE EVENT ev_cleanup_expired_carts
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP
COMMENT 'Removes stale abandoned cart rows older than 30 days'
DO
DELETE FROM quote WHERE is_active = 0 AND updated_at < NOW() - INTERVAL 30 DAY;
-- One-off event at a fixed point in time, definition kept afterward
CREATE EVENT ev_migration_followup
ON SCHEDULE AT '2026-09-01 02:00:00'
ON COMPLETION PRESERVE
DO
UPDATE catalog_product_flat_1 SET needs_reindex = 0;
3. Practical example: a recurring cleanup job for sessions and logs
An obvious use case for events is pure data housekeeping tightly bound to a single table that requires no external logic, such as removing expired session data or old rows from log tables like report_event. Tasks like these can be expressed entirely within an event, with the application never even needing to know the job exists.
What matters for production-grade cleanup events is capping the number of rows deleted per run, for example via LIMIT combined with more frequent execution, rather than a single, potentially very large delete operation that would hold long locks on busy tables and noticeably impact regular application traffic.
4. How this differs from Magento cron and application cron: when DB-internal events make sense
Magento already ships a powerful, application-integrated scheduling mechanism through the cron_schedule table and its own cron system, with access to the full object system, event observers, and external services. A MySQL event, by contrast, only knows SQL and runs entirely outside the PHP runtime, with no access to Magento's cache invalidation, event dispatching, or external APIs.
DB-internal events make sense, then, when a task is purely data-related, needs no interaction with the application layer, and should keep running reliably even if the application server is fully stopped, such as a pure database-level housekeeping task. As soon as a task touches Magento objects, caches, sending email, or any other application layer, it belongs firmly in Magento's own cron system instead of a MySQL event.
5. When events are NOT the right choice
Complex business logic that coordinates several systems, for instance a job that reads order data from MySQL, pushes it to an external ERP system, and then updates the order status back in Magento, clearly does not belong in a MySQL event. An event can neither make HTTP calls nor touch the file system nor send email, and attempting to work around that with sys_exec-style extensions opens up serious security holes.
Events are equally unsuitable for tasks that need retry logic, differentiated error handling, or observability through logging and metrics, the kind a modern job queue or Magento's own cron system already provides. A MySQL event simply offers no suitable tooling for that and should stay limited to plain, self-contained data operations.
6. Monitoring: information_schema.EVENTS and SHOW EVENTS
The status of every defined event, including its last execution time, can be queried systematically through the information_schema.EVENTS view, which fits neatly into automated monitoring. The LAST_EXECUTED column shows when an event last ran, while the STATUS column distinguishes between ENABLED, DISABLED, and SLAVESIDE_DISABLED.
A sensible monitoring check compares, for every expected recurring event, the last execution time against the configured interval and raises an alert as soon as an event has not run noticeably longer than expected, regardless of whether the cause is a disabled event, a stopped scheduler, or some other problem.
-- Overview of every defined event with its last execution time
SELECT EVENT_NAME, STATUS, LAST_EXECUTED, INTERVAL_VALUE, INTERVAL_FIELD
FROM information_schema.EVENTS
WHERE EVENT_SCHEMA = 'magento';
-- Shorthand via SHOW EVENTS
SHOW EVENTS FROM magento;
7. Error handling: what happens when an event fails
If the statement inside an event throws an unhandled error, for instance because a referenced table was renamed in the meantime, MySQL automatically disables the affected event by setting its status to DISABLED and writes a corresponding entry to the error log. No automatic retry happens at all, and without active monitoring, an event disabled this way can sit inactive, unnoticed, for months.
For robust events, an explicit DECLARE ... HANDLER block inside a compound statement is worth adding, catching expected errors, writing them to a dedicated log table, and thereby actively keeping the event alive instead of letting MySQL disable it automatically. That log table should in turn be part of regular monitoring, so failed individual runs surface promptly.
8. The Event Scheduler in replication environments and during failover
Event definitions replicate to every replica through standard replication, but by default they only ever execute on the source server, while replicas automatically show a status of SLAVESIDE_DISABLED. That prevents the same data change from running simultaneously on multiple servers and then being applied again through replication, which would cause data inconsistency.
After a failover, when a former replica gets promoted to the new primary, the Event Scheduler needs to be actively switched on on the new primary, since it was typically disabled there. This step is easy to forget in failover runbooks, since it is not part of the usual replication topology switch and needs to be handled separately, either manually or through an automation script.
9. Security considerations: DEFINER and best practices for production use
Every event runs with the full privileges of its definer, meaning the database account that was the current user at the time the event was created, regardless of which account is actually connected to the database later on. An event accidentally created under the root account or another highly privileged user permanently executes its statements with those broad privileges, even if the actual task would only ever need minimal privileges.
For a clean production setup, a dedicated database account, restricted to exactly the tables and operations actually needed, is recommended as the definer for every event, combined with a meaningful COMMENT on each one documenting its purpose and ownership. Paired with regular review through information_schema.EVENTS, that produces a tool that plays to its strengths on clearly bounded, purely data-related tasks, without turning into a confusing substitute for a full-fledged job scheduling system.
-- Create an event under a dedicated, restricted account
CREATE DEFINER='event_runner'@'localhost' EVENT ev_cleanup_expired_carts
ON SCHEDULE EVERY 1 HOUR
COMMENT 'Removes stale abandoned cart rows, owner: platform team'
DO
DELETE FROM quote WHERE is_active = 0 AND updated_at < NOW() - INTERVAL 30 DAY;
-- Check the definer of an existing event
SELECT EVENT_NAME, DEFINER FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'magento';
| CREATE EVENT Option | Meaning | Example | Note |
|---|---|---|---|
| ON SCHEDULE EVERY | Defines a recurring interval | EVERY 1 HOUR | Can be combined with STARTS/ENDS for a bounded lifetime |
| ON SCHEDULE AT | Single point-in-time execution | AT '2026-09-01 02:00:00' | Suited to one-off follow-up jobs, e.g. after migrations |
| ON COMPLETION PRESERVE | Keeps the definition after the last run | ON COMPLETION PRESERVE | Without it, a one-off event is deleted afterward |
| ENABLE / DISABLE | Enables or disables an event | ALTER EVENT ev_x DISABLE | Useful for planned maintenance windows without deleting the definition |
| COMMENT | Documentation attached to the event | COMMENT 'Purpose of the job' | Makes later auditing via information_schema.EVENTS easier |
Mironsoft
Database performance, index tuning, and Magento DB optimization
A Magento shop suffering from slow database queries?
We analyze MySQL databases for performance bottlenecks, optimize indexes and queries with purpose, and set up backup and replication strategies that actually work when it counts.
Performance Audit
Systematically investigate the slow query log and explain plans for bottlenecks.
Index Optimization
Build indexes with purpose for the shop's actual query load.
Backup Strategy
Set up reliable backup and restore processes for production Magento databases.
10. Summary
The MySQL Event Scheduler at a Glance
Enabling
event_scheduler = ON must be set explicitly and anchored permanently in the configuration.
Right use case
Suited to plain, self-contained data housekeeping, not to business logic that spans system boundaries.
Failure behavior
An unhandled error disables the event automatically, with no retry and no default notification.
Replication
Events only run on the primary server and must be actively reactivated there after a failover.