Spring Boot Database Connection Leaks Sinhala Guide | ගැටළු හඳුනාගැනීම සහ විසඳීම

Spring Boot Database Connection Leaks Sinhala Guide | ගැටළු හඳුනාගැනීම සහ විසඳීම

ආයුබෝවන්, developer යාළුවනේ! අද අපි කතා කරමු Spring Boot project එකක් කරනකොට ගොඩක් අයට මුහුණ දෙන්න වෙන පොදු ගැටලුවක් ගැන – ඒ තමයි Database Connection Leaks.

මේක ඇහුවම පොඩ්ඩක් අමුතු වගේ දැනුනත්, මේක හරියට හඳුනගෙන fix කරගත්තේ නැත්නම් ඔයාගේ application එකේ performance එකටයි, stability එකටයි ලොකු බලපෑමක් වෙන්න පුළුවන්. හිතන්න, ඔයාගේ app එකේ users ලා වැඩි වෙනකොට, database එකට requests ගොඩක් එනකොට, මේ connection leaks නිසා system එක slow වෙලා, අන්තිමට crash වෙන්නත් පුළුවන්. ඒ කියන්නේ server down වෙන්නත් ඉඩ තියෙනවා. ඉතින්, මේක ලොකු බරපතල තත්ත්වයක්. හැබැයි බය වෙන්න එපා!

අද මේ tutorial එකෙන් අපි connection leak එකක් කියන්නේ මොකක්ද, ඒක හඳුනාගන්නේ කොහොමද, ඒ වගේම අපිම leak එකක් simulate කරලා ඒක fix කරගන්නේ කොහොමද කියලා පැහැදිලිව, ප්‍රායෝගිකව ඉගෙන ගමු. මේක ඔයාගේ දවස්පතා වැඩ වලට ගොඩක් වැදගත් වෙයි. එහෙනම්, අපි පටන් ගමු!

1. Database Connection Leaks කියන්නේ මොකක්ද?

මුලින්ම අපි බලමු database connection leaks කියන්නේ හරියටම මොකක්ද කියලා. Spring Boot වගේ modern applications වල database එකක් එක්ක වැඩ කරනකොට, අපි සාමාන්‍යයෙන් straight forward විදිහට database connections manage කරන්නේ නැහැ. ඒ වෙනුවට අපි connection pool එකක් පාවිච්චි කරනවා.

Connection Pool එකක් කියන්නේ මොකක්ද?

  • හිතන්න, ඔයාගේ application එකට database එකට කනෙක්ට් වෙන්න අවශ්‍යතාවය ඇති වෙනවා. හැම වෙලාවකම අලුතින් connection එකක් හදන එක (establish කරන එක) වෙලාව නාස්ති වෙන, resource-intensive වැඩක්.
  • Connection pool එකකින් වෙන්නේ database connections ටිකක් කලින්ම establish කරලා, ඒවා pool එකක තියාගෙන ඉන්න එක (ready to use).
  • Request එකක් එනකොට pool එකෙන් connection එකක් අරන්, වැඩේ ඉවර වුන ගමන් ඒක pool එකට ආයෙත් දානවා (return කරනවා).
  • මේකෙන් connection establish කරන overhead එක අඩු වෙනවා, application එකේ performance එක වැඩි වෙනවා, ඒ වගේම database එකට එකවර එන connection request ගණනත් manage කරගන්න පුළුවන් වෙනවා. HikariCP කියන්නේ Spring Boot වල default connection pool එක වන අතර, ඒක මේ දේවල් ඉතාම කාර්යක්ෂමව කරනවා.

Connection Leak එකක් වෙන්නේ කොහොමද?

