Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 27f3ea6

Browse files
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)

AltStyle によって変換されたページ (->オリジナル) /