Scheduling work for later by storing a row and polling it is a scheduler you now maintain, and every broker worth using has a delay mechanism already.
use SymfonyComponentMessengerStampDelayStamp;
$bus->dispatch(new SendReminder($orderId), [new DelayStamp(3600000)]);
// AMQP: a delay exchange with a per-message TTL and a dead-letter route
// Doctrine transport: an available_at column and a WHERE clause
// Redis transport: a sorted set keyed on the delivery time
The implementations differ enough that the guarantees do too — the AMQP version depends on a plugin or a dead-letter trick, and the Redis one is a sorted set that a FLUSHDB will take with it. Knowing which mechanism is behind the abstraction matters the first time a delayed message does not arrive. Delays measured in days are usually a sign that a scheduled command reading the database is the better design, since a broker holding a message for a week is a broker you cannot upgrade.