/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.alipay.sofa.jraft.rhea.storage; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alipay.sofa.jraft.Lifecycle; import com.alipay.sofa.jraft.Status; import com.alipay.sofa.jraft.error.RaftError; import com.alipay.sofa.jraft.rhea.errors.Errors; import com.alipay.sofa.jraft.rhea.errors.StorageException; import com.alipay.sofa.jraft.rhea.metrics.KVMetrics; import com.alipay.sofa.jraft.rhea.util.StackTraceUtil; import com.codahale.metrics.Timer; import static com.alipay.sofa.jraft.rhea.metrics.KVMetricNames.DB_TIMER; /** * @author jiachun.fjc */ public abstract class BaseRawKVStore<T> implements RawKVStore, Lifecycle<T> { @Override public void get(final byte[] key, final KVStoreClosure closure) { get(key, true, closure); } @Override public void multiGet(final List<byte[]> keys, final KVStoreClosure closure) { multiGet(keys, true, closure); } @Override public void scan(final byte[] startKey, final byte[] endKey, final KVStoreClosure closure) { scan(startKey, endKey, Integer.MAX_VALUE, closure); } @Override public void scan(final byte[] startKey, final byte[] endKey, final boolean readOnlySafe, final KVStoreClosure closure) { scan(startKey, endKey, Integer.MAX_VALUE, readOnlySafe, closure); } @Override public void scan(final byte[] startKey, final byte[] endKey, final boolean readOnlySafe, final boolean returnValue, final KVStoreClosure closure) { scan(startKey, endKey, Integer.MAX_VALUE, readOnlySafe, returnValue, closure); } @Override public void scan(final byte[] startKey, final byte[] endKey, final int limit, final KVStoreClosure closure) { scan(startKey, endKey, limit, true, closure); } @Override public void scan(final byte[] startKey, final byte[] endKey, final int limit, final boolean readOnlySafe, final KVStoreClosure closure) { scan(startKey, endKey, limit, readOnlySafe, true, closure); } @Override public void reverseScan(final byte[] startKey, final byte[] endKey, final KVStoreClosure closure) { reverseScan(startKey, endKey, Integer.MAX_VALUE, closure); } @Override public void reverseScan(final byte[] startKey, final byte[] endKey, final boolean readOnlySafe, final KVStoreClosure closure) { reverseScan(startKey, endKey, Integer.MAX_VALUE, readOnlySafe, closure); } @Override public void reverseScan(final byte[] startKey, final byte[] endKey, final boolean readOnlySafe, final boolean returnValue, final KVStoreClosure closure) { reverseScan(startKey, endKey, Integer.MAX_VALUE, readOnlySafe, returnValue, closure); } @Override public void reverseScan(final byte[] startKey, final byte[] endKey, final int limit, final KVStoreClosure closure) { reverseScan(startKey, endKey, limit, true, closure); } @Override public void reverseScan(final byte[] startKey, final byte[] endKey, final int limit, final boolean readOnlySafe, final KVStoreClosure closure) { reverseScan(startKey, endKey, limit, readOnlySafe, true, closure); } @Override public void execute(final NodeExecutor nodeExecutor, final boolean isLeader, final KVStoreClosure closure) { final Timer.Context timeCtx = getTimeContext("EXECUTE"); try { if (nodeExecutor != null) { nodeExecutor.execute(Status.OK(), isLeader); } setSuccess(closure, Boolean.TRUE); } catch (final Exception e) { final Logger LOG = LoggerFactory.getLogger(getClass()); LOG.error("Fail to [EXECUTE], {}.", StackTraceUtil.stackTrace(e)); if (nodeExecutor != null) { nodeExecutor.execute(new Status(RaftError.EIO, "Fail to [EXECUTE]"), isLeader); } setCriticalError(closure, "Fail to [EXECUTE]", e); } finally { timeCtx.stop(); } } public long getSafeEndValueForSequence(final long startVal, final int step) { return Math.max(startVal, Long.MAX_VALUE - step < startVal ? Long.MAX_VALUE : startVal + step); } /** * If limit == 0, it will be modified to Integer.MAX_VALUE on the server * and then queried. So 'limit == 0' means that the number of queries is * not limited. This is because serialization uses varint to compress * numbers. In the case of 0, only 1 byte is occupied, and Integer.MAX_VALUE * takes 5 bytes. * * @param limit input limit * @return normalize limit */ protected int normalizeLimit(final int limit) { return limit > 0 ? limit : Integer.MAX_VALUE; } /** * Note: This is not a very precise behavior, don't rely on its accuracy. */ public abstract long getApproximateKeysInRange(final byte[] startKey, final byte[] endKey); /** * Note: This is not a very precise behavior, don't rely on its accuracy. */ public abstract byte[] jumpOver(final byte[] startKey, final long distance); /** * Init the fencing token of new region. * * @param parentKey the fencing key of parent region * @param childKey the fencing key of new region */ public abstract void initFencingToken(final byte[] parentKey, final byte[] childKey); // static methods // static Timer.Context getTimeContext(final String opName) { return KVMetrics.timer(DB_TIMER, opName).time(); } /** * Sets success, if current node is a leader, reply to * client success with result data response. * * @param closure callback * @param data result data to reply to client */ static void setSuccess(final KVStoreClosure closure, final Object data) { if (closure != null) { // closure is null on follower node closure.setData(data); closure.run(Status.OK()); } } /** * Sets failure, if current node is a leader, reply to * client failure response. * * @param closure callback * @param message error message */ static void setFailure(final KVStoreClosure closure, final String message) { if (closure != null) { // closure is null on follower node closure.setError(Errors.STORAGE_ERROR); closure.run(new Status(RaftError.EIO, message)); } } /** * Sets critical error and halt the state machine. * * If current node is a leader, first reply to client * failure response. * * @param closure callback * @param message error message * @param error critical error */ static void setCriticalError(final KVStoreClosure closure, final String message, final Throwable error) { // Will call closure#run in FSMCaller setClosureError(closure); if (error != null) { throw new StorageException(message, error); } } /** * Sets critical error and halt the state machine. * * If current node is a leader, first reply to client * failure response. * * @param closures callback list * @param message error message * @param error critical error */ static void setCriticalError(final List<KVStoreClosure> closures, final String message, final Throwable error) { for (final KVStoreClosure closure : closures) { // Will call closure#run in FSMCaller setClosureError(closure); } if (error != null) { throw new StorageException(message, error); } } static void setClosureError(final KVStoreClosure closure) { if (closure != null) { // closure is null on follower node closure.setError(Errors.STORAGE_ERROR); } } /** * Sets the result first, then gets it by {@link #getData(KVStoreClosure)} * when we ensure successful. * * @param closure callback * @param data data */ static void setData(final KVStoreClosure closure, final Object data) { if (closure != null) { // closure is null on follower node closure.setData(data); } } /** * Gets data from closure. * * @param closure callback * @return closure data */ static Object getData(final KVStoreClosure closure) { return closure == null ? null : closure.getData(); } }