wasp-lang / wasp

The fastest way to develop full-stack web apps with React & Node.js.
https://wasp-lang.dev
MIT License
13.54k stars 1.18k forks source link

We should run `prisma migrate deploy` as a step before deployment, not as part of deployed Docker container #215

Open Martinsos opened 3 years ago

Martinsos commented 3 years ago

As stated in Prisma docs:

migrate deploy should generally be part of an automated CI/CD pipeline, and we do not recommend running this command locally to deploy changes to a production database (for example, by temporarily changing the DATABASE_URL environment variable). It is not generally considered good practice to store the production database URL locally.

Right now we run migrate deploy inside the deployed docker container whenever the app is started, as a part of command that launches the app, which is good in the sense that it is not run locally, but it is bad because it runs too often (every time app starts instead of once when it is deployed) and also if migrate deploy fails we are left with the crashed app that doesn't run.

So Prisma recommends to make it a part of CI. Unless we are generating the CI files ourselves, the only way we can do this is by offering users a command that they can put into their CI. They can then also run this command locally if they want.

So, what we really need to do is let users run migrate deploy on their own, probably by having a command like wasp db migrate-prod or smth like that. They can then use it locally or in CI if they have it, as a step before pushing the docker container. They will however need to provide DATABASE_URL for a prod database. We don't want this in .env file since it is stored on their machine, that is dangerous. But, we can ask them to input it, or for example they can get it from Heroku with heroku config:get DATABASE_URL -a <app_name>, or they can provide it via env var directly in their command line. Once we do this, we should remove the migrate deploy from docker container and describe in docs they have to run it on their own.

One thing that I am worried about here is: what happens in the time between successful migrate deploy and successful deployment of actual server? What if deployment of server doesn't work or takes long time? We have app in the wrong state for some time. Can we roll-back easily? I am guessing there is no easy solution for this and we need a mechanism on the hosting provider side to handle this. Actually, looking at Heroku, they talk about this here https://devcenter.heroku.com/articles/release-phase -> it is specifically meant for these purposes (db migrations). From reading this, it seems we should do migrate deploy between heroku container:push and heroku container:release -> we can just tell users to do it this way. They also have special mechanism on Heroku to do this completely with containers: you push a container that you want to use in release' phase, and then you call upon it when you are callingcontainer:releasefor whatever you are releasing. So in our case, we would prepare container (by adding target to dockerfile) that runsprisma migrate deploy` and then users would have extra step between server container push and release, so all together it would be:

heroku container:push --app <myapp> web
heroku container:push --app <myapp> release # 'release' can also be some other name? Not sure.
heroku container:release --app <myapp> web release # This tells 'container:release' to use previously pushed 'release' container for its release step.

So, two obvious approaches seem to be a command to do it and container that does it, let's see what makes more sense.

Martinsos commented 2 years ago

Two commands they added in Prisma 3.13.0 (https://github.com/prisma/prisma/releases/tag/3.13.0) are also interesting:

They are worth checking out while we are figuring out how to best deal with migrations!

breadchris commented 1 year ago

I have a coworker that has drilled into me the value of forward-only migrations (no renaming, no dropped columns). By doing this, you can migrate before deployment, and you dont worry about the application going down. Once you deploy, your two apps can exist simultaneously without any problems. Over time, when you are confident the column is "dead", you can drop it, also without any problems.

This is a developer practice that is rarely seen, but it is kind of the only way to really do this safely.

Martinsos commented 1 year ago

Oh that sounds great! Aha so basically you ensure that you have a database that supports both old/currentl release and new release, so you can publish new release without modifying the database at the same time, instead you first prepare the DB, all works, then you do the new release. Yup, that is perfect, I remember at last job we were also trying to do it that way! I am not sure if we can somehow enforce that in Wasp though hm, maybe it could be an advice instead.