1.ProudNet Database Cache System version 2

Edit

게임 데이터베이스는 트랜잭션의 규모가 작은 대신에 많은 횟수의 접근이 있다는 특징이 있습니다. 그리고 대부분의 데이터가 불러오기와 저장하기의 반복이 많다는 특징이 있습니다.

ProudNet Database Cache System version 2 는 이러한 과정을 cache하여 데이터베이스 접근을 줄여줍니다. 즉 데이터베이스의 부하를 줄여줍니다.

먼저, DB cache의 이론적 이해를 이해하셔야 합니다.

ProudNet Database Cache System version 2 는 DB cache 를 위한 게임 데이터베이스 스키마 과 DB cache 클라이언트와 서버 로 구성되어 있습니다. 먼저 DB cache 를 위한 게임 데이터베이스 스키마 를 설정한 후 DB cache 클라이언트와 서버 를 이용해서 게임 데이터베이스를 다룰 수 있습니다.

이러한 기본 과정은 DB cache ver.2 기본 사용법 에서 설명하고 있습니다. DB cache 활용법 에서는 ProudNet Database Cache System version 2 와 관련한 모든 사용법을 설명하고 있습니다.

1.1DB cache의 이론적 이해

DB Cache되는 데이터의 구조

통상적으로 게임 데이터베이스에 저장되는 데이터는 tree 구조를 가지기 마련입니다. 1. ProudNet Database Cache System version 2 는 여기에 적합하게 만들어져 있습니다. 1. ProudNet Database Cache System version 2 는 데이터를 tree 단위로 다룹니다.

그림 1-1사용자 계정 정보를 DB 에 저장하는 예

이 tree 는 node들로 구성됩니다. 물론 각 node들은 부모자식관계를 가집니다. 그리고 각 node 는 이름-값 pair, 즉 property field들을 가집니다. 이 tree 는 데이터베이스에 다음과 같이 대응됩니다.

로딩된 Tree에서

데이터베이스에서

Node 1개

Record 1개

Node의 종류

Table의 이름

Node가 가진 프로퍼티들

레코드 내 필드들

Node의 부모자식 관계

레코드 내 필수 필드들

그림 1-2상기 사용자 계정 정보를 DB 에 저장하기 위한 테이블 구조

1. ProudNet Database Cache System version 2 에서 로딩하는 데이터 tree의 최상위 node, 즉 root node의 table name과 child node들의 table name 은 서로 달라야 합니다.

DB Cache되는 데이터를 접근하는 유형

DB cache 에서는 데이터를 독점적으로 로딩해서 다루는 것이 기본 사용법입니다. 자세한 것은 독점적 불러오기 를 참고하십시오.

1. ProudNet Database Cache System version 2에서는 다음과 같은 유형의 접근을 제공합니다.

접근 유형

활용예

cache 됨

즉시 리턴

독점적 로딩을 필요로 함

내부 처리 방식

일방적 데이터 변경하기

게임 플레이 중 플레이어 캐릭터의 잦은 정보 변경

YES

YES

YES

메모리에 먼저 Cache 후 DB 에 후 write

요청 응답형 데이터 변경하기

고유 이름을 가진 플레이어 캐릭터 새로 만들기

NO

NO

YES

DB 에 write 후 성공하면 메모리에 Cache

비독점적 데이터 접근하기

유료 아이템을 웹 서버에서 구매하기

NO

NO

NO

DB 에 write 후 성공하면 메모리에 Cache

독점적 불러오기

DB cache 시스템은 동일한 데이터를 1개의 DB cache client만 로딩할 수 있게 허락합니다. 이를 독점적 불러오기(exclusive load)라고 합니다.

독점적 불러오기의 목적은 5.4 Race Condition ( 경쟁 상태 ) 을 피하기 위함입니다.

DB Cache System은 한 DB Cache Client가 독점중일 때, 새로운 DB Cache Client가 독점 요청을 하게되면 독점권을 넘기게 되어 있습니다.

그 방식은 다음과 같습니다.

DB Cache System version 2

독점적 불러오기를 하는 주체는 DB Cache Client 입니다.

만약 한 DB Cache Client 가 독점적 불러오기를 한 상태에서 다른 DB Cache Client 가 독점적 불러오기를 하는 경우, 구 DB Cache Client는 독점권 이양 요청을 받게 되고 허가와 거부를 선택할 수 있게 됩니다.

이 때 구 DB Cache Client가 Data를 Unload하게 되면 새로운 DB Cache Client가 독점권을 받게 됩니다.

그림 1-3Server 1이 갖고 있던 Gamer X 를 Server 2가 독점적 불러온 상태

일방적 데이터 변경하기

일방적 데이터 변경하기 를 할 경우 데이터는 DB cache의 메모리에 일단은 즉시 반영됩니다. 그리고 반영된 변경 사항은 좀 지나서 데이터베이스에 실제 기록이 됩니다. 즉 진정한 의미의 데이터 cache 를 합니다.

일방적 데이터 변경하기 을 할 때의 내부 작동 방식

일방적 데이터 변경하기 처리는 즉시 리턴합니다. 즉 일방적 데이터 변경하기 를 사용하는 경우 데이터를 변경하는 루틴에서 대기 시간이 발생하지 않습니다. 따라서 데이터를 변경하고 나서 그 결과가 완료될 때까지 기다리는 과정이 불필요합니다. 가령, 아래 루틴처럼 unlock then lock 을 하는 과정이 불필요합니다.

UpdateSomeData()
{
    lock(X);
    ...
    unlock(X);
    UnilateralUpdateSomeData(X);
    lock(X);
    ...
    unlock(X);
}

그러나 몇 가지 주의 사항이 있습니다. 일방적 데이터 변경하기 는 DB cache의 메모리에 무조건 적용됩니다. 하지만 데이터베이스에 DB Constraints를 설정한 레코드에 변경 사항이 반영되지 못할 수 있습니다. 가령, Table X 가 Unique 를 보장하는 Field A 가 있다고 가정할 경우, 중복되는 값을 A 에 담고 있는 tree node 가 DB cache 에는 반영될 수 있지만 이는 데이터베이스에 실제 기록이 불가능합니다.

