9 months ago
Hi, I have an API service running on top of a Postgresql database. When I update my app, I regularly need to apply database migrations. I want to prevent race conditions that may be caused by queries issued by the API to a wrong database schema version.
Currently, when upgrading my service, there is a time window during which two versions are running at the same time against the same database. I am unable to find how to cleanly avoid this overlap. Is there a setting to easily enable such a use case with Railway?
I am surprised to find no one mentioning this use case. Am I doing something wrong?
I've been trying to implement this use case in an automated job in CI using the Railway CLI. I end up needing to run `railway down` but it seems that it doesn't work in a non-interactive environment with a RAILWAY_TOKEN. How can I overcome this?
Thanks a lot!
10 Replies
9 months ago
Hi Alexandre,
You may want to look at Healthchecks in Railway to ensure the handoff between deployments. See the Docs:
Regards,
Christian
Status changed to Awaiting User Response Railway • 10 months ago
9 months ago
Hi christian,
I am already using healthchecks, but I think it is not enough for my use case. While it does ensure zero-downtime handoff, it does not prevent overlap between two service versions.
To prevent data consistency issues at the database level, my workflow involves downtime (which is OK for me) and looks like this:
Service v1 is up with db schema v1
Service v1 is brought down
Database migrations are run, now db schema is at v2
Service v2 comes up
If two service versions are running at the same time, there is a chance that service v1 issues queries to a db with schema v2, or that service v2 issues queries to a db with schema v1. I want to ensure strict separation so that a query with mismatched versions never occurs.
I've seen discussion about an OVERLAP_SECONDS setting that could be set to 0, but I don't think it gives strict enough guarantees due to the time the migrations take to run.
How can I achieve this goal?
Additional note: not sure if that changes something but my database is not hosted on Railway.
Status changed to Awaiting Railway Response Railway • 10 months ago
9 months ago
Thank you for the additional context.
It seems like OVERLAP_SECONDS
= 0
might work for your use case. The default is 20 seconds. The variable is documented here.
How are you running your migrations? Are they run as part of the deployment steps for your service? Or via a separate service you deploy? If part of a separate service, you can use the Startup Order configuration to ensure your "Service v2" doesn't come up until your migration service has run.
Status changed to Awaiting User Response Railway • 10 months ago
9 months ago
By setting OVERLAP_SECONDS=0
is there a strong guarantee that there will be strictly no overlap between the two service deployments? If it's the case then it should indeed be the solution for me.
Thanks for pointing me to the Startup Order config, I didn't know about it and it may come to be useful. Currently I am running migrations as part of the service startup.
Status changed to Awaiting Railway Response Railway • 10 months ago
9 months ago
Yes, setting OVERLAP_SECODNS=0
will guarantee there will not be overlap between the services.
To ensure the migration steps run first, you can set your start command to something like:
[migration command] && [start command]
Status changed to Awaiting User Response Railway • 10 months ago
9 months ago
Thanks a lot for your help, I was able to implement what I needed.
I am now moving to a design with multiple services and am facing new issues regarding the same use case. I made the following design choices:
The db migrations are moved to a separate, ephemeral service that just runs the migrations and exits.
The services depending on the db check the db revision at startup and wait for it to be up to date before starting. This ensures that services v2 don't start emitting queries before the db schema is at v2.
The issue I have is about ensuring that services v1 are stopped before running the db migrations.
Currently all services are linked to the same repo, and are built with the same Dockerfile. Is the build common to all services, or does it run separately for each one?
This ensures that all services are redeployed, however, I can't control precisely when. By setting
OVERLAP_SECONDS=0
they are stopped as soon as the build finishes and the new container starts. But the migration service could well start before the other services v1 stop emitting queries. How could I ensure all services are stopped before starting the new ones?
Status changed to Awaiting Railway Response Railway • 10 months ago
9 months ago
sorry maybe not fit for your usecase but I use maintenance mode middleware in my app
so I can disable certain features on demand when apply migration and block user request and query
Status changed to Awaiting User Response christian • 9 months ago
9 months ago
Hi tonyhart7, thanks for your suggestion, thats seems like a very good idea that could work for my use case. How do you communicate with your services to turn on maintenance mode in your case?
My concern is that it would require substantial work. I was wondering if there were any Railway features that I could leverage to get it done easily?
My initial intent was to build Docker images externally and push them to a registry, use the Railway CLI to bring services down using railway down
, then back up with railway redeploy
. But it seems railway down
doesn't work in a non-interactive environment with a RAILWAY_TOKEN
. Is this intentional? How can I achieve this? Maybe through the Railway API?
Status changed to Awaiting Railway Response Railway • 9 months ago
9 months ago
Hi Alexandre,
For now, your best option might be to run your migration and app in the same service, and run them via a single command like [migration command] && [start command]
.
We are looking at implementing features in the future that will allow you to run pre-run and post-run commands, which would give you an extra set of controls like what you're after.
Regards,
Christian
Status changed to Awaiting User Response Railway • 9 months ago
alex-marty
Hi tonyhart7, thanks for your suggestion, thats seems like a very good idea that could work for my use case. How do you communicate with your services to turn on maintenance mode in your case?My concern is that it would require substantial work. I was wondering if there were any Railway features that I could leverage to get it done easily?My initial intent was to build Docker images externally and push them to a registry, use the Railway CLI to bring services down using railway down, then back up with railway redeploy. But it seems railway down doesn't work in a non-interactive environment with a RAILWAY_TOKEN. Is this intentional? How can I achieve this? Maybe through the Railway API?
9 months ago
well, I think it's depends in your tech stack (?)
i use rust webserver that read into redis when user hit certain critical endpoints and can disable that on demand via admin web
having that built in into railway is nice thought. soon maybe but for now, this is what I do
Status changed to Awaiting Railway Response Railway • 9 months ago
Status changed to Awaiting User Response christian • 9 months ago
2 months ago
This thread has been marked as solved automatically due to a lack of recent activity. Please re-open this thread or create a new one if you require further assistance. Thank you!
Status changed to Solved Railway • about 2 months ago