Mongodb Building for Windows With VC10

Mongodb 의 Windows 에서의 빌드 방법입니다.

일단 제 목표는 Mongodb의 소스 디버깅이므로 디버그 위주로 설명합니다. 조금만 손대면
Release도 가능하겠죠 ^^

(개인적으로 디버깅 환경 자체는 VC++ 이 좋다고 생각하는 사람입니다.)
사실 이것만 봐도 되긴합니다.
http://www.mongodb.org/display/DOCS/Building+for+Windows

1. mongodb 다운로드 -> http://www.mongodb.org
2. boost 다운로드 및 빌드
-> 귀찮으니 그냥 prebuilt 모듈을 사용하면됩니다.
-> http://www.mongodb.org/pages/viewpageattachments.action?pageId=12157032
3. spidermonkey 다운로드 및 빌드
-> https://github.com/dwight/vc2010_js
-> mongodb는 BSON(Binary Json)을 사용합니다. 이를 위해 자바스크립트 엔진으로 SpiderMonkey를 사용합니다.
-> 구글 쪽 v8이던가를 쓰면 더 괜찮지 않을까 라는 생각만 있습니다.

4. 환경 설정
-> db/db_10.sln 을 오픈합니다.
-> include 폴더에 boost 와 spidermonkey의 헤더가 가도록 설정합니다.
-> lib 폴더에 boost 라이브러리가 셋팅되도록 합니다.
-> spidermonkey 라이브러리는 소스보다 상위에 ../js/ 를 만들어서 4개의 파일을 넣어줍니다.

5. 그리고 빌드하면 끝
-> 결과물은 db/Debug 안에 들어갑니다. 물론 Debug로 빌드할 때입니다.

Hbase Standalone 으로 설치시 주의 사항

hbase-site.xml 내용안에 rootdir 은 다음과 같이 저장한다.

hbase.rootdir
file:///home1/irteam/data/hbase

이때 꼭 앞에 file:// 이 붙도록 주의하자.

hbase 가 standalone 으로 뜰때 기본 설정은 zookeeper 를 자체적으로 가지고 있는 녀석으로 띄운다.
이 설정을 바꿀려면 zookeeper 를 바라보도록 설정을 추가해줘야 한다.

Cassandra Simple Authentication Example with pycassa

There is a Simple Python Example
For Cassandra SimpleAuthentication

from pycassa import index, connect, connect_thread_local, gm_timestamp, ColumnFamily, 
        ColumnFamilyMap, ConsistencyLevel, NotFoundException, String, Int64, 
        Float64, DateTime, IntString, FloatString, DateTimeString

import pycassa

auth = {'username':'username','password':'password'}
pool = pycassa.connect('Tweet', ['localhost:9160'], True, 10, auth)

cf = ColumnFamily(pool, 'Standard1')

print cf.get('charsyam2')
print cf.get('charsyam')

Zookeeper Example with Python

Zookeeper 는 apache 에서 진행하는 분산 코디네이터 입니다. 이를 이용해서 분산 락이라든 지, 현재 사용가능 한 Worker or Server 정보를 얻어오는 등의 여러가지 작업을 쉽게 할 수 있습니다. zookeeper 의 구조에 대해서는 다음 기회에 좀 더 자세히 설명해 드리기로 하고 일단 간단한 zookeeper 를 이용한 example을 보여주려고 합니다.

Zookeeper 에는 임시노드를 생성할 수 있는 기능이 있습니다. 임시노드는 세션이 끊어지면, 지정된 시간뒤에 사라지는 노드입니다. 즉, 특정 서버가 죽거나 하면 연결된 정보가 자동적으로 사라집니다. 다음 예제는 Echo 서버가 시작될 때 Zookeeper 에 자신의 Information( ip, port ) 을 등록하고, 장애로 서버가 동작하지 못하는 경우에 대한 샘플입니다.

실제 동작은 세션이 끊어진 뒤에 일정 시간 동안은 노드가 남아있으므로, 잠시 동안 오류가 발생하다가, 시간이 지나면 해당 노드 정보가 사라지므로 클라이언트들은 정상인 서버로만 접속을 하게 됩니다. 이런 부분은 분산 컴퓨팅에서 장애의 허용, 복구 같은 내용과 연관이 되게 되는데, 잠시 동안만 실패하므로, 사용자는 거의 정상적인 서비스를 할 수 있게 됩니다.

다음 그림과 같이 서버는 최초에 실행되면서 zookeeper 에 자신의 정보를 저장합니다.

클라이언트는 시작시 zookeeper 에서 위의 정보를 읽어서 서버에 접속하게 됩니다.