따라서 일방적 데이터 변경하기는 DB Constraints를 설정하지 않거나 설정해도 위와 같은 문제가 없음이 확실한 경우에 쓰는 것이 좋습니다.

통상적인 온라인 게임에서 게이머와 월드 지역의 객체에 대한 데이터베이스 객체들은 일방적 데이터 변경하기가 안전하게 사용될 수 있습니다.

요청 응답형 데이터 변경하기

일방적 데이터 변경하기에서는 변경 사항이 먼저 DB cache의 메모리에 반영된 후 데이터베이스에 추후 기록됩니다. 하지만 요청 응답형 데이터 변경하기는 반대로 작동합니다. 먼저 데이터베이스에 기록을 하고, 기록이 성공하면 DB cache의 메모리에 반영됩니다.

데이터를 요청 응답형 데이터 변경하기로 다루는 것은 요청 응답형입니다. DB cache client 는 요청 응답형 데이터 변경하기를 DB cache server 에 요청하고 DB cache server 는 이를 데이터베이스에 실제 기록을 합니다. 그리고 기록 실패 혹은 성공 여부를 DB cache client 를 통해 확인받음으로 완료됩니다.

요청 응답형 데이터 변경하기을 할 때의 내부 작동 방식

요청 응답형 데이터 변경하기는 진정한 의미의 cache 는 아닙니다. 하지만 요청 응답형 데이터 변경하기 는 DB Constraints 때문에 기록이 실패하는 경우에 대한 내성을 가집니다. 왜냐하면 데이터베이스에 실제 기록이 성공하기 전까지는 DB cache 에 변경 사항을 반영하지 않기 때문입니다.

통상적인 온라인 게임에서 요청 응답형 데이터 변경하기가 필요한 경우는 중복된 이름을 금지하는 플레이어 캐릭터를 생성할 때가 대표적입니다.

비독점적 데이터 접근하기

일방적 데이터 변경하기요청 응답형 데이터 변경하기는 독점적으로 로딩된 데이터에 한해서만 가해질 수 있는 변경입니다. 하지만 이미 다른 DB cache client 에서 독점적 로딩된 데이터를 읽기 혹은 쓰기를 하고 싶을 때가 있습니다. 한 예로, 웹 서버에서 플레이어 캐릭터의 정보를 열람하거나, 유료 아이템 결재 서버에서 플레이어 캐릭터의 가방에 아이템을 추가 혹은 변경을 해야 할 때가 있습니다. 운영툴에서 로그온 중인 플레이어의 캐릭터 정보를 열람하거나 변경해야 하는 경우도 있습니다. 비독점적 데이터 접근하기는 이를 위한 기능입니다.

모든 비독점적 데이터 접근은 요청 응답형 데이터 변경하기 방식으로 작동합니다. 따라서 즉시 응답하지는 않으나 DB Constraints에 내성을 가집니다.

비독점적 데이터 접근하기를 할 때의 내부 작동 방식

비독점적으로 데이터를 변경하면 독점적 로딩을 한 DB cache client 에서는 다른 곳에 의해 데이터가 변경되었음을 통보받습니다.

DB cache가 다루는 데이터를 DB가 직접 억세스하기

DB cache가 로딩해서 다루고 있는 data tree의 데이터 상태는 DB에 있는 것보다 DB cache에 있는 것이 더 최신 상태를 유지합니다. 왜냐하면 데이터의 기록이 DB cache에서 먼저 이루어진 후 DB에 뒤늦게 기록되기 때문입니다.

그러하다보니 DB cache가 다루고 있는 data tree와 별개로 귀하가 DB에 직접 데이터를 기록하는 경우, DB에 있는 데이터가 잠깐 시간 동안 더 예전 상태이기 때문에 원하지 않는 결과 오류가 발생할 수 있습니다. 이는 data race condition과 비슷한 문제입니다.

그림 1-4Data race condition by DB cache vs. DB access

그런데, 귀하는 DB cache가 다루고 있는 data tree를 DB에서 직접 다루고 싶을 때가 있습니다. ProudNet은 이러한 경우에 data race condition을 피하기 위해 데이터 격리 기능(data isolation)을 제공합니다.

데이터 격리 기능이란, DB를 직접 억세스하더라도 data race condition이 발생하지 않도록 귀하가 원하는 data tree를 DB cache에서 사용권을 완전히 해제하는 기능입니다.

그림 1-5Data isolation

사용법은 다음과 같습니다.

그림 1-6Data isolation usage

DB Constraints

필드에 들어갈 값이 특정 조건을 만족하지 못하면 들어가지 못하게 하는 것을 망라해서 지칭합니다.

예를 들면, 다음과 같습니다.

Primary Key Unique Index 기타 등등 ( >, <, =, != )

Constraints 는 Unique Index, Trigger 등으로 구현됩니다.

1.2DB cache 를 위한 게임 데이터베이스 스키마

DB cache 를 위한 데이터베이스 스키마는 사용자가 원하는 대로 구성될 수 있습니다. 단, 다음 규칙을 준수해야 합니다.

그림 1-7MMORPG 에서 게이머 데이터를 저장하기 위한 데이터베이스 스키마의 예

< 3가지 필수 인덱스 추가 예제 스크립트 >

ALTER TABLE dbo.Gamer ADD CONSTRAINT
PK_Gamer PRIMARY KEY CLUSTERED
(
UUID
) WITH( IGNORE_DUP_KEY = OFF) ON [PRIMARY]

CREATE INDEX IX_Gamer_OwnerUUID ON dbo.Gamer
(
OwnerUUID
) WITH( IGNORE_DUP_KEY = OFF) ON [PRIMARY]

CREATE INDEX IX_Gamer_RootUUID ON dbo.Gamer
(
RootUUID
) WITH( IGNORE_DUP_KEY = OFF) ON [PRIMARY]

