MariaDB 10.1.2 하위 버전의 소숫점이 포함된 datetime 형식에 주의하자
MariaDB 10.1.2 하위 버전에서는 소숫점을 가진 datetime 값을 저장할 때 독특한 형식을 사용한다. 이 독특한 형식이 데이터의 저장이나 조회 등의 일반적인 작업에서는 문제점으로 드러나지 않는다. 하지만 binlog를 사용해야 하는 일부 작업에서는 문제가 발생할 수 있...
Tomcat 은 효율적인 Connection Pool 관리를 위해 Commons DBCP 보다 개선된 Tomcat DBCP 를 사용합니다. Idle Connection 수를 조정하고 Active 상태가 오래 지속중인 Connection 을 정리할 수 있습니다.
또한 validationInterval 기능을 통해 Idle Connection 이 DBMS 의 wait timeout 을 넘겨 재사용되지 못하는 상황을 막기 위해 wait_timeout 초기화를 위한 validation query 를 주기적으로 발생시키기도 합니다.
이런 기능들을 수행하는데 중요한 역할을 하는 클래스가 앞으로 소개할 PoolCleaner 입니다.
앞에서 Tomcat 의 Resource 설정을 통해 PoolCleaner 의 기능을 제어하는 방법을 정리합니다. 후반부에는 실제 서비스를 운영하면서 경험했던 예외 상황과 이를 해결하기 위한 방법을 공유합니다.
Tomcat 이 제공하는 몇 가지 Resource 설정은 PoolCleaner 의 기능을 제어할 수 있습니다. 실행시간이 오래걸리는 Connection 을 정리하고 Idle Connection 수를 관리하며 DBMS wait timeout 을 초기화하고 체크하기 위한 validation query 를 수행합니다.
// org.apache.tomcat.jdbc.pool.ConnectionPool.java
protected static class PoolCleaner extends TimerTask {
...
@Override
public void run() {
ConnectionPool pool = this.pool.get();
if (pool == null) {
stopRunning();
} else if (!pool.isClosed()) {
try {
if (pool.getPoolProperties().isRemoveAbandoned()
|| pool.getPoolProperties().getSuspectTimeout() > 0)
pool.checkAbandoned();
// Active 상태가 오래 지속중인 Connection 을 정리
if (pool.getPoolProperties().getMinIdle() < pool.idle
.size())
pool.checkIdle(); // Idle Connection 사이즈를 조정
if (pool.getPoolProperties().isTestWhileIdle())
// Idle Connection 을 체크하고 wait_timeout 을 초기화
pool.testAllIdle();
} catch (Exception x) {
log.error("", x);
}
}
}
...
}
<!-- Tomcat context.xml removeAbandoned 설정 예시 -->
<Context>
...
<Resource name="jdbc/Database"
...
removeAbandoned="true"
removeAbandonedTimeout="60" />
</Resource>
...
</Context>
removeAbandoned 설정이 true 라면 PoolCleaner 는 오래 실행중인 Connection 을 체크하고 정리합니다. default 설정은 false 입니다. 실행 시간이 removeAbandonedTimeout 설정보다 길 경우 정리 대상으로 판단합니다.
// org.apache.tomcat.jdbc.pool.ConnectionPool.java
public void checkAbandoned() {
...
long time = con.getTimestamp();
long now = System.currentTimeMillis();
if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {
busy.remove(con);
abandon(con);
setToNull = true;
}
...
}
이 때, 정리대상인 connection 에서 쿼리가 수행중일 수 있습니다. 이런 경우 의도치 않게 진행중인 쿼리가 종료될 수 있으므로 되도록 removeAbandonedTimeout 설정 값은 수행시간보다 큰 값을 사용도록 합니다.
<!-- Tomcat context.xml minIdle 설정 예시 -->
<Context>
...
<Resource name="jdbc/Database"
...
InitialSize="15"
minIdle="15" />
</Resource>
...
</Context>
Connection Pool 은 최초 InitialSize 만큼 생성되고 이후 Idle 커넥션을 minIdle 보다 작게 유지되도록 조정합니다.
// org.apache.tomcat.jdbc.pool.ConnectionPool.java
public void checkIdle(boolean ignoreMinSize) {
...
while ((ignoreMinSize || (idle.size() >= getPoolProperties().getMinIdle()))
&& unlocked.hasNext()) {
...
}
}
<!-- Tomcat context.xml testWhileIdle 설정 예시 -->
<Context>
...
<Resource name="jdbc/Database"
...
testWhileIdle="true"
validationQuery="/* ping */ SELECT 1"
timeBetweenEvictionRunsMillis="5000" />
</Resource>
...
</Context>
testWhileIdle 이 true 라면 validationQuery 에 설정된 쿼리를 수행하여 Connection 의 이상유무를 판단합니다. 이를 통해 문제가 되는 Connection 을 제거하거나 DBMS 상의 Sleep Time 을 초기화시켜 wait_timeout 에 의해 해당 Connection 이 종료되는 것을 방지합니다.
timeBetweenEvictionRunsMillis 설정으로 아래 PoolCleaner 스케쥴의 Period 가 결정됩니다. 또한 Idle Connection 체크를 위한 Query 는 validationQuery 설정이 사용됩니다.
PoolCleanTimer 는 싱글 스레드입니다. 예외 상황으로 인해 PoolCleanTimer 가 중지 된다면 여러 문제가 발생합니다. 특히 PoolCleanTimer 는 한 번 중지되면 재시작이 불가능합니다. 중지 된 상황에서 webapp 이 reload 되면 Timer already cancelled 에러 문구와 함께 IllegalStateException 이 발생합니다.
[http-nio-8080-exec-11] org.apache.naming.NamingContext.lookup Unexpected exception resolving reference
java.lang.IllegalStateException: Timer already cancelled.
이후 Timer 스케쥴이 동작되지 않게되고 DBMS 의 wait_timeout 을 넘어선 Idle Connection 은 사용할 수 없게 됩니다.
간혹 JDBC Driver library 를 Tomcat webapp 의 WEB-INF/lib/ 에서 관리하는 경우가 있습니다. 이 경우 최초 동작시 문제는 없습니다. 다만 webapp reload 시 새로운 ConnectionPool 을 생성하게 되면서 문제가 발생합니다. 이미 생성되었던 ConnectionPool 이 close 처리가 되지 않을 경우 종료된 webapp 의 ClassLoader 를 사용하게 되고 아래와 같은 예외가 발생합니다.
17-Nov-2020 05:56:47.940 정보 [mysql-cj-abandoned-connection-cleanup]
org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading
Illegal access: this web application instance has been stopped already.
이는 라이브러리 위치에 따른 Tomcat 의 Class loading 방식때문입니다.
tomcat/lib 의 라이브러리는 CommonClassloader 에 의해 Class loading 됩니다. 반면에 WEB-INF/lib 의 라이브러리는 각 webapp 에 독립적인 WebaappClassloader 를 통해 Class loading 되기 때문입니다.
JDBC Driver Library 로 인해 발생하는 PoolCleanTimer 중지 문제를 해결하기 위해서 여러 방법이 존재합니다.
tomcat/lib 에 JDBC Driver 가 위치하게 되면 해당 라이브러리는 webapp 에 독립적인 ClassLoader 가 아닌 CommonClassLoader 에 의해 관리됩니다. 이후 webapp 이 reload 될 경우에도 종료된 webapp 이 사용하는 ConnectionPool 의 ClassLoader 가 CommonClassLoader 이므로 정상적으로 close 되게 됩니다.
GlobalNamingResource 를 사용하게 되면 모든 webapp 이 ConnectionPool 을 공유하게 됩니다. 이 경우 webapp 이 reload 되더라도 ConnectionPool 을 다시 생성하지 않아 위 문제가 발생하지 않습니다.
<!-- GlobalNamingResource 사용 예제 -->
<!-- server.xml -->
<GlobalNamingResources ...>
...
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource"
description="Employees Database for HR Applications"/>
...
</GlobalNamingResources>
<!-- WEB-INF/web.xml -->
<resource-ref>
<description>Employees Database for HR Applications</description>
<res-ref-name>jdbc/EmployeeDB</res-ref-name>
<res-ref-type>javax.sql.DataSource</res-ref-type>
<res-auth>Container</res-auth>
</resource-ref>
Resource 에 closeMethod 를 설정해주어 종료된 webapp 의 ConnectionPool 을 clean up 하는 방법이 있습니다.
<!-- Tomcat context.xml closeMethod 설정 예제 -->
<Context>
...
<Resource name="jdbc/Database"
...
closeMethod="close" /> <!-- mysql driver 의 경우 close -->
</Resource>
...
</Context>
위 설정을 사용하면 webapp reload 시 ConnectionPool 을 clean up 하기 위해 closeMethod 의 설정된 메소드를 사용하게 됩니다. 해당 메소드는 벤더사마다 다른 점에 유의합니다.
Tomcat 의 ConnectionPool 관리를 위한 기능 중 PoolCleaner 를 알아보았습니다. 문서상의 설정들이 실제 어떤식으로 동작하는지 확인하는 과정에서 오픈소스인 Tomcat 의 장점을 활용할 수 있었습니다. 특히 PoolCleaner 가 중지된 케이스의 원인은 오픈소스가 아니었다면 파악하기까지 오랜기간이 걸렸을 것입니다.
MariaDB 10.1.2 하위 버전에서는 소숫점을 가진 datetime 값을 저장할 때 독특한 형식을 사용한다. 이 독특한 형식이 데이터의 저장이나 조회 등의 일반적인 작업에서는 문제점으로 드러나지 않는다. 하지만 binlog를 사용해야 하는 일부 작업에서는 문제가 발생할 수 있...
AWS managed 서비스는 각각의 로깅방식을 제공하고 대부분 Cloudwatch logs를 통해 지원한다. API Gateway도 마찬가지로 AWS 콘솔 설정을 통해 실행 로그와 액세스 로그를 기록할 수 있다. 다만 로그 보관주기를 Gateway 콘솔에서 설정할 수 없다는 단점...
ubuntu 서버에서 apt install 사용 시 apt lock 이 발생하며 설치가 실패하는 경우가 간헐적으로 발생합니다. 저의 경우 ubuntu 이미지를 기반으로 진행되는 초반 provisiong 시점에 자주 발생했습니다. [stdout]Waiting for cache loc...
AWS 상에서 다양한 아키텍처를 구성하다보면 서비스의 상태를 모니터링하거나 이벤트 알림을 원하는 채널에서 받을 필요가 있습니다. 원하는 서비스의 콘솔을 통해 제공하는 경우도 있으나 디테일한 설정은 어렵거나 제공되지 않는 경우가 많습니다.
django logging django는 python의 builtin 로깅 모듈을 사용하여 시스템 로깅 작업을 수행합니다. settings.py LOGGING을 통해 설정이 가능합니다. setting.py 에 LOGGING 설정이 없을 경우 아래 예시처럼 default 설정을 제공...
Tomcat 의 ConnectionPool Tomcat 은 효율적인 Connection Pool 관리를 위해 Commons DBCP 보다 개선된 Tomcat DBCP 를 사용합니다. Idle Connection 수를 조정하고 Active 상태가 오래 지속중인 Connection 을...
python testing tool python 에는 널리 알려져 사용되는 여러 도구들이 이미 많습니다. 특히 pytest 는 좀 더 직관적인 assert 구문과 test 결과를 제공하며, 많은 plugin 을 보유하고 있습니다. 하지만 여러 도구들을 알아보기 전에 python t...