2024년 6월 6일 목요일

Spring Boot와 MyBatis 환경에서 롤백이 되지 않는 문제

 상황은 여러 개의 insert가 있는 경우, 앞의 쿼리는 정상적으로 insert되었지만

 뒤의 insert가 실패할 때 앞의 쿼리가 롤백되지 않고 insert된 상태로 남아 있는 문제였다. 


원인은 MyBatis의 autocommit이 기본적으로 true로 설정되어 있어 발생한 문제였다.

쿼리가 실행될 때마다 autocommit이 실행되어 롤백이 되지 않는 현상이 있었다.


해결책으로는 SqlSessionFactory를 직접 받아서 autocommit을 false로 설정하고, 

try-catch 블록을 사용해 명시적으로 롤백을 선언하는 방법을 사용했다.


// SqlSessionFactory를 주입받습니다.

private final SqlSessionFactory sqlSessionFactory;


...


@Transactional(rollbackFor = Exception.class)

public void 메소드() {

    // autocommit을 false로 설정하여 SqlSession을 생성합니다.

    SqlSession sqlSession = sqlSessionFactory.openSession(false);


    try {

        // 여러 insert 작업을 수행합니다.

        insert;

        insert;

    } catch (Exception ex) {

        // 예외 발생 시 롤백을 수행합니다.

        sqlSession.rollback();

        throw new RuntimeException(ex);

    } finally {

        // 세션을 닫아줍니다.

        sqlSession.close();

    }

}


SqlSession을 통해 직접 롤백 처리를 했기 때문에 @Transactional 어노테이션은 필요 없을 것 같다.

 

2022년 2월 11일 금요일

mysqldump 중 에러 발생. ERROR 1102 (42000): Incorrect database name '#mysql50#.gnome2'

아래와 같이 mysql 전체 백업을 진행하려고 하였다.

mysqldump -uroot -p'daims102938!' --all-databases > mysqldump.sql

에러가 발생.
ERROR 1102 (42000): Incorrect database name '#mysql50#.gnome2'

mysql data path로 가서 ls -al 로 전체 디렉토리를 확인하면 .gnome2와 .mozilla 디렉토리가 숨어있다.

mysql은 data path에 있는 디렉토리를 database로 인식한다. 그래서 .gnome2와 .mozilla를 database로 인식하였기 때문에 발생한 문제다.

원인은 리눅스를 gui로 설치하고 mysql계정을 만든 후 해당 계정을 mysql의 data path로 등록했기 때문이다.

mysql 계정으로 gui환경으로 접속 할 일이 없기 때문에 해당 디렉토리를 삭제하여 정상 처리 하였다.

2021년 12월 13일 월요일

OCR 인식률 비교. 테서렉트 vs paddleocr vs easyocr vs 카카오 api vs < 네이버 api


최근 디아블로2 레저렉션을 시작 했다. 예전과 다르게 유니크 아이템 외에도 확인 해야 하는 아이템들이 많았다.

그런 정보를 다 찾고 확인 하는 게 힘들었다. 

디아블로 아이템의 아이템명이나, 속성들을 감별해서 미리 등록해 놓은 DB와 비교하여 가지고 있어야 할 아이템을 판별하는 프로그램을 만들고 싶었다.

결론은 실패. OCR 문자 인식부터 막혔다.

OCR 인식에 총 3일을 투자 했는데, 이틀을 테서랙트(tesseract)에 투자했다.  


테서렉트

인식률 향상을 위해 gray 변환, medianBlur로 노이즈 제거, thresholding, erosion, dilation 등 온갖 기법을 다 써도 인식률이 크게 향상되지는 않았다.

psm 옵션과 oem옵션을 아무리 조절해도 인식률은 크게 향상 되지 않았다.

디아블로2의 폰트 자체가 일반적인 폰트가 아니다 보니 인식률이 떨어 지는 것으로 보인다.

디아블로 폰트를 새로 학습하여 인식을 해 보기로 했다.

블리자드에서 공식적으로 제공하는 폰트는 없고 비슷하게 만든 폰트는 있었다.

이중에 kodia만 한글을 지원해서 사용했다.