샘플 데이터베이스 구축하기

MS SQL Server 샘플 데이터베이스 구축하기

먼저, 샘플 게임 데이터베이스 인스턴스를 만들어야 합니다. 이를 위해 MS SQL Server 2008, 2012 중 하나를 설치해야 합니다. MS SQL Server 2005, 2008 Express Edition도 사용 가능합니다. 이는 Microsoft 웹 사이트에서 다운로드하실 수 있습니다.

MS SQL Server 설치가 완료되면 사용하시려는 Sample 에 맞는 이름의 데이터베이스 인스턴스를 만들어야 합니다. 다음의 테이블은 Sample 별 데이터베이스의 이름 및 해당 DB 스키마의 위치입니다.

Sample

Database

DB Schema Path

Casual Game 2

ProudDB2-TEST

<설치 폴더>/ProudNet/Samples/DbmsSchema/ProudDB2

Log-Test

<설치 폴더>/ProudNet/Samples/DbmsSchema/Logon&DBLog

DbLogViewer

Log-Test

<설치 폴더>/ProudNet/Samples/DbmsSchema/Logon&DBLog

RealtimeSocialVille

ProudDB-Sng

<설치 폴더>/ProudNet/Sample/RealtimeSocialVille/SngDb

SimpleAdo

ADO_Test2

<설치 폴더>/ProudNet/Samples/DbmsSchema/SimpleAdo

SimpleAdoImage

Ado_ImageTest

<설치 폴더>/ProudNet/Samples/DbmsSchema/SimpleAdoImage

SimpleAdoMysql

proudado-test

<설치 폴더>/ProudNet/Samples/DbmsSchema/SimpleAdoMysql

SimpleAdoXmlField

test

<설치 폴더>/ProudNet/Samples/DbmsSchema/SimpleAdoXmlField

SimpleDB2

ProudDB2-Test

<설치 폴더>/ProudNet/Samples/DbmsSchema/ProudDB2

SQL Server 2005 또는 이후 버전의 경우 SQL Server 2005의 네트워크 설정하기, SQL Server 2008의 네트워크 설정하기도 해야 합니다.

그리고 나서 DB 스키마를 설치해야 합니다.

•Sample 이 사용하는 데이터베이스 인스턴스를 먼저 생성하십시오. SQL Server 에 익숙하지 않은 경우 Nettention 기술 지원에 문의하시기 바랍니다.

•위의 테이블을 참조하시어 각 .sql 파일을 실행해서 DB 스키마를 생성합니다.

구축이 모두 끝나면 아래와 같은 형태를 확인할 수 있습니다.

그림 1-8샘플 데이터베이스의 설치 완료 예(SQL Server 2008)

SQL Server 2005의 네트워크 설정하기

본 설정은 SQL Server 2005를 쓰는 경우에만 필요한 설정입니다. SQL Server 2005 및 이후 버전은 기본적으로 통신 접속을 받지 않도록 설정되어 있습니다. 이 설정을 완료하기 전에는 SQL Server 2005로의 접근을 할 수 없습니다.

먼저 시작 메뉴에서 아래 프로그램을 실행합니다.

그리고 나서 아래 그림처럼 "로컬 및 원격 연결 허용'을 설정한 후 "확인"을 합니다.

그림 1-9SQL Server 연결 허용 설정

필요한 경우 방화벽 설정도 하셔야 합니다.

그림 1-10Windows 방화벽 설정

SQL Server 2008의 네트워크 설정하기

본 설정은 SQL Server 2008를 쓰는 경우에만 필요한 설정입니다. SQL Server 2008 및 이후 버전은 기본적으로 통신 접속을 받지 않도록 설정되어 있습니다. 이 설정을 완료하기 전에는 SQL Server 2008로의 접근을 할 수 없습니다.

먼저 시작 메뉴에서 아래 프로그램을 실행합니다.

그림 1-11SQL Server Configuration Manager 실행

그리고 나서 아래 그림처럼 "로컬 및 원격 연결 허용"을 설정한 후 "확인"을 합니다.

필요한 경우 방화벽 설정도 하셔야 합니다.

그림 1-12Windows 방화벽 설정

MySQL 샘플 데이터베이스 구축하기

먼저, 샘플 게임 데이터베이스 인스턴스를 만들어야 합니다. 이를 위해 MySQL 을 설치해야 합니다. 본 버전은 MySQL 5.1 기준으로 만들어져 있습니다. MySQL 은 http://www.mysql.com 에서 다운로드 또는 구매하셔야 합니다. (주의! MySQL 은 상업용 목적으로 사용시 구매를 해야 하는 것으로 알려져 있습니다만 자세한 라이선스 정보는 MySQL 측에 질의해 주시기 바랍니다.)

MySQL 을 GUI 환경에서 쉽게 조작하시려면 SqlYog 등의 툴을 혼용하시면 됩니다. SqlYog 는 http://www.webyog.com 에서 다운로드 또는 구매하실 수 있습니다.

MySQL 설치를 진행하십시오.

그림 1-13MySQL 에서 언어 설정 바꾸기

이제 DB 스키마를 설치해야 합니다.

구축이 모두 끝나면 아래와 같은 형태를 확인할 수 있습니다.

그림 1-14샘플 데이터베이스의 설치 완료 예

생성된 테이블에는 FieldA, FieldB등과 같은 이름을 가진 필드들이 있습니다. 이들은 샘플 프로그램에서만 사용 되므로, 삭제하셔도 무방합니다.

1.3DB cache 클라이언트와 서버

1. ProudNet Database Cache System version 2 의 DB cache 시스템은 게임 데이터베이스를 읽기/쓰기를 하게 하는 서브시스템입니다. 데이터베이스 내용은 서버 메모리에 cache되기 때문에 DBMS 에 걸리는 부하를 줄일 수 있습니다.