හැබැයි connection leak එකක් කියන්නේ, application එක connection pool එකෙන් connection එකක් අරගෙන, ඒක පාවිච්චි කරලා ඉවර වුනාට පස්සේ ආයෙත් pool එකට release කරන්නේ නැති එකයි. මේක හරියට library එකකින් පොතක් අරගෙන, කියවලා ඉවර වුනාට පස්සේ ආයෙත් library එකට නොදී තියාගෙන ඉන්නවා වගේ වැඩක්. එතකොට අනිත් අයට පොත් ගන්න බැරි වෙනවා, අන්තිමට library එකේ පොත් නැති වෙනවා.

ගොඩක් වෙලාවට මේ වගේ leaks වෙන්නේ developer ගේ අතින් වෙන වැරදීමකින්. Connection එක හරියට close() නොකරන එක, try-catch-finally block එකක් ඇතුලේ finally block එකේ close නොකරන එක, නැත්නම් try-with-resources වගේ modern approaches පාවිච්චි නොකරන එක මේකට හේතු වෙන්න පුළුවන්. සමහර වෙලාවට exception එකක් ආවම code එක finally block එකට නොගිහින් skip වෙලා යන අවස්ථා වලදීත් connection එක close නොවී ඉන්න පුළුවන්.

Connection Leaks වල බලපෑම:

  • Performance Degradation: Connections ක්‍රමයෙන් pool එකෙන් ඉවත් වෙන නිසා, අලුත් requests වලට connection එකක් ගන්න වැඩි වෙලාවක් යනවා.
  • Application Freezes/Crashes: Connection pool එකේ තියෙන connections සේරම ඉවර වුනාම, අලුත් database operations කරන්න බැරි වෙනවා. මේකෙන් SQLException හෝ TimeoutException වගේ errors ඇවිත් application එක crash වෙන්න පුළුවන්.
  • Resource Exhaustion: Database server එකේත් connection limit එකක් තියෙනවා. Leaks නිසා server එක පැත්තෙන් connections ගොඩක් open වෙලා තියෙන්න පුළුවන්.

2. Connection Leaks හඳුනාගන්නේ කොහොමද? (Detection)

Connection leaks හඳුනාගන්න එක ටිකක් අමාරු වෙන්න පුළුවන්, මොකද ඒවා ටිකෙන් ටික තමයි build වෙන්නේ. හැබැයි මේ පොදු ලක්ෂණ ටික ගැන සැලකිලිමත් වුනොත් leak එකක් අඳුරගන්න පුළුවන්.

2.1. Application Performance සහ User Feedback

  • Performance Slowdowns: application එකේ performance එක gradually slow වෙනවා. Database queries වලට යන වෙලාව වැඩි වෙනවා, users ලට response එන්න පරක්කු වෙනවා.
  • Frequent Timeouts: Users ලට 'Service Unavailable' or 'Request Timeout' වගේ errors ලැබෙනවා.

2.2. Application Logs පරීක්ෂා කිරීම

Logs කියන්නේ අපේ හොඳම යාළුවා! application logs බැලුවොත් SQLException එකක් නැත්නම් TimeoutException එකක් දකින්න පුළුවන්. විශේෂයෙන්ම, HikariCP වගේ connection pool එකකින් එන messages වැදගත්:

  • 'Timeout waiting for idle object'
  • 'Connection pool exhausted'
  • 'Failed to obtain JDBC connection'
  • 'HikariPool-1 - Connection is not available, request timed out after X ms.'

මේ වගේ messages දැක්කොත්, connection pool එකේ connections ප්‍රමාණවත් නැහැ කියන එක හෝ connections release වෙන්නේ නැහැ කියන එකට හොඳ ඉඟියක්.

2.3. Monitoring Tools භාවිතය (Spring Boot Actuator, Micrometer, Prometheus/Grafana)

