Category Archives: NoSQL

[Cassandra] 0.8.1로 업그레이드 후 AbstractCassandraDaemon Fatal exception in thread Thread 에러 수정

사용자 삽입 이미지

아직 1.0도 못된 Cassandra를 운영함에 있어 여러가지 이해할수 없는 버그스러운 일이 발생하곤 합니다. 하지만 다행이도 하늘이 무너져도 솟아날 구멍은 항상 있더군요. 이번에도 아주 활당한 경험담을 적어볼까 합니다. 하지만 생각보다 검색해 보면 많은 글이 나오는걸 보면 저와같은 경험담을 가진 사람들이 많을듯 하군요.

잘 운영하던 카산드라 서버를 이번에 새로나온 0.8.1로 업그레이드를 할 경우 다음과 같은 무지막지한 에러를 보게 됩니다.

[code]ERROR [ReadStage:594] 2011-07-22 06:17:20,877 AbstractCassandraDaemon.java (line 113) Fatal exception in thread Thread[ReadStage:594,5,main]
java.io.IOError: java.io.EOFException: EOF after 12559390 bytes out of 842137600
        at org.apache.cassandra.db.columniterator.SimpleSliceReader.<init>(SimpleSliceReader.java:66)
        at org.apache.cassandra.db.columniterator.SSTableSliceIterator.createReader(SSTableSliceIterator.java:91)
        at org.apache.cassandra.db.columniterator.SSTableSliceIterator.<init>(SSTableSliceIterator.java:67)
        at org.apache.cassandra.db.filter.SliceQueryFilter.getSSTableColumnIterator(SliceQueryFilter.java:66)
        at org.apache.cassandra.db.filter.QueryFilter.getSSTableColumnIterator(QueryFilter.java:80)
        at org.apache.cassandra.db.ColumnFamilyStore.getTopLevelColumns(ColumnFamilyStore.java:1292)
        at org.apache.cassandra.db.ColumnFamilyStore.getColumnFamily(ColumnFamilyStore.java:1189)
        at org.apache.cassandra.db.ColumnFamilyStore.getColumnFamily(ColumnFamilyStore.java:1146)
        at org.apache.cassandra.db.Table.getRow(Table.java:385)
        at org.apache.cassandra.db.SliceFromReadCommand.getRow(SliceFromReadCommand.java:61)
        at org.apache.cassandra.db.ReadVerbHandler.doVerb(ReadVerbHandler.java:69)
        at org.apache.cassandra.net.MessageDeliveryTask.run(MessageDeliveryTask.java:72)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.io.EOFException: EOF after 12559390 bytes out of 842137600
        at org.apache.cassandra.io.util.FileUtils.skipBytesFully(FileUtils.java:229)
        at org.apache.cassandra.io.sstable.IndexHelper.skipBloomFilter(IndexHelper.java:50)
        at org.apache.cassandra.db.columniterator.SimpleSliceReader.<init>(SimpleSliceReader.java:57)
        … 14 more
 INFO [CompactionExecutor:9] 2011-07-22 06:18:11,575 CompactionManager.java (line 743) Retrying from row index; data is -8 bytes starting at 14898883
 WARN [CompactionExecutor:9] 2011-07-22 06:18:11,575 CompactionManager.java (line 767) Retry failed too.  Skipping to next row (retry’s stacktrace follows)
java.io.IOError: java.io.EOFException: bloom filter claims to be 905969664 bytes, longer than entire row size -8
        at org.apache.cassandra.io.sstable.SSTableIdentityIterator.<init>(SSTableIdentityIterator.java:149)
        at org.apache.cassandra.io.sstable.SSTableIdentityIterator.<init>(SSTableIdentityIterator.java:90)
        at org.apache.cassandra.db.compaction.CompactionManager.scrubOne(CompactionManager.java:748)
        at org.apache.cassandra.db.compaction.CompactionManager.doScrub(CompactionManager.java:633)
        at org.apache.cassandra.db.compaction.CompactionManager.access$600(CompactionManager.java:65)
        at org.apache.cassandra.db.compaction.CompactionManager$3.call(CompactionManager.java:250)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.io.EOFException: bloom filter claims to be 905969664 bytes, longer than entire row size -8
        at org.apache.cassandra.io.sstable.IndexHelper.defreezeBloomFilter(IndexHelper.java:111)
        at org.apache.cassandra.io.sstable.SSTableIdentityIterator.<init>(SSTableIdentityIterator.java:119)
        … 10 more[/code]
