흐르는 시간의 블로그...

현재 읽고 있는 GO IN ACTION 책의 5.4 절의 인터페이스부에 나오는 내용이다.

다른 책을 읽을때 인터페이스 (정확히는 메소드와 리시버 부분)에 의문이 있었다.

이 책에서 그 부분을 해소해주고 있다.


읽는 도중에 잊지않기 위해 적는 것이니 자세한 글의 내용은 추후 정리하도록 한다.


리시버를 포인터 형태로 지정할 수 있고 값 형태로 지정할 수 있다.

책에서는 언어 명세의 규격으로 설명하고 있다.


이것을 다르게 풀어서 설명하는게 낫다고 생각한다.

내 머릿속 내용이긴 하지만... 풀어보자면 다음과 같다.


리시버를 값 형태로 등록한 인터페이스(메소드)가 있다고 하자.

이를 호출할때 포인터에서 호출하면 포인터의 역참조를 통해 값으로 변환하여 호출 할 수 있다.

그리 한다고 해서 그 내용이 달라질게 전혀 없다.


반대의 상황을 보자.

리시버를 포인터 형태로 등록한 인터페이스가 있다고 하자.

이를 호출할때 역참조를 통해 해당 값을 수정할 수도 있다.

호출시 값 형태로 넘어온 것을 컴파일러가 포인터 형태로 변경해서 처리해준다고 생각해보자.


문법적인 형태의 위험은 값 형태로 넘어온 것에 대한 역참조를 통한 값의 변경을 개발자가 모를 수 있다.

컴파일러를 다른 형태로 구현한다면 값 형태로 넘어간다는 것은 복사본의 값이 넘어가게 된다.

이럴 경우 역참조하여 아무리 수정하여도 의미가 없을수 밖에 없다.


아마도 규격을 정할때 이런 부분들을 다 고려했던거 같다.

그래서 리시버에 포인터를 사용하는 경우는 

반드시 포인터인자에 대한 메소드에 대해서만 동일 인터페이스로 취급한다.


자세한 코드는 나중에 책에서 발췌하여 정리하도록 한다.


아래의 코드에서 admin과 user의 notify()의 리시버를 보면 

하나는 포인터로 지정하였고 하나는 값 방식으로 지정하였다.


sendNotification() 호출시 

lisa 대해서 포인터를 지정하든 값 방식으로 사용하든 모두 문제가 없다

bill에 대해서 포인터를 사용하지 않게 되면 문제가 발생한다


package main

import "fmt"

type notifier interface {
	notify()
}

type user struct {
	name  string
	email string
}

func (u *user) notify() {
	fmt.Printf("사용자에게 메일을 전송합니다: %s<%s>\n",
		u.name,
		u.email)
}

type admin struct {
	name  string
	email string
}

func (a admin) notify() {
	fmt.Printf("사용자에게 메일을 전송합니다: %s<%s>\n",
		a.name,
		a.email)
}

func sendNotification(n notifier) {
	n.notify()
}

func main() {
	bill := user{"Bill", "bill@email.com"}
	sendNotification(&bill)

	lisa := admin{"Lisa", "lisa@mail.com"}
	sendNotification(lisa)
}


GO LANG에 대한 테스트 프로젝트를 시작해 본다.


타켓은 운영중인 리버스지오코딩 엔진...


선택한 이유는...

내가 맘대로 때려 고쳐볼 수 있으며...

요구 사항이 많지 않으며...

웹 서버 코딩이며...

현재 서비스 하고 있는 프로젝트 중 제일 많은 초당 요청을 가지고 있다 (초당 150건 내외.)



작년 즈음에 개발해 두고 상용으로 거의 2년만에 돌리고자 하는 시스템이 있다.

4대의 시스템에서 도는 것을 한대에서 돌도록 통합 개발했던 것이다.


이유 없이 시스템에서 DB 처리가 안되어 일정 기간동안 데이터가 날아갔다.

오늘 복구 작업을 진행하려고 이것저것 처리했는데 로그상에 이상한 것이 보이는 것이다.

insert 작업 처리를 무려 100만개 넘게 못하고 들고 있는 것이었다.


이유가 뭔가 하고 찾다가... 결국 찾기는 찾았다.


프로그램은 다음과 같이 처리하도록 되어 있다.


1. 데이터 수신 

2. 기록테이블에 데이터 삽입

3. 최종 상태 테이블에 데이터 업데이트

4. 업데이트 후 mysql_affected_rows()를 통해 적용 row수 체크

5. row가 0인 경우 최종 상태에 업데이트 할 row가 없는 것으로 인식 ---- 문제!!!

6. 업데이트 할 row가 없으면 최종 상태에 새로운 row insert --- 문제!!!


위의 두가지 문제라는 것들은 예상하지 못했던 부분들이다.

둘 중 하나만 예상대로 되었다면 문제는 발생하지 않았을 것이다.