학습에는 jTessBoxEditor를 사용했다. 

일단 학습완료는 되긴 하는 데, 오류가 나는 부분이 많다.

APPLY_BOXES: boxfile line 1/가 ((104,3153),(145,3193)): FAILURE! Couldn't find a matching blob

그래도 완료된 모델로 돌려 보았으나 여전히 인식률은 좋지 못했다.

학습이 제대로 안 된 것 같다.

이틀동안 이 삽질을 하고 있으니 저 오류 나는 것도 찾기 싫어졌다. 테서랙트는 포기.


EasyOCR

다음으로 시도한 건 EasyOCR. 이거 꽤 좋다. 설치도 쉽고, 사용법도 굉장히 쉽다. 인식률 자체도 테서렉트보다 확실히 좋다.

이미지 전처리를 시도한 거랑, 안한 거랑 크게 인식률 향상은 없었다.

다만 내가 원하는 정도의 인식률은 안 되었다.


API

이쯤되면 그냥 돈내고 사용하는 API를 사용해볼까하는 생각이 들었다.

테스트 해본 API는 카카오랑 네이버

카카오 ocr api 테스트

네이버 ocr 테스트 - CLOVA OCR

네이버는 테스트를 위해서 이름, 이메일, 사업체를 등록해야 한다. 좀 꺼림칙했지만 일단 테스트만 해보기 위해 넣었다.

결과는 네이버의 압승. 단 2군데만 조금 틀린 결과를 보여줬다.

카카오 OCR은 생각보다 좋지 않았다. EasyOCR보다 못한 것 같다.



주관적인 OCR 인식률 비교

테서렉트 == paddleocr < 카카오 api < easyocr < 네이버 api


인식에 사용한 이미지



@Scheduled 어노테이션 파라미터를 환경변수로 사용하기

@Scheduled  어노테이션으로 10초마다 실행하는 스케쥴은 아래와 같이 쉽게 만들 수 있다.

