[Developer] 전역변수

Developer 2020. 5. 28. 22:57

Simple한 얘기를 해보자. 

 

  • 전역 변수 - 파일 내 어디서든 접근 가능하며, 변하는 값(변수)

엄청나게 유용해 보인다. 소스 파일 내의 어디서는 접근이 가능하다니!! 

하지만

'변수'라는 상태가 문제다.

소스 파일내 어디서든 이 값을 변경할 수 있다는 것이다. 

 

만약 프로그램이 One Thread의 순차처리 방식이라면 당연히 문제가 없겠지만

Multi Thread일 경우 누가 어느 시점에 이 값을 바꿀지 모른다.

 

 

[ 프로그램 동작 전제 ]

  1. Non-Block Packet Receive로 지속적으로 메시지를 수신하여 동일 로직을 처리한다.
  2. 사용자는 1개의 세션을 통해 연결 후, 모든 Transaction이 끝날 때까지(즉, End 메시지를 전송할 때까지) 메모리에 데이터를 유지한다.
  3. 세션관리자는 사용자의 Transaction 진행 여부를 알 수 있다.
  4. 세션 연결 후, 첫 메시지 처리 과정에 사용자 정보를 구조체 배열 메모리(Context Index 기준)에 저장 후, 두 번째 이후 메시지 부터는 사용자 번호를 기준으로 Context Index를 가져와 사용자 정보를 조회/갱신 한다.
  5. Transaction 종료 시, 구조체 배열 메모리 및 세션 관리에서 데이터를 삭제한다.

위의 케이스는 전에 다니던 회사에서 겪은, 실제 운영 시스템에서 발생한 장애 상황을 심플하게 줄여서 정리한 것이다. (실제로는 훨씬..무지막지하게 복잡하다...)

한 시점에 여러명의 사용자가 지속적인 Transaction 처리 중인 상태가 될 수 있기 때문에, Non-Block 상태에서 여러 Transaction을 처리하는 프로그램의 세션, Context 관리란 정말 골치 아프다. 

 

A 사용자가 Transaction을 진행하고 있다. a라는 메시지를 시작으로 30번의 Context ID를 할당받고, b라는 메시지를 전달했을 때, 세션관리자가 A 사용자라는 것을 인지하고 30번의 Context ID를 알려주어 아주 빠른 메모리 접근으로 작업을 처리하도록 했다.

문제는 세션관리자가 Context ID를 'Global Index'에 넣어주고 Process #1은 이 값으로 메모리에 접근하게 되어 있었다는 것이다. (아마 처음 이 프로그램을 만든 사람은...a라는 메시지를 처리하는 함수와 b라는 메시지를 처리하는 메시지가 별도의 파일로 관리되고 있어 전역 변수를 선언해서 extern 명령어로 어디서든 접근하기 쉽게 하려고 했던것 같다.)

 

만약 A 사용자가 a 메시지를 처리하고 b 메시지를 전달후 b 메시지에 대한 함수를 타기 바로 전에 B 사용자가 전송한 새로운 a 메시지가 먼저 처리되면...? 

A 사용자의 b 메시지를 처리하기 바로 직전에 Global Index는 A 사용자의 Index 30에서 B 사용자의 Index 25로 변경될 것이다. 그럼 A 사용자의 b 메시지를 처리할 때는 당연히 Index 25에 접근하게 되어 잘못된 데이터를 처리하게 될 것이다.

 

이것이 얼마나 심각한 문제일지는 겪어본 사람은 알겠지...

거대한 운영 시스템에서 원인 분석만 꼬박 일주일을 잡아 먹은적도 있을 정도다. 

장애 이후 전역변수를 없애는 일 또한 엄청난 곤욕이다.

 

오늘부로 아래 3가지만 실천해 보자.

  1. 모든 전역'변수'는 제거한다.
  2. 변수는 반드시 함수의 Parameter(인자)로 넘겨줘서 사용한다. Call-by-Value든 Call-by-Reference든 상관없다.
  3. 전역으로 선언할 수 있는 것은 오직, 값을 변경할 수 없는 '상수'뿐이란 것을 명심하자.

 

다음에는 위 문제로 새로 만들었던 'Context 관리 라이브러리'를 포스팅 해봐야겠다.

'Developer' 카테고리의 다른 글

[Developer] Input Validation - 입력값 검증  (0) 2020.05.26
[Developer] 개발할 때가 좋았는데.....  (0) 2020.05.25
Posted by [ 브랜든 ]
,