SimpleIsBest.NET

유경상의 닷넷 블로그

COM+ 트랜잭션과 오라클 DB 링크

by 블로그쥔장 | 작성일자: 2005-10-12 오후 6:25:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
최근 어떤 분이 질문을 하셔서 COM+ 트랜잭션에서 오라클 DB 링크를 사용하는 방법에 대해 테스트 해봤습니다. 오라클에 대해 잘 알지 못하기 때문에 무쟈게 해멨습니다. SQL Server에서 링크 서버(linked server)는 좀 써봤어도 오라클에서 DB 링크는 한번도 써본적이 없기 때문에... 사실 전 오라클에서 저장 프로시저도 만들어 본적이 없으니까요... -_-;;

COM+ Transaction & Oracle DB Link

필자의 이번 삽질은 아주 큰 삽이 필요했다. 천삽푸고 허리펴기 운동도 아니고... OTN(Oracle Technical Network) 에서 졸라 검색하고 자료 찾느라 고생을 좀 했다. 그 놈의 호기심이 먼지... 에혀... 울 마누라가 새벽까지 테스트하는 필자의 꼬라지를 쳐다보더니 이렇게 말하드라.

"왜 사서 고생을해?"

그러게 말이다... -_-

좀 치사하지만... 이번 포스트는 정보를 전달한다는 목적보다 필자의 테스트 수기임을 강조하는 바이다. 필자 같은 호좁 나부랭이가, 그것도 잘 알지 못하는 오라클 이야기를 하기 때문이기도 하지만 무엇보다도 실무 프로젝트에 필자가 직접 적용해본 경험이 없기 때문이다. 주위에 오라클 전문가가 있다면 이 글의 내용에 대해 문의해 보고 확인 사살해 주기 바란다. 피드백도 잊지 말고 해주길...

Scenario

시나리오는 이렇다. COM+를 사용하는 어플리케이션 서버 혹은 웹 서버에서 오라클 DB 서버를 호출 한다. 당연 빤쑤로다가 COM+의 분산 트랜잭션을 사용한다. 이 때 호출하는 오라클 DB 서버가 또 다른 오라클 DB 서버를 DB 링크로 연결하는 경우 발생하는 이슈에 대해 설명하고자 한다.


그림 1. DB 링크를 사용하는 COM+ 분산트랜잭션 시나리오

Test Environment Settings

무식하면 용감하다고 했던가? 테스트 환경을 위해서는 두 대 의 오라클 서버가 필요하다. 필자 같은 가난뱅이가 어디 가서 서버를 구하겠는가? 당연 빤쭈로 Virtual PC를 이용하기로 했다. 이미 오라클 9.2가 설치된 Windows 2003 에 대한 Virtual PC 이미지가 있었기 때문에 이 이미지를 복사해서 Virtual PC 기반의 오라클 서버를 한 대 더 준비했다. 말이 Virtual PC를 사용해서 쉬운 것 처럼 보이지만 4.7GB에 달하는 하드 디스크 이미지가 두 개나 필요하다. -_-;;

두 오라클 서버는 앞서 언급한대로 Windows 2003 Standard Edition이 설치되어 있고 오라클 9.2 서버가 설치되어 있다. 이들 서버는 모두 256MB의 메모리를 사용하도록 설정되어 있으며 컴퓨터 이름은 각각 Ora92Test, Ora92Test2 이다.

두 오라클 서버가 DB 링크를 통해 서로를 액세스할 수 있도록 각각의 서버에 TNS 설정을 해줬다. 즉 Ora92Test에서 SQL*Plus를 이용하여 Ora92Test2 서버에 접속할 수 있도록 하고, 반대로 Ora92Test에서도 SQL*Plus를 통해 Ora92Test에 접속이 가능하게 만든 것이다. 사실 DB 링크를 수행하기 위해 이 설정이 필요한지는 필자도 확실하지 않다. 다만 어디서 주워들은 기억이 있어서 이렇게 설정해 준 것이다. -_-;; (주위에 오라클 전문가에게 물어보길...)