@Scheduled(fixedDelay = 10000)
public void scheduleTask() {


여기서 10000 값을 환경변수로 적용하려고 하면 fixedDelay는 final long 타입이여야 등록 가능한데,  환경변수로 가져오는 값은 final로 처리 할 수가 없다.

그럴때는 아래처럼 fixedDelayString을 사용하면 된다.

  @Scheduled(fixedDelayString = "${myscheduler.period}")
  public void scheduleTask() {

2021년 11월 25일 목요일

자바에서 퍼센트율(나누기)을 구할 때 유의사항.

일부값 / 전체값 * 100   X 

100d * 일부값 / 전체값 O


long part = 1;
long total = 3;

double ok = 100d * part / total;
double no = part / total * 100;

System.out.println(ok);
System.out.println(no);

33.333333333333336
0.0

1 / 3 = 0.333333 이다.

'part / total' 은 항상 정수형을 반환한다. part과 total의 타입이 long 타입이기 때문에 묵시적 형변환을 통해 끝수를 버리고 long타입으로 변환 된다.

만약 part나 total이 double 타입이라면 정상적으로 0.33333을 반환한다.

그래서 처음 100d를 통해 double 타입으로 반환하라고 명시 하면 오류 없이 퍼센트 결과를 얻을 수 있다.

2021년 9월 15일 수요일

수식없는 Isolation Forest 분석

 이상 탐지는 '정상'이 아닌 데이터를 식별하는 것이다. 여기서 '정상'의 정의는 관찰되는 현상과 속성에 따라 다르다.

이상 탐지는 정상을 정의하고 그 안에 속하지 않는 모든 것을 비정상으로 판별한다.


기본적으로 정상을 정의 하는 방법은 통계적 방법(statistical methods), 분류(classification ) 또는 클러스터링(clustering)을 사용하지만 프로세스 자체는 동일하다. 

정상을 정의하고 다른 모든것은 비정상으로 판별하는 것이다.

이러한 방식은 비정상을 감지하는 데, 최적화 되어 있지 않고, 정상을 찾는 데에 최적화되어 있다. 그로 인해 너무 많은 오탐이나 너무 적은 비정상이 감지 될 수 있다.

또 계산의 복잡성으로 인해 저차원 이나 작은 크기의 데이터에 적합하다.


Isolation Forest 알고리즘은 위 두가지 문제를 해결하고 이상현상을 감지하는 데 효율적이고 정확한 방법을 제공한다.

Isolation Forest의 핵심원리는 의사 결정 트리(decision tree)를 생성하여 이상을 '격리'하는 것이다.

격리에 필요한 분할 수가 작은 값은 이상 데이터로 볼 수 있다.


여러번의 분할을 거친 데이터는 정상이라고 판별한다.

이상 데이터는 단 4번의 분할로 격리 되었다.


정상 데이터를 분리하기 위해서는 수많은 분할(depth)이 필요한 데 반해, 이상 데이터는 정상 데이터에 비해 훨씬 낮은 분할만을 필요로 한다.

분할은 무작위(random)으로 발생한다. 여기서 분할 수는 이상 점수(anomaly score)을 생성하는 데 사용된다.

이상 점수는 하나의 데이터를 격리하기 위해 사용된 분할 수의 평균이다. 이상 점수, 즉 분할 수의 평균보다 낮은 분할 수를 이상이라고 판별한다.




서브샘플링

정상 데이터가 이상 데이터에 가까울 수록 격리가 어려워 잘못 된 판별을 할 수 있다. 이 때 

서브샘플링을 이용하면 알고리즘을 효율을 높일 수 있다.

Isolation Forest 알고리즘은 전체 데이터 세트가 아닌 서브샘플링 된 데이터 세트에서 더 잘 작동한다. 


성능 향상

이상 탐지는 depth가 낮은 값을 판별하므로 하나의 데이터를 격리하기 위해 깊게 들어갈 필요가 없다. 그렇기에 일정한 깊이에 제한을 걸어 성능을 향상 시킬 수 있다.

이 값은 log2(nodes_count)을 사용한다. 이는 노드에서 생성할 수 있는 적절한 이진 트리의 평균 높이이다.



Isolation Forest 는 수직, 수평 방향으로만 분할 하기 때문에 잘못된 Scoring 이 발생할 가능성이 있다. 이를 해결하기 위해 Extended isolation forest 알고리즘을 사용한다. 다만 연산량은 더 많다.


참조

https://medium.com/@arpitbhayani/isolation-forest-algorithm-for-anomaly-detection-f88af2d5518d

https://en.wikipedia.org/wiki/Isolation_forest

2021년 8월 27일 금요일

AttributeError: 'Sequential' object has no attribute 'predict_classes'

 텐서플로우의 예제를 colab을 사용하지 않고 다운 받아서 돌리려고 할 때 문제 발생.

공식 문서의 예제에는 "!pip install -q tensorflow-gpu==2.0.0-rc1" 명령어로 텐서플로우 버전을 지정해서 다운로드 한다.

이걸 무시하고 최신버전(2.6.0)의 tensorflow를 설치해서 나는 오류. 

predict_classes 는 2021-01-01 이후에 제거됨.


https://androidkt.com/get-class-labels-from-predict-method-in-keras

Could not load dynamic library 'cudart64_110.dll'; dlerror: cudart64_110.dll not found

컴퓨터에 그래픽 카드가 없어서 발생하는 에러. 혹은 그래픽 드라이버가 설치 되지 않았다.

tensorflow가 아닌 tensorflow-cpu를 설치한다.

2021년 7월 15일 목요일

ObjectMapper로 형변환이 안되는 문제. (대문자 스네이크 케이스)

 DTO 객체명이 대문자 스네이크 케이스 형태일 경우 ObjectMapper convertValue 메소드로 형변환시 값이 들어가지 않는다.


여기에서 대문자 스네이크 케이스 코드를 받아서 적용했더니 정상적으로 값이 들어간다.


아래와 같이 네이밍 전략을 선언해서 사용한다.

ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(CustomUpperSnakeCaseStrategy.UPPER_SNAKE_CASE);

ObjectMapper로 List Map 타입을 List Dto 타입으로 변환 방법

 

ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> listMap = new ArrayList<>();
...
List<MyDto> testDtoList =
mapper.convertValue(listMap, TypeFactory.defaultInstance().constructCollectionType(List.class, MyDto.class));


2021년 5월 4일 화요일

maven resource 파일 지정.

 legacy 프로젝트를 maven으로 바꾸면서 properties 파일이나 mybatis xml 파일을 찾지 못하는 현상이 있었다.


Caused by: java.io.FileNotFoundException: class path resource [applicationResources.properties] cannot be opened because it does not exist


기존 프로젝트는 resourse 디렉토리가 따로 있는 게 아니라, 여기 저기 중구난방으로 되어 있다.

maven에서 아래의 설정을 추가하면 된다.

        <resources>
            <resource>
                <directory>src</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>


2021년 3월 29일 월요일

Java로 무한 loop 쉘스크립트 실행

 java에서 쉘스크립트를 실행하는 기능을 추가 했다.

문제는 자바 프로그램이 종료되면 쉘스크립트 역시 1~2초 후에 멈춰 버린다.

쉘스크립트는 while true로 무한루프로 실행이 되는 스크립트였다.


자바에서 실행 명렁어를 nohup, &, sh -c 등 이것 저것 다 해 보았지만 쉘스크립트는 멈췄다.

결국 찾은 방법은 두가지이다. 두가지 방법 다 정석적인 해결책은 아니고 trick으로 볼수 있는 방법이다.


하나는 중간 launch 스크립트를 두는 방식이다.

실제 내가 실행해야 할 스크립트가 target.sh 이면 launcher.sh를 둬서 자바에서는 launcher.sh를 실행하는 방법이다.

launcher.sh에는 아래와 같은 명령을 넣는다.


#/bin/sh

nohup ./target.sh 1> /dev/null 2>&1 &



다른 하나는 trap을 사용한 방법이다. 

trap 명령어는 특정 시그널이 들어올 때 어떤 일을 할 지 적용할 수 있다.


실제 적용한 방법은 두번째 방법으로 trap명령어를 통한 방법이다.

적용 방법은 아래와 같다.


trap "method_name" 0


0은 EXIT 인 경우 이다. 자바 프로세스 종료시 EXIT 시그널이 오는 데, 그 때 loop를 사용하는 method_name를 다시 한번 사용하게 하였다.

종료는 9(SIGKILL) 시그널을 발생시켜서 종료하므로 문제 없이 stop을 할 수 있다.

2021년 3월 20일 토요일

netstat말고 ss명령을 사용합시다.


tcp 파일을 이용해서 현재 접속 현황을 가지고 오는 자바로 만들어진 프로그램이 있다.
이 프로그램에서 cpu 사용량이 15% 이상을 치는 문제가 발생했다.

리눅스에서 tcp와 udp의 소켓 정보는 /proc/net/tcp, tcp5, udp, udp6 파일에서 확인 할 수 있다.
문제의 프로그램은 위 파일을 읽고 파싱하여 데이터를 가져온다.
커넥션이 적을 때는 문제가 없지만, 커넥션이 많아지면서 문제가 발생한 것이다. 

커넥션 수가 20000개가 넘어가면 읽는 시간은 느려지지 않지만, cpu사용율이 15% 가까이 발생한다. 
커넥션 수가 50000개가 넘어가면 전체 데이터를 읽어오는 시간은 20초 가까이 걸리며 cpu사용율을 100% 가까이 사용한다.

tcp, udp 파일에서 하나의 라인은 하나의 커넥션이다.
처음에는 5만 라인이 굉장히 많은 줄 알았다. 그러나 /proc/net/tcp 파일을 복사해서 그 복사한 파일을 읽었더니 굉장히 빨랐다. 
/proc/net/tcp 파일 자체가 문제였던 거다. 구글링으로 문제에 대해 검색을 시작했다. 

구글링 후 알게 된 하나는 netstat명령과 ss 명령의 차이점이었다. 둘 다 네트워크의 상태를 보는 명령이지만 구조 자체가 다르다. 
netstat은 /proc/net/tcp 파일을 읽어들인다. 그렇기 때문에 커넥션이 많을 때는 netstat 명령 역시 느려진다. 
반대로 ss 명령은 커넥션이 많을 때도 굉장히 빠르다. 


AF_NETLINK를 이용하기 위해서는 C 언어를 이용해야 했다. C 언어 자체는 잘 모르지만 예제 샘플을 이용해서 원하는 형태의 프로그램은 만들 수 있을 것 같았다. 
만들어진 프로그램을 JNI을 이용해서 가지고 오면 될것으로 봤다.

여기에는 몇가지 문제가 있었다. kernel 과 직접 통신하기 때문에 커널 버전에 맞는 각각의 실행파일을 따로 준비해야 한다. (문제가 있었던 프로그램은 여러 서버에서 돌아가야 했다.)
커스터마이징을 한다고 해도 c 코드 자체를 수정하기가 쉽지 않다. 관리포인트가 늘어나는 문제도 있다.

고민을 거듭하다 방향을 바꿔서 그냥 ss 명령을 사용하는 방법을 생각했다.
ss 명령은 iproute 패키지의 일부분이다. iproute 패키지는 소스가 공개되어있다.
문제가 되었던 프로그램이 돌아가던 운영체제는 대부분이 centos나 redhat이었다.
centos 4,5,6,7 버전의 minimal 설치 패키지를 조사하여 iproute 패키지가 있는 지 확인하였다. 전부 있는 것으로 확인하였다.

ss 커맨드를 이용한 방법으로 개발을 진행하겠다고 컨펌을 받고 개발을 진행하였고 잘 마무리 되었다.

나중에 알게된 솔라윈즈에서 작성한 글이 딱 내가 겪었던 일을 잘 설명해 주어서 링크한다.

2021년 1월 27일 수요일

단락 연산자(short-circuit operator)

 리눅스에서 명령어를 연속 해서 사용 할 때가 있다.

보통 || 와 &&을 사용한다. 이를 단락 연산자(short-circuit operator)라고 한다.

첫번째 명령어를 실행하고 곧이어 두번째 명령어를 실행하는 역할이다.


예제는 다음과 같다.

[root@localhost ~]# true || echo 'ok'
[root@localhost ~]# false || echo ok
ok
[root@localhost ~]#

||는 앞의 명령어 성공하면 뒤의 echo 'ok'는 실행하지 않는다.
앞의 명령이 실패하면 뒤의 echo 'ok' 명령를 실행한다.

&&은 ||과 반대이다. 앞의 명령이 성공하면 뒤의 명령을 실행한다.
앞의 명령이 실패하면 뒤의 명령을 실행하지 않는다.

[root@localhost ~]# true && echo 'ok'
ok
[root@localhost ~]# false && echo 'ok'
[root@localhost ~]#

앞 명령어와 상관없이 실행하고자 할 때는 ;를 쓴다.
[root@localhost ~]# echo '1ok'; echo '2ok'
1ok
2ok
[root@localhost ~]#

2020년 12월 17일 목요일

modelmapper memory leak

최근 추가한 코드를 테스트하는 개발서버에서 아래의 에러 메시지가 발생했다.


java.lang.OutOfMemoryError: Direct buffer memory
        at java.nio.Bits.reserveMemory(Bits.java:694)
        at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)
        at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
        at io.netty.buffer.UnpooledUnsafeDirectByteBuf.allocateDirect(UnpooledUnsafeDirectByteBuf.java:111)
        at io.netty.buffer.UnpooledUnsafeDirectByteBuf.(UnpooledUnsafeDirectByteBuf.java:68)
        at io.netty.buffer.UnsafeByteBufUtil.newUnsafeDirectByteBuf(UnsafeByteBufUtil.java:626)
        at io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:65)
        at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:179)
        at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:170)
        at io.netty.buffer.AbstractByteBufAllocator.ioBuffer(AbstractByteBufAllocator.java:131)
        at io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator$MaxMessageHandle.allocate(DefaultMaxMessagesRecvByteBufAllocator.java:73)
        at io.netty.channel.socket.nio.NioDatagramChannel.doReadMessages(NioDatagramChannel.java:243)
        at io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:75)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:642)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:565)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:479)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:441)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
        at java.lang.Thread.run(Thread.java:748)