DB cache 시스템은 client/server 구성입니다. DB cache server 는 DBMS 에 직접 접근하는 유일한 프로세스입니다. 게임 서버나 인증 서버 등 여타 서버들은 DB cache client 를 갖고 있어야 합니다. DB cache client 는 DB cache server 에 접속해서, 주로 stored procedure 를 통해 간접적으로 게임 DBMS 를 접근합니다.

그림 1-15ProudDB Cache 서버 및 클라이언트 구성도

DB Cache Server

DB cache server 는 게임 DBMS 에 직접 접근하는 유일한 프로세스입니다. DB cache server만이 게임 DBMS로 접근하는 이유는, DB cache의 최신 내용이 아직 게임 DBMS 에 덜 반영된 순간이 수시로 있을 수 있기 때문입니다. DB cache 가 접근중인 게임 DBMS의 내용을 다른 곳에서도 변경을 가하고 싶으면 비독점적 데이터 접근하기를 사용함을 권합니다.

DB Cache Client

DB cache client 에서는 게임 DBMS의 객체 정보를 load합니다. 그리고 그 정보는 DB cache client 에서 DBMS의 부하 없이 얼마든지 읽기를 할 수 있습니다.

만약 DB cache client 에서 load한 정보에 일방적 데이터 변경하기로 수정을 가하면 그 수정 내용은 DB cache server로 전송이 됩니다. DB cache 시스템은 DB cache client로부터의 수정 요청이 들어오면 그것을 바로 DBMS 에 기록하지 않습니다. 대신, DB cache server의 메모리에서 값을 갱신해 둡니다. 그리고 일정 시간이나 중요 시점(서버 사용자가 정의한) 이 도래하면 DBMS 에 기록합니다

1.4DB cache ver.2 기본 사용법

아래 내용은 1. ProudNet Database Cache System version 2을 사용하는 기본적인 사용방법입니다. 본 사용법의 예제 프로그램은 5. SimpleDB2 예제입니다. 5. SimpleDB2 예제와 병행해서 사용법을 익히시는 것을 권장합니다.

DB cache 를 위한 데이터베이스 인스턴스 구축하기

1. ProudNet Database Cache System version 2를 사용하려면 먼저 데이터베이스 스키마(혹은 인스턴스)를 생성해야 합니다. 1.2 DB cache 를 위한 게임 데이터베이스 스키마 를 참고하시기 바랍니다.

위 과정과 관련해서 어려움이 있으시면 파트 XIV. Nettention 기술 지원을 통해 도움을 받으십시오.

DB Cache2 서버 준비

1.2 DB cache 를 위한 게임 데이터베이스 스키마 생성 후에는 DB cache server 인스턴스를 생성해야 합니다.

DB cache server 인스턴스는 Proud.CDbCacheServer2 타입입니다.

DB cache server 인스턴스는 각 게임 서버들 사이에서 단 1개만 있으면 충분합니다.

DB cache server 인스턴스를 생성하는 메서드는 Proud.CDbCacheServer2.New입니다.

생성한 후에는 Proud.CDbCacheServer2.Start를 실행합니다.

DB Cache2 클라이언트 준비

각 게임 서버(인증 서버, 게임 zone 서버, 로비 서버 등)은 DB cache client 인스턴스를 생성해야 합니다. Proud.CDbCacheClient2.New를 실행해서 client 인스턴스를 생성합니다. DB cache client의 타입은 Proud.CDbCacheClient2 입니다.

생성한 후에는 Proud.CDbCacheClient2.Connect 를 통해 DB cache server 에 접속을 시도해야 합니다. 접속 시도는 비동기로 이루어지며 접속이 성공하거나 실패하면 Proud.IDbCacheClientDelegate2.OnJoinDbCacheServerComplete 가 콜백됩니다.

DB cache client 는 내부적으로 Proud.CLanClient 를 사용합니다. 스레드풀링 방식이므로 따로 FrameMove 가 없습니다.

DB cache server와의 연결 해제를 위해서는 Proud.CDbCacheClient2 객체를 파괴하거나 Proud.CDbCacheClient2.Disconnect 을 호출하면 됩니다. 이때 DB cache 클라이언트는 Proud.IDbCacheClientDelegate2.OnLeaveDbCacheServer가 콜백됩니다.

독점적 데이터 불러오기 및 새 데이터 추가하기 방법

독점적 불러오기를 위한 메서드는 Proud.CDbCacheClient2.RequestExclusiveLoadData입니다. 로딩할 데이터를 검색하기 위해 인자 fieldName과 cmpValue 에 찾을 조건을 넣어야 합니다. 아래는 사용 예 입니다.

Proud::Guid outSessetionGuid;
Proud::CDbCacheClient2* c = ...;
c->RequestExclusiveLoadData(L"Gamer",L"GamerID",gamername,outSessetionGuid);

Proud.CDbCacheClient2.RequestExclusiveLoadNewData 메서드도 마찬가지로 독점적 불러오기 하는 함수이나, RootTable 에 data 를 추가한 뒤 바로 쓰실때 사용하시면 됩니다. 아래는 사용예 입니다.

CPropNodePtr newNode = CPropNodePtr(new CPropNode(L"Gamer"));

newNode->Fields[L"GamerID"] = "Ulelio";

Proud::Guid outSessetionGuid;
Proud::CDbCacheClient2* c = ...;
c->RequestExclusiveLoadNewData(L"Gamer", newNode, outSessetionGuid);

일방적 데이터 변경하기

일방적 데이터 변경하기를 하는 메서드들은 이것들입니다.

이들 메서드들은 DB cache의 메모리에 변경을 반영한 후 추후에 DBMS 에 비동기로 실제 기록을 합니다. 따라서 이들 메서드들은 호출 즉시 리턴합니다. 또한 응답 메서드 콜백이 없습니다.

DBMS 에 먼저 반영되는 요청 응답형 메서드 및 blocked 메서드 사용

DBMS 에 먼저 반영되는 요청 응답형 메서드 및 blocked 메서드 사용 는 요청 응답형 데이터 변경하기를 위한 메서드들입니다.

