원인
MongoDB의 shard key size 제한이 512 바이트인데, insert 하려는 document에서 shard key에 해당하는 부분이 512 바이트를 넘어서기 때문에 발생하는 에러
해결
사이즈 기준이 각 키/값별 데이터 사이즈의 합계인지, 아니면 JSON 문자열의 길이인지 뭔지 몰라서 헤맸는데,
BSON size가 기준이었다.
MongoDB 소스코드에서 shard key size를 체크하는 함수의 매개변수 부분을 보면 BSONObj가 넘어가는 걸 확인할 수 있다.
https://github.com/mongodb/mongo/blob/v3.0/src/mongo/s/shard_key_pattern.cpp#L59
(내가 운용하는 MongoDB 버전이 3.0이라 3.0 버전 소스코드 기준임)
const int ShardKeyPattern::kMaxShardKeySizeBytes = 512;
const unsigned int ShardKeyPattern::kMaxFlattenedInCombinations = 4000000;
Status ShardKeyPattern::checkShardKeySize(const BSONObj& shardKey) {
if (shardKey.objsize() <= kMaxShardKeySizeBytes)
return Status::OK();
return Status(ErrorCodes::ShardKeyTooBig,
stream() << "shard keys must be less than " << kMaxShardKeySizeBytes
<< " bytes, but key " << shardKey << " is " << shardKey.objsize()
<< " bytes");
}
PHP에서 BSON size를 어떻게 구할 수 있을까 찾아보다가,
php-mongo driver의 함수 중 bson_encode() 가 PHP의 변수를 받아 BSON으로 변환해주는 구현체라는 걸 발견했고,
https://github.com/mongodb/mongo-php-driver-legacy/blob/v1.6/bson.c#L1412
/* {{{ proto string bson_encode(mixed document)
Takes any type of PHP var and turns it into BSON */
PHP_FUNCTION(bson_encode)
{
zval *z;
mongo_buffer buf;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) {
return;
}
/** 이하 생략 **/
bson_encode() 함수와 strlen() 함수를 이용해서 MongoDB shell 명령어인 Object.bsonsize() 와 동일한 결과를 얻을 수 있다는 사실을 알아냈다.
아래와 같은 방식으로 shard key의 size를 미리 체크해서 insert 하기 전에 사고를 방지할 수 있다.
// shard key가 이런 모양이고 key_X가 얼마나 늘어날지 모르는 상황에서 에러가 발생한다면
$shard_key = array(
'key_A' => 'AA',
'key_B' => 11,
'key_C' => array(1, 2, 3),
'key_X' => 'this string can exceed 512 bytes...'
);
// BSON size를 계산해보고 512를 넘으면 적절한 조치를 취해주자
if (strlen(bson_encode($shard_key)) > 512) {
$shard_key['key_X'] = 'INVALID_STRING';
}
bson_encode() 함수는 deprecated된 php-mongo driver(legacy)에 포함된 함수임.
현역인 php-mongodb driver를 이용한다면 bson_encode() 대신, MongoDB\BSON\fromPHP() 함수를 이용하면 될 듯.
BSON spec을 보니, 맨 앞의 4바이트(int32)가 document의 길이를 나타내므로 bin2hex, hexdec 등을 이용하는 방법을 먼저 해봤는데, 그냥 strlen이 성능상 빠르고 편함.
참고
MongoDB shard key size 제한: https://docs.mongodb.com/manual/reference/limits/#Shard-Key-Size
php-mongo drvier(legacy): http://php.net/mongo
bson_encode(): http://php.net/manual/en/function.bson-encode.php
MongoDB\BSON\fromPHP(): http://php.net/manual/en/function.mongodb.bson-fromphp.php
BSON spec: http://bsonspec.org/spec.html
.