Monitoring tools පාවිච්චි කරනවා නම් (Prometheus, Grafana, Spring Boot Actuator, Micrometer) connection pool metrics වල වෙනස්කම් බලන්න පුළුවන්. මේ metrics ටික ගැන විශේෂයෙන් අවධානය දෙන්න:

  • hikari_connections_active: මේකේ අගය ක්‍රමයෙන් වැඩි වෙනවා නම්, නමුත් අඩුවීමක් නැත්නම්, connection leak එකක් වෙන්න පුළුවන්.
  • hikari_connections_idle: මේකේ අගය ක්‍රමයෙන් අඩු වෙනවා නම්, ඒ කියන්නේ connections pool එකේ හිස් වෙන්නේ නැහැ.
  • hikari_connections_pending: මේකේ අගය වැඩි වෙනවා නම්, connection එකක් එනකන් බලාගෙන ඉන්න requests වැඩි වෙනවා කියන එකයි.
  • hikari_connections_max: Pool එකේ තියෙන connections වල උපරිම ගාන.

මේ metrics වල trend එක (වැඩි වීම හෝ අඩු වීම) අනුව leak එකක් හඳුනාගන්න පුළුවන්. Spring Boot applications වලදී, spring-boot-starter-actuator dependency එක එකතු කරලා, application.properties එකේ management.endpoints.web.exposure.include=* දාලා, /actuator/metrics/hikari.connections endpoint එකෙන් මේ metrics බලන්න පුළුවන්.

2.4. HikariCP Leak Detection Threshold

HikariCP වල leakDetectionThreshold කියන property එක පාවිච්චි කරලා leaks detect කරන්න පුළුවන්. මේකට අපි milliseconds වලින් limit එකක් දෙනවා. ඒ limit එකට වඩා connection එකක් open කරගෙන හිටියොත් warning message එකක් log කරනවා. මේක ඉතාම වැදගත් feature එකක් leaks කලින්ම අඳුරගන්න.

spring.datasource.hikari.leakDetectionThreshold=20000 # 20 seconds

මේකෙන් වෙන්නේ connection එකක් pool එකෙන් අරන්, මේ threshold එකට වඩා වෙලාවක් පාවිච්චි කරලා තවමත් ආයෙත් release කරලා නැත්නම්, HikariCP එකෙන් warning message එකක් log කරන එකයි. මේක production environment එකේදී connections release නොකරන අවස්ථා අඳුරගන්න ගොඩක් උදව් වෙනවා.

3. Leak එකක් Simulate කරමු (Practical Example)

දැන් අපි බලමු connection leak එකක් simulate කරලා ඒක ඇත්තටම වෙන්නේ කොහොමද කියලා. මේකෙන් leak එකක ස්වභාවය ගැන හොඳ අවබෝධයක් ලැබෙයි.

3.1. Spring Boot Project Setup

මුලින්ම අපි Spring Boot project එකක් හදාගමු. අපි PostgreSQL database එකක් පාවිච්චි කරමු. ඔයාට H2 වගේ in-memory database එකක් පාවිච්චි කරන්නත් පුළුවන්. pom.xml එකට අවශ්‍ය dependencies එකතු කරගන්න:

<!-- pom.xml additions -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Spring Boot Actuator for monitoring (optional but recommended) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Other Spring Boot starters as needed -->

application.properties එකට database config එක දාමු. අපි connection pool එකේ size එක අඩු කරමු (උදා: 5), එතකොට leak එකක් reproduce කරන එක පහසු වෙනවා. ඒ වගේම leakDetectionThreshold එකක් දාමු warnings බලන්න.

spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=testuser
spring.datasource.password=testpassword
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# HikariCP Configuration for demonstration
spring.datasource.hikari.maximumPoolSize=5 # Small pool for easy reproduction
spring.datasource.hikari.minimumIdle=2
spring.datasource.hikari.connectionTimeout=5000 # 5 seconds
spring.datasource.hikari.idleTimeout=30000 # 30 seconds
spring.datasource.hikari.leakDetectionThreshold=5000 # 5 seconds for quick detection

# Actuator exposure
management.endpoints.web.exposure.include=*

ඔයාගේ local machine එකේ PostgreSQL database එකක් run වෙලා තියෙන්න ඕනේ, ඒ වගේම testdb නමින් database එකකුත් testuser/testpassword කියන credentials වලින් access වෙන්නත් ඕනේ. නැත්නම් ඒ විස්තර ඔයාගේ database එකට අනුව හදාගන්න.