이들 메서드는 호출 즉시 리턴합니다. 변경 사항은 실제 DBMS 에 기록이 완료된 후에야 DB cache의 메모리에도 반영됩니다.

변경 사항이 DBMS 에 기록이 성공 혹은 실패가 되면 아래 메서드가 콜백됩니다.

만약 상기처럼 요청 응답형이 아니라 blocked 호출을 원할 경우 이들 메서드를 사용하십시오.

요청 응답형 SessionGuid

이 Session Guid는 한개의 독점 Client들의 식별값입니다.

독점 Client 들이 각각 한개의 Session을 가지게 되고, DB Cache Client는 각각의 Session들을 구분하기 위하여 Guid값을 사용합니다.

요청 시 Output Parameter로 SessionGuid값을 발급받게 되고, 응답 Event의 CCallbackArgs::m_sessionGuid 로 발급받았던 값을 받게 됩니다.

outSessionGuid를 저장하였다가 비교하여 요청한 session을 확인할 수 있습니다.

요청

CDbCacheClient2::RequestExclusiveLoadNewData(String rootTableName, CPropNodePtr addData, Guid &outSessionGuid);

응답

IDbCacheClientDelegate2::OnExclusiveLoadDataFailed(CCallbackArgs args)

요청의 outSessionGuid는 응답의 args.m_sessionGuid와 같습니다.

비독점적으로 데이터를 접근하는 방법

비독점적 접근을 허락하려면 Proud.CDbCacheServer2StartParameter.m_allowNonExclusiveAccess를 설정해야 합니다.

독점 데이터 접근을 위한 함수들과 다른 점은 NonExclusive라는 단어가 붙은 함수라는 것입니다.

이것을 생각하시고 보시면 좀 더 구분이 편합니다.

비독점적 데이터 접근하기를 위해서 다음 메서드들을 호출하면

DBMS 에의 반영 시도 후 결과가 아래 메서드들을 통해 콜백됩니다.

비독점적 데이터 변경이 성공하면 해당 데이터의 변경 사항이 독점적 로딩했던 DB cache client 에 신고됩니다. 신고를 위해 다음 메서드들이 콜백됩니다.

쉽게 볼 수 있도록 각 요청과 응답관계를 묶어 보면 다음과 같습니다.

Proud.CDbCacheClient2.RequestNonExclusiveSnapshotData : Snapshot 얻어오기

// 응답:
IDbCacheClientDelegate2::OnNonExclusiveSnapshotDataSuccess (CCallbackArgs args)
IDbCacheClientDelegate2::OnNonExclusiveSnapshotDataFailed (CCallbackArgs args)

Proud.CDbCacheClient2.RequestNonExclusiveAddData : 데이터 추가하기

// 응답:
IDbCacheClientDelegate2::OnNonExclusiveAddDataAck (CCallbackArgs args)
// 독점중인 DB Cache Client의 Callback Event:
IDbCacheClientDelegate2::OnSomeoneAddData(CCallbackArgs& args)

Proud.CDbCacheClient2.RequestNonExclusiveRemoveData : 데이터 제거하기

// 응답:
IDbCacheClientDelegate2::OnNonExclusiveRemoveDataAck (CCallbackArgs args)
// 독점중인 DB Cache Client의 Callback Event:
IDbCacheClientDelegate2::OnSomeoneRemoveData (CCallbackArgs& args)

Proud.CDbCacheClient2.RequestNonExclusiveModifyValue : 데이터에 특정한 연산을 시킵니다.(+, -, *, /)

// 응답:
IDbCacheClientDelegate2::OnNonExclusiveModifyValueSuccess (CCallbackArgs args)
IDbCacheClientDelegate2::OnNonExclusiveModifyValueFailed (CCallbackArgs args)
// 독점중인 DB Cache Client의 Callback Event:
IDbCacheClientDelegate2::OnSomeoneModifyValue (CCallbackArgs& args)

Proud.CDbCacheClient2.RequestNonExclusiveSetValueIf : 두 값을 비교하여 저장시키도록 요청합니다.

// 응답:
IDbCacheClientDelegate2::OnNonExclusiveSetValueIfSuccess (CCallbackArgs args)
IDbCacheClientDelegate2::OnNonExclusiveSetValueIfFailed (CCallbackArgs args)

// 독점중인 DB Cache Client의 Callback Event:
IDbCacheClientDelegate2::OnSomeoneSetValue (CCallbackArgs& args)

DB cache 사용시 주의사항

일방형과 요청 요청 응답형(독점 Data 요청 응답형 & Block형 & 비독점 데이터 접근) 혼용시 주의사항입니다.

응답 요청 형 메서드가 사용되는 동안 일방형 메서드를 사용하게 되면 Proud.IDbCacheClientDelegate2.OnAccessError가 콜백될 수 있습니다. 변경한 Data에 대한 응답을 받지 못한 상태에서 일방형 메서드를 사용하게 되면 잘못된 Data가 들어갈 수 있기 때문입니다.

ProudDB cache 시스템과 ADO 를 혼용하기

1. ProudNet Database Cache System version 22. ADO Wrapper API를 혼용해서 사용할 수 있습니다.

그런데, 1.3 DB cache 클라이언트와 서버에서 독점적으로 접근하고 있는 테이블을 사용자가 직접 다룰 때에는 다음을 주의해야 합니다.

위와 같은 문제의 대안으로 DB cache가 다루는 데이터를 DB가 직접 억세스하기 를 참고 부탁드립니다.

1.5DB cache 활용법

DB cache 성향 조절하기

DB Cache의 파라메터 설정은 양날검과 같습니다. Cache된 DB 정보를 DBMS 에 기록하는 주기를 짧게 잡으면 DBMS의 성능 저하를 댓가로 DB cache server 가 비정상 종료할 경우(가령 정전시) 미처 저장되지 못한 데이터가 소실될 위험성을 줄입니다. 관련 메서드는 Proud.CDbCacheServer2.SetDbmsWriteIntervalMs 입니다.

