I've read here that:
Heroku restarts dynos at least once per day, in addition to being restarted as needed for the overall health of the system and your app
My web app is a game and the game state is held in-memory. When the dyno restarts, all the memory is lost and all active games disappear, randomly booting all players out.
What is the canonical way to handle this? Save all server state to redis in the seconds I have before a forced shutdown? How do servers handle this who have thousands of games running at once?
Sounds like you want to intercept the Dyno SIGTERM notification after which you have 30 seconds to gracefully terminate your application.
Depending on your language of choice this can be either very simple or rather complicated, but effectively you really need to handle that SIGTERM event in order to do whatever you need to gracefully reboot.
If you are looking for notifications of events, you are looking for Dyno Webhook Events which are currently in public preview.
Dyno will call your webhook in response to various lifecycle changes of your Dyno including scaling and app restarts.
Note that there is no way to prevent a Dyno being restarted, both of these events (the SIGTERM signal and webhook call) are simply notifications which inform you of changes.
Edit: According to the Automatic Dyno Restarts documentation:
If you continually make changes to your application without a 24 hour gap, you won’t see cycling at all.
So I suppose to "prevent" a Dyno ever cycling, you'd need to deploy to it on a (less than 24-hour) schedule