Ensure a Read Replica Cluster is Strictly Read-Only

In YugabyteDB, read replicas are specialized nodes designed to handle read-heavy workloads by offloading read operations from the primary cluster. They are typically deployed in geographically distributed locations to serve low-latency reads closer to the user or application.

Read replicas serve as a read-only extension of the primary data in the universe.

While applications can send write requests to read replicas, these requests are internally redirected to the source of truth—the primary cluster. This redirection is possible because read replicas have full awareness of the universe’s topology.

For your use case, instead of allowing the read replica to redirect writes, you may prefer to have it reject write requests entirely.

This can be achieved by configuring the read replica cluster to set all default session transactions as read-only using the YSQL parameter default_transaction_read_only.

Here’s a quick example using yugabyted:

				
					[root@cloud-server-0 yb2]# yugabyted start --advertise_address=127.0.0.1 --base_dir=~/yb01 --cloud_location aws.us-east-1.us-east-1a > start_primary.log

[root@cloud-server-0 yb2]# yugabyted start --read_replica --cloud_location aws.us-west-2.us-west-2a --advertise_address=127.0.0.2 --base_dir=~/yb02 --join=127.0.0.1 --tserver_flags="ysql_pg_conf_csv={default_transaction_read_only=true}" > start_read_replica.log

[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.1 -c "SELECT host, node_type, region FROM yb_servers() ORDER BY host;"
   host    |  node_type   |  region
-----------+--------------+-----------
 127.0.0.1 | primary      | us-east-1
 127.0.0.2 | read_replica | us-west-2
(2 rows)

[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.1 -c "CREATE TABLE test(c1 INT PRIMARY KEY, c2 TEXT);"
CREATE TABLE

[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.1 -c "INSERT INTO test (c1, c2) VALUES (1, 'A'), (2, 'B'), (3, 'C');"
INSERT 0 3

[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.1 -c "SELECT * FROM test;"
 c1 | c2
----+----
  1 | A
  2 | B
  3 | C
(3 rows)

[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.2 -c "INSERT INTO test (c1, c2) VALUES (4, 'D'), (5, 'E'), (6, 'F');"
ERROR:  cannot execute INSERT in a read-only transaction
				
			

Note that while we can read data while connected to the read replica (127.0.0.2), but we could not write data.

The key is to set default_transaction_read_only=true using the ysql_pg_conf_csv TServer gFlag specifically on the read replica cluster, without applying it to the primary cluster.

If you are using YugabyteDB Anywhere, you can also set gFlags independently for the read replica clusters. 

Keep in mind that in our example above that setting default_transaction_read_only=true is applied at the Universe level, meaning every connection will default to this setting.

However, individual sessions can override this parameter and set it back to false

While the title of this tip is “Ensure a Read Replica Cluster is Strictly Read-Only,” this approach only changes the default behavior of the read replicas rather than enforcing strict read-only access.

Here’s a quick example of overriding default_transaction_read_only at the session level, which allows for INSERT operations:

				
					[root@cloud-server-0 yb2]# ysqlsh -h 127.0.0.2 -c "SET default_transaction_read_only=OFF;" -c "INSERT INTO test (c1, c2) VALUES (4, 'D'), (5, 'E'), (6, 'F');"
SET
INSERT 0 3
				
			

Have Fun!

Bought this year's new Christmas Tree ornaments at Soergel Orchards!