DB cache client 에서 데이터를 사용한 후 unload 를 하게 되면 해당 게이머 정보는 DB cache server의 메모리에 잔존합니다. 그리고 일정 시간이 지나면 DB cache server의 메모리에서도 소멸합니다. 만약 이 일정 시간을 짧게 잡으면 DB cache server의 사용 메모리가 증가합니다. 대신 DBMS 에서 데이터를 불러오는 횟수가 줄기 때문에 DBMS의 부하가 적어집니다. 관련 메서드는 Proud.CDbCacheServer2.SetUnloadedDataHibernateDurationMs 입니다.

DB cache 부분로드 하기

DB 를 사용하다 보면 부분적으로 로드를 해야 할때가 있습니다. 가령 예를 들면, 'Gamer의 캐릭터들을 로딩하여,한 캐릭터를 고르면 고른 캐릭터에 대한 정보만 로딩한다.' 라는 것이 필요 할수 있습니다. ProudNet Database Cache System version 2 는 그것을 가능하게 할수 있습니다.

ProudNet Database Cache System version 2 는 트리 구조를 사용합니다. 한개의 RootNode 에 대하여 트리처럼 하위에 데이터들을 가지고 있습니다. 여기서, 사용자는 DBMS의 Schema 를 이렇게 정의 합니다.

이렇게 정의가 되면, 독점적 데이터 불러오기 및 새 데이터 추가하기 방법을 사용하여, Gamer 을 로딩한후, 유저가 고르는 캐릭에 따라서, CharacterInfo 를 로딩할수 있습니다. 아래는 대략적인 코드 입니다.

Proud::Guid outSessionGuid;
Proud::CDbCacheClient2* c = ...;
c->RequestExclusiveLoadData(L"Gamer",L"GamerID",gamername,outSessionGuid);//이요청으로 loadedGamerData 를 받습니다.

...//사용자가 캐릭터를 고름.loadedGamerData 에서 사용자가 고른 캐릭을 찾음.

c->RequestExclusiveLoadData(L"CharacterInfo",L"HeroIndex",findCharacter->heroIndex,outSessionGuid); //사용자가 고른 캐릭에 관한 정보트리를 로드.

1.6DB cache ver 1로부터 옮겨오기

DB cache ver 1로부터 옮겨오기 전에, DB cache ver 1과의 차이를 먼저 이해하셔야 합니다.

차이 항목

Ver 1

Ver 2

대처 방법

Credential 발급

있음

없음

Credential 발급을 직접 구현해야 함

DB cache에 의해 게이머 인증 콜백

있음

없음

게이머 인증 콜백 대신 직접 콜을 해야 함

DB cache ver 1에서는 Gamer,Hero,WorldObject Table을 다룹니다. 이들을 계속 사용하시려면 OwnerUUID,RootUUID,ParentUUID를 지정해야 합니다.

DB cache ver 1에서는 CLoadedHeroData,CLoadedItemData, CLoadedGamerData 를 다룹니다. 이들 대신 CLoadedData 를 다루어야 합니다.

1.7DB Cache 튜토리얼

DB Cache 를 사용하는 기본적인 방법을 익힙니다.

DB Cache2 Sample 을 참고하시면 됩니다.

서버 시작하기

가장 먼저 ".\include\ProudDB.h" 파일을 include 하셔야 합니다.

IDBCacheServerDelegate2를 상속받은 객체와 CDBCacheServer2객체가 필요합니다.

IDBCacheServerDelegate2는 DB Cache Server 의 이벤트를 상속받기 위한 Interface Class 입니다.

namespace Proud;
Class CDBServer : public IDBCacheServerDelegate2
{
    ~CDBServer()
    {
        if(m_dbServer != NULL)
        {
            m_dbServer->Stop();
            delete m_dbServer;
        }
    }

    // DB cache server 인스턴스는 각 게임 서버들 사이에서 단 1개만 있으면 충분합니다.
    CDBCacheServer2 *m_dbServer;
    void Start();

    // virtual 함수들은 생략
}

DB Cache Server를 시작하는 예제 로직입니다.

CDbCacheServer2StartParameter p;
p.m_delegate = this;
p.m_tcpPort = 44442; // Port

// DB cache server에서 DB Cache Client를 인증하기 위한 Key 입니다.
p.m_authenticationKey = L”Sample Key should be something”;

// DB 인스턴스에 접속하는 connection string 은 ADO connection string 형식이며, 샘플에서 참고하실 수 있습니다.
p.m_DBmsConnectionString = L” Data Source=localhost;Database=TestDB;Trusted_Connection=yes”

CCachedTableName userTable;
userTable.m_rootTableName = L"Gamer";
userTable.m_childTableNames.Add(L"Hero");
userTable.m_childTableNames.Add(L"Item");

param.m_tableNames.Add(m_userTable);

// DB cache server 인스턴스를 생성하는 메서드는 Proud.CDbCacheServer2::New() 입니다.
CDbCacheServer2 *dbServer = CDbCacheServer2::New();

dbServer->Start(p);

클라이언트 시작하기

IDBCacheClientDelegate2를 상속받은 객체와 CDBCacheServer2객체가 필요합니다.

IDBCacheClientDelegate2는 DB Cache Server 의 이벤트를 상속받기 위한 Interface Class 입니다.

namespace Proud;
Class CDBClient : public IDBCacheClientDelegate2
{
    ~CDBClient ()
    {
        if(m_dbClient != NULL)
        {
            m_dbClient->Stop();
            delete m_dbClient;
        }
    }
    CDBCacheServer2 *m_dbClient;
    void Start();
    // virtual 함수들은 생략
}

DB Cache Client를 시작하는 예제 입니다.

CDBCacheClient2ConnectParameter p;
p.m_delegate = this;
p.m_serverAddr = L”192.168.xx.xx”;
p.m_serverPort = 33355;



// DB cache server에서 DB Cache Client를 인증하기 위한 Key 입니다.
p.m_authenticationKey = L”Sample Key should be something”;



