Skip to content

Commit 27f3ea6

Browse files
committed
HHH-19708 document how to handle read-only replicas
1 parent a31a9b6 commit 27f3ea6

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

documentation/src/main/asciidoc/introduction/Advanced.adoc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,59 @@ Within a given session, our data is automatically filtered so that only rows tag
338338
Native SQL queries are _not_ automatically filtered by tenant id; you'll have to do that part yourself.
339339
====
340340

341+
[[read-only-replicas]]
342+
=== Read-only replicas
343+
344+
A similar but distinct problem is accessing data held in a read-only replica of the main production database.
345+
One way to handle this problem is to simply instantiate two instances of `SessionFactory`:
346+
347+
- read-only transactions use a `SessionFactory` configured to access the read-only replica, while
348+
- other transactions use the `SessionFactory` configured to read from and write to the main database.
349+
350+
[CAUTION]
351+
====
352+
The second-level cache doesn't play well with replication, and so a `SessionFactory` with access to a read-only replica should be configured with the second-level cache disabled.
353+
====
354+
355+
Alternatively, Hibernate 7.2 introduces experimental support for accessing replicas via a single instance of `SessionFactory`.
356+
A `Session` which accesses a read-only replica must be created in a special read-only mode:
357+
358+
[source,java]
359+
----
360+
Session readOnlySession =
361+
factory.withOptions()
362+
.readOnly(true)
363+
.initialCacheMode(CacheMode.IGNORE)
364+
.openSession();
365+
----
366+
367+
There are now two possibilities.
368+
369+
- Some JDBC drivers (MySQL) are able to automatically direct read-only sessions to the read-only replica.
370+
In this case, there's no more work to do, since Hibernate will automatically call `Connection.setReadOnly(true)` to signal to the JDBC driver that the read-only replica may be used.
371+
372+
- Other drivers (Postgres, Oracle) don't feature any special support for read-only replicas, and in this case we need to supply our own custom link:{doc-javadoc-url}/org/hibernate/engine/jdbc/connections/spi/ConnectionProvider.html[`ConnectionProvider`] or link:{doc-javadoc-url}/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.html[`MultiTenantConnectionProvider`] and implement link:{doc-javadoc-url}/org/hibernate/engine/jdbc/connections/spi/ConnectionProvider.html#getReadOnlyConnection()[`getReadOnlyConnection()`] to return a connection to the read-only replica.
373+
374+
Notice that we created the read-only session with link:{doc-javadoc-url}/org/hibernate/CacheMode.html#IGNORE[`CacheMode.IGNORE`], indicating that access to the read-only replica should <<second-level-cache-management,bypass the second-level cache>>.
375+
There's two different phenomena we need to consider here:
376+
377+
1. A read-only replica might contain stale data which has already been updated or deleted from the main database and invalidated in the second-level cache.
378+
A read-only session might read this stale data and recache it, exposing the stale data to subsequent sessions.
379+
Use of `CacheMode.GET` in a read-only session prevents this phenomenon.
380+
381+
2. A session might read data which has not yet been replicated from the main database and add it to the second-level cache.
382+
If a read-only session reads this data from the cache, it might fail to resolve references to other data which has not yet been replicated.
383+
Use of `CacheMode.PUT` in a read-only session prevents this phenomenon.
384+
385+
Use of `CacheMode.IGNORE` in a read-only session prevents both phenomena.
386+
387+
[CAUTION]
388+
====
389+
The first issue is objectively more serious than the second, and the second issue can often be avoided by careful programming by someone _who really understands what they're doing_.
390+
We do not, therefore, require the use of `CacheMode.IGNORE`, but we strongly encourage the use of _at least_ `CacheMode.GET` in every read-only session.
391+
====
392+
393+
341394
[[custom-sql]]
342395
=== Using custom-written SQL
343396

0 commit comments

Comments
 (0)