3.2. Leaky Service එකක් නිර්මාණය කිරීම

දැන් අපි leaky service එකක් හදමු. මේ service එකේදී අපි DataSource එකෙන් connection එකක් අරගෙන, ඒක close කරන්නේ නැහැ. මේක තමයි leak එකට හේතුව.

// LeakyService.java
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;

@Service
public class LeakyService {

    private final DataSource dataSource;

    public LeakyService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void createLeak() {
        Connection connection = null;
        try {
            System.out.println("Attempting to get a connection...");
            connection = dataSource.getConnection(); // Connection obtained but NOT closed!
            System.out.println("Connection obtained: " + connection);

            // Simulate some database operation that takes time
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT pg_sleep(3), 1;"); // Sleep for 3 seconds
            if (resultSet.next()) {
                System.out.println("Result from leaky operation: " + resultSet.getInt(2));
            }
            // IMPORTANT: No close() calls for statement, resultSet, or connection!
            // This causes the connection to remain open and not returned to the pool.
            System.out.println("Leak created. Connection is NOT returned to the pool.");
            
            // Wait longer than leakDetectionThreshold to ensure the warning is logged
            TimeUnit.SECONDS.sleep(6); // Total sleep: 3s (DB) + 6s (Java) = 9s > 5s leakDetectionThreshold

        } catch (SQLException e) {
            System.err.println("SQL Exception in leaky service: " + e.getMessage());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("Thread interrupted during leak simulation.");
        } finally {
            // Intentionally missing connection.close() here to simulate a leak
            // If you uncomment connection.close(), the leak won't happen
            // if (connection != null) {
            //     try { connection.close(); } catch (SQLException e) { e.printStackTrace(); }
            // }
        }
    }
}

3.3. Leak Controller එක

දැන් අපි Controller එකක් හදලා මේ createLeak() method එක repeat request කිහිපයකට call කරමු. මෙතනදී අපි /create-multiple-leaks endpoint එකෙන් pool size එකට වඩා වැඩි request ප්‍රමාණයක් යවනවා.

// LeakController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LeakController {

    private final LeakyService leakyService;

    public LeakController(LeakyService leakyService) {
        this.leakyService = leakyService;
    }

    @GetMapping("/create-leak")
    public String triggerLeak() {
        leakyService.createLeak();
        return "Leak attempt initiated. Check logs for warnings/errors.";
    }

    @GetMapping("/create-multiple-leaks")
    public String triggerMultipleLeaks() {
        // Call createLeak multiple times, exceeding the pool size (which is 5 in our config)
        for (int i = 0; i < 7; i++) { 
            System.out.println("Triggering leak #" + (i + 1));
            leakyService.createLeak();
        }
        return "Multiple leak attempts initiated. Expect connection pool exhaustion and timeouts.";
    }
}

3.4. Leak එකක් අත්දැකීම

මේ application එක run කරලා /create-multiple-leaks endpoint එකට browser එකෙන් හෝ Postman/curl එකෙන් request එකක් යවන්න. ඔයාට ටික වෙලාවකින් console එකේ මේ වගේ warnings සහ errors දකින්න ලැබෙයි:

  • මුලින්ම, leakDetectionThreshold එක පැනපු ගමන් මේ වගේ warnings: 'HikariPool-1 - Connection leak detection initiated for connection org.postgresql.jdbc.PgConnection'
  • ඊට පස්සේ, pool එකේ තියෙන connections සේරම ඉවර වුනාම, 'Timeout waiting for connection from pool' or 'Connection is not available, request timed out' වගේ errors.