// Proud.CDbCacheClient2.New 를 실행해서 client 인스턴스를 생성합니다.
CDBCacheClient2 *dbClient = CDBCacheClient2::New();
m_dbClient->Connect(p);

응답 Parameter - CCallbackArgs

요청 응답형의 경우 DB Cache Client는 Callback Event의 parameter 인 CCallbackArgs 를 통하여 상태 정보를 알 수 있습니다.

CCallbackArgs 에는 요청에 대한 결과가 저장되어 있으며, 요청 성공 시 DB 의 Snapshot(복사본) 정보를 확인 하실 수 있습니다.

CCallbackArgs 로 부터 받은 data 는 복사본이기 때문에 CCallbackArgs 내의 정보를 바꾸셔도 엔진 내부의 Data 영향이 없습니다.

CCallbackArgs 의 Snapshot 지원기능은 다음과 같습니다.

CCallbackArgs.m_loadedData

CCallbackArgs.m_data

Snapshot의 활용 예

CPropNode* child = Args.m_loadedData->GetRootNode()->GetChild();

While(child)
{
    // Table의 이름을 얻어 이름을 비교할 수 있습니다.
    Proud::String txt = Child.TypeName;
    if(txt.Compare(L”CHARACTER”) == 0)
    {
        return child;
    }
    // 다음 Table Node를 얻습니다.
    Child = child->sibling;
}
CPropNode* root = Args.m_loadedData->GetRootNode();

// 해당 레코드의 값을 얻습니다.
Proud::String id = Root->Field[L”UserID”];
Proud::String id = Root->Field[L”Passworld”];
Int index = Root->Field[L”Index”];

독점 로딩의 예

먼저 두개의 객체를 있다고 가정해 보겠습니다.

// ./include/ProudDB.h를 추가 해주셔야 합니다.
using namespace Proud;

class CRemote
{
    Guid m_sessionGuid;
    CLoadedData2Ptr m_loadedData;
}

class CSample : public IDbCacheClientDelegate2
{
    CFastArray<*CRemote> m_remotes;
    CRemote* FindByGuid(Guid&);

    DBCacheClient2 *m_dbClient;

    // virtual 함수들 생략
}

데이터를 추가하면서 독점로딩 하는 예제 입니다.

요청:

CRemote *remote = new CRemote;

// 추가시킬 Table의 이름을 넣어 줍니다.
CPropNodePtr newNode = CPropNodePtr(new CPropNode(L"Gamer"));
Guid guid = Guid::RandomGuid();

// 추가할 Data를 Setting합니다.
newNode->Fields[L"UUID"] = guid;
newNode->Fields[L"OwnerUUID"] = guid;
newNode->Fields[L"RootUUID"] = guid;
newNode->Fields[L"GamerID"] = gamerID;
newNode->Fields[L"Password"] = password;

Guid outSessionGuid;
if(m_dbClient->RequestExclusiveLoadNewData(L"Gamer", newNode, outSessionGuid) 
    == true)
{
    remote->m_sessionGuid = outSessionGuid;
    m_remotes.Add(remote);
}

RequestExclusiveLoadNewData의 인자는 다음과 같습니다. 첫번째: 추가할 Table Name 두번쨰: 추가할 노드정보 세번쨰: 발급받을 sessionGuid out Parameter 값입니다. 이 값은 한개의 독점 Client에 대한 식별값이 됩니다.

IDbCacheClientDelegate2 으로 상속받은 함수가 Callback됩니다.

응답:

CSample::OnExclusiveLoadDataFailed(CCallbackArgs args)

CSample::OnExclusiveLoadDataSuccess(CCallbackArgs args)
{
    // CRemote라는 객체가 있다고 가정합니다.
    CRemote *remote = FindByGuid(args.m_sessionGuid);

    // m_loadedData를 복사하여 사용할 수 있습니다.
    remote,m_loadedData = args.m_loadedData;
}

위 예와 같이 sessionGuid를 통하여 요청 시점이나, 객체 등을 확인할 수 있습니다.

data를 독점 로딩한 후에는 응답 요청 형을 사용하실 수 있습니다.

독점적 데이터 - 요청 응답 형

요청 응답 형 사용 예제 입니다.

먼저 예제 코드를 진행하기 위하여 아래의 class가 있다고 가정하겠습니다.

Class CRemote
{
    CLoadedData2Ptr m_loadedData;
    Guid m_heroGuid;
}

class CSample : public IDbCacheClientDelegate2
{
    CFastMap2<HostID, *CRemote> m_remotes;
    CRemote *FindByHostID(HostID);

    DBCacheClient2 *m_dbClient;

    // virtual 함수들 생략
}

Proud.CDbCacheClient2.RequestAddData

Data를 추가합니다.

앞서 로드된 Data가 있다고 가정합니다.

요청:

CRemote *remote = FindByHostID(hostID);



CPropNodePtr newNode = CPropNodePtr(new CPropNode(L"Hero"));
newNode->Fields[L"Name"] = m_heroName;
newNode->Fields[L"Removed"] = 0;
newNode->Fields[L"HeroType"] = 1;



if(m_dbClient->RequestAddData(remote->m_loadedData->RootUUID, remote->m_loadedData->RootUUID, newNode, hostID) == true)
{
}

RequestAddData의 각 인자는 아래와 같습니다.

IDbCacheClientDelegate2 으로 상속받은 함수가 Callback됩니다.

응답:

CSample::OnAddDataSuccess(CCallbackArgs args)
{
    CRemote *remote = FindByHostID((HostID)args.m_tag);
    // load된 data를 새로 갱신해 줍니다.
    remote.m_loadedData = args.m_loadedData;
}
CSample::OnAddDataFailed(CCallbackArgs args)
{
    // Add 실패 입니다.
}

Proud.CDbCacheClient2.RequestUpdateData

DB의 Data를 수정합니다.

기존에 로드된 Data가 있다고 가정 하겠습니다. 요청:

CRemote *remote = FindByHostID((HostID)args.m_tag);

CPropNodePtr heroNode = remote->m_loadedData->GetNode(hostID);

