Cassandra 2.0.3 / 1.2.12 를 실행시킬 때, 아래와 같은 에러를 볼 수 있다. 


[main] ERROR cassandra.cql3.QueryProcessor - Unable to initialize MemoryMeter (jamm not specified as javaagent).  This means Cassandra will be unable to measure object sizes accurately and may consequently OOM.



에러까지는 아니고 MemoryMeter 클래스를 포함한 jamm java agent를 java runtime때 포함시키지 않아서 난 것이다. 이것때문에 CI나 Local 환경에서는이슈가 없다. 


그러나 상용(Production) 서버에서는 OOM을 발생하지 않도록 조심해야 하기 때문이다. 






2.0.2, 1.2.11 버전에 EmbeddedCassandraService 클래스를 사용시 아래와 같은 에러가 발생했다. (개인적으로 이 부분 패치를 원했는데.. 잘 되었다~^^;;)


 ERROR Native-Transport-Requests:109 org.apache.cassandra.transport.messages.ErrorMessage - Unexpected exception during request
java.lang.IllegalStateException: Instrumentation is not set; Jamm must be set as -javaagent





https://issues.apache.org/jira/browse/CASSANDRA-6293

Cassandra 6293 의 의거.. QueryProcessor에 MemoryMetter 초기화 여부에 따른 일부 코드가 추가되었다. 만약 초기화되지 않았으면, 'ERROR cassandra.cql3.QueryProcessor - Unable to initialize MemoryMeter (jamm not specified as javaagent).  This means Cassandra will be unable to measure object sizes accurately and may consequently OOM.' 라는 로그가 뜨도록 되어 있다. 


 


 static

    {

        if (MemoryMeter.isInitialized())

        {

            preparedStatements = new ConcurrentLinkedHashMap.Builder<MD5Digest, CQLStatement>()

                                 .maximumWeightedCapacity(MAX_CACHE_PREPARED_MEMORY)

                                 .weigher(cqlMemoryUsageWeigher)

                                 .build();

            thriftPreparedStatements = new ConcurrentLinkedHashMap.Builder<Integer, CQLStatement>()

                                       .maximumWeightedCapacity(MAX_CACHE_PREPARED_MEMORY)

                                       .weigher(thriftMemoryUsageWeigher)

                                       .build();

        }

        else

        {

            logger.error("Unable to initialize MemoryMeter (jamm not specified as javaagent).  This means "

                         + "Cassandra will be unable to measure object sizes accurately and may consequently OOM.");

            preparedStatements = new ConcurrentLinkedHashMap.Builder<MD5Digest, CQLStatement>()

                                 .maximumWeightedCapacity(MAX_CACHE_PREPARED_COUNT)

                                 .build();

            thriftPreparedStatements = new ConcurrentLinkedHashMap.Builder<Integer, CQLStatement>()

                                       .maximumWeightedCapacity(MAX_CACHE_PREPARED_COUNT)

                                       .build();

        }

    }



MemoryMeter가 초기화되었으면, weigher 인스턴스를 생성하는데. capacity 개수를 결정할 수 있도록 한다. 

즉, QueryProcessor에 key, value로 된 pair의 개수를 제한할 수 있다. 

MemoryMeter가 초기화되면 MAX_CACHE_PREPARED_MEMORY로 capacity를 설정하고 key-value paring을 지정할 수 있도록 하는 반면.. MemoryMetter가 초기화되지 않으면 capacity는 MAX_CACHE_PREPARED_COUNT로만 지정할 수 있다.대신 count만 가지고 제한하는 경우는 역시 메모리 크기가 이슈가 될 수 있으니 OOM (Out Of Memory)를 마음으로 준비하라는 뜻이다. 


final 변수는 다음과 같다. 

  1. private static final long MAX_CACHE_PREPARED_MEMORY = Runtime.getRuntime().maxMemory() / 256;
  2. private static final int MAX_CACHE_PREPARED_COUNT = 10000;


DB의 prepared statment size를 결정하는 것으로 보이는 부분이라 할 수 있을 것 같다. 이를 통해서 DB의 jdbc 와 비슷하게 cassandra 내부에서 결정할 수 있게 한다. 이를 추적할 수 있는 부분을 제공한 것이다. 



출처 : http://concurrentlinkedhashmap.googlecode.com/svn/wiki/release-1.1-LRU/com/googlecode/concurrentlinkedhashmap/ConcurrentLinkedHashMap.html


 A Weigher instance determines how many units of capacity that a value consumes. The default weigher assigns each value a weight of 1 to bound the map by the total number of key-value pairs.



MemoryMeter에 대한 코드는 아래 jamm github에서 확인해볼 수 있다. 

https://github.com/jbellis/jamm


Posted by 김용환 '김용환'