Ora92Test 서버는 그림 1에서 Stored Procedure를 수행하는 서버가 되겠다(그림에서 위쪽 오라클 서버). Ora92Test 서버는 DB 링크를 통해 Ora92Test2 서버에 접근한다. 따라서 Ora92Test 서버에 DB 링크를 다음과 같이 생성하였다.

CREATE PUBLIC DATABASE LINK "TESTLINK2"  CONNECT TO "SCOTT" 
    
IDENTIFIED BY "tiger"  
    
USING 'ORA92TEST2'

이실직고(以實直告)하자면 필자 같은 호좁 나부랭이가 오라클에서 DB 링크를 만들어 봤을 리 만무하다. 오라클 엔터프라이즈 매니저(맞나? -_-)에서 DB 링크를 만들면서 생성된 SQL 스크립트를 개발자의 강력한 무기 중 하나인 Copy&Paste 로 이 글에 붙여 넣었다. 어찌 되었건 DB 링크를 성공적으로 만들었으며 이 DB 링크를 통해 간단한 조회에 성공하였다. 참! Ora92Test 와 Ora92Test2 서버는 모두 scott 계정을 사용하여 테스트 하고 있다. DB 링크 역시 이 계정을 사용하도록 하였다(바루 위 DB 링크 생성 스크립트 참조).

이제 테스트로 사용할 테이블을 생성한다. Scott 스키마의 테이블을 사용할 수도 있겠지만 테이블을 필자가 잘 모르므로 새로운 테이블을 생성하였다. 먼저 Ora92Test 서버에 테이블을 다음과 같이 생성하였다.