// 히어로의 정보를 바꾼다.
heroNode->Fields[L"HeroType"] = 2;

// 히어로의 정보를 업데이트한다.
if(m_dbClient->RequestUpdateData(heroNode, hostID) == true)
{
}

RequestUpdateData의 각 인자는 아래와 같습니다.

IDbCacheClientDelegate2 으로 상속받은 함수가 Callback됩니다.

응답:

CSample::OnUpdateDataSuccess(CCallbackArgs args)
{
    CRemote *remote = FindByHostID((HostID)args.m_tag);
    // load된 data를 새로 갱신해 줍니다.
    remote.m_loadedData = args.m_loadedData;
}

CSample::OnUpdateDataFailed(CCallbackArgs args)
{
    // update 실패
}

Proud.CDbCacheClient2.RequestRemoveData

DB의 Data를 제거 합니다.

기존에 로드된 Data가 있다고 가정하겠습니다.

요청:

CRemote *remote = FindByHostID(hostID);

if(m_dbClient->RequestRemoveData(remote->m_loadedData->RootUUID, remote->m_heroGuid, hostID) == true)
{
}

RequestRemoveData의 인자는 다음과 같습니다.

IDbCacheClientDelegate2 으로 상속받은 함수가 Callback됩니다.

응답:

CSample::OnRemoveDataSuccess(CCallbackArgs args)
{
    CRemote *remote = FindByHostID((HostID)args.m_tag);
    // load된 data를 새로 갱신해 줍니다.
    remote.m_loadedData = args.m_loadedData;
}
CSample::OnRemoveDataFailed(CCallbackArgs args)
{
    // remove 실패
}

독점적 데이터 - 일방 형

일방 형 함수를 사용하기 위한 예 입니다.

먼저 예제 코드를 진행하기 위하여 아래의 class가 있다고 가정하겠습니다.

Class CRemote
{
    CLoadedData2Ptr m_loadedData;
    Guid m_heroGuid;
}

class CSample
{
    CFastMap2<HostID, *CRemote> m_remotes;
    CRemote *FindByHostID(HostID);

    DBCacheClient2 *m_dbClient;
}

Node를 제거하는 예제 입니다. 일방 형 응답을 받지 않기 때문에 load된 data를 요청 후 직접 수정하시면 됩니다.

CRemote *remote = FindByHostID(hostId);

if(m_dbClient->UnilateralRemoveData(remote->m_loadedData->RootUUID, remote->m_heroGuid, true) == true)
{
    CPropNodePtr heroNode = remote->m_loadedData->GetNode(m_heroGuid);

    // 바로 수정합니다.
    remote->m_loadedData->RemoveNode(heroNode);
}

UnilateralRemoveData 의 인자는 다음과 같습니다.

비독점 로딩 예

비 독점적으로 Data 얻어오기 예제 입니다.

비 독점적으로 Data의 최신 상태를 얻어옵니다.

먼저 예제 코드를 진행하기 위하여 아래의 class가 있다고 가정하겠습니다.

Class CRemote
{
    CLoadedData2Ptr m_loadedData;
    Guid m_heroGuid;
}

class CSample : public IDbCacheClientDelegate2
{
    CFastMap2<HostID, *CRemote> m_remotes;
    CRemote *FindByHostID(HostID);

    DBCacheClient2 *m_dbClient;

    // virtual 함수 생략
}

요청:

Proud::String strSearch;
strSearch.Format(L"GamerID='%s'",m_GamerName);

m_dbClient->RequestNonExclusiveSnapshotData(L"Gamer",strSearch, (intptr_t)hostID);

DBCacheClient2::RequestNonExclusiveSnapshotData 의 인자는 다음과 같습니다.

IDbCacheClientDelegate2 으로 상속받은 함수가 Callback됩니다.

응답:

CSample::OnNonExclusiveSnapshotDataSuccess (CCallbackArgs args)
{
    CRemote* remote = FindByHostID((HostID)args.m_tag);
    remote.m_loadedData = args.m_loadedData;
}
CSample::OnNonExclusiveSnapshotDataFailed (CCallbackArgs args)
{
    // 실패 입니다. 로그를 남기세요
}

비독점 형 데이터 사용 예

먼저 예제 코드를 진행하기 위하여 아래의 class가 있다고 가정하겠습니다. 기존에 이미 로드된 Data가 있다고 가정합니다.

Class CRemote
{
    CLoadedData2Ptr m_loadedData;
    Guid m_heroGuid;
}

class CSample : public IDbCacheClientDelegate2
{
    CFastMap2<HostID, *CRemote> m_remotes;
    CRemote *FindByHostID(HostID);

    DBCacheClient2 *m_dbClient;

    // virtual 함수 생략
}

DBCacheClient2::RequestNonExclusiveAddData 비 독점적으로 Data추가 하기 입니다.

요청:

CPropNodePtr newData = CPropNodePtr(new CPropNode(L"Hero"));
newData->SetField(L"Name",m_heroName);
newData->SetField(L"Removed",0);
newData->SetField(L"HeroType",1);

// DB에 Hero 정보를 추가 합니다.
m_dbClient->RequestNonExclusiveAddData(RootTableName, RootUUID, RootUUID, newData, m_diffCustomArg, Proud::ByteArray());

IDbCacheClientDelegate2 를 상속받은 함수가 Callback됩니다.

응답:

// 비 독점 형 사용에서는 처음의 Data 로딩 외에는 m_loadedData를 접근하실 수 없습니다. CCallbackArgs::m_data를 사용하십시오.
CSample::OnNonExclusiveAddDataAck (CCallbackArgs args)
{
    // args.m_data;
}

// 아래는 원래의 독점을 하고 있었던 DB Cache Client가 받게 되는 Callback입니다.
CSample::OnSomeoneAddData( CCallbackArgs& args )
{
    // 기존 loadedData를 저장하고 있다면, 바뀐 Data로 다시 저장해 주셔야 합니다.
    m_client->m_loadedData = args.m_loadedData;
}