두가지 모두 예상 밖 혹은 문제였기에 최종적으로 문제를 발생 시켰다.


~~~~ #1 ~~~~~~~~~~

최종 상태 테이블에 ID당 하나씩만 존재했어야 한다.

하지만 해당 테이블에 key를 unique가 아닌 일반 key로 해뒀다.

이것이 가장 큰 문제이긴 했다.

어쨋든 여러개의 ID가 들어갈 수 있는 상태가 되었다.

그래서 수만개 이상의 row가 들어가는 상황이 만들어 졌다.

~~~~~~~~~~~~~~~~~


~~~~ #2 ~~~~~~~~~~

mysql_affected_rows()가 "0"을 반환한다고 where 절에 해당하는 row가 0인 것은 아니다!


https://mariadb.com/kb/en/mariadb/mysql_affected_rows/ 의 내용을 보자.

"When using UPDATE, MariaDB will not update columns where the new value is the same as the old value."


단말기가 이상하여 아주 짧은 시간에 많은 데이터를 올리다 보니 여러 데이터가 완벽히 같은 경우가 존재했다.

실질적으로는 시간만 하더라도 달랐어야 하는데 말이다.

그래서 결국 최종 데이터가 있음에도 mysql_affected_rows()는 update 쿼리 실행후 실제로 업데이트한 row가 존재하지 않았다.

~~~~~~~~~~~~~~~~~


~~~~ #3 ~~~~~~~~~~

저런 문제를 드러나게 한 것은 

우리 단말이 잘못된 설정과 문제로 인해 매우 짧은 시간에 같은 데이터를 반복적으로 보냈기 때문이다.

~~~~~~~~~~~~~~~~~

저런 문제들이 중복되어...
동일 ID에 대한 수만개 이상의 row가 최종 테이블에 존재했으며...
해당 ID의 단말은 1초에도 10개씩 update를 시도 했다.
그러다 보니 매 업데이트 때마다 수만개 이상의 row를 업데이트 했다.
거기에 마지막 업데이트는 직전의 업데이트와 동일했고 수많은 row가 꾸준히 추가되었다.
결국 계속 update 대상 row가 증가한 것이다.

...........................
문제의 해결은 unique key를 달아 놓는 것으로 끝냈다.
소스상에서 update 후 mysql_affected_rows()가 0인 경우 insert를 시도한다.
그 경우 unique key에 의해 insert가 실패한다.

소스상에서 저 부분을 가드 하려면 두가지 방법이 있다.
1. 직전의 데이터를 가지고 비교하여 같으면 업데이트 쿼리와 삽입 쿼리 모두를 작동 시키지 않는 것이다.
2. mysql_affected_rows()가 아닌 매번 select를 하여 row가 존재하면 update를 진행하는 것이다.

unique key가 존재하면 두가지 모두 꼭 필요하지는 않아 작업을 진행하지 않았다.
...

참고: https://mariadb.com/kb/en/mariadb/mysql_affected_rows/

난 정말 JAVA를 공부한 적이 없다구요.


첫 테스트 소스 작성 날짜를 보니 8월24일이었다.

참 길게 봤다. 뭐 그간 다른 책을 두권이나 함께 보다보니 길어지긴 했다.

그 사이에 Java로 간단한 프로젝트도 하나 진행하기도 했다.

프로그래밍을 전혀 모르는 사람도 충분히 읽기 좋은 책이다.

저자의 세심함과 노력이 돋보인다.

꼼꼼한 예제와 차분한 설명은 초보자에게 큰 도움이 될것이라 생각한다.

물론 나처럼 아주 오래전 자바를 했다가 최근 다시한번 확인차 보는 사람에게도 물론 도움이 된다.

사실 이 책이전에 보려고 시도 했던 책이 있다.

...

"이펙티브 자바 2판"

...


지금 읽고 있는 웹서버 책과 Go Lang 책을 다 읽고 이펙티브 자바를 진행할지 고민해보자. 

그래도 또 한권을 읽고 정리하고 넘어섰다. ^^

책을 읽고 간단한 프로젝트를 하고나서 자바에 대해 느낀 점은 아래와 같다.

C++ 보다 편하다.

느린점은 분명하다.

하지만 다양한 도구가 큰 도움이 된다.

레퍼런스가 참 많다.

OOP에 대해 어느정도 알면 컴퓨터에 대해 잘 몰라도 쉽게 코딩할 수 있겠다.

이 정도이다.

'' 카테고리의 다른 글

7가지 동시성 모델  (0) 2016.09.22
C++11 STL 프로그래밍  (0) 2015.12.11
Pro Oracle Spatial for Oracle Database 11g  (0) 2011.11.15
오라클SQL튜닝(쿼리의 본질과 성능 튜닝에 대한 37가지 이슈)  (0) 2011.11.15
Head First PMP  (0) 2010.03.06