추가한 부분은 udp 클라이언트 용도의 프로그램이었다. 


소스를 분석해 봤지만 문제되는 부분을 알 수 없었다.


visulavm을 이용해 모니터링을 해봤더니, classes의 Total loaded 개수가 계속 증가하면서, Metaspace 영역이 계속 증가하는 현상을 확인했다.

dump를 받아서 mat 프로그램으로 분석했다. 잘 사용할 줄은 몰랐지만 대충 보다 보니 modelmapper가 보인다. 뭐지...


'modelmapper 성능 이슈'로 검색해 봤더니, 이런 글을 찾았다. 


설마 하는 생각으로 modelmapper 부분을 제외했더니, 정상적으로 돌아왔다.


modelmapper는 예전부터 web 프로젝트에서 많이 사용했었기 때문에 이런 문제가 발생할 줄 몰랐다.


'modelmapper memory leak'으로 검색해서 더 알게 된 정보는 modelmapper는싱글톤으로 구성하여 사용하라는 것이다.


modelmapper를 많이 사용하지 않았기 때문에 전부 걷어내고 직접 지정하는 방식으로 변경했다.


이런 문제가 한 번 생기고 나니 modelmapper를 다시 사용하기 꺼려진다.


modelmapper memory leak으로 찾아 본 글

