/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.aws.jdbc.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* {@link org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource} implementation that routes to different
* read only data source in a random fashion if the current transaction is read-only. This is useful for database
* platforms that support read-replicas (like MySQL) to scale up the access to the database for read-only accesses.
* <p>
* <b>Note:</b> In order to use read-only replicas it is necessary to wrap this data source with a {@link
* org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy} to ensure that the connection is not fetched
* during transaction creation, but during the first physical access. See the LazyConnectionDataSourceProxy
* documentation for more details.
* </p>
*
* @author Agim Emruli
* @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
*/
public class ReadOnlyRoutingDataSource extends AbstractRoutingDataSource {
private final List<Object> dataSources = new ArrayList<>();
private List<Object> dataSourceKeys;
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
this.dataSourceKeys = new ArrayList<>(targetDataSources.keySet());
this.dataSources.addAll(targetDataSources.values());
}
@Override
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
this.dataSources.add(defaultTargetDataSource);
}
@Override
protected Object determineCurrentLookupKey() {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly() && !this.dataSourceKeys.isEmpty()) {
return this.dataSourceKeys.get(getRandom(this.dataSourceKeys.size()));
}
return null;
}
public List<Object> getDataSources() {
return this.dataSources;
}
private static int getRandom(int high) {
//noinspection UnsecureRandomNumberGeneration
return (int) (Math.random() * high);
}
}