com.moilioncircle.redis.replicator.rdb.RdbParser Maven / Gradle / Ivy
Show all versions of redis-replicator Show documentation
/*
* Copyright 2016 leon chen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.moilioncircle.redis.replicator.rdb;
import com.moilioncircle.redis.replicator.AbstractReplicator;
import com.moilioncircle.redis.replicator.event.Event;
import com.moilioncircle.redis.replicator.event.PostFullSyncEvent;
import com.moilioncircle.redis.replicator.event.PreFullSyncEvent;
import com.moilioncircle.redis.replicator.io.RedisInputStream;
import com.moilioncircle.redis.replicator.rdb.datatype.DB;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_AUX;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_EOF;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_EXPIRETIME;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_EXPIRETIME_MS;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_RESIZEDB;
import static com.moilioncircle.redis.replicator.Constants.RDB_OPCODE_SELECTDB;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_HASH;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_HASH_ZIPLIST;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_HASH_ZIPMAP;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_LIST;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_LIST_QUICKLIST;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_LIST_ZIPLIST;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_MODULE;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_MODULE_2;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_SET;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_SET_INTSET;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_STRING;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_ZSET;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_ZSET_2;
import static com.moilioncircle.redis.replicator.Constants.RDB_TYPE_ZSET_ZIPLIST;
import static com.moilioncircle.redis.replicator.Status.CONNECTED;
/**
* Redis RDB format
*
*
* @author Leon Chen
* @see rdb.c
* @see Redis-RDB-Dump-File-Format
* @since 2.1.0
*/
public class RdbParser {
protected final RedisInputStream in;
protected final RdbVisitor rdbVisitor;
protected final AbstractReplicator replicator;
protected static final Log logger = LogFactory.getLog(RdbParser.class);
public RdbParser(RedisInputStream in, AbstractReplicator replicator) {
this.in = in;
this.replicator = replicator;
this.rdbVisitor = this.replicator.getRdbVisitor();
}
/**
* ----------------------------# RDB is a binary format. There are no new lines or spaces in the file.
*
* 52 45 44 49 53 # Magic String "REDIS"
*
* 30 30 30 33 # RDB Version Number in big endian. In this case, version = 0003 = 3
*
* ----------------------------
*
* FE 00 # FE = code that indicates database selector. db number = 00
*
* ----------------------------# Key-Value pair starts
*
* FD $unsigned int # FD indicates "expiry time in seconds". After that, expiry time is read as a 4 byte unsigned int
*
* $value-type # 1 byte flag indicating the type of value - set, map, sorted set etc.
*
* $string-encoded-name # The name, encoded as a redis string
*
* $encoded-value # The value. Encoding depends on $value-type
*
* ----------------------------
*
* FC $unsigned long # FC indicates "expiry time in ms". After that, expiry time is read as a 8 byte unsigned long
*
* $value-type # 1 byte flag indicating the type of value - set, map, sorted set etc.
*
* $string-encoded-name # The name, encoded as a redis string
*
* $encoded-value # The value. Encoding depends on $value-type
*
* ----------------------------
*
* $value-type # This name value pair doesn't have an expiry. $value_type guaranteed != to FD, FC, FE and FF
*
* $string-encoded-name
*
* $encoded-value
*
* ----------------------------
*
* FE $length-encoding # Previous db ends, next db starts. Database number read using length encoding.
*
* ----------------------------
*
* ... # Key value pairs for this database, additonal database
*
* FF ## End of RDB file indicator
*
* 8 byte checksum ## CRC 64 checksum of the entire file.
*
*
* @return read bytes
* @throws IOException when read timeout
*/
public long parse() throws IOException {
/*
* ----------------------------
* 52 45 44 49 53 # Magic String "REDIS"
* 30 30 30 33 # RDB Version Number in big endian. In this case, version = 0003 = 3
* ----------------------------
*/
this.replicator.submitEvent(new PreFullSyncEvent());
rdbVisitor.applyMagic(in);
int version = rdbVisitor.applyVersion(in);
DB db = null;
/*
* rdb
*/
loop:
while (this.replicator.getStatus() == CONNECTED) {
int type = rdbVisitor.applyType(in);
Event event = null;
switch (type) {
case RDB_OPCODE_EXPIRETIME:
event = rdbVisitor.applyExpireTime(in, db, version);
break;
case RDB_OPCODE_EXPIRETIME_MS:
event = rdbVisitor.applyExpireTimeMs(in, db, version);
break;
case RDB_OPCODE_AUX:
event = rdbVisitor.applyAux(in, version);
break;
case RDB_OPCODE_RESIZEDB:
rdbVisitor.applyResizeDB(in, db, version);
break;
case RDB_OPCODE_SELECTDB:
db = rdbVisitor.applySelectDB(in, version);
break;
case RDB_OPCODE_EOF:
long checksum = rdbVisitor.applyEof(in, version);
this.replicator.submitEvent(new PostFullSyncEvent(checksum));
break loop;
case RDB_TYPE_STRING:
event = rdbVisitor.applyString(in, db, version);
break;
case RDB_TYPE_LIST:
event = rdbVisitor.applyList(in, db, version);
break;
case RDB_TYPE_SET:
event = rdbVisitor.applySet(in, db, version);
break;
case RDB_TYPE_ZSET:
event = rdbVisitor.applyZSet(in, db, version);
break;
case RDB_TYPE_ZSET_2:
event = rdbVisitor.applyZSet2(in, db, version);
break;
case RDB_TYPE_HASH:
event = rdbVisitor.applyHash(in, db, version);
break;
case RDB_TYPE_HASH_ZIPMAP:
event = rdbVisitor.applyHashZipMap(in, db, version);
break;
case RDB_TYPE_LIST_ZIPLIST:
event = rdbVisitor.applyListZipList(in, db, version);
break;
case RDB_TYPE_SET_INTSET:
event = rdbVisitor.applySetIntSet(in, db, version);
break;
case RDB_TYPE_ZSET_ZIPLIST:
event = rdbVisitor.applyZSetZipList(in, db, version);
break;
case RDB_TYPE_HASH_ZIPLIST:
event = rdbVisitor.applyHashZipList(in, db, version);
break;
case RDB_TYPE_LIST_QUICKLIST:
event = rdbVisitor.applyListQuickList(in, db, version);
break;
case RDB_TYPE_MODULE:
event = rdbVisitor.applyModule(in, db, version);
break;
case RDB_TYPE_MODULE_2:
event = rdbVisitor.applyModule2(in, db, version);
break;
default:
throw new AssertionError("unexpected value type:" + type + ", check your ModuleParser or ValueIterableRdbVisitor.");
}
if (event == null) continue;
if (replicator.verbose() && logger.isDebugEnabled()) logger.debug(event);
this.replicator.submitEvent(event);
}
return in.total();
}
}