https://www.programmersought.com/article/1444628366/
https://better-dev.netlify.app/java/2020/10/26/compare_objectmapper/
http://modelmapper.org/user-manual/faq/
https://github.com/modelmapper/modelmapper/issues/375

2020년 12월 5일 토요일

오라클 접속이 되다 안되다 하는 현상.

스프링 기반으로 만든 소스에서 오라클 접속이 로컬에서 개발 할 때는 잘 되었다. 
문제는 개발서버에서는 연결 자체가 안됐다. 아예 안 되는 건 아니고, 가끔 연결이 될 때도 있었다. 
오류메시지가 연결되지 않은 것에 대한 메시지만 나오니 이 에러메시지로는 해결책을 찾기가 어려웠다.
이것저것 여러가지를 적용해 봤지만, 해결이 안 됬다. 다음날 다시 검색 시작. 해결법을 찾았다.

해결법은 이거였다.
-Djava.security.egd=file:/dev/./urandom
리눅스에서 오라클 jdbc 드라이버는 기본적으로 random 을 사용하는 데, random은 서버의 엔트로피를 사용하여 random 값을 생성한다고 한다. 여기서 엔트로피란 서버의 노이즈라고 한다.
노이즈는 서버의 디스크 읽기, 키보드 입력, 네트워크 패킷등이라고 한다.
random은 이런 엔트로피가 일정조건까지 채워져야 값을 생성해 준다고 한다. 이 기다림이 문제 였다. 반면 urandom 일정조건을 채우지 않고도 바로 값을 준다고 한다.

