Chapter 10 · Stored Programs, Views, Triggers, Events, and Server-Side Logic

Event Scheduler, Recurring Maintenance Jobs, Failure Handling, and Observability

Operate MySQL scheduled events as server-side jobs: verify scheduler state, privileges and time-zone semantics, create a safe short-lived event, prove execution from metadata and side effects, diagnose failure, and cleanly disable/drop it.

Beginner → Intermediate125–165 minevent-scheduler labMySQL Community Server 8.4.10 LTS · InnoDB · free local labevents + schedulingLast reviewed: August 2026

Learning outcomes

The Event Scheduler is MySQL's in-server clock-driven job runner. It can be convenient for small database-local tasks, but a job that runs “somewhere inside MySQL” can be difficult to own unless scheduling, time zone, definer privileges, execution evidence, failure logging, and cleanup are all explicit.

01

Inspect event_scheduler state and distinguish ON, OFF, and startup-disabled DISABLED behavior.

02

Create one-time and recurring events with an explicit definer, session time-zone awareness, and appropriate EVENT privilege.

03

Prove event execution using side-effect rows, INFORMATION_SCHEMA.EVENTS/SHOW EVENTS metadata, and error-log expectations.

04

Diagnose an event that exists but does not successfully perform its body because of scheduler or privilege state.

05

Compare in-server events with external schedulers and choose an ownership/observability boundary deliberately.

A realistic problem: a cleanup job that “sometimes did not run”

ServiceHub wants to record a lightweight maintenance heartbeat every few seconds in a disposable lab. In production the real job might prune a staging table or roll up small metadata. A developer creates an event and assumes that seeing it in SHOW EVENTS proves the task is running. It does not.

An event is a schema object with a schedule and SQL body. The Event Scheduler is the server thread that executes enabled events. An event can exist while the scheduler is off. Event execution uses its definer privileges, and scheduled time expressions are interpreted using the session time_zone when the event is created.

Inspect scheduler state before creating anything

sql · scheduler, timezone, and process evidence
SELECT @@GLOBAL.event_scheduler AS event_scheduler,       @@GLOBAL.time_zone AS global_time_zone,       @@SESSION.time_zone AS session_time_zone,       @@system_time_zone AS system_time_zone;SHOW VARIABLES LIKE 'event_scheduler';SHOW PROCESSLIST;

Current MySQL 8.4 documentation lists ON as the default, but never assume the actual server uses the default. OFF means events are not executed until an authorized administrator turns the scheduler on. DISABLED means it was disabled at startup and cannot simply be switched on dynamically; change the startup configuration and restart the disposable server if you need the feature.

Privilege boundary

Changing the global scheduler variable is an administrative operation requiring sufficient system-variable privilege. Creating or altering an event is governed by the EVENT privilege at global or schema scope. Application accounts should not receive either simply because one maintenance event exists.

Use an explicit session time zone for reproducible scheduling

Set the owner session to UTC before creating the lab event. MySQL records the event's time-zone context so future server time-zone changes do not silently reinterpret the schedule.

sql · create a short-lived recurring event
USE servicehub_logic_lab;SET SESSION time_zone = '+00:00';DROP EVENT IF EXISTS e_ch10_heartbeat;CREATE EVENT e_ch10_heartbeatON SCHEDULE EVERY 10 SECOND  STARTS CURRENT_TIMESTAMP + INTERVAL 5 SECOND  ENDS CURRENT_TIMESTAMP + INTERVAL 45 SECONDON COMPLETION PRESERVEENABLECOMMENT 'Chapter 10 disposable heartbeat'DO  INSERT INTO maintenance_runs(job_name,executed_at,execution_user,note)  VALUES('ch10_heartbeat',CURRENT_TIMESTAMP(6),CURRENT_USER(),'event fired');SHOW CREATE EVENT e_ch10_heartbeat\GSHOW EVENTS FROM servicehub_logic_lab LIKE 'e_ch10_heartbeat';

The event is preserved after its end time so you can inspect final metadata. Without ON COMPLETION PRESERVE, an expired one-time/default nonpreserved event can disappear, which makes postmortem diagnosis confusing for beginners.

Prove execution with three kinds of evidence

Wait long enough for at least one scheduled time, then inspect both metadata and side effects:

sql · event metadata and side-effect evidence
SELECT EVENT_NAME,DEFINER,TIME_ZONE,EVENT_TYPE,INTERVAL_VALUE,INTERVAL_FIELD,       STARTS,ENDS,STATUS,LAST_EXECUTEDFROM INFORMATION_SCHEMA.EVENTSWHERE EVENT_SCHEMA='servicehub_logic_lab'  AND EVENT_NAME='e_ch10_heartbeat';SELECT run_id,job_name,executed_at,execution_user,noteFROM maintenance_runsWHERE job_name='ch10_heartbeat'ORDER BY run_id;SHOW EVENTS FROM servicehub_logic_lab;

A non-NULL LAST_EXECUTED and inserted rows are stronger evidence than STATUS='ENABLED' alone. Even then, the rows prove only that this body reached its insert statement; a complex event needs job-specific success/failure evidence.

Failure case 1: scheduler OFF

If your local scheduler is currently ON, you may demonstrate this only if you have administrative privilege and first record the original value. Do not do this on a shared server.

sql · optional local-only scheduler-off experiment
-- ADMIN-ONLY, DISPOSABLE INSTANCE ONLY.SELECT @@GLOBAL.event_scheduler AS original_scheduler_state;SET GLOBAL event_scheduler = OFF;-- The event metadata still exists, but scheduled executions stop.SELECT EVENT_NAME,STATUS,LAST_EXECUTEDFROM INFORMATION_SCHEMA.EVENTSWHERE EVENT_SCHEMA='servicehub_logic_lab'  AND EVENT_NAME='e_ch10_heartbeat';-- Restore the original operational policy when finished.SET GLOBAL event_scheduler = ON;