මේකෙන් පෙන්නුම් කරන්නේ connection pool එකේ තියෙන connections ඉවර වෙලා, අලුත් connections ගන්න බැරි තත්ත්වයකට ඇවිත් තියෙනවා කියන එකයි. ඔයාට /actuator/metrics/hikari.connections endpoint එක බලලා active connections ප්‍රමාණය වැඩි වෙන හැටිත්, idle connections ප්‍රමාණය අඩු වෙන හැටිත් දකින්න පුළුවන්.

4. Leak එකක් Fix කරමු (Resolution)

හරි, දැන් අපි දන්නවා leak එකක් වෙන්නේ කොහොමද කියලා. දැන් බලමු ඒක fix කරගන්නේ කොහොමද කියලා. මේකට ප්‍රධාන වශයෙන් ක්‍රම දෙකක් තියෙනවා.

4.1. try-with-resources භාවිතය (Manual Connection Management සඳහා)

Database connections, statements, result sets වගේ resources පාවිච්චි කරලා ඉවර වුන ගමන්ම ඒවා close කරන එක ඉතාම වැදගත්. මේ සඳහා හොඳම ක්‍රමය තමයි Java 7 වල හඳුන්වා දුන්න try-with-resources statement එක පාවිච්චි කරන එක.

try-with-resources වලදී, try block එක ඇතුලේ initialize කරන resources, try block එක ඉවර වුන ගමන් automatically close වෙනවා. ඒක error එකක් ආවත් close වෙනවා, සාමාන්‍ය විදිහට ඉවර වුනත් close වෙනවා. ඒ නිසා අපිට finally block එකක් ලියලා connection.close(), statement.close() වගේ දේවල් manually කරන්න ඕනේ නැහැ. මේක code එක cleaner කරනවා වගේම resource management mistakes අඩු කරනවා.

අපි දැන් කලින් හදපු LeakyService එක FixedService එකක් විදිහට හදමු:

// FixedService.java
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

@Service
public class FixedService {

    private final DataSource dataSource;

    public FixedService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void fixLeak() {
        try (Connection connection = dataSource.getConnection(); // Connection will be closed automatically
             Statement statement = connection.createStatement(); // Statement will be closed automatically
             ResultSet resultSet = statement.executeQuery("SELECT 1")) { // ResultSet will be closed automatically

            if (resultSet.next()) {
                System.out.println("Result from fixed operation: " + resultSet.getInt(1));
            }
            System.out.println("Operation completed. All resources closed and connection returned to the pool.");
        } catch (SQLException e) {
            System.err.println("SQL Exception in fixed service: " + e.getMessage());
        }
    }
}

මේ FixedService එක LeakController එකේ පාවිච්චි කරන්න. එවිට /create-multiple-leaks request එකක් යැව්වත්, leaks වෙන්නේ නැහැ. Logs වල warnings හෝ errors දකින්න ලැබෙන්නේ නැහැ.

4.2. Spring Data JPA / JDBC Template වැනි Abstractions භාවිතය

මතක තියාගන්න, Spring Data JPA, Spring JDBC Template, Hibernate වගේ Spring Framework එකේ abstractions පාවිච්චි කරනකොට, connection management එක Spring විසින්ම handle කරනවා. ඒ නිසා අපිට manually connections close කරන්න ඕනේ වෙන්නේ නැහැ. මේ frameworks design කරලා තියෙන්නේ transactions සහ connection lifecycle එක නිවැරදිව manage කරන්නයි. මේ නිසා leak එකක් වෙන්න තියෙන ඉඩකඩ ගොඩක් අඩුයි. මේ වගේ manual DataSource.getConnection() calls කරන්නේ නැතිව ඉන්න එක හොඳම පුරුද්දක්.

උදාහරණයක් ලෙස, Spring Data JPA repository එකක් පාවිච්චි කරනකොට, ඔයාට මෙහෙම ලිව්වම ඇති:

// Example using Spring Data JPA (No manual connection handling needed)
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Spring Data JPA handles connection management for these methods
    User findByEmail(String email);
}

// In a service method:
@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserDetails(String email) {
        return userRepository.findByEmail(email);
    } // Connection is automatically managed and returned to pool
}

