diff --git a/Account-Service/pom.xml b/Account-Service/pom.xml index 3d4401c..25152a7 100644 --- a/Account-Service/pom.xml +++ b/Account-Service/pom.xml @@ -42,6 +42,14 @@ org.springframework.cloud spring-cloud-starter-openfeign + + org.springframework.boot + spring-boot-starter-cache + + + com.github.ben-manes.caffeine + caffeine + org.springframework.boot @@ -64,6 +72,11 @@ spring-boot-starter-test test + + com.h2database + h2 + runtime + diff --git a/Account-Service/src/main/java/org/training/account/service/AccountServiceApplication.java b/Account-Service/src/main/java/org/training/account/service/AccountServiceApplication.java index 1949174..151beac 100644 --- a/Account-Service/src/main/java/org/training/account/service/AccountServiceApplication.java +++ b/Account-Service/src/main/java/org/training/account/service/AccountServiceApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients +@EnableCaching public class AccountServiceApplication { public static void main(String[] args) { diff --git a/Account-Service/src/main/java/org/training/account/service/external/TransactionService.java b/Account-Service/src/main/java/org/training/account/service/external/TransactionService.java index 497582b..0b00281 100644 --- a/Account-Service/src/main/java/org/training/account/service/external/TransactionService.java +++ b/Account-Service/src/main/java/org/training/account/service/external/TransactionService.java @@ -8,7 +8,7 @@ import java.util.List; -@FeignClient(name = "transaction-service", configuration = FeignConfiguration.class) +@FeignClient(name = "transaction-service", url = "http://localhost:8082", configuration = FeignConfiguration.class) public interface TransactionService { /** diff --git a/Account-Service/src/main/java/org/training/account/service/service/implementation/AccountServiceImpl.java b/Account-Service/src/main/java/org/training/account/service/service/implementation/AccountServiceImpl.java index 13728fb..486761c 100644 --- a/Account-Service/src/main/java/org/training/account/service/service/implementation/AccountServiceImpl.java +++ b/Account-Service/src/main/java/org/training/account/service/service/implementation/AccountServiceImpl.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.training.account.service.exception.*; @@ -162,6 +163,7 @@ public String getBalance(String accountNumber) { * @return A list of transaction responses */ @Override + @Cacheable(value = "transactions", key = "#accountId") public List getTransactionsFromAccountId(String accountId) { return transactionService.getTransactionsFromAccountId(accountId); @@ -213,4 +215,4 @@ public AccountDto readAccountByUserId(Long userId) { return accountDto; }).orElseThrow(ResourceNotFound::new); } -} \ No newline at end of file +} diff --git a/Account-Service/src/main/resources/application.yml b/Account-Service/src/main/resources/application.yml index 094aca9..208a4a9 100644 --- a/Account-Service/src/main/resources/application.yml +++ b/Account-Service/src/main/resources/application.yml @@ -7,17 +7,30 @@ spring: not_found: 404 datasource: - url: jdbc:mysql://localhost:3306/account_service - username: root - password: root + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: password jpa: + database-platform: org.hibernate.dialect.H2Dialect hibernate: - ddl-auto: update - show-sql: true - properties: - hibernate: - format_sql: true + ddl-auto: create-drop + show-sql: false + + cache: + type: caffeine + caffeine: + spec: maximumSize=1000,expireAfterWrite=5m + cache-names: + - transactions server: port: 8081 + +# Direct URL configuration for Feign clients +feign: + client: + config: + transaction-service: + url: http://localhost:8082 diff --git a/Account-Service/src/test/java/org/training/account/service/CachePerformanceTest.java b/Account-Service/src/test/java/org/training/account/service/CachePerformanceTest.java new file mode 100644 index 0000000..e283588 --- /dev/null +++ b/Account-Service/src/test/java/org/training/account/service/CachePerformanceTest.java @@ -0,0 +1,91 @@ +package org.training.account.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.training.account.service.external.TransactionService; +import org.training.account.service.model.dto.external.TransactionResponse; +import org.training.account.service.service.AccountService; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@SpringBootTest +@ActiveProfiles("test") +public class CachePerformanceTest { + + @Autowired + private AccountService accountService; + + @MockBean + private TransactionService transactionService; + + @Test + public void testTransactionCachePerformance() { + String testAccountId = "TEST_ACCOUNT_001"; + + List mockTransactions = Arrays.asList( + TransactionResponse.builder() + .referenceId("TXN001") + .accountId(testAccountId) + .transactionType("DEPOSIT") + .amount(BigDecimal.valueOf(1000)) + .localDateTime(LocalDateTime.now()) + .transactionStatus("COMPLETED") + .comments("Test transaction") + .build() + ); + + when(transactionService.getTransactionsFromAccountId(anyString())) + .thenAnswer(invocation -> { + Thread.sleep(100); + return mockTransactions; + }); + + System.out.println("=== Cache Performance Test ==="); + + long startTime1 = System.currentTimeMillis(); + List transactions1 = accountService.getTransactionsFromAccountId(testAccountId); + long endTime1 = System.currentTimeMillis(); + long firstCallTime = endTime1 - startTime1; + + System.out.println("First call (cache miss): " + firstCallTime + "ms"); + System.out.println("Transactions found: " + (transactions1 != null ? transactions1.size() : 0)); + + long startTime2 = System.currentTimeMillis(); + List transactions2 = accountService.getTransactionsFromAccountId(testAccountId); + long endTime2 = System.currentTimeMillis(); + long secondCallTime = endTime2 - startTime2; + + System.out.println("Second call (cache hit): " + secondCallTime + "ms"); + System.out.println("Transactions found: " + (transactions2 != null ? transactions2.size() : 0)); + + if (firstCallTime > 0) { + double improvement = ((double)(firstCallTime - secondCallTime) / firstCallTime) * 100; + System.out.println("Performance improvement: " + String.format("%.1f", improvement) + "%"); + + if (improvement > 90) { + System.out.println("✅ Cache performance target achieved (>90% improvement)"); + } else if (improvement > 50) { + System.out.println("⚠️ Cache working but improvement below target (<90%)"); + } else { + System.out.println("❌ Cache may not be working effectively"); + } + } + + long startTime3 = System.currentTimeMillis(); + List transactions3 = accountService.getTransactionsFromAccountId(testAccountId); + long endTime3 = System.currentTimeMillis(); + long thirdCallTime = endTime3 - startTime3; + + System.out.println("Third call (cache hit): " + thirdCallTime + "ms"); + System.out.println("=== Test Complete ==="); + } +} diff --git a/Account-Service/src/test/resources/application-test.yml b/Account-Service/src/test/resources/application-test.yml new file mode 100644 index 0000000..0b86dc2 --- /dev/null +++ b/Account-Service/src/test/resources/application-test.yml @@ -0,0 +1,39 @@ +spring: + application: + name: account-service-test + ok: 200 + not_found: 404 + bad_request: 400 + conflict: 409 + + datasource: + url: jdbc:h2:mem:testdb + driver-class-name: org.h2.Driver + username: sa + password: password + + jpa: + database-platform: org.hibernate.dialect.H2Dialect + hibernate: + ddl-auto: create-drop + show-sql: false + + cache: + type: caffeine + caffeine: + spec: maximumSize=100,expireAfterWrite=1m + cache-names: + - transactions + +# Mock external services for testing +feign: + client: + config: + default: + connectTimeout: 5000 + readTimeout: 5000 + +logging: + level: + org.training.account.service: DEBUG + org.springframework.cache: DEBUG diff --git a/test-cache-performance.sh b/test-cache-performance.sh new file mode 100755 index 0000000..a7255f0 --- /dev/null +++ b/test-cache-performance.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +echo "=== Cache Performance Testing Script ===" +echo "This script will test the cache implementation by making HTTP requests" +echo + +ACCOUNT_ID="ACC0000001" +BASE_URL="http://localhost:8081" +ENDPOINT="/accounts/${ACCOUNT_ID}/transactions" + +echo "Testing endpoint: ${BASE_URL}${ENDPOINT}" +echo + +make_request() { + local request_num=$1 + echo "Request #${request_num}:" + + start_time=$(date +%s%3N) + response=$(curl -s -w "%{http_code}" -o /tmp/response_${request_num}.json "${BASE_URL}${ENDPOINT}") + end_time=$(date +%s%3N) + + duration=$((end_time - start_time)) + + if [ "$response" = "200" ]; then + transaction_count=$(cat /tmp/response_${request_num}.json | jq '. | length' 2>/dev/null || echo "unknown") + echo " ✅ Success (${duration}ms) - Transactions: ${transaction_count}" + else + echo " ❌ Failed (HTTP ${response}) - Time: ${duration}ms" + fi + + echo " Response time: ${duration}ms" + return $duration +} + +echo "Making 5 consecutive requests to test cache performance..." +echo + +times=() +for i in {1..5}; do + make_request $i + times+=($?) + echo + sleep 1 +done + +echo "=== Performance Analysis ===" +echo "Request times: ${times[@]}ms" + +if [ ${#times[@]} -ge 2 ]; then + first_time=${times[0]} + second_time=${times[1]} + + if [ $first_time -gt 0 ]; then + improvement=$(( (first_time - second_time) * 100 / first_time )) + echo "Performance improvement from 1st to 2nd request: ${improvement}%" + + if [ $improvement -gt 90 ]; then + echo "✅ Cache performance target achieved (>90% improvement)" + elif [ $improvement -gt 50 ]; then + echo "⚠️ Cache working but improvement below target" + else + echo "❌ Cache may not be working effectively" + fi + fi +fi + +echo +echo "=== Test Complete ==="