잘 이해가 안 가서 검색을 하니, 잘 정리된 블로그가 있었다.

역시 검색을 하려고 해도 뭘 알아야 검색을 할 수 있다.

결국 옵션 하나로 해결한 문제였지만 문제의 원인을 알 수 없을 때는 꽤 골치 아픈 문제였다.

2020년 10월 22일 목요일

ORA-12528, TNS:listener: all appropriate instances are blocking new connections.

 1. 오라클 접속 하려고 했더니 아래와 같은 에러 발생.


oracle.net.ns.NetException: Listener refused the connection with the following error:

ORA-12528, TNS:listener: all appropriate instances are blocking new connections.


2. 오라클 서버에 접속해서 리스너 상태 확인.

lsnrctl stat


상태가 BLOCKED로 나옴.

Instance "orcl", status BLOCKED, has 1 handler(s) for this service...


3. sqlplus 접속해서 오라클 재시작. ORA-00205 에러가 난다.

SQL> shutdown abort;

ORACLE instance shut down.

SQL> startup;

ORACLE instance started.

...

Total System Global Area 1.0133E+10 bytes

Fixed Size     3721272 bytes

Variable Size 1778386888 bytes

Database Buffers 8321499136 bytes

Redo Buffers    29831168 bytes

ORA-00205: error in identifying control file, check alert log for more info

