Spring boot での冗長化複数DBの実現方法

83 views
Skip to first unread message

よし

unread,
Apr 11, 2023, 5:19:51 AM4/11/23
to DBFluteユーザの集い
Spring bootで 冗長化複数DB接続を行いたいのですが、どこかにサンプル等無いでしょうか?

具体的には、AWS Auroraのリーダーインスタンスを活用するために、Select関係は リーダーインスタンスを見に行くようにしたいというものです。

DataSouceの複数登録はできているのですが、それらの切り替え方法がわからない状態です。


よし

unread,
Apr 12, 2023, 1:19:59 AM4/12/23
to DBFluteユーザの集い
自己レスです。

以下のサイトを参考にできそうな感じになってきました。


[Spring Boot] 動的データソース変更
https://qiita.com/syukai/items/0d4bf27f82fef9965cdd

2023年4月11日火曜日 18:19:51 UTC+9 よし:

kubo

unread,
Apr 12, 2023, 2:18:59 AM4/12/23
to dbf...@googlegroups.com
jfluteです。よしさん、こんにちは。

DBFluteのSpringBootのExampleは、以下のリポジトリとなっていますが...
さすがに冗長化までは入ってないと思うので、Qiitaの記事に頼るしか無いですね...

でも、ぱっと見ると、やりたいことは ThreadLocal でデータソースの切り替えるってところは、
LastaFluteでやってるやり方とほぼ同じっぽいですね。

DataSourceから戻ってくるConnectionさえ切り替わればDBFlute側はそれで検索しに行きますので、たぶん大丈夫なんじゃないかなとは思います。






よし

unread,
Apr 13, 2023, 2:20:23 AM4/13/23
to DBFluteユーザの集い
よしです。 jfluteさんご回答ありがとうございます。

結果として Qiitaの記事の内容で実装していって切り替えができるようになりました。

途中、ユニットテストで投入したテストデータが読み込めない現象が発生しはまりました。

原因はDetaSourceの実装クラスが独自のクラスになったため、トランザクションの連携が
うまくいかなくなったようです。

以下のページを参考に「DBFluteInitialiezrをオーバーライド」する形で独自のクラスを、
連携対象に追加し、うまく動作することができるようになりました。
2023年4月12日水曜日 15:18:59 UTC+9 jflute:

よし

unread,
Apr 13, 2023, 2:56:17 AM4/13/23
to DBFluteユーザの集い
よしです。

うまくいったと思っていたのですが、トランザクションの連携の設定を入れると
切り替えがうまくいかなくなりました

設定前は切り替わっていたのですが。。


2023年4月13日木曜日 15:20:23 UTC+9 よし:

よし

unread,
Apr 13, 2023, 8:05:21 PM4/13/23
to DBFluteユーザの集い
よしです。

トランザクションが始まる前であれば切り替わるようですので、
トランザクション内での切り替えはできないということのようです。

とりあえず、うまくいきました。

2023年4月13日木曜日 15:56:17 UTC+9 よし:

kubo

unread,
Apr 14, 2023, 1:37:56 AM4/14/23
to dbf...@googlegroups.com
jfluteです。

よしさん、状況を細かくありがとうございます!
ノウハウとして残るので助かります。


なるほど、トランザクションの始まる前に切り替えないとですか...

あれ!? LastaFluteの場合はトランザクション内でも大丈夫のような気がするので、
何かしら仕組みが違うのかも!? ですね。今週バタバタして追っかけられてないのですが、
どこかで比較してみたいと思います。


でもまあ、業務的にそんなに困らないですかね?
(どうだろう...ちょっと想像しているのですが)

更新処理が混ざるときだけトランザクションを掛けるスタイルであれば、
更新処理が混ざる処理の中でリーダーの方に行きたいってことはあまりなさそうで...

検索だけでも一貫性のために複数検索にトランザクションに掛ける場合なら、
一部だけリーダーに行きたいってことはあるかも!?







よし

unread,
Apr 14, 2023, 5:14:20 PM4/14/23
to DBFluteユーザの集い
よしです。

コネクションを取得する部分のソースを追ってみた内容を書きます。

DBFluteConfig.java
 ここで「DataSourceUtils」を使って「Connection 」を取っています。

    public static class SpringTransactionalDataSourceHandler implements DataSourceHandler {

        public Connection getConnection(DataSource ds) throws SQLException {
            final Connection conn = getConnectionFromUtils(ds);
            if (isConnectionTransactional(conn, ds)) {
                return new NotClosingConnectionWrapper(conn);
            } else {
                return conn;
            }
        }

        public Connection getConnectionFromUtils(DataSource ds) {
            return DataSourceUtils.getConnection(ds);
        }

        public boolean isConnectionTransactional(Connection conn, DataSource ds) {
            return DataSourceUtils.isConnectionTransactional(conn, ds);
        }
    }

DataSourceUtils.java
 ここで「ConnectionHolder」から 「Connection 」を取り出しているって感じです。
 ここからは想像ですが、トランザクション開始時に「Connection 」を保存してそれを使いまわしているのかなと思っています。

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
}
catch (IllegalStateException ex) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
}
}

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");

ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.

logger.debug("Fetching JDBC Connection from DataSource");
Connection con = fetchConnection(dataSource);

if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
catch (RuntimeException ex) {
// Unexpected exception from external delegation call -> close Connection and rethrow.
releaseConnection(con, dataSource);
throw ex;
}
}

return con;
}
2023年4月14日金曜日 14:37:56 UTC+9 jflute:
Reply all
Reply to author
Forward
0 new messages