fly status --app <app name> or fly image show --app <app name> to view the image name and version.
Older Fly Postgres apps use stolon for leader election and streaming replication between two or more Postgres servers.
Adding replicas
The easiest way to add replicas (standbys) is with thefly machine clone command:
fly machine clone command clones the spec from the source Machine and uses it to create the new replica in the specified region.
Performing a failover
To perform a failover, first make sure you have 3 or more servers in your primary region. Then run the following command:Note: Only healthy servers residing in your primary region will be considered for leadership.
Performing a regional failover
There may be situations where you want to move leadership into a completely new region.- Add at least three replicas into the new region to maintain quorum when switching leadership. See Adding replicas.
-
Run
fly status --app <app name>to check the number of servers in each region. For example:The output shows the primary and two replicas in ewr, and three replicas in iad, which is the new target region for this example. -
If you haven’t already pulled down your
fly.tomlconfiguration file, you can do so by running: -
Open the
fly.tomlfile and set theprimary_regionand thePRIMARY_REGIONenvironment variable to your new target region. For example, to move leadership into the iad region: -
Before deploying this change, identify which Postgres image you are currently running. Use
fly statusto get the image info. For example: -
Copy the image name, in this example
flyio/postgres-flex:15.3, you’ll use it for the next command. -
Deploy the app to pick up the changes you made to the
fly.tomlfile, specifying the image to use. For example:Once the deploy process has completed, you might need to wait a moment for all the servers to become healthy. -
Run the
fly pg failovercommand to move the primary server to the new region:
fly status to verify your changes. Continuing the example, the output shows that the primary server has switched from ewr to iad:
Connecting to read replicas
The generated connection string uses port5432 to connect to PostgreSQL. This port always forwards you to a writable instance. Port 5433 is direct to the PostgreSQL member, and used to connect to read replicas directly.
You can use the proxy port (5432) to connect from every region, but it will be quite slow. Connecting to “local” replicas is much quicker, but does take some app logic.
The basic logic to connect is:
- Set a
PRIMARY_REGIONenvironment variable on your app. - Check the
FLY_REGIONenvironment variable at connect time, useDATABASE_URLas is whenFLY_REGIONis the same asPRIMARY_REGION. - Modify the
DATABASE_URLwhen running in other regions:- Change the port to
5433
- Change the port to
DATABASE_URL and connect to port 5432:
5433:
Detecting write requests
Catch read-only errors
PostgreSQL conveniently sends a “read only transaction” error when you attempt to write to a read replica. All you need to do to detect write requests is catch this error.Replay the request
Once caught, just send afly-replay header specifying the primary region, for example fly-replay: region=scl, and we’ll take care of the rest.
If you’re working in Rails, just add this to your ApplicationController:
Library support
We would like to build libraries to make this seamless for most application frameworks and runtimes. If you have a particular app you’d like to distribute with PostgreSQL, post in our community forums.Consistency model
This is a fairly typical read replica model. Read replicas are usually eventually consistent, and can fall behind the leader. Running read replicas across the world can exacerbate this effect and make read replicas stale more frequently.Request with writes
Requests to the primary region are strongly consistent. When you use the replay header to target a particular region, the entire request runs against the leader database. Your application will behave like you expect.Read only requests
Most apps accept aPOST or PUT, do a bunch of writes, and then redirect the user to a GET request. In most cases, the database will replicate the changes before the user makes the second request. But not always!
Most read-heavy applications aren’t especially sensitive to stale data on subsequent requests. A lagging read replica might result in an out-of-date view for users, but this might be reasonable for your use case.
If your app is sensitive to stale data (meaning, you never, under any circumstances want to show users stale data), you should be careful using read replicas.
Managing eventual consistency
For apps that are sensitive to consistency issues, you can add a counter or timestamp to user sessions that indicates what “version” of the database a particular user is expecting. When the user makes a request and the session’s data version differs from the replica, you can use the samefly-replay header to redirect their request to the primary region – and then you’ll know it’s not stale.
In theory, you could run PostgreSQL with synchronous replication and block until replicas receive writes. This probably won’t work well for far flung read replicas.