...



4. alert log 확인. alert log의 기본위치는 아래와 같다.

10g 이하는 $ORACLE_BASE/admin/$ORACLE_SID/bdump

11g부터는 $ORACLE_BASE/diag/rdbms/'DATABASE NAME'/$ORACLE_SID/trace



아래와 같은 에러 확인.

Wed Oct 21 17:55:04 2020

starting up 1 dispatcher(s) for network address '(ADDRESS=(PARTIAL=YES)(PROTOCOL=TCP))'...

starting up 1 shared server(s) ...

ORACLE_BASE from environment = /home/oracle/app

Wed Oct 21 17:55:04 2020

ALTER DATABASE   MOUNT

Wed Oct 21 17:55:04 2020

ORA-00210 : cannot open the specified control file 

ORA-00202: control file: '/oradata/oradata/mydatabase/control01.ctl'

ORA-27086: unable to lock file - already in use

Linux-x86_64 Error: 11: Resource temporarily unavailable

Additional information: 8

ORA-205 signalled during: ALTER DATABASE   MOUNT...

Wed Oct 21 17:55:07 2020

Using default pga_aggregate_limit of 6440 MB


5.

/oradata 위치는 nfs로 연결된 스토리지이다. 

/oradata/oradata/로 이동해서 파일 권한, 소유자 확인 했지만, 이상없음.

파일생성, 삭제, 수정 해봤지만, 정상임.


6.

에러 원인 확인 불가. 해결법은 찾음.

해결법은 에러가 나는 control01.ctl 파일이름을 변경 후 다시 원래 이름으로 복사.

오라클 중지 후 작업한다.

mv control01.ctl control01.ctl.bak

cp control01.ctl.bak control01.ctl


오라클 시작.

control01.ctl의 에러는 사라졌지만, /oradata 위치의 몇몇 파일들이 같은 문제를 일으킴. 역시 위와 같은 방식으로 다시 처리.


정상화 완료.


파일 디스크립터가 어떤 이유로 스토리지에서 열린 상태로 유지되어 발생하는 오류로 보인다. 아마도 네트워크에 뭔가 문제가 생겼을 때, 그냥 끊어져 버린게 아닌가 싶다.





2020년 10월 15일 목요일

버팀목전세자금 대출, 목적물 변경 및 증액 상담 내용.

 최근 직장을 옮기고, 직장 근처로 이사를 위해 집을 알아 보고 있다. 전세로 가기 위해 준비하고 있으나, 내 예산안에 맞는 집을 찾기가 어렵다. 검색해 보다, 기존 전세대출을 목적물 변경과 증액을 이용하는 방법을 찾았다.

목적물 변경은 이사를 하게 되는 집을 바꾼다고 보면 되고, 증액은 기존 대출금에서 더 대출을 요청한다고 볼 수 있다.


인터넷으로는 알아보는 정보에 한계가 있고, 제대로된 정보를 얻기 위해 실제 은행에서 대출상담을 받았다.

내가 알아본 은행은 우리은행이다. 기존에 우리은행을 통해서 버팀목전세자금 대출을 받고 있었다.


대출 상담 내용을 요약하면 이렇다.

1. 1금융권은 재직 3개월이상만 대출이 가능하다고 한다.

2. 대출 증액은 계좌가 새로 생성되는 개념으로 버팀목전세자금 대출의 자격요건을 다시 본다. 증액을 받을 시 자격요건이 맞지 않으면 대출이 안된다.

3. 목적물 변경은 기존 계좌를 사용하는 개념으로 자격요건을 다시 보지 않는다.

4. 대출금을 갚으면 갚은 한도 만큼만 목적물 변경시 다시 대출이 가능하다. 예를 들어 처음 버팀목전세자금 대출을 받을 때, 1억을 대출 받고 도중에 6천을 갚으면, 목적물 변경시 적용되는 대출금은 4천이다. 나가는 이자를 조금이라도 줄이기 위해 대출금을 중간에 조금 갚은 게 오히려 독이 됐다.


