Hmm, that doesn’t seem to be the “OTP Way” though?
How/Where do you run this code? How will you deal with failure? Retries? Ensuring that the rest of the app can start up around it? It may be an umbrella app with various dependencies that the OS author doesn’t know about ahead of time?
I’m struggling to see how you would implement your suggestion in various use cases. eg imagine I compile some app to be something like a desktop app for a user. This is a normal end user, who is expecting to double click and go. Now the app is upgraded (brew upgrade, etc) and they expect to be able to double click and go again. Where will this deploy logic fit?
Similarly, I have an embedded app, running on a box which is in the middle of a field that is a 10 hour journey away from any engineer who can visit it. It gets a remote upgrade (could be via a USB stick, remote net connection, whatever). It reboots and needs to deal with upgrading itself from whatever previous position it found itself in. For extra fun that app will be an umbrella app with various optional modules, eg we might have a choice of two control UIs, one is a basic web UI, simple DB used for storing config. The other is a sophisticated IOT device which needs a completely different DB to store all it’s config. We don’t necessarily know ahead of time which the box has installed, and of course the needs or quantity of DBs may change dynamically through time, so I can’t assume that the OS level knows enough to run the migrations
So yes, it’s all just code… So, agreed, it makes little practical difference if the OS pre-runs app.migrate, then boots the app, or if it just boots the app and the app runs app.migrate as part of startup. However, it may make a big speed difference on a little box which is upgraded infrequently, but needs to boot rapidly. So it’s impractical to run a separate migrate step on each boot. OK, so now we need a boot step which runs the migrations and in turn cascades those calls out to an as yet unknown number of apps within the umbrella, ie I need an app which is composable at runtime and doesn’t necessarily know about all the migrations of each of it’s subcomponents. So sure, I can run some code at app boot, but what if that code fails? How to re-run it, create a dependency tree, ie do all the OTP stuff. Well then it seems like we want an OTP module to be running our migratations and for that to be happening as part of the normal startup process - then we get all the goodness of our OTP system to manage failure, retries, etc.
Which brings us full circle to:
- tada. Here’s some skeleton code to make it easy to build an OTP module that you can boot as part of your app startup to wrap your Repo.migration stuff and ensure that a) it’s run each startup and b) it’s within the OTP framework in order to be reliable
I realise that this forum has a large number of developers who are specifically paid to manage individual services and babysit their upgrade process. However, there is a large opportunity space for apps which are deployed without this babysitting and still need to be highly reliable, and manage their own upgrades without the benefit of a skilled developer babysitting it. If this isn’t you, then no problems, but I personally hate Gitlab and the immensely long and complicated Rails migration scripts that are needed. If the migration process is fixed then have the app run it as part of startup?!!
I claim that the rest of you guys are all wrong… I claim that (in many cases) the evolution is from being given some SQL to run as part of the upgrade, through to wrapping this in a migrator which is run as a separate “mix migrate” step, through to running this step automatically as part of the app’s normal (release) startup process (which of course should be done through OTP friendly mechanisms). In my case I add a few conditionals to this migration to avoid it being done repeatedly by automated tools within my editor (eg test watchers or linters, etc), I would usually avoid it being run automatically in dev env, however, I would probably have it run automatically in release/test environments.