This experiment separates object existence from scheduler execution. If your original value was OFF, restore OFF rather than blindly leaving it ON. If the server reports DISABLED, do not attempt the runtime SET.

Failure case 2: the event definer lacks privileges

MySQL allows an event definition to exist even when its body later requires privileges the definer does not possess. The execution fails at runtime, and errors/warnings from event execution are written to the server error log. Use a disposable account to demonstrate the principle without weakening your real owner account.

sql · create a deliberately underprivileged event owner
-- Admin session.DROP USER IF EXISTS 'event_limited'@'127.0.0.1';CREATE USER 'event_limited'@'127.0.0.1'  IDENTIFIED BY '<Disposable-Lab-Event-2026!>';GRANT EVENT ON servicehub_logic_lab.*TO 'event_limited'@'127.0.0.1';-- Connect as event_limited using -h 127.0.0.1, then:USE servicehub_logic_lab;SET SESSION time_zone='+00:00';CREATE EVENT e_ch10_expected_failureON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 10 SECONDON COMPLETION PRESERVEDO  INSERT INTO maintenance_runs(job_name,executed_at,execution_user,note)  VALUES('expected_failure',CURRENT_TIMESTAMP(6),CURRENT_USER(),'should lack INSERT');

The account has EVENT but no INSERT on maintenance_runs. Creation can succeed, while execution later fails under the event's definer privileges. Inspect INFORMATION_SCHEMA.EVENTS and your local MySQL error log. The exact log path depends on Chapter 02 configuration, so discover it rather than guessing.

sql · discover error-log configuration and verify no side effect
SHOW VARIABLES LIKE 'log_error';SELECT EVENT_NAME,DEFINER,STATUS,LAST_EXECUTEDFROM INFORMATION_SCHEMA.EVENTSWHERE EVENT_SCHEMA='servicehub_logic_lab'  AND EVENT_NAME='e_ch10_expected_failure';SELECT COUNT(*) AS unexpected_rowsFROM maintenance_runsWHERE job_name='expected_failure';

The repair is not “grant ALL.” Either give the dedicated event definer the one permission its job requires or redesign the task under an external scheduler/service identity. For this demonstration, clean the failing object up instead of broadening privileges.

Recurring-job overlap is a real design problem

If an event takes longer than its recurrence interval, multiple instances can overlap. MySQL's event documentation explicitly warns about this. A safe recurring maintenance design therefore needs idempotency or a serialization mechanism such as advisory locking (GET_LOCK()) or a row/table coordination strategy, plus bounded work per run.

Do not build a hidden queue

The Event Scheduler is not a distributed workflow orchestrator. It has no rich DAG ownership model, cross-service retry policy, deployment UI, or external alerting semantics. Once the job needs those capabilities, an external scheduler is usually the clearer boundary.

Disable and drop cleanly

sql · cleanup the Chapter 10 events
ALTER EVENT e_ch10_heartbeat DISABLE;SHOW EVENTS FROM servicehub_logic_lab LIKE 'e_ch10_heartbeat';DROP EVENT e_ch10_heartbeat;DROP EVENT IF EXISTS e_ch10_expected_failure;-- Admin cleanup after disconnecting event_limited:DROP USER IF EXISTS 'event_limited'@'127.0.0.1';SELECT COUNT(*) AS heartbeat_rowsFROM maintenance_runs WHERE job_name='ch10_heartbeat';

Keep the heartbeat rows as evidence for Lesson 5, or delete them only after you have recorded the lab result.

Event Scheduler versus external scheduler

CriterionMySQL Event SchedulerExternal scheduler / service
Data localityExcellent for small SQL-local workRequires a database connection but can coordinate multiple systems
Atomic SQL workCan execute database statements directlyCan still use explicit transactions through a driver
Deployment ownershipDatabase schema/DBA workflowApplication/platform workflow
ObservabilityMetadata + server error log + custom job tablesUsually richer logs, metrics, alerts, retries, dashboards
Cross-service orchestrationPoor fitUsually the correct boundary
Failover/topology awarenessMust be designed with MySQL topology semanticsCan coordinate with service discovery/orchestration but adds external dependencies

Hands-on lab acceptance checklist

  • You recorded the actual event_scheduler state instead of assuming the default.
  • The successful event records UTC schedule metadata and produces observable heartbeat rows.
  • You can explain why STATUS='ENABLED' does not prove successful execution.
  • The underprivileged definer case creates no maintenance row and points you to error-log evidence.
  • All disposable events and the limited account are disabled/dropped at the end.

Knowledge check

  1. What three values can event_scheduler have?
  2. What privilege creates/alters/drops events in a schema?
  3. Whose privileges execute an event body?
  4. Which time zone interprets CREATE EVENT schedule times?
  5. Where does MySQL report event executions that terminate with errors or warnings?
Reveal answers
  1. ON, OFF, and DISABLED. DISABLED is a startup-level disabled state, not equivalent to simply setting OFF.
  2. EVENT at schema or global scope.
  3. The event definer account.
  4. The current session time_zone at creation; MySQL records that event time-zone context.
  5. The MySQL server error log, in addition to event metadata and any custom job evidence you design.

Production judgment and next step

Use events for small, database-local recurring work with a dedicated definer, explicit schedule/time zone, bounded runtime, idempotency, and observable success/failure. Prefer an external scheduler when jobs need cross-system coordination, sophisticated retry/alerting, independent scaling, or application-team ownership.

Lesson 5 brings the chapter together by deciding which logic belongs in views, routines, triggers, events, or application code.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.