장애시에는 다음과 같이 자동으로 연결 정보가 사라지므로 클라이언트는 정상 서버로 계속 접속하게 됩니다.

다음은 서버 Sample 입니다.

#/usr/bin/env python

import zookeeper, time, threading
from gevent.server import StreamServer
from gevent import monkey; monkey.patch_socket()
import socket
monkey.patch_socket()

END_MARK='rn'

def handler(socket, address):
    print 'New connection from %s:%s' % address
    # using a makefile because we want to use readline()
    packet = ''
    socket.settimeout(2)

    while True:
        data = socket.recv(1024)
        if not data:
            print 'Connection Close from %s:%s' % address
            return

        packet += data
        if END_MARK in packet:
            socket.sendall(packet)
            print packet
            break

PORT_NUM = 12345

if __name__ == '__main__':
    # to make the server use SSL, pass certfile and keyfile arguments to the constructor
    server = StreamServer(('0.0.0.0', PORT_NUM),handler)
    # to start the server asynchronously, use its start() method;
    # we use blocking serve_forever() here because we have no other jobs

    connected = False
    conn_cv = threading.Condition()

    def my_connection_watcher(handle,type,state,path):
        global connected, conn_cv
        print "Connected, handle is ", handle
        conn_cv.acquire()
        connected = True
        conn_cv.notifyAll()
        conn_cv.release()

    conn_cv.acquire()
    print "Connecting to Zookeeper -- "
    handle = zookeeper.init("172.27.0.2:2181,172.27.0.3:2181,172.27.0.4:2181", my_connection_watcher)
    while not connected:
        conn_cv.wait()

    conn_cv.release()

    ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"};

    rootnode_name = "/zk-servers"
    ret = zookeeper.exists(handle, rootnode_name, None )
    if None == ret:
        zookeeper.create(handle, rootnode_name, "data", [ZOO_OPEN_ACL_UNSAFE], 0 )

    host = socket.gethostbyname(socket.gethostname())
    data = host + ":" + str(PORT_NUM)
    zookeeper.create(handle, "/zk-servers/echoserverlist", data, [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERA
L|zookeeper.SEQUENCE)

    print 'Starting echo server on port %s' % PORT_NUM
    server.serve_forever()

다음은 클라이언트 입니다. 모두 최초 접속시에 목록 리스트를 가져와서 랜덤하게 하나를 선택해서
접속하게 됩니다.

import zookeeper, time, threading
from gevent.server import StreamServer
from gevent import monkey; monkey.patch_socket()
import random
import socket

connected = False
conn_cv = threading.Condition()

def my_connection_watcher(handle,type,state,path):
    global connected, conn_cv
    print "Connected, handle is ", handle
    conn_cv.acquire()
    connected = True
    conn_cv.notifyAll()
    conn_cv.release()

conn_cv.acquire()
print "Connecting to localhost:2181 -- "
handle = zookeeper.init("172.27.0.2:2181,172.27.0.3:2181,172.27.0.4:2181", my_connection_watcher)
while not connected:
    conn_cv.wait()
conn_cv.release()

ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"};

rootnode_name = "/zk-servers"
ret = zookeeper.exists(handle, rootnode_name, None )
if None == ret:
    print "There is no zk-servers keys"
    exit()

children = zookeeper.get_children(handle, rootnode_name, None)
size = len(children)
if 0 == size:
    print "There is no zk-servers echo server lists."
    exit()

idx = random.randint(0,size-1)


child = children[idx]
nodename = rootnode_name + "/" +  child
(data,stat) = zookeeper.get(handle, nodename, None)
print idx, data, stat

ip, port = data.split(':')
print ip, port

clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsock.connect( (ip, int(port)) )
clientsock.sendall("Hi, this is test!rn")
recvdata = clientsock.recv(1024)
print "Data: ", recvdata
clientsock.close()

Cassandra Secondary Index 사용방법, Cassandra 0.7.0 에서는 name 을 바로 Column 명으로 사용하지 못합니다.

Cassandra 에서 Secondary Index 를 사용하기 위해서 테스트를 하다가 좀 삽질을 하게
되었습니다.

현재 https://issues.apache.org/jira/browse/CASSANDRA-1995 에 등록되어 있고
0.7.1 에서는 Fix 된 문제라고 합니다. 문제는 간단하게

$ bin/cassandra-cli –host localhost
Connected to: “Test Cluster” on localhost/9160
Welcome to cassandra CLI.

Type ‘help;’ or ‘?’ for help. Type ‘quit;’ or ‘exit;’ to quit.
[default@unknown] create keyspace demo;
[default@unknown] use demo;
[default@demo] create column family users with comparator=UTF8Type
… and column_metadata=[{column_name: full_name, validation_class: UTF8Type},
… {column_name: birth_date, validation_class: LongType, index_type: KEYS}];

[default@demo] set users[bsanderson][full_name] = ‘Brandon Sanderson’;
[default@demo] set users[bsanderson][birth_date] = 1975;
[default@demo] set users[prothfuss][full_name] = ‘Patrick Rothfuss’;
[default@demo] set users[prothfuss][birth_date] = 1973;
[default@demo] set users[htayler][full_name] = ‘Howard Tayler’;
[default@demo] set users[htayler][birth_date] = 1968;

[default@demo] get users where birth_date = 1973;

위와 같은 형태로 Column Family 가 생성이 가능합니다. birth_data 가 Seconday Index 로 설정이
되었습니다.

LongType 말고 UTF8Type 도 같은 방식으로 생성이 가능합니다.

[default@demo] update column family users with comparator=UTF8Type
… and column_metadata=[{column_name: full_name, validation_class: UTF8Type},
… {column_name: birth_date, validation_class: LongType, index_type: KEYS},
… {column_name: state, validation_class: UTF8Type, index_type: KEYS}];

[default@demo] get users where state = ‘UT’;

그런데 위에 column_name 을 name 으로만 지정하면 제대로 동작하지 않습니다.
이는 show cluster name 이라는 keyword 를 파싱하기 위한 동작의 side-effect 라고 합니다.

그러면 꼭 name 을 사용하지 못하는가? 는 아닙니다.

get Users[‘charsyam’];
get Users[charsyam];

이 동일하게 동작하는 것 처럼 name 대신에 ‘name’으로 지정해주면 문제는 해결됩니다.
단, 사용할 때도 이때는 꼭 get Users[‘charsyam’][‘name’] 형태로 이용해야 합니다.

pycassa example in cassandra 0.7.0

간단한 pycassa 를 이용한 Cassandra Connect example

#!/usr/bin/env python
import pycassa
client = pycassa.connect_thread_local('MyKeySpace', ['172.27.0.3:9160','172.27.0.4:9160'])
cf = pycassa.ColumnFamily( client, 'User' )
value = list(cf.get_range())
value_len = len(value)
print value_len
print value[0][0]
print value[0][1]
print value[0][1].keys()

result:

./test.py

1

charsyam

OrderedDict([(‘age’, ’32’), (’email’, ‘charsyam@naver.com’), (‘name’, ‘DaeMyung Kang’)])

[‘age’, ’email’, ‘name’]

Cassandra 0.6 -> 0.7 로의 변화

Cassandra 0.7 버전이 2011년 1월 9일에 발표되었습니다.

많이 쓰지 않아서 크게 무엇이 바뀌었는지를 알 수 없지만, 제가 아는 것들만 조금 정리해보자면

제일 크게 바뀐 것은 다음 두 가지 입니다.

1. online schema manipulation(add/remove)

2. support secondary index

원래 Cassandra 의 경우, 온라인에서 동적으로 Keyspace, Column Family 등이 생성되지 않았습니다

그래서 schema 변경이 필요하면, 모든 서버가 한번씩 내려갔다가 다시 올라와야 했습니다.  실제로

그럴 일이 많지는 않겠지만,  크게 뭔가가 추가되어야 하면, 서버들을 모두 한번씩 내렸다가 올려야 합니다.

Replica 를 관리하는 서버들의 특성이 그렇긴 하지만, 서버 장애시 Replication을 재조정 하는 작업은

Disk I/O를 많이 쓰기 때문에, 전반적으로 시스템 성능의 저하를 가져옵니다.

Secondary Index는 기존에는 원래의 Key 로 만 데이터를 찾을 수 있었는데, 추가로 Column 에

Index를 추가할 수 있게 되었습니다. 다음 사이트를 참고하면 자세한 설명이 있습니다.

http://www.riptano.com/docs/0.7/data_model/secondary_indexes

주된 불만 중에 하나가 해결된 느낌입니다.

그 외로 소소하게 바뀐 것들은 cli 에서 라인의 끝에 무조건 ‘;’ 을 붙여야 한다는 것!(이걸 몰라서 크게 헤매었습니다.)

그리고 storage 설정이 xml 에서 yaml 로 바뀌었습니다. 기본 설정 값도 바뀌어 있네요.

그리고 cli 에서 이전에는 set keyspace.Standard1[‘charsyam’][‘name’] = ‘value’;

이런식으로 사용하던 것을

use keyspace keyspace;

set Standard1[‘charsyam’][‘name’]=’value’; 형식으로 사용해야 한다는 부분이 바뀌었습니다.

계속 어떻게 발전할 지 궁금하네요.