결국은 기존에 살던 집의 전세를 넘는 집으로는 가기 어려울 것 같다.


목적물 변경만을 위해 필요한 준비물 및 순서는 다음과 같다.

1. 이사 이후에 전입신고를 한다.

2. 주민등록등본과 확정일자를 받은 임대차 계약서, 등기부 등본을 가지고 은행을 방문하여 신청한다.

3. 처음 대출을 받은 지점과 다른 지점인 경우 필요서류를 해당 지점으로 보내서 처리한다고 한다.


ps . 완료 시점에 가족관계증명서를 제출 요청함. 등기부 등본 제출할 때 같이 제출하면 좋을 듯 하다.

2020년 9월 25일 금요일

Elasticsearch SQL parsing_exception

 엘라스틱서치에서 sql로 쿼리 실행시 특수문자가 들어간 경우 parsing_exception 에러가 난다.

이럴 때는 \" 를 사용하면된다.


ex>

{
    "query" : "select * from \"my.data.day.20200925\""
}

2020년 9월 16일 수요일

사이드 프로젝트는 성취감입니다.

 최근에 이동식 에어컨을 팔았다. 올리자마자 여기저기서 연락이 온다. 대충 비슷하게 시세 알아보고 올리는 데도 항상 너무 싸게 올리는 것 같다. 내가 생각하는 가격에 조금 더 비싸게 올려놓고, 안 팔리면 조금씩 가격을 내려야 하는데, 자꾸 잊어먹는다. 나는 장사 체질이 아닌가 보다. 

파이썬을 배운 김에 뭔가 하나를 만들어 보자는 생각에 중고가격을 한눈에 알아보는 사이트를 만들었다. 중고가격을 책정하는 데에 도움이 되는 사이트가 있으면 좋을 것 같았다. 찾아보니 비슷한 사이트가 없었다. 

중고 가격은 네이버 카페 중고나라에서 가져왔다. 처음에는 크롤링을 하려고 했으나, 네이버 카페 글 검색 api가 있었다. 내가 딱 원하는 api는 아니지만, 가공하면 쓸만한 정보를 얻을 수 있었다. 다 만들고 나니 꽤 그럴듯했다. 그래서 이왕 하는 김에 공부도 할 겸 Vuejs로 프론트도 만들었다. 프론트도 다 만들고 나니 아예 오픈하는 것도 나쁘지 않은 것 같았다. 그래서 도메인도 사서 연결했다. 서버는 AWS 1년 free 서버를 사용했다.

그래서 완성한 사이트가 바로 www.findusedprice.com 다. 많은 애용바란다.  

도메인 만료. 사이트 중단...


사이트를 열고 보니 크롬에서 안전하지 않은 사이트로 뜨는 게 마음에 걸린다.  ‎Let's Encrypt를 이용해서 SSL까지 적용했다.

이것저것 검색해 보니 가격이 완벽하지가 않다. IX940 같은 모델명으로 검색하면 정확한 데이터가 나오지만, 광범위한 검색은 제대로 된 가격을 얻기가 힘들다. 제외 키워드를 넣어서 그나마 조금 더 괜찮은 데이터가 나오도록 했다.

도메인은 3년짜리를 사면 할인을 해 주는데, 3년까지 저 서비스가 남아있을 것 같지가 않다. 일단 1년으로 했다. 구글 애널리틱스를 붙였다. 도메인 만료 전까지 사용자가 어느 정도 있으면 연장을 하고 거의 없으면 그대로 사장 시킬 예정이다.

최근에 취업하게 되어 출퇴근하고 있다. 출퇴근한다는 거 자체가 별거 아닌 거 같지만 꽤 힘들다. 정시 퇴근을 하고 집에 와도, 씻고 밥 먹고 조금 TV보고 하면 금방 잘 시간이다. 밥 먹으면서 졸기도 한다. 그 잠깐 시간하고 주말에 시간을 내서 완성하고 보니, 뿌듯함이 생긴다. 이래서 작은 일이라도 끝내는 일이 중요하다고 하나 보다.