Applies to: Mattermost Server (self-managed) on PostgreSQL or MySQL, deployments using database read replicas
Symptoms: Read queries are not offloaded to replicas even though a proxy or load balancer sits in front of the database cluster, so all traffic lands on the primary.
🛑 Problem
A common assumption is that placing a proxy or load balancer in front of a replicated database cluster automatically distributes reads across the replicas. Whether that is true depends on the class of proxy:
-
Node-role (connection) proxies route a connection based on a node's role (primary vs replica), usually by querying a cluster manager's health checks. They work at the connection/TCP level (layer 4) and never inspect the SQL. Examples: HAProxy fronting a Patroni cluster (via
/primaryand/replicaendpoints), a virtual IP (VIP) - a single floating IP address that is reassigned to whichever node is currently primary - and connection poolers like pgBouncer (which multiplex connections but do not split reads from writes). - SQL-aware (statement) proxies parse each statement, classify it as a read or write, and route accordingly. Examples: ProxySQL with query rules, Pgpool-II in load-balancing mode.
Only a SQL-aware proxy produces a read/write split at the database layer. A node-role proxy cannot, because nothing in its path checks whether a query is a SELECT or an INSERT.
What happens when a node-role proxy is addressed only through DataSource
If you set a single DataSource pointing at a node-role proxy (or VIP) and leave DataSourceReplicas empty, every query, read and write, resolves to whichever node the proxy classifies as primary:
DataSource -> node-role proxy / VIP -> primary node DataSourceReplicas = [] (no reads are ever sent to replicas)
The primary then carries 100 percent of the query load, the replicas serve only streaming replication, and no read offloading occurs regardless of how healthy the cluster is.
âś… Solution
Because most cluster proxy stacks are node-role, not SQL-aware, read/write separation is Mattermost's responsibility. Mattermost performs this split itself: it sends writes and reads-that-must-be-current to DataSource, and distributes offloadable reads across the endpoints listed in DataSourceReplicas. For this reason, configuring SqlSettings.DataSourceReplicas is always recommended whenever you have one or more replicas you want to read from.
Configure DataSourceReplicas So Mattermost Performs the Split
Populate SqlSettings.DataSourceReplicas with one or more replica endpoints. With a node-role proxy stack, point DataSource at the endpoint that resolves to the primary and DataSourceReplicas at the endpoint that resolves to the replicas. Mattermost then decides, per query, which endpoint to use.
"SqlSettings": {
"DataSource": "<connection string that resolves to the primary>",
"DataSourceReplicas": ["<connection string that resolves to the replica(s)>"]
}⚠️ Important: A restart of the Mattermost server is required for
SqlSettingsconnection-string changes to take effect. These settings are not reloadable from the System Console.
When a Single DataSource Is Sufficient
Configuring only DataSource (with an empty DataSourceReplicas) achieves a read/write split at the database layer only if the endpoint is a SQL-aware proxy that parses statements and load-balances reads across replica nodes itself. If your proxy is node-role (HAProxy/Patroni, a VIP, or pgBouncer), a single DataSource will not offload any reads; use DataSourceReplicas.
Additional Resources
For more information, see:
High Availability Cluster-Based Deployment - Database Configuration
Comments
Article is closed for comments.