මේ විදිහට abstraction layer එකක් පාවිච්චි කරන එක security, performance, සහ code maintainability අතින් ගොඩක් වාසිදායකයි.

4.3. HikariCP Configuration Optimizations

HikariCP configuration වලදී මේ properties හරියට set කරන එකත් connection leaks minimize කරගන්නත්, ඒවා detect කරගන්නත් වැදගත්:

  • spring.datasource.hikari.maximumPoolSize: Pool එකේ තියෙන්න පුළුවන් උපරිම connections ගාන. මේක ඕනාවට වඩා වැඩි වුනොත් database එකට බර වැඩියි. අඩු වුනොත් bottlenecks ඇති වෙන්න පුළුවන්.
  • spring.datasource.hikari.minimumIdle: Pool එකේ හැමවෙලේම තියෙන්න ඕනේ idle connections ගාන.
  • spring.datasource.hikari.connectionTimeout: Connection එකක් pool එකෙන් ගන්න පුළුවන් උපරිම වෙලාව (milliseconds වලින්). මේ වෙලාව ඇතුළත connection එකක් ගන්න බැරි වුනොත් timeout එකක් දෙනවා. Leak එකක් වෙලාවක මේක තමයි අපිට මුලින්ම පේන error එකක්.
  • spring.datasource.hikari.idleTimeout: Idle වෙලා තියෙන connection එකක් close කරලා pool එකෙන් අයින් කරන වෙලාව. (minimumIdle එකට වඩා වැඩි connections සඳහා).
  • spring.datasource.hikari.maxLifetime: Connection එකක් pool එකේ ජීවත් වෙන්න පුළුවන් උපරිම වෙලාව. Database side එකෙන් connection එකක් terminate කරනවා නම්, මේක ඒකට කලින් තියන්න හොඳයි.
  • spring.datasource.hikari.leakDetectionThreshold: මේක ගැන අපි කලින් කතා කළා. Connection එකක් pool එකෙන් අරන් මේ වෙලාවට වඩා වැඩියෙන් පාවිච්චි කරනවා නම් warning එකක් log කරනවා. Production environment එකක මේක 15-30 seconds වගේ අගයකට තියෙන එක හොඳයි.

අවසාන වශයෙන් (Conclusion)

ඉතින් developer යාළුවනේ, අපි අද Spring Boot වල database connection leaks ගැන ගොඩක් දේවල් ඉගෙන ගත්තා. Connection leaks කියන්නේ පොඩි දෙයක් වගේ පෙනුනත්, ඔයාගේ application එකේ stability එකටයි, performance එකටයි ලොකු බලපෑමක් කරන්න පුළුවන් දෙයක්. ඒ නිසා, database operations කරනකොට connections හරියට close වෙනවාද කියලා දෙපාරක් හිතන එක ඉතාම වැදගත්.

try-with-resources පාවිච්චි කරන එක, Spring Data JPA වගේ abstractions හරහා database එක එක්ක වැඩ කරන එක හොඳම practices විදිහට හඳුන්වන්න පුළුවන්. ඒ වගේම HikariCP configuration එක නිවැරදිව කරන එකත්, විශේෂයෙන්ම leakDetectionThreshold එකක් set කරලා තියෙන එකත්, leaks කලින්ම අඳුරගෙන විසඳගන්න ගොඩක් උදව් වෙනවා.

ඔයාගේ projects වලට මේ ඉගෙන ගත්ත දේවල් apply කරලා බලන්න. මොකද, අතින් කරලා බලනකොට තමයි හොඳටම මතක හිටින්නේ. ඔයාට මේ ගැන තියෙන ප්‍රශ්න, අත්දැකීම් පහළ comment section එකේ share කරන්න. අපි හැමෝම එකතු වෙලා ඉගෙන ගමු. එහෙනම්, තවත් අලුත් දෙයක් එක්ක ඉක්මනටම හම්බෙමු! සුභ දවසක්!