음…좀 이쁘게 보여드리고 싶었는데 달리 방법이 없네요. 위와 같은 에러가 무한정 발생을 하는데요. 위의 에러를 고칠려면 다음의 명령 한방이면 해결이 됩니다. 각각의 노드마다 한번씩 실행해 주셔야 합니다. 보유 데이터량에 따라 소요되는 시간이 다릅니다.
[code]nodetool -h localhost scrub[/code]
scrub 명령은 원하는 키스페이스 또는 컬럼패밀리만을 정하여 sstable을 새로 생성합니다. 기왕이면 서버의 사용량이 적을때를 이용해서 실행하시는것이 정신건강에 좋습니다.

노드만 충분하다면 scrub을 실행하실때에 다음의 명령을 이용하여 서비스 노드에서 제거하시는것이 좋습니다. 이유는 클러스터간에 상태가 꼬여있는 상태(?)일수가 있는데 이경우 scrub을 실행하게 되면 scrub을 실행하는 노드뿐만 아니라 다음의 노드의 부하가 올라가더니 뻗는 경우가 있더군요.
[code]nodetool -h localhost disablegossip[/code]
위의 명령을 실행하게 되면 기존의 노드들간의 클러스터링에서 제외..라는 표현보다는 사라진척 한다는 말이 맞겠네요. 서버들간의 상태를 주고 받는데에 사용되는 gossip을 끄게됩니다. 켤때는 반대로 enablegossip으로 켜주시면 됩니다.

만약에 불가피하게 서비스중에 위의 작업들을 수행해야만 하는 경우라면 클라이언트 소스를 수정할 필요 없이 다음의 명령으로 클라이언트의 요청에 응답해야 할 의무를 버릴 수 있습니다. (thrift를 시용할시)
[code]nodetool -h localhost disablethrift[/code]
마찬가지로 다시 켤때는 반대로 enablethrift를 실행하시면 됩니다.

[NoSQL/Cassandra] PHPCassa 에서 TimeUUIDType을 어떻게 활용하는가?

사용자 삽입 이미지
별거 아닌 내용을 적어보려고 합니다. 하지만 처음 접할때 생각보다 어려웠던 기억이 나는군요. 이 글을 보시기 전에 PHPCassa에 대한 이해가 필요합니다. [이글]을 먼저 보시길 추천합니다.

Cassandra에는 기본적으로 TimeUUIDType이라는 것이 존재합니다. 이 타입을 이용하여 여러개의 컬럼키들을 시간순으로 정렬하는것이 가능해집니다. 기존의 MySQL등에서는 Auto Increment 옵션을 사용하여 Unique한 PK를 만들어냅니다만 Cassandra에서는 이 TimeUUIDType을 이용하여 사실상 거의 중복되지 않는 키값을 만들어 낼 수 있습니다.

그렇다면 이제 이 키값을 이용하여 서버-클라이언트간에 주고 받을 수 있어야 하는데요, 이것이 생각보다 간단하지 않습니다.(사실 알고보면 간단합니다만;) 우선 소스코드부터 보여드리겠습니다.
[code]// 1. 새로운 UUID를 생성
$newUUID = CassandraUtil::uuid1();
echo “<p>새로운 UUID생성: $newUUID</p>”;

// 2. 클라이언트/서버와 주고받을때 사용할 수 있는 문자형태로 변환
$strUUID = strval(CassandraUtil::import($newUUID));
echo “<p>식별가능한 문자열UUID로 변경: $strUUID</p>”;

// 3. 다시 바이너리 형태의 UUID로 변경
$importedUUID = CassandraUtil::import($strUUID)->bytes;
echo “<p>바이너리 형태의 UUID로 변경: $importedUUID</p>”;[/code]
위의 코드는 현재의 시간(64bit microtime)을 이용하여 UUID를 생성한 다음 그것을 서버-클라이언트간에 식별하기 쉬운 문자열로 바꾼후에 다시 그 문자열을 Cassandra에서 바로 사용할 수 있는 형태로 되돌리는 과정을 수행합니다. 위의 코드는 아래와 같은 결과를 보여줍니다.

사용자 삽입 이미지
첫번째와 마지막이 사실상 같다는 것을 알 수 있습니다. Cassandra에서는 기본적으로 이 Binary형태의 값을 사용합니다. 하지만 개발자가 사용하기 어렵고 이렇게 생성된 값을 서버/클라이언트간 주고 받는다고 해도 생각해볼점이 많아집니다. 이 경우 두번째와 같은 형태로 변경하여 사용할 수 있습니다.