Skip to content

Commit

Permalink
feat: propagation supports is not recommended without open db transac…
Browse files Browse the repository at this point in the history
…tion and can lead to connection leak issues. (#309)

Co-authored-by: Mateusz Czeladka <[email protected]>
  • Loading branch information
matiwinnetou and Mateusz Czeladka authored Mar 5, 2025
1 parent dc55d83 commit 5c467fe
Show file tree
Hide file tree
Showing 19 changed files with 80 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.account.model.entity.AddressUtxoEntity;
import org.cardanofoundation.rosetta.api.account.model.entity.UtxoId;

@Repository
public interface AddressUtxoRepository extends JpaRepository<AddressUtxoEntity, UtxoId> {

@Query(value =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.openapitools.client.model.Currency;

Expand All @@ -24,7 +23,7 @@
@Slf4j
@RequiredArgsConstructor
@Component
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@Transactional(readOnly = true)
public class LedgerAccountServiceImpl implements LedgerAccountService {

private final AddressUtxoRepository addressUtxoRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class BlockApiImpl implements BlockApi {

@Override
public ResponseEntity<BlockResponse> block(@RequestBody BlockRequest blockRequest) {
if(offlineMode) {
if (offlineMode) {
throw ExceptionFactory.notSupportedInOfflineMode();
}
networkService.verifyNetworkRequest(blockRequest.getNetworkIdentifier());
Expand All @@ -51,7 +51,7 @@ public ResponseEntity<BlockResponse> block(@RequestBody BlockRequest blockReques
@Override
public ResponseEntity<BlockTransactionResponse> blockTransaction(
@RequestBody BlockTransactionRequest blockReq) {
if(offlineMode) {
if (offlineMode) {
throw ExceptionFactory.notSupportedInOfflineMode();
}
networkService.verifyNetworkRequest(blockReq.getNetworkIdentifier());
Expand All @@ -63,6 +63,6 @@ public ResponseEntity<BlockTransactionResponse> blockTransaction(
BlockTx blockTx = blockService.getBlockTransaction(blockId, blockHash, txHash);

return ResponseEntity.ok(mapper.mapToBlockTransactionResponse(blockTx));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.BlockEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.projection.BlockIdentifierProjection;


@Repository
public interface BlockRepository extends JpaRepository<BlockEntity, Long> {

@Query("FROM BlockEntity b WHERE b.prev.hash IS NULL ORDER BY b.number ASC LIMIT 1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.DelegationEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.DelegationId;

@Repository
public interface DelegationRepository extends JpaRepository<DelegationEntity, DelegationId> {

List<DelegationEntity> findByTxHashIn(List<String> txHashes);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.EpochParamEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.ProtocolParamsEntity;

@Repository
public interface EpochParamRepository extends JpaRepository<EpochParamEntity, Integer> {

@Query("SELECT e.params FROM EpochParamEntity e ORDER BY e.epoch DESC LIMIT 1")
ProtocolParamsEntity findLatestProtocolParams();

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.cardanofoundation.rosetta.api.block.model.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.InvalidTransactionEntity;

@Repository
public interface InvalidTransactionRepository extends JpaRepository<InvalidTransactionEntity, String> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.LocalProtocolParamsEntity;

@Repository
public interface LocalProtocolParamsRepository extends JpaRepository<LocalProtocolParamsEntity, Long> {

@Query(value = """
SELECT p FROM LocalProtocolParamsEntity p ORDER BY p.epoch DESC LIMIT 1
"""
)
Optional<LocalProtocolParamsEntity> getLocalProtocolParams();

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.PoolRegistrationEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.PoolRegistrationId;

@Repository
public interface PoolRegistrationRepository extends
JpaRepository<PoolRegistrationEntity, PoolRegistrationId> {

List<PoolRegistrationEntity> findByTxHashIn(List<String> txHashes);

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.account.model.entity.UtxoId;
import org.cardanofoundation.rosetta.api.block.model.entity.TxInputEntity;

@Repository
public interface TxInputRepository extends JpaRepository<TxInputEntity, UtxoId> {

@Query(value = """
SELECT spentTxHash FROM TxInputEntity
WHERE txHash = :txHash AND outputIndex = :outputIndex
""")
List<String> findSpentTxHashByUtxoKey(@Param("txHash") String txHash, @Param("outputIndex") Integer outputIndex);

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.TxnEntity;

@Repository
public interface TxRepository extends JpaRepository<TxnEntity, Long> {

List<TxnEntity> findTransactionsByBlockHash(@Param("blockHash") String blockHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import org.cardanofoundation.rosetta.api.block.model.entity.WithdrawalEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.WithdrawalId;

@Repository
public interface WithdrawalRepository extends JpaRepository<WithdrawalEntity, WithdrawalId> {

List<WithdrawalEntity> findByTxHashIn(List<String> txHashes);

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import org.cardanofoundation.rosetta.api.block.model.domain.Block;
Expand All @@ -17,7 +16,7 @@
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@Transactional(readOnly = true)
public class BlockServiceImpl implements BlockService {

private final LedgerBlockService ledgerBlockService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.validation.constraints.NotNull;
Expand All @@ -15,7 +16,6 @@
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.lang3.ObjectUtils;

Expand All @@ -28,32 +28,18 @@
import org.cardanofoundation.rosetta.api.block.model.domain.Block;
import org.cardanofoundation.rosetta.api.block.model.domain.BlockIdentifierExtended;
import org.cardanofoundation.rosetta.api.block.model.domain.BlockTx;
import org.cardanofoundation.rosetta.api.block.model.entity.BlockEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.DelegationEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.PoolRegistrationEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.PoolRetirementEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.StakeRegistrationEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.TxnEntity;
import org.cardanofoundation.rosetta.api.block.model.entity.WithdrawalEntity;
import org.cardanofoundation.rosetta.api.block.model.repository.BlockRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.DelegationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.InvalidTransactionRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.PoolRegistrationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.PoolRetirementRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.StakeRegistrationRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.TxRepository;
import org.cardanofoundation.rosetta.api.block.model.repository.WithdrawalRepository;
import org.cardanofoundation.rosetta.api.block.model.entity.*;
import org.cardanofoundation.rosetta.api.block.model.repository.*;
import org.cardanofoundation.rosetta.common.exception.ExceptionFactory;
import org.cardanofoundation.rosetta.common.services.ProtocolParamService;

import static java.util.concurrent.TimeUnit.MINUTES;

@Slf4j
@RequiredArgsConstructor
@Component
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@Transactional(readOnly = true)
public class LedgerBlockServiceImpl implements LedgerBlockService {

private final ProtocolParamService protocolParamService;

private final BlockRepository blockRepository;
private final TxRepository txRepository;
private final StakeRegistrationRepository stakeRegistrationRepository;
Expand Down Expand Up @@ -109,6 +95,7 @@ private Block toModelFrom(BlockEntity blockEntity) {
TransactionInfo fetched = findByTxHash(transactions);
Map<UtxoKey, AddressUtxoEntity> utxoMap = getUtxoMapFromEntities(fetched);
transactions.forEach(tx -> populateTransaction(tx, fetched, utxoMap));

return model;
}

Expand All @@ -134,6 +121,7 @@ public List<BlockTx> mapTxnEntitiesToBlockTxList(List<TxnEntity> txList) {
TransactionInfo fetched = findByTxHash(transactions);
Map<UtxoKey, AddressUtxoEntity> utxoMap = getUtxoMapFromEntities(fetched);
transactions.forEach(tx -> populateTransaction(tx, fetched, utxoMap));

return transactions;
}

Expand Down Expand Up @@ -170,32 +158,25 @@ public BlockIdentifierExtended findGenesisBlockIdentifier() {

private TransactionInfo findByTxHash(List<BlockTx> transactions) {
List<String> txHashes = transactions.stream().map(BlockTx::getHash).toList();

List<String> utxHashes = transactions
.stream()
.flatMap(t -> Stream.concat(t.getInputs().stream(), t.getOutputs().stream()))
.map(Utxo::getTxHash)
.toList();

try (var executorService = Executors.newFixedThreadPool(12)) {
Future<List<AddressUtxoEntity>> utxos = executorService.submit(() ->
addressUtxoRepository.findByTxHashIn(utxHashes));
Future<List<StakeRegistrationEntity>> sReg = executorService.submit(() ->
stakeRegistrationRepository.findByTxHashIn(txHashes));
Future<List<DelegationEntity>> delegations = executorService.submit(() ->
delegationRepository.findByTxHashIn(txHashes));
Future<List<PoolRegistrationEntity>> pReg = executorService.submit(() ->
poolRegistrationRepository.findByTxHashIn(txHashes));
Future<List<PoolRetirementEntity>> pRet = executorService.submit(() ->
poolRetirementRepository.findByTxHashIn(txHashes));
Future<List<WithdrawalEntity>> withdrawals = executorService.submit(() ->
withdrawalRepository.findByTxHashIn(txHashes));

return new TransactionInfo(utxos.get(), sReg.get(), delegations.get(), pReg.get(), pRet.get(),
withdrawals.get());
} catch (InterruptedException | ExecutionException e) {
List<String> utxHashes = transactions.stream()
.flatMap(t -> Stream.concat(t.getInputs().stream(), t.getOutputs().stream()))
.map(Utxo::getTxHash)
.toList();

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
var utxos = executor.submit(() -> addressUtxoRepository.findByTxHashIn(utxHashes));
var sReg = executor.submit(() -> stakeRegistrationRepository.findByTxHashIn(txHashes));
var delegations = executor.submit(() -> delegationRepository.findByTxHashIn(txHashes));
var pReg = executor.submit(() -> poolRegistrationRepository.findByTxHashIn(txHashes));
var pRet = executor.submit(() -> poolRetirementRepository.findByTxHashIn(txHashes));
var withdrawals = executor.submit(() -> withdrawalRepository.findByTxHashIn(txHashes));

return new TransactionInfo(utxos.get(10, MINUTES), sReg.get(10, MINUTES), delegations.get(10, MINUTES), pReg.get(10, MINUTES), pRet.get(10, MINUTES), withdrawals.get(10, MINUTES));
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.error("Error fetching transaction data", e);

Thread.currentThread().interrupt();

throw ExceptionFactory.unspecifiedError("Error fetching transaction data");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class CardanoConstructionServiceImpl implements CardanoConstructionServic
private final RestTemplate restTemplate;
private final OfflineSlotService offlineSlotService;

private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor(); // TODO this probably gives more overhead than it should

@Value("${cardano.rosetta.NODE_SUBMIT_API_PORT}")
private int nodeSubmitApiPort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.openapitools.client.model.Operator;

Expand All @@ -26,7 +25,7 @@
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@Transactional(readOnly = true)
public class LedgerSearchServiceImpl implements LedgerSearchService {

private final TxRepository txRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.lang3.ObjectUtils;
import org.openapitools.client.model.AccountIdentifier;
Expand All @@ -25,7 +24,7 @@
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
@Transactional(readOnly = true)
public class SearchServiceImpl implements SearchService {

private final BlockMapper blockMapper;
Expand Down
Loading

0 comments on commit 5c467fe

Please sign in to comment.