CREATE TABLE TempTable (
    PK 
int NOT NULL ,
    Data1 
int NULL ,
    Data2 
varchar (32NULL ,
    
CONSTRAINT PK_TempTable PRIMARY KEY ( PK )
)
;

스크립트를 보여주는 것 조차 민망한 간단한 테이블 스크립트이므로 추가 설명은 읍다. 이제 Ora92Test2 서버에도 테이블을 생성할 것이다. 이 테이블은 Ora92Test에서 DB 링크를 통해 액세스할 테이블이다.

CREATE TABLE LinkTable (
    R_PK 
int NOT NULL ,
    R_Data1 
int NULL ,
    R_Data2 
varchar (32NULL ,
    
CONSTRAINT PK_LinkTable PRIMARY KEY ( PK )
)
;

이 스크립트 역시 매우 간단하므로 추가 설명은 스킵! 이제 Ora92Test 서버에서 DB 링크를 통해 트랜잭션이 수행되나 확인을 다음 스크립트를 통해 수행해 보았다.

INSERT INTO TempTable(PK, DATA1, DATA2) VALUES(104'444');
INSERT INTO 
LinkTable@TESTLINK2(R_PK, R_DATA1, R_DATA2) VALUES(44'Remote 444');
ROLLBACK;

위 스크립트는 Ora92Test 서버의 TempTable에 1건의 레코드를 삽입(INSERT)하고, DB 링크를 통해 Ora92Test2 서버의 LinkTable에 1건을 삽입한다. 그리고 ROLLBACK을 수행하여 두 서버 모두 삽입된 레코드가 롤백 되었는가를 확인했다. 당연 빠따로다가 롤백은 잘 된다.

DB 링크를 통해 트랜잭션이 수행되고 롤백이 제대로 되는가를 확인했으니 그림1 과 같은 시나리오를 만들기 위해서는 저장 프로시저(Stored Procedure; SP)를 만들어 로컬 서버의 테이블과 DB 링크로 연결된 원격 서버의 테이블에 모두 INSERT를 수행하도록 해 보자. 저장 프로시저 역시 대단히 간단하다.

CREATE OR REPLACE PROCEDURE LinkTestSP
(
    PK_VAL        INTEGER,
    DATA1_VAL    INTEGER,
    DATA2_VAL    VARCHAR2
)
AS
BEGIN
    INSERT INTO 
TempTable(PK, DATA1, DATA2) VALUES(PK_VAL, DATA1_VAL, DATA2_VAL);
    INSERT INTO 
LinkTable@TESTLINK2(R_PK, R_DATA1, R_DATA2) VALUES(PK_VAL, DATA1_VAL, DATA2_VAL);
END

위 저장 프로시저는 Ora92Test 서버에 생성하였으며, 매우 간단하므로 추가 언급을 하지 않겠다. 사실 필자는 오라클에서 저장 프로시저를 만들어본 경험이 없어서 대충 만들었으므로 이해해 주기 바란다. -_-; 이 저장 프로시저는 Ora92Test 에서 사용되었을 때 매우 잘 작동하였다.

이로써 데이터베이스 측의 설정이 끝났다. 별 내용은 없지만 좀 긴듯하다... COM+가 수행되는 어플리케이션 서버(그림1에서 Application Server)는 필자의 노트북으로서 ODP.NET 10g를 설치하였다. 대개의 경우, 오라클 클라이언트(ODP.NET을 포함하는)의 상위 버전은 하위 버전의 오라클 서버에 대해 하위 호환성(backward compatibility)을 갖으므로 최신 버전을 사용하는 것이 좋은 것으로 알고 있다. 특히 ODP.NET 9.x 버전에 버그가 있다는 말을 어디서 주워들은 적이 있기 때문에 최신 버전의 ODP.NET을 사용했다. ODP.NET을 사용한 이유는 ODP.NET의 .NET Data Provider를 사용하고자 함은 아니다. 사실 ODP.NET에 포함된 OraMTS를 사용하기 위함이다. 닷넷 프레임워크 1.1에 포함된 Managed Data Provider for Oracle이 분산 트랜잭션에 사용되기 위해서 OraMTS를 요구하기 때문이다. 실제 테스트 코드 역시 ODP.NET 이 아닌 Managed Data Provider for Oracle(System.Data.OracleClient 네임스페이스)이 사용되었다.

어플리케이션 서버가 원격 오라클 서버(Ora92Test2 서버)에 대해 직접적인 액세스가 없도록 하기 위해서 어플리케이션 서버의 TNS 설정을 Ora92Test에 대해서만 설정해 주었으며, 혹시나 어플리케이션 서버의 DTC가 Ora92Test2와 상호작용이 발생함이 없음을 확인하기 위해 DTC의 보안 설정 중에서 XA 트랜잭션 옵션을 Off 하였다.

DB Link Transaction

OTN 문서에 의하면 DB 링크를 통한 원격 서버 액세스는 XA 트랜잭션이 사용된다고 되어 있다. 즉, 로컬 오라클 서버가 DB 링크를 통해 원격 서버를 액세스하면 이 액세스는 XA 트랜잭션을 동반하며 트랜잭션에 대한 TM(Transaction Manager)는 로컬 오라클 서버가 된다. 따라서 로컬 오라클 서버에서 트랜잭션을 롤백하면 DB 링크를 통해 원격 서버에 대해 수행된 추가/수정/삭제 작업 역시 롤백되게 된다.

DB 링크가 XA 트랜잭션을 사용하므로 오라클 서버에 대해 XA 트랜잭션 설정을 수행해 주어야 한다. 오라클 7.x는 XA 설정을 위해 관련된 뷰(OTN 문서를 참고하기 바란다)를 SYS 스키마에 생성해 주어야 하고 XA 트랜잭션을 사용하는 사용자 계정에 대해 이 뷰에 대해 SELECT 권한을 주었어야 했다. 하지만 최근 버전의 오라클은 분산 트랜잭션에 대한 뷰(DBA_PENDING_TRANSACTION)는 이미 생성되어 있으며 권한 역시 적절히 주어져 있는 것 같다. 특별한 설정 없이도 XA 분산 트랜잭션은 잘 작동했다.

TEST !!!

이제 COM+ 트랜잭션에서 앞서 작성한 LinkTestSP 저장 프로시저를 호출해 보자. 별다른 문제가 없다면 호출은 성공할 것이고, COM+ 코드가  ContextUtil.SetComplete 메쏘드를 호출한다면 트랜잭션은 커밋될 것이며 ContextUtil.SetAbort가 호출된다면 트랜잭션은 롤백되어 Ora92Test 서버와 Ora92Test2 서버에 수행된 두 INSERT 문장은 모두 롤백되어야 할 것이다.

테스트에 사용한 C# 코드는 다음과 같다. 오라클을 액세스하는 코드야 뻔한 것이니 첨부된 소스를 참고하면 될 것이고, 여기 표시된 코드는 COM+에 관련된 코드이다. 컴포넌트를 사용하지 않고 COM+ 의 SWC (Services Without Components) 를 이용하여 COM+ 분산 트랜잭션을 사용했다. SWC에 대해 친숙하지 않은 독자는 아래 코드에서 Enter() 호출부터 Leave() 호출 까지의 코드가 COM+ 분산 트랜잭션으로 묶인다는 것만 알아두면 되겠다. (추후 SWC에 대한 아티클을 올릴 계획이다)

ServiceConfig config = new ServiceConfig();
config.Transaction TransactionOption.Required;
config.TrackingEnabled = true;
config.TrackingAppName "OraTxSimpleTest";
config.TrackingComponentName "TxInsert";

int 
maxval GetLastData();   // Ora92Test 서버의 TempTable에서 MAX 값을 읽는다.

ServiceDomain.Enter(config);
try 
{
    InvokeTestSP(maxval+1)
;  // LinkTestSP 저장 프로시저를 호출한다.
    
ContextUtil.SetComplete();
}
catch (Exception ex) {
    ContextUtil.SetAbort()
;
    
Console.WriteLine(ex.ToString());
}
TransactionStatus status 
ServiceDomain.Leave();
Console.WriteLine("Transaction Status = {0}", status);

테스트 코드를 수행하면, 수행결과는 ORA#24777 오류를 발생한다. 이 오류 메시지는 "전이할 수 없는 트랜잭션"이 발생했다는 오류이다(영문 오류 메시지: use of non-migratable database link not allowed). 생전 첨보는 오류인지라 필자는 당황했고... 바로 gg 치고 그만둘 수도 있었지만 은근한 오기가 발동해서 OTN에서 검색을 수행해 보았다. OTN의 검색결과에서 ORA#24777 오류의 원인을 찾을 수 있었는데, 그 이유는 바로 DB 링크의 제약사항 중 하나였다.

COM+ 혹은 Tuxedo/Tmax 와 같은 미들웨어에 의해 분산 트랜잭션이 시작되었고 그 트랜잭션 내에서 DB 링크가 사용되려면 오라클 서버는 Dedicate Server가 아닌 Shared Server(Multi-threaded Server)로 셋업 되어야 한다는 것 이였다. 아무 생각없이 Ora92Test 서버의 설정을 Dedicate Server에서 Shared Server로 바꾸었다. 설정을 바꾸는 것은 매우 간단했다. Database Configuration Assistant를 통해 데이터베이스 접속 옵션을 전용 서버(dedicate server) 모드에서 공유 서버(shared server) 모드로 바꾸는 것이 전부 였다.


Database Configuration Assistant를 이용한 Shared Server 설정

Shared Server로 설정하는 방법으로 호좁한 필자는 Data Configuration Assistant를 사용했지만, 오라클 전문가라면 initXXX.ora 파일을 직접 수정하는 방법을 사용할 것이다. 설정파일을 직접 수정하는 것... 이거 웬지 있어 보이지 않은가? (요런 것이 오라클 서버 DBA에 대한 진입장벽이 아닌가 싶다. 아님 말고... -_-)

Ora92Test 서버를 Shared Server 로 설정한 후 테스트 코드를 다시 수행해 보았다. 코드는 매우 잘 작동했다. 트랜잭션은 성공했으며, SetComplete 메쏘드 대신  SetAbort 메쏘드를 호출하면 트랜잭션은 성공적으로 롤백 된다(두 서버 모두 롤백 된다).

Dedicated Server vs. Shared Server

UNIX 상에서 오라클을 전용 서버로 설정하면 클라이언트마다 프로세스가 하나씩 생성된다. 이 프로세스는 해당 클라이언트 만을 위한 서비스를 제공한다. 오라클에 접속을 하는 클라이언트가 2000명이라면 2000개의 프로세스가 서버상에서 작동하는 것이다(웬지 무식해 보인다). 전용 서버는 안정성이 뛰어나다고 한다. 그리고 초기(7.x)의 공유 서버 모드가 상당히 불안했으므로 더욱 전용 서버를 선호했고, 지금 공유 서버 모드가 안정적이라고 할지라도 이미 공유 서버 모드에 등을 돌린 DBA들이 전용 서버 모드를 더 선호하는 것은 당연하다고 볼 수 있다.

반면 공유 서버로 설정하면 클라이언트는 일정 프로세스(설정 가능한 프로세스 개수) 내의 쓰레드에 의해 서비스된다. 일반적으로 2-tier C/S 어플리케이션에 대해서는 전용 서버로, COM+와 같이 커넥션 풀을 사용하는 미들웨어가 사용되는 3-tier 어플리케이션은 공유 서버를 사용하는 것으로 필자는 알고 있다. 구글에서 검색을 해보면, 전용 서버와 공유 서버에 대해 장단점을 구분하는 글들이 많이 있다. 귀차니즘과 오라클에 대한 전문 지식 부족으로 인해 어느 것이 좋은지는 필자의 의견을 개진하기도 힘들다. 주위의 오라클 전문가에게 조언을 구하기 바란다. 다만... 전반적으로 공유 서버 모드는 선호하지 않는 것 같긴 하다. 공유 서버 모드가 그렇게 불안한가? 주위에 오라클 전문가에게 물어보기 바란다. 필자도 무지하게 궁금하다.

Summary

정리하자면 이렇다. COM+ 에서 트랜잭션이 시작되고 이 트랜잭션 내에서 DB 링크를 사용하고자 한다면 해당 오라클 서버는 공유 서버(Shared Server) 모드로 작동해야만 한다. COM+ 서버(어플리케이션 서버 혹은 웹 서버)는 오라클과 OraMTS를 통해 트랜잭션을 수행하고 이 오라클 서버에서 DB 링크로 연결된 서버는 XA 트랜잭션을 사용한다. 주의할 점은 COM+ 서버의 DTC와 DB 링크로 연결된 원격 오라클 서버는 트랜잭션 상의 상호 작용을 전혀 하지 않는다는 점이다. 즉, MSDTC는 OraMTS 와만 OLE Transaction 프로토콜을 통해 통신하며 오라클 서버와의 트랜잭션은 OCI를 통해 트랜잭션을 수행한다. 그리고 DB 링크에 의한 트랜잭션은 오라클 서버끼리 XA 트랜잭션 프로토콜을 통해 트랜잭션이 진행된다. 이 XA 트랜잭션의 관리는 오라클에 의해 진행되며 MSDTC와 무관하다. 하지만 DB 링크를 통한 트랜잭션이 실패하면 전체 트랜잭션이 Rollback 되므로 전체 트랜잭션의 범위 내에 DB 링크 트랜잭션이 포함된다는 점만은 분명하다.

(주) MSDTC, OraMTS 에 의해 진행되는 트랜잭션의 프로토콜 및 연관 관계는 OTN 자료를 참고하기 바란다.