diff --git a/src/main/kotlin/com/bitclave/node/configuration/DataSourceConfig.kt b/src/main/kotlin/com/bitclave/node/configuration/DataSourceConfig.kt new file mode 100644 index 00000000..60ec157c --- /dev/null +++ b/src/main/kotlin/com/bitclave/node/configuration/DataSourceConfig.kt @@ -0,0 +1,56 @@ +package com.bitclave.node.configuration + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import javax.sql.DataSource +import java.util.HashMap +import com.zaxxer.hikari.HikariDataSource +import com.zaxxer.hikari.HikariConfig +import com.bitclave.node.routingdatasource.RoutingDataSource +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.core.env.Environment + +/** + * Replication DataSources Configuration + * + * `@Primary` and `@DependsOn` are the key requirements for Spring Boot. + */ +@Configuration +class DataSourceConfig { + private val PRIMARY_DATASOURCE_PREFIX = "spring.primary.datasource" + private val REPLICA_DATASOURCE_PREFIX = "spring.replica.datasource" + + @Autowired + private val environment: Environment? = null + + @Bean + @Primary + fun dataSource(): DataSource { + val routingDataSource = RoutingDataSource() + + val primaryDataSource = buildDataSource("PrimaryHikariPool", PRIMARY_DATASOURCE_PREFIX) + val replicaDataSource = buildDataSource("ReplicaHikariPool", REPLICA_DATASOURCE_PREFIX) + + val targetDataSources = HashMap() + targetDataSources[RoutingDataSource.Route.PRIMARY] = primaryDataSource + targetDataSources[RoutingDataSource.Route.REPLICA] = replicaDataSource + + routingDataSource.setTargetDataSources(targetDataSources) + routingDataSource.setDefaultTargetDataSource(primaryDataSource) + + return routingDataSource + } + + private fun buildDataSource(poolName: String, dataSourcePrefix: String): DataSource { + val hikariConfig = HikariConfig() + + hikariConfig.poolName = poolName + hikariConfig.jdbcUrl = environment!!.getProperty(String.format("%s.url", dataSourcePrefix)) + hikariConfig.username = environment.getProperty(String.format("%s.username", dataSourcePrefix)) + hikariConfig.password = environment.getProperty(String.format("%s.password", dataSourcePrefix)) + hikariConfig.driverClassName = environment.getProperty(String.format("%s.driver", dataSourcePrefix)) + + return HikariDataSource(hikariConfig) + } +} diff --git a/src/main/kotlin/com/bitclave/node/repository/account/AccountCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/account/AccountCrudRepository.kt index 7f0a1f5d..0d45c405 100644 --- a/src/main/kotlin/com/bitclave/node/repository/account/AccountCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/account/AccountCrudRepository.kt @@ -12,13 +12,17 @@ import java.util.Date @Transactional interface AccountCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findAllBy(pageable: Pageable): Slice + @Transactional(readOnly = true) fun findByPublicKey(key: String): Account? + @Transactional(readOnly = true) fun findAllByPublicKeyIn(key: List): List fun deleteByPublicKey(key: String) + @Transactional(readOnly = true) fun findByCreatedAtAfter(createdAt: Date): List } diff --git a/src/main/kotlin/com/bitclave/node/repository/account/PostgresAccountRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/account/PostgresAccountRepositoryImpl.kt index 5f210c7f..9e8fa1a9 100644 --- a/src/main/kotlin/com/bitclave/node/repository/account/PostgresAccountRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/account/PostgresAccountRepositoryImpl.kt @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.util.Date @Component @@ -38,6 +39,7 @@ class PostgresAccountRepositoryImpl(val repository: AccountCrudRepository) : Acc .toList() } + @Transactional(readOnly = true) override fun getTotalCount(): Long { return repository.count() } diff --git a/src/main/kotlin/com/bitclave/node/repository/data/PostgresClientDataRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/data/PostgresClientDataRepositoryImpl.kt index 5d69a7e8..f46488fd 100644 --- a/src/main/kotlin/com/bitclave/node/repository/data/PostgresClientDataRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/data/PostgresClientDataRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.bitclave.node.services.errors.DataNotSavedException import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -16,6 +17,7 @@ class PostgresClientDataRepositoryImpl( return emptyList() } + @Transactional(readOnly = true) override fun getData(publicKey: String): Map { return repository.findByIdOrNull(publicKey)?.data ?: emptyMap() } diff --git a/src/main/kotlin/com/bitclave/node/repository/file/FileCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/file/FileCrudRepository.kt index e5922435..aec0b76e 100644 --- a/src/main/kotlin/com/bitclave/node/repository/file/FileCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/file/FileCrudRepository.kt @@ -9,13 +9,16 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface FileCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findByPublicKey(publicKey: String): List + @Transactional(readOnly = true) fun findById(id: Long): UploadedFile? fun deleteByIdAndPublicKey(id: Long, publicKey: String): Long fun deleteByPublicKey(publicKey: String): Long + @Transactional(readOnly = true) fun findByIdAndPublicKey(id: Long, publicKey: String): UploadedFile? } diff --git a/src/main/kotlin/com/bitclave/node/repository/offer/OfferCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/offer/OfferCrudRepository.kt index 011b6b2a..5acebe35 100644 --- a/src/main/kotlin/com/bitclave/node/repository/offer/OfferCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/offer/OfferCrudRepository.kt @@ -15,24 +15,31 @@ import java.math.BigInteger @Transactional interface OfferCrudRepository : PagingAndSortingRepository { + @Transactional(readOnly = true) fun findAllByIdIn(ids: List, pageable: Pageable): Page + @Transactional(readOnly = true) @Query("SELECT id FROM Offer WHERE owner = :owner", nativeQuery = true) fun findIdsByOwner(@Param("owner") owner: String): List + @Transactional(readOnly = true) fun findByOwner(owner: String): List + @Transactional(readOnly = true) fun findByOwner(owner: String, pageable: Pageable): Page fun deleteByIdAndOwner(id: Long, owner: String): Long fun deleteByOwner(owner: String): List + @Transactional(readOnly = true) fun findByIdAndOwner(id: Long, owner: String): Offer? + @Transactional(readOnly = true) @Query("FROM Offer o JOIN o.tags t WHERE o.owner = :owner and KEY(t) = :tagKey") fun getOfferByOwnerAndTag(@Param("owner") owner: String, @Param("tagKey") tagKey: String): List + @Transactional(readOnly = true) @Query( value = """ select * from offer o @@ -48,6 +55,7 @@ interface OfferCrudRepository : PagingAndSortingRepository { ) fun getAllOffersExceptProducts(@Param("pageable") pageable: Pageable): Page + @Transactional(readOnly = true) @Query( value = """ select * from offer o @@ -58,8 +66,10 @@ interface OfferCrudRepository : PagingAndSortingRepository { ) fun getAllOffersExceptProductsSlice(pageable: Pageable): Slice + @Transactional(readOnly = true) fun getAllOffersBy(pageable: Pageable): Slice + @Transactional(readOnly = true) @Query( value = """ SELECT o.* FROM offer o diff --git a/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt index eeb06086..65b45ff3 100644 --- a/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt @@ -20,6 +20,7 @@ import org.springframework.data.domain.Slice import org.springframework.data.domain.SliceImpl import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.math.BigInteger import java.util.HashMap import javax.persistence.EntityManager @@ -94,6 +95,7 @@ class PostgresOfferRepositoryImpl( return syncElementCollections(repository.findAllByIdIn(ids, pageable)) } + @Transactional(readOnly = true) override fun findByIds(ids: List): List { return syncElementCollections(repository.findAllById(ids).toList()) } @@ -102,10 +104,12 @@ class PostgresOfferRepositoryImpl( return syncElementCollections(repository.findByIdAndOwner(id, owner)) } + @Transactional(readOnly = true) override fun findAll(): List { return syncElementCollections(repository.findAll().toList()) } + @Transactional(readOnly = true) override fun findAll(pageable: Pageable): Page { var result: Page? = null val step1 = measureTimeMillis { @@ -117,6 +121,7 @@ class PostgresOfferRepositoryImpl( return syncElementCollections(result!!) } + @Transactional(readOnly = true) override fun getTotalCount(): Long { return repository.count() } @@ -177,6 +182,7 @@ class PostgresOfferRepositoryImpl( return SliceImpl(result, pageable, slice.hasNext()) } + @Transactional(readOnly = true) private fun syncElementCollections( offers: List, syncCompare: Boolean = true, @@ -299,6 +305,7 @@ class PostgresOfferRepositoryImpl( return result } + @Transactional(readOnly = true) private fun syncPriceRules(offerPriceIds: List): Map>> { val ids = offerPriceIds.joinToString(",") var result = mapOf>>() diff --git a/src/main/kotlin/com/bitclave/node/repository/price/OfferPriceCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/price/OfferPriceCrudRepository.kt index b188e9a9..24b1ec6c 100644 --- a/src/main/kotlin/com/bitclave/node/repository/price/OfferPriceCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/price/OfferPriceCrudRepository.kt @@ -3,11 +3,14 @@ package com.bitclave.node.repository.price import com.bitclave.node.repository.entities.OfferPrice import org.springframework.data.repository.CrudRepository import org.springframework.stereotype.Repository -import javax.transaction.Transactional +import org.springframework.transaction.annotation.Transactional @Repository @Transactional interface OfferPriceCrudRepository : CrudRepository { + + @Transactional(readOnly = true) fun findByOfferId(id: Long): List + fun deleteAllByOfferIdIn(ids: List): List } diff --git a/src/main/kotlin/com/bitclave/node/repository/priceRule/OfferPriceRulesCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/priceRule/OfferPriceRulesCrudRepository.kt index f871ceef..a3365332 100644 --- a/src/main/kotlin/com/bitclave/node/repository/priceRule/OfferPriceRulesCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/priceRule/OfferPriceRulesCrudRepository.kt @@ -3,11 +3,12 @@ package com.bitclave.node.repository.priceRule import com.bitclave.node.repository.entities.OfferPriceRules import org.springframework.data.repository.CrudRepository import org.springframework.stereotype.Repository -import javax.transaction.Transactional +import org.springframework.transaction.annotation.Transactional @Repository @Transactional interface OfferPriceRulesCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findByOfferPriceId(offerPriceId: Long): List } diff --git a/src/main/kotlin/com/bitclave/node/repository/rank/OfferRankCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/rank/OfferRankCrudRepository.kt index 5f367a9a..76a906ae 100644 --- a/src/main/kotlin/com/bitclave/node/repository/rank/OfferRankCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/rank/OfferRankCrudRepository.kt @@ -11,8 +11,10 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface OfferRankCrudRepository : PagingAndSortingRepository { + @Transactional(readOnly = true) fun findByOfferId(offerId: Long): List + @Transactional(readOnly = true) fun findByOfferIdAndRankerId(offerId: Long, rankerId: String): OfferRank? @Modifying diff --git a/src/main/kotlin/com/bitclave/node/repository/rank/PostgresOfferRankRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/rank/PostgresOfferRankRepositoryImpl.kt index 44aa7c5a..e67e4a9c 100644 --- a/src/main/kotlin/com/bitclave/node/repository/rank/PostgresOfferRankRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/rank/PostgresOfferRankRepositoryImpl.kt @@ -4,6 +4,7 @@ import com.bitclave.node.repository.entities.OfferRank import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -23,6 +24,7 @@ class PostgresOfferRankRepositoryImpl( return repository.save(rankOffer) } + @Transactional(readOnly = true) override fun findById(id: Long): OfferRank? { return repository.findByIdOrNull(id) } diff --git a/src/main/kotlin/com/bitclave/node/repository/request/PostgresRequestDataRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/request/PostgresRequestDataRepositoryImpl.kt index b82d7f7d..34a9a489 100644 --- a/src/main/kotlin/com/bitclave/node/repository/request/PostgresRequestDataRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/request/PostgresRequestDataRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.bitclave.node.services.errors.DataNotSavedException import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -49,6 +50,7 @@ class PostgresRequestDataRepositoryImpl(val repository: RequestDataCrudRepositor repository.getReshareByClientsAndKeysAndRootPk(clientsPk, keys, rootPk) } + @Transactional(readOnly = true) override fun findById(id: Long): RequestData? { return repository.findByIdOrNull(id) } diff --git a/src/main/kotlin/com/bitclave/node/repository/request/RequestDataCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/request/RequestDataCrudRepository.kt index c0b366ed..7c258366 100644 --- a/src/main/kotlin/com/bitclave/node/repository/request/RequestDataCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/request/RequestDataCrudRepository.kt @@ -10,16 +10,22 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface RequestDataCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findByFromPk(from: String): List + @Transactional(readOnly = true) fun findByToPk(to: String): List + @Transactional(readOnly = true) fun findByFromPkAndToPk(from: String, to: String): List + @Transactional(readOnly = true) fun findByFromPkAndToPkAndRequestData(from: String, to: String, requestData: String): RequestData? + @Transactional(readOnly = true) fun findByRequestDataAndRootPk(requestData: String, rootPk: String): List + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM request_data WHERE to_pk = ?1 AND from_pk IN ?2 AND request_data IN ?3 @@ -28,6 +34,7 @@ interface RequestDataCrudRepository : CrudRepository { ) fun getByFromAndToAndKeys(to: String, from: List, keys: List): List + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM request_data WHERE to_pk = ?1 AND from_pk IN ?2 @@ -36,6 +43,7 @@ interface RequestDataCrudRepository : CrudRepository { ) fun getByFromAndTo(to: String, from: List): List + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM request_data WHERE (from_pk IN ?1 OR to_pk IN ?1) AND request_data IN ?2 AND root_pk=?3 @@ -48,6 +56,7 @@ interface RequestDataCrudRepository : CrudRepository { rootPk: String ): List + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM request_data WHERE (from_pk IN ?1 OR to_pk IN ?1) AND root_pk=?2 diff --git a/src/main/kotlin/com/bitclave/node/repository/search/PostgresSearchRequestRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/search/PostgresSearchRequestRepositoryImpl.kt index 7e77ea4f..1af00458 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/PostgresSearchRequestRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/PostgresSearchRequestRepositoryImpl.kt @@ -12,6 +12,7 @@ import org.springframework.data.domain.Slice import org.springframework.data.domain.SliceImpl import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.math.BigInteger import java.util.HashMap import javax.persistence.EntityManager @@ -42,10 +43,12 @@ class PostgresSearchRequestRepositoryImpl( return repository.deleteByIdIn(ids) } + @Transactional(readOnly = true) override fun findById(id: Long): SearchRequest? { return syncElementCollections(repository.findByIdOrNull(id)) } + @Transactional(readOnly = true) override fun findById(ids: List): List { return syncElementCollections(repository.findAllById(ids).toList()) } @@ -58,10 +61,12 @@ class PostgresSearchRequestRepositoryImpl( return syncElementCollections(repository.findByIdAndOwner(id, owner)) } + @Transactional(readOnly = true) override fun findAll(): List { return syncElementCollections(repository.findAll().toList()) } + @Transactional(readOnly = true) override fun findAll(pageable: Pageable): Page { return syncElementCollections(repository.findAll(pageable)) } @@ -73,6 +78,7 @@ class PostgresSearchRequestRepositoryImpl( override fun findByOwnerInSlice(owners: List, pageable: Pageable): Slice = syncElementCollections(repository.findByOwnerIn(owners, pageable)) + @Transactional(readOnly = true) override fun getTotalCount(): Long { return repository.count() } @@ -112,6 +118,7 @@ class PostgresSearchRequestRepositoryImpl( return SliceImpl(result, pageable, slice.hasNext()) } + @Transactional(readOnly = true) private fun syncElementCollections(searchRequests: List): List { val ids = searchRequests.map { it.id }.distinct().joinToString(",") diff --git a/src/main/kotlin/com/bitclave/node/repository/search/SearchRequestCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/search/SearchRequestCrudRepository.kt index e8d45cbe..0a8814d6 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/SearchRequestCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/SearchRequestCrudRepository.kt @@ -14,10 +14,13 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface SearchRequestCrudRepository : PagingAndSortingRepository { + @Transactional(readOnly = true) fun findAllBy(pageable: Pageable): Slice + @Transactional(readOnly = true) fun findByOwnerIn(owners: List, pageable: Pageable): Slice + @Transactional(readOnly = true) fun findByOwner(owner: String): List fun deleteByIdAndOwner(id: Long, owner: String): Long @@ -38,8 +41,10 @@ interface SearchRequestCrudRepository : PagingAndSortingRepository): Int + @Transactional(readOnly = true) fun findByIdAndOwner(id: Long, owner: String): SearchRequest? + @Transactional(readOnly = true) @Query("FROM SearchRequest s JOIN s.tags t WHERE s.owner = :owner and KEY(t) = :tagKey") fun getRequestByOwnerAndTag( @Param("owner") owner: String, @@ -65,6 +70,7 @@ interface SearchRequestCrudRepository : PagingAndSortingRepository): Int + @Transactional(readOnly = true) @Query( value = """ select sr.* from @@ -81,6 +87,7 @@ interface SearchRequestCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT search_request.* FROM search_request left outer join account on owner = public_key diff --git a/src/main/kotlin/com/bitclave/node/repository/search/interaction/OfferInteractionCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/search/interaction/OfferInteractionCrudRepository.kt index 96435583..34d15e7e 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/interaction/OfferInteractionCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/interaction/OfferInteractionCrudRepository.kt @@ -12,20 +12,28 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface OfferInteractionCrudRepository : PagingAndSortingRepository { + @Transactional(readOnly = true) fun findByOwner(owner: String): List + @Transactional(readOnly = true) fun findByOfferIdAndOwner(offerId: Long, owner: String): OfferInteraction? + @Transactional(readOnly = true) fun findByOfferIdInAndOwnerIn(offerIds: List, owners: List): List + @Transactional(readOnly = true) fun findByOfferIdInAndOwner(offerIds: List, owner: String): List + @Transactional(readOnly = true) fun findByOfferId(offerId: Long): List + @Transactional(readOnly = true) fun findByOfferIdIn(offerIds: List): List + @Transactional(readOnly = true) fun findByOwnerAndStateIn(owner: String, states: List): List + @Transactional(readOnly = true) fun findByOwnerAndOfferIdInAndStateIn( owner: String, offers: List, @@ -48,6 +56,7 @@ interface OfferInteractionCrudRepository : PagingAndSortingRepository): Int + @Transactional(readOnly = true) @Query( value = """ SELECT i FROM OfferInteraction i diff --git a/src/main/kotlin/com/bitclave/node/repository/search/interaction/PostgresOfferInteractionRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/search/interaction/PostgresOfferInteractionRepositoryImpl.kt index 5a5f6785..3034cc9b 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/interaction/PostgresOfferInteractionRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/interaction/PostgresOfferInteractionRepositoryImpl.kt @@ -7,6 +7,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import java.math.BigInteger import javax.persistence.EntityManager @@ -74,6 +75,7 @@ class PostgresOfferInteractionRepositoryImpl( return PageImpl(result, pageable, page.totalElements) } + @Transactional(readOnly = true) private fun syncElementCollections(interactions: List): List { val ids = interactions.map { it.id }.distinct().joinToString(",") diff --git a/src/main/kotlin/com/bitclave/node/repository/search/offer/OfferSearchCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/search/offer/OfferSearchCrudRepository.kt index 8cc06a60..f72acbe5 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/offer/OfferSearchCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/offer/OfferSearchCrudRepository.kt @@ -55,29 +55,40 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) fun findBySearchRequestId(id: Long, pageable: Pageable): Page + @Transactional(readOnly = true) fun findBySearchRequestIdIn(ids: List): List + @Transactional(readOnly = true) fun findByOfferId(id: Long): List + @Transactional(readOnly = true) fun findBySearchRequestIdAndOfferId(searchRequestId: Long, offerId: Long): List + @Transactional(readOnly = true) fun findBySearchRequestIdAndOfferIdIn( searchRequestId: Long, offerIds: List ): List + @Transactional(readOnly = true) fun findBySearchRequestIdInAndOwner(searchRequestIds: List, owner: String): List + @Transactional(readOnly = true) fun findByOwner(owner: String): List + @Transactional(readOnly = true) fun findAllBy(pageable: Pageable): Slice + @Transactional(readOnly = true) fun findBySearchRequestIdIn(ids: List, pageable: Pageable): Slice + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM offer_search s @@ -89,12 +100,16 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository): List + @Transactional(readOnly = true) fun findAllByOwnerAndSearchRequestIdIn(owner: String, searchIds: List): List + @Transactional(readOnly = true) fun findByOwnerAndOfferId(owner: String, offerId: Long): List + @Transactional(readOnly = true) fun findByOwnerAndOfferIdIn(owner: String, offerIds: List): List + @Transactional(readOnly = true) @Query( value = """ SELECT * FROM offer_search s @@ -110,6 +125,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CASE WHEN r.rank IS NULL THEN 0 ELSE r.rank END AS united_rank, @@ -123,6 +139,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT *, @@ -136,6 +153,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT DISTINCT @@ -149,6 +167,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT *, CAST( t.tags AS FLOAT ) AS cashback @@ -160,6 +179,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT *, CASE WHEN r.rank IS NULL THEN 0 ELSE r.rank END AS united_rank @@ -175,6 +195,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT * @@ -189,6 +210,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT DISTINCT @@ -206,6 +228,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CAST( t.tags AS FLOAT ) AS cashback @@ -221,6 +244,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CASE WHEN r.rank IS NULL THEN 0 ELSE r.rank END AS united_rank, @@ -237,6 +261,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, @@ -253,6 +278,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CAST( t.tags AS FLOAT ) AS cashback @@ -271,6 +297,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CASE WHEN r.rank IS NULL THEN 0 ELSE r.rank END AS united_rank @@ -306,6 +334,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT DISTINCT @@ -322,6 +351,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT DISTINCT @@ -340,6 +370,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = """ SELECT *, CAST( t.tags AS FLOAT ) AS cashback @@ -356,6 +387,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository ): List + @Transactional(readOnly = true) @Query( value = "SELECT s.* from offer_search s, " + "( SELECT b.offer_id, b.owner from " + @@ -385,8 +417,10 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) fun countBySearchRequestId(id: Long): Long + @Transactional(readOnly = true) @Query( value = """ SELECT os.* FROM offer_search os @@ -398,6 +432,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT os.* FROM offer_search os @@ -409,6 +444,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT os.* FROM offer_search os @@ -420,6 +456,7 @@ interface OfferSearchCrudRepository : PagingAndSortingRepository + @Transactional(readOnly = true) @Query( value = """ SELECT os.* FROM offer_search os diff --git a/src/main/kotlin/com/bitclave/node/repository/search/offer/PostgresOfferSearchRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/search/offer/PostgresOfferSearchRepositoryImpl.kt index 34d359d0..2d9d4220 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/offer/PostgresOfferSearchRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/offer/PostgresOfferSearchRepositoryImpl.kt @@ -11,6 +11,7 @@ import org.springframework.data.domain.Slice import org.springframework.data.domain.Sort import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional import kotlin.system.measureTimeMillis @Component @@ -42,10 +43,12 @@ class PostgresOfferSearchRepositoryImpl( override fun save(item: OfferSearch): OfferSearch = repository.save(item) + @Transactional(readOnly = true) override fun findById(id: Long): OfferSearch? { return repository.findByIdOrNull(id) } + @Transactional(readOnly = true) override fun findById(ids: List): List { return repository.findAllById(ids) .toList() @@ -210,10 +213,12 @@ class PostgresOfferSearchRepositoryImpl( return repository.findByOwnerAndOfferIdIn(owner, offerIds) } + @Transactional(readOnly = true) override fun findAll(pageable: Pageable): Page { return repository.findAll(pageable) } + @Transactional(readOnly = true) override fun findAll(): List { return repository.findAll() .asSequence() @@ -229,6 +234,7 @@ class PostgresOfferSearchRepositoryImpl( return repository.findAllDiff() } + @Transactional(readOnly = true) override fun getTotalCount(): Long { return repository.count() } diff --git a/src/main/kotlin/com/bitclave/node/repository/search/query/QuerySearchRequestCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/search/query/QuerySearchRequestCrudRepository.kt index c414c068..e60846e2 100644 --- a/src/main/kotlin/com/bitclave/node/repository/search/query/QuerySearchRequestCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/search/query/QuerySearchRequestCrudRepository.kt @@ -3,12 +3,13 @@ package com.bitclave.node.repository.search.query import com.bitclave.node.repository.entities.QuerySearchRequest import org.springframework.data.repository.CrudRepository import org.springframework.stereotype.Repository -import javax.transaction.Transactional +import org.springframework.transaction.annotation.Transactional @Repository @Transactional interface QuerySearchRequestCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findAllByOwner(owner: String): List fun deleteAllByOwner(owner: String): Long diff --git a/src/main/kotlin/com/bitclave/node/repository/services/PostgresExternalServicesRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/services/PostgresExternalServicesRepositoryImpl.kt index dfa83612..677bd9f3 100644 --- a/src/main/kotlin/com/bitclave/node/repository/services/PostgresExternalServicesRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/services/PostgresExternalServicesRepositoryImpl.kt @@ -4,6 +4,7 @@ import com.bitclave.node.repository.entities.ExternalService import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -11,8 +12,10 @@ class PostgresExternalServicesRepositoryImpl( val repository: ExternalServicesCrudRepository ) : ExternalServicesRepository { + @Transactional(readOnly = true) override fun findById(id: String): ExternalService? = repository.findByIdOrNull(id) + @Transactional(readOnly = true) override fun findAll(): List = repository.findAll().toList() override fun save(entity: ExternalService): ExternalService = repository.save(entity) diff --git a/src/main/kotlin/com/bitclave/node/repository/share/OfferShareCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/share/OfferShareCrudRepository.kt index f2e27e61..db07e070 100644 --- a/src/main/kotlin/com/bitclave/node/repository/share/OfferShareCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/share/OfferShareCrudRepository.kt @@ -9,7 +9,9 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface OfferShareCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findByOfferOwner(owner: String): List + @Transactional(readOnly = true) fun findByOfferOwnerAndAccepted(owner: String, accepted: Boolean): List } diff --git a/src/main/kotlin/com/bitclave/node/repository/share/PostgresOfferShareRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/share/PostgresOfferShareRepositoryImpl.kt index caf7091f..f503c6cd 100644 --- a/src/main/kotlin/com/bitclave/node/repository/share/PostgresOfferShareRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/share/PostgresOfferShareRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.bitclave.node.services.errors.DataNotSavedException import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -16,6 +17,7 @@ class PostgresOfferShareRepositoryImpl( repository.save(shareData) ?: throw DataNotSavedException() } + @Transactional(readOnly = true) override fun findByOfferSearchId(id: Long): OfferShareData? { return repository.findByIdOrNull(id) } diff --git a/src/main/kotlin/com/bitclave/node/repository/site/SiteCrudRepository.kt b/src/main/kotlin/com/bitclave/node/repository/site/SiteCrudRepository.kt index a4ca6c52..cd300b7c 100644 --- a/src/main/kotlin/com/bitclave/node/repository/site/SiteCrudRepository.kt +++ b/src/main/kotlin/com/bitclave/node/repository/site/SiteCrudRepository.kt @@ -9,5 +9,6 @@ import org.springframework.transaction.annotation.Transactional @Transactional interface SiteCrudRepository : CrudRepository { + @Transactional(readOnly = true) fun findByOrigin(origin: String): Site? } diff --git a/src/main/kotlin/com/bitclave/node/routingdatasource/ReadOnlyRouteInterceptor.kt b/src/main/kotlin/com/bitclave/node/routingdatasource/ReadOnlyRouteInterceptor.kt new file mode 100644 index 00000000..89fc2582 --- /dev/null +++ b/src/main/kotlin/com/bitclave/node/routingdatasource/ReadOnlyRouteInterceptor.kt @@ -0,0 +1,38 @@ +package com.bitclave.node.routingdatasource + +import org.aspectj.lang.ProceedingJoinPoint +import org.aspectj.lang.annotation.Around +import org.aspectj.lang.annotation.Aspect +import org.slf4j.LoggerFactory + +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +/** + * ReadOnlyRouteInterceptor routes connections by `@Transaction(readOnly=true|false)` + */ +@Aspect +@Component +@Order(0) +class ReadOnlyRouteInterceptor { + + @Around("@annotation(transactional)") + @Throws(Throwable::class) + fun proceed(proceedingJoinPoint: ProceedingJoinPoint, transactional: Transactional): Any? { + try { + if (transactional.readOnly) { + RoutingDataSource.setReplicaRoute() + logger.info("Routing database call to the read replica") + } + return proceedingJoinPoint.proceed() + } finally { + RoutingDataSource.clearReplicaRoute() + } + } + + companion object { + + private val logger = LoggerFactory.getLogger(ReadOnlyRouteInterceptor::class.java) + } +} diff --git a/src/main/kotlin/com/bitclave/node/routingdatasource/RoutingDataSource.kt b/src/main/kotlin/com/bitclave/node/routingdatasource/RoutingDataSource.kt new file mode 100644 index 00000000..e5f0bc96 --- /dev/null +++ b/src/main/kotlin/com/bitclave/node/routingdatasource/RoutingDataSource.kt @@ -0,0 +1,25 @@ +package com.bitclave.node.routingdatasource + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource + +class RoutingDataSource : AbstractRoutingDataSource() { + + enum class Route { + PRIMARY, REPLICA + } + + companion object { + private val routeContext = ThreadLocal() + fun clearReplicaRoute() { + routeContext.remove() + } + + fun setReplicaRoute() { + routeContext.set(Route.REPLICA) + } + } + + override fun determineCurrentLookupKey(): Any? { + return routeContext.get() + } +} diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 7fd432d3..e9d8e4fd 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -4,13 +4,19 @@ spring: # ddlAuto: create-drop ddlAuto: update - datasource: -# data: classpath*:data-docker.sql - driverClassName: org.postgresql.Driver - platform: postgres - url: ${JDBC_DATABASE_URL} - username: ${JDBC_DATABASE_USERNAME} - password: ${JDBC_DATABASE_PASSWORD} + primary: + datasource: + # data: classpath*:data-docker.sql + driver: org.postgresql.Driver + url: ${JDBC_DATABASE_URL}?reWriteBatchedInserts=true&sslmode=require + username: ${JDBC_DATABASE_USERNAME} + password: ${JDBC_DATABASE_PASSWORD} + replica: + datasource: + driver: org.postgresql.Driver + url: ${JDBC_REPLICA_DATABASE_URL}?sslmode=require + username: ${JDBC_REPLICA_DATABASE_USERNAME} + password: ${JDBC_REPLICA_DATABASE_PASSWORD} rtsearch: url: ${RT_SEARCH_URL} diff --git a/src/main/resources/application-gke.yml b/src/main/resources/application-gke.yml index 36f87dd5..c211300f 100644 --- a/src/main/resources/application-gke.yml +++ b/src/main/resources/application-gke.yml @@ -3,12 +3,18 @@ spring: hibernate: ddlAuto: update - datasource: - driverClassName: org.postgresql.Driver - platform: postgres - url: ${JDBC_DATABASE_URL}?reWriteBatchedInserts=true&sslMode=require - username: ${JDBC_DATABASE_USERNAME} - password: ${JDBC_DATABASE_PASSWORD} + primary: + datasource: + driver: org.postgresql.Driver + url: ${JDBC_DATABASE_URL}?reWriteBatchedInserts=true&sslmode=require + username: ${JDBC_DATABASE_USERNAME} + password: ${JDBC_DATABASE_PASSWORD} + replica: + datasource: + driver: org.postgresql.Driver + url: ${JDBC_REPLICA_DATABASE_URL}?sslmode=require + username: ${JDBC_REPLICA_DATABASE_USERNAME} + password: ${JDBC_REPLICA_DATABASE_PASSWORD} logstash: endpoint: ${LOGSTASH_ENDPOINT} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 08f27592..0310c17b 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -3,12 +3,18 @@ spring: hibernate: ddlAuto: update - datasource: - driverClassName: org.postgresql.Driver - platform: postgres - url: jdbc:postgresql://localhost:5432/postgres - username: postgres - password: bitclave + primary: + datasource: + password: bitclave + username: postgres + driver: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/postgres + replica: + datasource: + password: bitclave + username: postgres + driver: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/postgres server: port: 8080 diff --git a/src/main/resources/application-product.yml b/src/main/resources/application-product.yml index c7059c91..c211300f 100644 --- a/src/main/resources/application-product.yml +++ b/src/main/resources/application-product.yml @@ -3,12 +3,18 @@ spring: hibernate: ddlAuto: update - datasource: - driverClassName: org.postgresql.Driver - platform: postgres - url: ${JDBC_DATABASE_URL}?reWriteBatchedInserts=true - username: ${JDBC_DATABASE_USERNAME} - password: ${JDBC_DATABASE_PASSWORD} + primary: + datasource: + driver: org.postgresql.Driver + url: ${JDBC_DATABASE_URL}?reWriteBatchedInserts=true&sslmode=require + username: ${JDBC_DATABASE_USERNAME} + password: ${JDBC_DATABASE_PASSWORD} + replica: + datasource: + driver: org.postgresql.Driver + url: ${JDBC_REPLICA_DATABASE_URL}?sslmode=require + username: ${JDBC_REPLICA_DATABASE_USERNAME} + password: ${JDBC_REPLICA_DATABASE_PASSWORD} logstash: endpoint: ${LOGSTASH_ENDPOINT} diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 17d03f21..904897dd 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -2,19 +2,18 @@ spring: jpa: hibernate: ddlAuto: create-drop - - datasource: - # driverClassName: org.postgresql.Driver - # platform: postgres - # url: jdbc:postgresql://localhost:5432/testdb - # username: postgres - # password: bitclave - - - driverClassName: org.h2.Driver - url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - username: postgres1 - password: bitclave1 + primary: + datasource: + driver: org.h2.Driver + url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: postgres1 + password: bitclave1 + replica: + datasource: + driver: org.h2.Driver + url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: postgres1 + password: bitclave1 hybrid: nodeUrl: http://localhost:9545/