Hi everyone,
Iโve been working with read replicas in Frappe/ERPNext to reduce query load on the primary database. While setting this up, I learned something important from the community and docs that Iโd like to share here for clarity.
In Frappe, every request runs inside a transaction. Because of this, we cannot automatically route some queries to the replica โ doing so could lead to inconsistent results, especially if the replica lags behind the primary.
The recommended approach is:
- Identify top-level functions or reports that are read-only (e.g., fetching invoices, generating reports).
- Add the decorator
@frappe.read_only()
to those functions. - This ensures those queries will run on the replica safely, without affecting write operations.
Example:
@frappe.read_only()
def fetch_data(filters):
return frappe.db.get_all("Sales Invoice", filters=filters, fields=["name", "grand_total"])
This way, you can offload heavy read queries to the replica where itโs safe, while keeping write-related operations on the primary.
As a reminder, do follow the official guide to correctly configure read replica in Frappe/ERPNext settings:
https://docs.frappe.io/framework/user/en/guides/database-settings/setup-read-from-secondary-db
Hope this helps others who are trying to optimize with replicas!