Java tutorial
/** * 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.alibaba.wasp.fserver; import com.alibaba.wasp.*; import com.alibaba.wasp.fserver.metrics.MetricsEntityGroup; import com.alibaba.wasp.fserver.redo.*; import com.alibaba.wasp.messagequeue.Message; import com.alibaba.wasp.messagequeue.MessageQueue; import com.alibaba.wasp.meta.*; import com.alibaba.wasp.plan.action.*; import com.alibaba.wasp.protobuf.ProtobufUtil; import com.alibaba.wasp.protobuf.generated.WaspProtos.Mutate; import com.alibaba.wasp.protobuf.generated.WaspProtos.Mutate.MutateType; import com.alibaba.wasp.storage.StorageServices; import com.alibaba.wasp.storage.StorageServicesImpl; import com.alibaba.wasp.storage.StorageTableNotFoundException; import com.alibaba.wasp.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HConstants.OperationStatusCode; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.monitoring.MonitoredTask; import org.apache.hadoop.hbase.monitoring.TaskMonitor; import org.apache.hadoop.hbase.util.*; import org.cliffc.high_scale_lib.Counter; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * * The kernel of Wasp. EntityGroup stores data for a certain eg of a table,it * stores all columns for each row which includes index and entity. A given * table consists of one or more EntityGroups. * */ public class EntityGroup extends Thread implements EntityGroupServices, MessageQueue<OperationStatus> { public static final Log LOG = LogFactory.getLog(EntityGroup.class); final EntityGroupInfo entityGroupInfo; final Configuration conf; final Redo redo; private long transactionRetryPause; final AtomicBoolean closed = new AtomicBoolean(false); protected final FServerServices services; final Counter readRequestsCount = new Counter(); final Counter writeRequestsCount = new Counter(); private final ConcurrentHashMap<HashedBytes, CountDownLatch> lockedRows = new ConcurrentHashMap<HashedBytes, CountDownLatch>(); private Leases leases; private final ConcurrentHashMap<String, HashedBytes> selectForUpdateLocks = new ConcurrentHashMap<String, HashedBytes>(); final int rowLockWaitDuration; static final int DEFAULT_ROWLOCK_WAIT_DURATION = 30000; private EntityGroupSplitPolicy splitPolicy; private final MetricsEntityGroup metricsEntityGroup; private final int lockTimeoutPeriod; /* * Closing can take some time; use the closing flag if there is stuff we don't * want to do while in closing state; e.g. like offer this entityGroup up to * the master as a entityGroup to close if the carrying FServer is overloaded. * Once set, it is never cleared. */ final AtomicBoolean closing = new AtomicBoolean(false); /* * Data structure of write state flags used coordinating closes. */ static class WriteState { // Gets set in close. volatile boolean writesEnabled = true; // Set if entityGroup is read-only volatile boolean readOnly = false; /** * Set flags that make this entityGroup read-only. * * @param onOff * flip value for entityGroup r/o setting */ synchronized void setReadOnly(final boolean onOff) { this.writesEnabled = !onOff; this.readOnly = onOff; } boolean isReadOnly() { return this.readOnly; } static final long HEAP_SIZE = ClassSize.align(ClassSize.OBJECT + 5 * Bytes.SIZEOF_BOOLEAN); } final WriteState writestate = new WriteState(); final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private byte[] explicitSplitPoint = null; private FTable ftableDescriptor = null; private Thread thread; private StorageServices storageServices = null; /** * default * * @throws java.io.IOException **/ @SuppressWarnings("unchecked") public EntityGroup(Configuration conf, EntityGroupInfo egi, FTable table, FServerServices service) throws IOException { this.conf = conf; this.ftableDescriptor = table; this.entityGroupInfo = egi; try { Class<? extends Redo> redoClass = (Class<? extends Redo>) conf.getClass(FConstants.REDO_IMPL, RedoLog.class); Constructor<? extends Redo> c = redoClass.getConstructor(EntityGroupInfo.class, Configuration.class); this.redo = c.newInstance(entityGroupInfo, conf); } catch (Throwable e) { throw new IllegalStateException("Could not instantiate a entityGroup instance.", e); } this.transactionRetryPause = conf.getInt(FConstants.WASP_TRANSACTION_RETRY_PAUSE, FConstants.DEFAULT_WASP_TRANSACTION_RETRY_PAUSE); this.rowLockWaitDuration = conf.getInt("wasp.rowlock.wait.duration", DEFAULT_ROWLOCK_WAIT_DURATION); this.services = service; this.leases = new Leases( conf.getInt(FConstants.THREAD_WAKE_FREQUENCY, FConstants.DEFAULT_THREAD_WAKE_FREQUENCY)); this.lockTimeoutPeriod = conf.getInt(FConstants.SELECT_FOR_UPDATE_LOCK_TIMEOUT, FConstants.DEFAULT_SELECT_FOR_UPDATE_LOCK_TIMEOUT); if (this.services != null) { this.metricsEntityGroup = new MetricsEntityGroup(new MetricsEntityGroupWrapperImpl(this)); } else { this.metricsEntityGroup = null; } if (LOG.isDebugEnabled()) { // Write out EntityGroup name as string and its encoded name. LOG.debug("Instantiated " + this); } } /** * Initialize this entityGroup. * * @return What the next sequence (edit) id should be. * @throws java.io.IOException * e */ public void initialize() throws IOException { redo.initlize(); this.closed.set(false); this.closing.set(false); this.writestate.readOnly = false; this.writestate.writesEnabled = true; thread = new Thread(this); thread.start(); // Initialize split policy this.splitPolicy = EntityGroupSplitPolicy.create(this, conf); if (services != null) { this.storageServices = new StorageServicesImpl(this.services.getActionManager()); } } public boolean isAvailable() { return !isClosed() && !isClosing(); } /** @return true if entityGroup is closed */ public boolean isClosed() { return this.closed.get(); } /** * @return True if closing process has started. */ public boolean isClosing() { return this.closing.get(); } /** @return start key for entityGroup */ public byte[] getStartKey() { return this.entityGroupInfo.getStartKey(); } /** @return end key for entityGroup */ public byte[] getEndKey() { return this.entityGroupInfo.getEndKey(); } /** @return entityGroup id */ public long getEntityGroupId() { return this.entityGroupInfo.getEntityGroupId(); } /** @return entityGroup name */ public byte[] getEntityGroupName() { return this.entityGroupInfo.getEntityGroupName(); } /** @return entityGroup name as string for logging */ public String getEntityGroupNameAsString() { return this.entityGroupInfo.getEntityGroupNameAsString(); } /** @return FTableDescriptor for this entityGroup */ public FTable getTableDesc() { return this.ftableDescriptor; } /** @return Configuration object */ public Configuration getConf() { return this.conf; } /** @return StorageServices object */ public StorageServices getStorageServices() { return storageServices; } /** * @param storageServices * the StorageServices to set */ public void setStorageServices(StorageServices storageServices) { this.storageServices = storageServices; } /** * No-index query using primary key. * * @param action * @return * @throws java.io.IOException * @throws com.alibaba.wasp.storage.StorageTableNotFoundException */ public Result get(GetAction action) throws IOException, StorageTableNotFoundException { Get get = new Get(RowBuilder.build().buildEntityRowKey(this.conf, action.getFTableName(), action.getCombinedPrimaryKey())); get.setMaxVersions(1); // as a result of the maximum timestamp value is exclusive, but we need the // inclusive one . so plus one to timestamp if (action.isForUpdate()) { this.internalObtainRowLock(action.getCombinedPrimaryKey(), action.getSessionId()); } else { if (action.getReaderMode() == ReadModel.CURRENT) { this.internalObtainRowLock(action.getCombinedPrimaryKey()); this.releaseRowLock(action.getCombinedPrimaryKey()); } } readRequestsCount.increment(); get.setTimeRange(0, timestamp(action.getReaderMode()) + 1); return storageServices.getRow(StorageTableNameBuilder.buildEntityTableName(action.getFTableName()), get); } /** * Return an iterator that scans over the EntityGroup, returning the indicated * columns and rows specified by the * * <p> * This Iterator must be closed by the caller. * * @param action * @return * @throws java.io.IOException * @throws com.alibaba.wasp.storage.StorageTableNotFoundException */ public EntityGroupScanner getScanner(ScanAction action) throws IOException, StorageTableNotFoundException { startEntityGroupOperation(); readRequestsCount.increment(); try { return instantiateEntityGroupScanner(action); } finally { closeEntityGroupOperation(); } } /** * return EntityGroupScanner. * * @param action * @return * @throws java.io.IOException * @throws com.alibaba.wasp.storage.StorageTableNotFoundException */ protected EntityGroupScanner instantiateEntityGroupScanner(ScanAction action) throws StorageTableNotFoundException, IOException { long timestamp = timestamp(action.getReaderMode()); return new EntityGroupScanner(this, action, timestamp); } /** * * @param readerMode * current,snapshot,inconsistent * @return * @throws java.io.IOException */ private long timestamp(ReadModel readerMode) throws IOException { long timestamp; if (readerMode == ReadModel.CURRENT) { timestamp = currentReadTimeStamp(); } else if (readerMode == ReadModel.SNAPSHOT) { timestamp = snapshotTimeStamp(); } else if (readerMode == ReadModel.INCONSISTENT) { timestamp = System.currentTimeMillis(); } else { throw new IOException("CODE INCONSISTENT."); } return timestamp; } /** * This method needs to be called before any public call that reads or * modifies data. It has to be called just before a try. Acquires checks if * the entityGroup is closing or closed. * * @throws com.alibaba.wasp.NotServingEntityGroupException * when the entityGroup is closing or closed */ private void startEntityGroupOperation() throws NotServingEntityGroupException { if (this.closing.get()) { throw new NotServingEntityGroupException(entityGroupInfo.getEntityGroupNameAsString() + " is closing"); } lock.readLock().lock(); if (this.closed.get()) { lock.readLock().unlock(); throw new NotServingEntityGroupException(entityGroupInfo.getEntityGroupNameAsString() + " is closed"); } } private void closeEntityGroupOperation() { lock.readLock().unlock(); } /** * Log insert transaction to redoLog. * * @param action * @return */ public OperationStatus insert(InsertAction action) throws IOException { startEntityGroupOperation(); writeRequestsCount.increment(); try { internalObtainRowLock(action.getCombinedPrimaryKey()); Transaction transaction = new Transaction(currentReadTimeStamp()); try { prepareInsertEntity(action, transaction); } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareInsertEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updateInsert(); } return submitTransaction(action, transaction); } finally { closeEntityGroupOperation(); } } public OperationStatus transaction(TransactionAction transactionAction) throws IOException { startEntityGroupOperation(); writeRequestsCount.increment(); try { Transaction transaction = new Transaction(currentReadTimeStamp()); for (DMLAction dmlAction : transactionAction.getDmlActions()) { if (dmlAction instanceof InsertAction) { InsertAction action = (InsertAction) dmlAction; internalObtainRowLock(action.getCombinedPrimaryKey()); try { prepareInsertEntity(action, transaction); } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareInsertEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } } else if (dmlAction instanceof UpdateAction) { UpdateAction action = (UpdateAction) dmlAction; internalObtainRowLock(action.getCombinedPrimaryKey()); try { if (!prepareUpdateEntity(action, transaction)) { this.releaseRowLock(action.getCombinedPrimaryKey()); return OperationStatus.FAILURE; } } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareUpdateEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } } else if (dmlAction instanceof DeleteAction) { DeleteAction action = (DeleteAction) dmlAction; internalObtainRowLock(action.getCombinedPrimaryKey()); try { if (!prepareDeleteEntity(action, transaction)) { this.releaseRowLock(action.getCombinedPrimaryKey()); return OperationStatus.FAILURE; } } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareDeleteEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } } } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updateInsert(); } OperationStatus status = submitTransaction(null, transaction); for (DMLAction dmlAction : transactionAction.getDmlActions()) { if (dmlAction instanceof InsertAction) { this.releaseRowLock(((InsertAction) dmlAction).getCombinedPrimaryKey()); } else if (dmlAction instanceof UpdateAction) { this.releaseRowLock(((UpdateAction) dmlAction).getCombinedPrimaryKey()); } else if (dmlAction instanceof DeleteAction) { this.releaseRowLock(((DeleteAction) dmlAction).getCombinedPrimaryKey()); } } return status; } finally { closeEntityGroupOperation(); } } /** * Make insert action into transaction; * * @param action * insert action * @param transaction * transaction * @throws java.io.IOException * @throws com.alibaba.wasp.storage.StorageTableNotFoundException */ private void prepareInsertEntity(InsertAction action, Transaction transaction) throws IOException, StorageTableNotFoundException { long before = EnvironmentEdgeManager.currentTimeMillis(); RowBuilder builder = RowBuilder.build(); TableSchemaCacheReader metaReader = TableSchemaCacheReader.getInstance(this.conf); LinkedHashMap<String, Index> indexs = metaReader.getSchema(action.getFTableName()).getIndex(); if (LOG.isDebugEnabled()) { LOG.debug("prepareInsertEntity indexs:" + indexs.values()); } NavigableMap<byte[], NavigableMap<byte[], byte[]>> set = new TreeMap<byte[], NavigableMap<byte[], byte[]>>( Bytes.BYTES_COMPARATOR); for (ColumnStruct col : action.getColumns()) { byte[] family = Bytes.toBytes(col.getFamilyName()); NavigableMap<byte[], byte[]> cols = set.get(family); if (cols == null) { cols = new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR); } set.put(family, cols); cols.put(Bytes.toBytes(col.getColumnName()), col.getValue()); } String entityTableName = StorageTableNameBuilder.buildEntityTableName(action.getFTableName()); // entity put Put entityPut = builder.buildPut(action); transaction.addEntity(ProtobufUtil.toMutate(MutateType.PUT, entityPut, entityTableName)); storageServices.checkRowExistsBeforeInsert(action, entityTableName, entityPut); // index put if (indexs != null) { for (Index index : indexs.values()) { Pair<byte[], String> indexPut = builder.buildIndexKey(index, set, entityPut.getRow()); if (indexPut != null) { Put put = new Put(indexPut.getFirst()); put.add(FConstants.INDEX_STORING_FAMILY_BYTES, FConstants.INDEX_STORE_ROW_QUALIFIER, entityPut.getRow()); for (Entry<String, Field> entry : index.getStoring().entrySet()) { ColumnStruct storing = action.getName2Column().get(entry.getKey()); if (storing != null) { put.add(FConstants.INDEX_STORING_FAMILY_BYTES, Bytes.toBytes(entry.getKey()), storing.getValue()); } } transaction.addEntity(ProtobufUtil.toMutate(MutateType.PUT, put, indexPut.getSecond())); } } } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updatePrepareInsertEntity(EnvironmentEdgeManager.currentTimeMillis() - before); } } /** * Log update transaction to redoLog. * * @param action * , basic action of recording update information. * @return * @throws java.io.IOException */ public OperationStatus update(UpdateAction action) throws NotServingEntityGroupException, IOException { startEntityGroupOperation(); writeRequestsCount.increment(); try { if (StringUtils.isNullOrEmpty(action.getSessionId())) { internalObtainRowLock(action.getCombinedPrimaryKey()); } else { HashedBytes rowkey = selectForUpdateLocks.remove(action.getSessionId()); if (rowkey == null || lockedRows.get(rowkey) == null) { throw new SelectForUpdateLockNotFoundException( "the lock not found for session id :" + action.getSessionId() + " and rowkey is " + (rowkey == null ? "NULL" : Bytes.toStringBinary(rowkey.getBytes()))); } } currentReadTimeStamp(); Transaction transaction = new Transaction(); try { if (!prepareUpdateEntity(action, transaction)) { this.releaseRowLock(action.getCombinedPrimaryKey()); return OperationStatus.FAILURE; } } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareUpdateEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } return submitTransaction(action, transaction); } finally { closeEntityGroupOperation(); } } private void relationIndexAndField(Map<String, Field> referField, Set<Index> indexs) { for (Index index : indexs) { for (Field field : index.getIndexKeys().values()) { referField.put(field.getName(), field); } } } /** * First delete old records,then insert or update new record. * * @param action * @param transaction * @throws java.io.IOException * @throws com.alibaba.wasp.storage.StorageTableNotFoundException */ private boolean prepareUpdateEntity(UpdateAction action, Transaction transaction) throws IOException, StorageTableNotFoundException { long before = EnvironmentEdgeManager.currentTimeMillis(); TableSchemaCacheReader tableSchemaReader = TableSchemaCacheReader.getInstance(this.conf); // use meta reader fetch relation index and relation field Map<String, Field> referField = new HashMap<String, Field>(); Set<Index> indexs = new HashSet<Index>(0); for (ColumnStruct column : action.getColumns()) { indexs.addAll(tableSchemaReader.getIndexsByField(action.getFTableName(), column.getColumnName())); if (indexs.size() == 0) { continue; } relationIndexAndField(referField, indexs); column.setIndex(true); } RowBuilder builder = RowBuilder.build(); // fetch entity data Get get = new Get( builder.buildEntityRowKey(this.conf, action.getFTableName(), action.getCombinedPrimaryKey())); for (Entry<String, Field> fieldEntry : referField.entrySet()) { get.addColumn(Bytes.toBytes(fieldEntry.getValue().getFamily()), Bytes.toBytes(fieldEntry.getValue().getName())); } Result result = storageServices.getRowBeforeUpdate(action, StorageTableNameBuilder.buildEntityTableName(action.getFTableName()), get); if (result == null || result.size() == 0) { return false; } // entity put Put entityPut = builder.buildPut(action); transaction.addEntity(ProtobufUtil.toMutate(MutateType.PUT, entityPut, StorageTableNameBuilder.buildEntityTableName(action.getFTableName()))); // index delete NavigableMap<byte[], NavigableMap<byte[], byte[]>> oldValues = result.getNoVersionMap(); for (Index index : indexs) { Pair<byte[], String> delete = builder.buildIndexKey(index, oldValues, result.getRow()); if (delete != null) { transaction.addEntity(ProtobufUtil.toMutate(MutateType.DELETE, new Delete(delete.getFirst()), delete.getSecond())); } } // index put NavigableMap<byte[], NavigableMap<byte[], byte[]>> newValues = prepareUpdateValues(oldValues, action); for (Index index : indexs) { Pair<byte[], String> indexPut = builder.buildIndexKey(index, newValues, entityPut.getRow()); if (indexPut != null) { Put put = new Put(indexPut.getFirst()); put.add(FConstants.INDEX_STORING_FAMILY_BYTES, FConstants.INDEX_STORE_ROW_QUALIFIER, entityPut.getRow()); for (Entry<String, Field> entry : index.getStoring().entrySet()) { byte[] columnNameByte = Bytes.toBytes(entry.getKey()); ColumnStruct storing = action.getName2Column().get(entry.getKey()); if (storing != null) { put.add(FConstants.INDEX_STORING_FAMILY_BYTES, columnNameByte, storing.getValue()); } } transaction.addEntity(ProtobufUtil.toMutate(MutateType.PUT, put, indexPut.getSecond())); } } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updatePrepareUpdateEntity(EnvironmentEdgeManager.currentTimeMillis() - before); } return true; } /** * Use new value replace old value. * * @param oldValues * @param action * @return */ private NavigableMap<byte[], NavigableMap<byte[], byte[]>> prepareUpdateValues( NavigableMap<byte[], NavigableMap<byte[], byte[]>> oldValues, UpdateAction action) { Iterator<ColumnStruct> iterator = action.getColumns().iterator(); while (iterator.hasNext()) { ColumnStruct column = iterator.next(); if (column.isIndex()) { NavigableMap<byte[], byte[]> family = oldValues.get(Bytes.toBytes(column.getFamilyName())); byte[] columnName = Bytes.toBytes(column.getColumnName()); family.put(columnName, column.getValue()); } } return oldValues; } /** * Log delete transaction to redoLog. * * @param action * ,basic action of recording delete information * @return * @throws java.io.IOException */ public OperationStatus delete(DeleteAction action) throws NotServingEntityGroupException, IOException { startEntityGroupOperation(); writeRequestsCount.increment(); try { internalObtainRowLock(action.getCombinedPrimaryKey()); currentReadTimeStamp(); Transaction transaction = new Transaction(); try { if (!prepareDeleteEntity(action, transaction)) { this.releaseRowLock(action.getCombinedPrimaryKey()); return OperationStatus.FAILURE; } } catch (StorageTableNotFoundException e) { this.releaseRowLock(action.getCombinedPrimaryKey()); throw new TableNotFoundException(action.getFTableName()); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("PrepareDeleteEntity failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } return submitTransaction(action, transaction); } finally { closeEntityGroupOperation(); } } private boolean prepareDeleteEntity(DeleteAction action, Transaction transaction) throws IOException, StorageTableNotFoundException { long before = EnvironmentEdgeManager.currentTimeMillis(); RowBuilder factory = RowBuilder.build(); String entityTableName = StorageTableNameBuilder.buildEntityTableName(action.getFTableName()); // fetch entity data Get get = new Get( factory.buildEntityRowKey(this.conf, action.getFTableName(), action.getCombinedPrimaryKey())); Result result = storageServices.getRowBeforeDelete(action, entityTableName, get); if (result == null || result.size() == 0) { return false; } // entity delete Delete entityDelete = new Delete(get.getRow()); transaction.addEntity(ProtobufUtil.toMutate(MutateType.DELETE, entityDelete, entityTableName)); // index delete NavigableMap<byte[], NavigableMap<byte[], byte[]>> oldValues = result.getNoVersionMap(); TableSchemaCacheReader metaReader = TableSchemaCacheReader.getInstance(this.conf); LinkedHashMap<String, Index> indexs = metaReader.getSchema(action.getFTableName()).getIndex(); if (indexs != null) { for (Index index : indexs.values()) { Pair<byte[], String> delete = factory.buildIndexKey(index, oldValues, get.getRow()); if (delete != null) { transaction.addEntity(ProtobufUtil.toMutate(MutateType.DELETE, new Delete(delete.getFirst()), delete.getSecond())); } } } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updatePrepareDeleteEntity(EnvironmentEdgeManager.currentTimeMillis() - before); } return true; } /** * Append transaction to redoLog. * * @param transaction * @return */ private OperationStatus submitTransaction(Primary action, Transaction transaction) { long before = EnvironmentEdgeManager.currentTimeMillis(); try { redo.append(action, transaction); } catch (AlreadyExsitsTransactionIdException e) { transaction.setTransactionID(System.currentTimeMillis()); submitTransaction(action, transaction); } catch (Throwable e) { this.releaseRowLock(action.getCombinedPrimaryKey()); LOG.error("Submit transaction failed.", e); return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updateRedoLogAppend(EnvironmentEdgeManager.currentTimeMillis() - before); } return OperationStatus.SUCCESS; } /** * Obtains or tries to obtain the given row lock. * * @param row * if true, will block until the lock is available. Otherwise, just * tries to obtain the lock and returns null if unavailable. */ private void internalObtainRowLock(final byte[] row) throws IOException { checkRow(row, "row lock"); long before = EnvironmentEdgeManager.currentTimeMillis(); HashedBytes rowKey = new HashedBytes(row); CountDownLatch rowLatch = new CountDownLatch(1); // loop until we acquire the row lock (unless !waitForLock) while (true) { CountDownLatch existingLatch = lockedRows.putIfAbsent(rowKey, rowLatch); if (existingLatch == null) { break; } else { try { if (!existingLatch.await(this.rowLockWaitDuration, TimeUnit.MILLISECONDS)) { throw new DoNotRetryIOException( "Timed out on getting lock for row=" + Bytes.toStringBinary(row)); } } catch (InterruptedException ie) { // Empty } } } if (this.metricsEntityGroup != null) { this.metricsEntityGroup.updateObtainRowLock(EnvironmentEdgeManager.currentTimeMillis() - before); } } private void internalObtainRowLock(final byte[] row, String sessionId) throws IOException { try { this.internalObtainRowLock(row); HashedBytes rowKey = new HashedBytes(row); this.selectForUpdateLocks.put(sessionId, rowKey); this.leases.createLease(sessionId, this.lockTimeoutPeriod, new LockTimeoutListener(sessionId)); } catch (IOException e) { throw e; } } /** * Instantiated as a lock lease. If the lease times out, the lcck is released */ public class LockTimeoutListener implements LeaseListener { private final String sessionName; protected LockTimeoutListener(final String n) { this.sessionName = n; } public void leaseExpired() { HashedBytes rowkey = selectForUpdateLocks.remove(this.sessionName); if (rowkey != null) { LOG.info("lock rowkey " + Bytes.toStringBinary(rowkey.getBytes()) + " lease expired."); releaseRowLock(rowkey.getBytes()); } else { LOG.info("lock rowkey " + Bytes.toStringBinary(rowkey.getBytes()) + " lease expired."); } } } /** * Release the row lock! * * @param row * The lock ID to release. */ public void releaseRowLock(final byte[] row) { HashedBytes rowKey = new HashedBytes(row); CountDownLatch rowLatch = lockedRows.remove(rowKey); if (rowLatch == null) { LOG.error("Releases row not locked,row: " + row); return; } rowLatch.countDown(); } /** * * @return * @throws com.alibaba.wasp.NotServingEntityGroupException * @throws java.io.IOException */ private long currentReadTimeStamp() throws IOException { Transaction currentTransaction = null; WALEdit edit = redo.peekLastUnCommitedTransaction(); if (edit != null) { currentTransaction = edit.getT(); } if (currentTransaction != null) { return currentTransaction.getTransactionID(); } else { return snapshotTimeStamp(); } } private long snapshotTimeStamp() throws IOException { WALEdit edit = redo.lastCommitedTransaction(); Transaction currentTransaction = null; if (edit != null) { currentTransaction = edit.getT(); } if (currentTransaction != null) { return currentTransaction.getTransactionID(); } else { return System.currentTimeMillis(); } } /** * @throws java.io.IOException * @throws com.alibaba.wasp.NotServingEntityGroupException * @see com.alibaba.wasp.messagequeue.MessageQueue#doAsynchronous(com.alibaba.wasp.messagequeue.Message) */ @Override public OperationStatus doAsynchronous(Message message) { try { if (message instanceof DeleteAction) { return this.delete((DeleteAction) message); } else if (message instanceof UpdateAction) { return this.update((UpdateAction) message); } else if (message instanceof InsertAction) { return this.insert((InsertAction) message); } else { throw new IOException("Unkown action."); } } catch (Exception e) { return new OperationStatus(OperationStatusCode.FAILURE, e.getMessage(), e.getClass().getName()); } } /** * Close down this EntityGroup. Finish the transaction. don't service any more * calls. * * <p> * This method could take some time to execute, so don't call it from a * time-sensitive thread. * * @return Vector of all transactions that the EntityGroup has not commited. * Returns empty vector if already closed and null if judged that it * should not close. * * @throws java.io.IOException * e */ public void close() throws IOException { close(false); } private final Object closeLock = new Object(); /** * Close down this EntityGroup. Finish the transaction unless abort parameter * is true, don't service any more calls. * * This method could take some time to execute, so don't call it from a * time-sensitive thread. * * @param abort * true if server is aborting (only during testing) * @return Vector of all transactions that the EntityGroup has not commited. * Returns empty vector if already closed and null if judged that it * should not close. * * @throws java.io.IOException * e */ public boolean close(final boolean abort) throws IOException { // Only allow one thread to close at a time. Serialize them so dual // threads attempting to close will run up against each other. MonitoredTask status = TaskMonitor.get() .createStatus("Closing entityGroup " + this + (abort ? " due to abort" : "")); status.setStatus("Waiting for close lock"); try { synchronized (closeLock) { return doClose(abort, status); } } finally { status.cleanup(); } } private boolean doClose(final boolean abort, MonitoredTask status) throws IOException { if (isClosed()) { LOG.warn("EntityGroup " + this + " already closed"); return true; } synchronized (writestate) { // Disable transaction by background threads for this // entityGroup. writestate.writesEnabled = false; LOG.debug("Closing " + this); } this.closing.set(true); status.setStatus("Disabling writes for close"); lock.writeLock().lock(); try { if (this.isClosed()) { status.abort("Already got closed by another process"); // SplitTransaction handles the null return false; } status.setStatus("Disabling transaction for entityGroup"); LOG.debug("Updates disabled for entityGroup " + this); thread.interrupt(); try { thread.join(); } catch (InterruptedException e) { LOG.warn("interrupt the transaction thread by close entityGroup", e); } // Don't commit the transaction if we are aborting if (!abort) { LOG.info("Running commit transaction of " + this.getEntityGroupNameAsString() + " before close"); internalCommitTransaction(status); } // close redo redo.close(); this.closed.set(true); status.markComplete("Closed"); LOG.info("Closed " + this); return true; } finally { lock.writeLock().unlock(); } } protected void internalCommitTransaction() throws IOException { MonitoredTask status = TaskMonitor.get().createStatus("Commiting " + this.getEntityGroupNameAsString()); status.setStatus("Acquiring write lock on entityGroup"); internalCommitTransaction(this.redo, status); } protected void internalCommitTransaction(MonitoredTask status) throws IOException { internalCommitTransaction(this.redo, status); } /** * commit the transaction and close redoLog * * @param redoLog * @param status * @return * @throws java.io.IOException */ protected void internalCommitTransaction(Redo redoLog, MonitoredTask status) throws IOException { while (true) { WALEdit edit = null; edit = redo.peekLastUnCommitedTransaction(); if (edit == null) { return; } try { edit = redo.lastUnCommitedTransaction(); } catch (InterruptedException e) { if (LOG.isErrorEnabled()) { LOG.error("Getting last uncommited transaction of redo log failed", e); } } catch (NotInitlizedRedoException e) { if (LOG.isErrorEnabled()) { LOG.error("the redo log has not been initialized", e); } } catch (RedoLogNotServingException e) { if (LOG.isErrorEnabled()) { LOG.error("the redo log didn't serving any more", e); } } try { commitTransaction(edit); } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error("the redo log commit error.", e); } return; } } } /** @return RedoLog in use for this entityGroup */ public Redo getLog() { return this.redo; } private synchronized void commitTransaction(WALEdit edit) throws IOException { Transaction t = edit.getT(); if (LOG.isDebugEnabled()) { LOG.debug("EntityGroup commitTransaction:" + t.getTransactionID()); } List<Mutate> mutates = t.getEdits(); CompletionService<InsureStatus> completionService = new ExecutorCompletionService<InsureStatus>( this.services.getThreadPool()); for (Mutate mutate : mutates) { String tableName = mutate.getTableName(); try { if (mutate.getMutateType() == Mutate.MutateType.PUT) { Put put = ProtobufUtil.toPut(mutate, t.getTransactionID()); completionService.submit(new InsurePut(tableName, put)); } else if (mutate.getMutateType() == Mutate.MutateType.DELETE) { Delete delete = ProtobufUtil.toDelete(mutate); completionService.submit(new InsureDelete(tableName, delete)); } } catch (DoNotRetryIOException e) { if (LOG.isErrorEnabled()) { LOG.error("convert mutate to Put or Delete error.", e); } } } int errors = 0; for (int i = 0; i < mutates.size(); i++) { try { Future<InsureStatus> result = completionService.take(); if (InsureStatus.SUCCESS == result.get()) { // nothing,this operator is successful. } else if (InsureStatus.FAILED == result.get()) { errors++; } else { LOG.warn("What happened?"); errors++; } } catch (InterruptedException e) { if (LOG.isErrorEnabled()) { LOG.error("transaction execute error", e); } } catch (ExecutionException e) { if (LOG.isErrorEnabled()) { LOG.error("transaction execute error", e); } } } if (errors != 0) { String message = "transaction id=" + t.getTransactionID() + " process occur " + errors + " errors"; LOG.warn(message); throw new IOException(message); } try { redo.commit(edit); } catch (AlreadyCommitTransactionException e) { if (LOG.isErrorEnabled()) { LOG.error("the transaction id=" + t.getTransactionID() + " has all ready commited", e); } } catch (NotInitlizedRedoException e) { if (LOG.isErrorEnabled()) { LOG.error("the transaction id=" + t.getTransactionID() + " commited failed as a result of the redo log has a error ", e); } } catch (RedoLogNotServingException e) { if (LOG.isErrorEnabled()) { LOG.error("the transaction id=" + t.getTransactionID() + " commited failed as a result of the redo log has been closed ", e); } } Primary primary = edit.getAction(); if (primary != null) { this.releaseRowLock(primary.getCombinedPrimaryKey()); } } /* * (non-Javadoc) * * @see java.lang.Thread#run() */ @Override public void run() { while (!closing.get()) { WALEdit edit = null; Transaction t = null; try { edit = redo.lastUnCommitedTransaction(); t = edit.getT(); } catch (InterruptedException e) { if (LOG.isErrorEnabled()) { LOG.error("Getting last uncommited transaction of redo log failed", e); } return; } catch (NotInitlizedRedoException e) { if (LOG.isErrorEnabled()) { LOG.error("the redo log has not been initialized", e); } return; } catch (RedoLogNotServingException e) { if (LOG.isErrorEnabled()) { LOG.error("the redo log didn't serving any more", e); } return; } List<Mutate> mutates = t.getEdits(); if (!checkMutates(mutates)) { LOG.info("the mutates of transaction check failed. tid=" + t.getTransactionID()); return; } try { long before = EnvironmentEdgeManager.currentTimeMillis(); commitTransaction(edit); if (this.metricsEntityGroup != null) { this.metricsEntityGroup .updateBackgroundRedoLog(EnvironmentEdgeManager.currentTimeMillis() - before); } } catch (IOException e) { return; } } } private boolean checkMutates(List<Mutate> mutates) { return true; } /** * The status of insure operator. * */ private enum InsureStatus { SUCCESS, FAILED } private class InsurePut implements Callable<InsureStatus> { private String tableName; private Put put; InsurePut(String tableName, Put put) { this.tableName = tableName; this.put = put; } @Override public InsureStatus call() throws Exception { while (true) { try { if (LOG.isDebugEnabled()) { LOG.debug("InsurePut tableName:" + tableName + " Put " + put); } EntityGroup.this.storageServices.putRow(tableName, put); return InsureStatus.SUCCESS; } catch (StorageTableNotFoundException e) { if (LOG.isErrorEnabled()) { LOG.error("table '" + tableName + "' has not found", e); } } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error("table " + tableName + " put error, row:" + Bytes.toString(put.getRow()), e); } try { Thread.sleep(transactionRetryPause); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } return InsureStatus.FAILED; } } } private class InsureDelete implements Callable<InsureStatus> { private String tableName; private Delete delete; InsureDelete(String tableName, Delete delete) { this.tableName = tableName; this.delete = delete; } @Override public InsureStatus call() throws Exception { while (true) { try { if (LOG.isDebugEnabled()) { LOG.debug("InsureDelete tableName:" + tableName + " Delete " + delete); } EntityGroup.this.storageServices.deleteRow(tableName, delete); return InsureStatus.SUCCESS; } catch (StorageTableNotFoundException e) { if (LOG.isErrorEnabled()) { LOG.error("table '" + tableName + "' has not found", e); } } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error("table " + tableName + " put error, row:" + Bytes.toString(delete.getRow()), e); } try { Thread.sleep(transactionRetryPause); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } return InsureStatus.FAILED; } } } // Utility methods /** * A utility method to create new instances of EntityGroup based on the * configuration property. * * @param conf * is global configuration settings. * @param entityGroupInfo * - EntityGroupInfo that describes the entityGroup is new), then * read them from the supplied path. * @param ftd * @param fsServices * @return the new instance */ public static EntityGroup newEntityGroup(Configuration conf, EntityGroupInfo entityGroupInfo, final FTable ftd, FServerServices fsServices) { try { @SuppressWarnings("unchecked") Class<? extends EntityGroup> entityGroupClass = (Class<? extends EntityGroup>) conf .getClass(FConstants.ENTITYGROUP_IMPL, EntityGroup.class); Constructor<? extends EntityGroup> c = entityGroupClass.getConstructor(Configuration.class, EntityGroupInfo.class, FTable.class, FServerServices.class); return c.newInstance(conf, entityGroupInfo, ftd, fsServices); } catch (Throwable e) { throw new IllegalStateException("Could not instantiate a entityGroup instance.", e); } } /** * Open a EntityGroup. * * @param info * Info for entityGroup to be opened. * @param conf * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup openEntityGroup(final EntityGroupInfo info, final FTable ftd, final Configuration conf) throws IOException { return openEntityGroup(info, ftd, conf, null, null); } /** * Open a EntityGroup. * * @param info * Info for entityGroup to be opened. * @param conf * @param reporter * An interface we can report progress against. * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup openEntityGroup(final EntityGroupInfo info, final FTable table, final Configuration conf, final FServerServices fsServices, final CancelableProgressable reporter) throws IOException { if (info == null) { throw new NullPointerException("Passed EntityGroup info is null"); } LOG.info("EntityGroup.openEntityGroup EntityGroup name ==" + info.getEntityGroupNameAsString()); if (LOG.isDebugEnabled()) { LOG.debug("Opening EntityGroup: " + info); } EntityGroup eg = EntityGroup.newEntityGroup(conf, info, table, fsServices); eg.initialize(); return eg; } /** * Open EntityGroup. Calls initialize and sets sequenceid. * * @param reporter * @return Returns <code>this</code> * @throws java.io.IOException */ protected EntityGroup openEntityGroup(final CancelableProgressable reporter) throws IOException { this.initialize(); return this; } /** * This will do the necessary cleanup a call to * {@link #createEntityGroup(com.alibaba.wasp.EntityGroupInfo, org.apache.hadoop.conf.Configuration, com.alibaba.wasp.meta.FTable, com.alibaba.wasp.fserver.redo.RedoLog, FServerServices)} * associated {@link com.alibaba.wasp.fserver.redo.RedoLog} file. You use it if you call the other * createEntityGroup, the one that takes an {@link com.alibaba.wasp.fserver.redo.RedoLog} instance but don't * be surprised by the call to the * {@link com.alibaba.wasp.fserver.redo.RedoLog#close()} ()} on the * {@link com.alibaba.wasp.fserver.redo.RedoLog} the EntityGroup was carrying. * * @param entityGroup * @throws java.io.IOException */ public static void closeEntityGroup(final EntityGroup entityGroup) throws IOException { if (entityGroup == null) { return; } entityGroup.close(); if (entityGroup.getLog() != null) { entityGroup.getLog().close(); } } /** * Determines if the specified row is within the row range specified by the * specified EntityGroupInfo * * @param info * EntityGroupInfo that specifies the row range * @param row * row to be checked * @return true if the row is within the range specified by the * EntityGroupInfo */ public static boolean rowIsInRange(EntityGroupInfo info, final byte[] row) { return ((info.getStartKey().length == 0) || (Bytes.compareTo(info.getStartKey(), row) <= 0)) && ((info.getEndKey().length == 0) || (Bytes.compareTo(info.getEndKey(), row) > 0)); } /** * Merge two EntityGroups. The entityGroups must be adjacent and must not * overlap. * * @param srcA * @param srcB * @return new merged EntityGroup * @throws java.io.IOException */ public static EntityGroup mergeAdjacent(final EntityGroup srcA, final EntityGroup srcB) throws IOException { EntityGroup a = srcA; EntityGroup b = srcB; // Make sure that srcA comes first; important for key-ordering during // write of the merged file. if (srcA.getStartKey() == null) { if (srcB.getStartKey() == null) { throw new IOException("Cannot merge two entityGroups with null start key"); } // A's start key is null but B's isn't. Assume A comes before B } else if ((srcB.getStartKey() == null) || (Bytes.compareTo(srcA.getStartKey(), srcB.getStartKey()) > 0)) { a = srcB; b = srcA; } if (!(Bytes.compareTo(a.getEndKey(), b.getStartKey()) == 0)) { throw new IOException("Cannot merge non-adjacent entityGroups"); } return merge(a, b); } /** * Merge two entityGroups whether they are adjacent or not. * * @param a * entityGroup a * @param b * entityGroup b * @return new merged entityGroup * @throws java.io.IOException */ public static EntityGroup merge(EntityGroup a, EntityGroup b) throws IOException { if (!a.getEntityGroupInfo().getTableNameAsString().equals(b.getEntityGroupInfo().getTableNameAsString())) { throw new IOException("EntityGroups do not belong to the same table"); } // Compact each entityGroup so we only have one store file per family Configuration conf = a.getConf(); FTable tabledesc = a.getTableDesc(); // Presume both are of same entityGroup type final byte[] startKey = Bytes.compareTo(a.getStartKey(), b.getStartKey()) < 0 ? a.getStartKey() : b.getStartKey(); final byte[] endKey = Bytes.compareTo(a.getEndKey(), b.getEndKey()) > 0 ? a.getEndKey() : b.getEndKey(); EntityGroupInfo newEntityGroupInfo = new EntityGroupInfo(Bytes.toBytes(tabledesc.getTableName()), startKey, endKey); LOG.info("Creating new entityGroup " + newEntityGroupInfo.toString()); LOG.info("starting merge of entityGroups: " + a + " and " + b + " into new entityGroup " + newEntityGroupInfo.toString() + " with start key <" + Bytes.toStringBinary(startKey) + "> and end key <" + Bytes.toStringBinary(endKey) + ">"); // close a & b entityGroups a.close(); b.close(); EntityGroup dstEntityGroup = EntityGroup.newEntityGroup(conf, newEntityGroupInfo, null, null); dstEntityGroup.readRequestsCount.set(a.readRequestsCount.get() + b.readRequestsCount.get()); dstEntityGroup.writeRequestsCount.set(a.writeRequestsCount.get() + b.writeRequestsCount.get()); dstEntityGroup.initialize(); LOG.info("merge completed. New EntityGroup is " + dstEntityGroup); return dstEntityGroup; } @Override public String toString() { return this.entityGroupInfo.getEntityGroupNameAsString(); } byte[] getExplicitSplitPoint() { return this.explicitSplitPoint; } /** @return a EntityGroupInfo object for this EntityGroup */ public EntityGroupInfo getEntityGroupInfo() { return this.entityGroupInfo; } void forceSplit(byte[] sp) { // NOTE : this EntityGroup will go away after the forced split is // successfull // therefore, no reason to clear this value if (sp != null) { this.explicitSplitPoint = sp; } } public byte[] checkSplit() { byte[] ret = splitPolicy.getSplitPoint(this.explicitSplitPoint); if (ret != null) { try { checkRow(ret, "calculated split"); } catch (IOException e) { LOG.error("Ignoring invalid split", e); return null; } } return ret; } /** * Convenience method creating new EntityGroups. Used by createTable and by * the bootstrap code in the FMaster constructor. Note, this method creates an * {@link com.alibaba.wasp.fserver.redo.RedoLog} for the created entityGroup. It needs to be closed * explicitly. Use {@link EntityGroup#getLog()} to get access. <b>When done * with a entityGroup created using this method, you will need to explicitly * close the {@link com.alibaba.wasp.fserver.redo.RedoLog} it created too; it will not be done for you. Not * closing the log will leave at least a daemon thread running.</b> Call * {@link #closeEntityGroupOperation()} and it will do necessary cleanup for * you. * * @param info * Info for entityGroup to create. * @param conf * @param table * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup createEntityGroup(final EntityGroupInfo info, final Configuration conf, final FTable table, final FServerServices services) throws IOException { return createEntityGroup(info, conf, table, null, services); } /** * Convenience method creating new EntityGroups. Used by createTable. The * {@link com.alibaba.wasp.fserver.redo.RedoLog} for the created entityGroup needs to be closed explicitly, * if it is not null. Use {@link EntityGroup#getLog()} to get access. * * @param info * Info for entityGroup to create. * @param conf * @param table * @param redoLog * shared RedoLog * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup createEntityGroup(final EntityGroupInfo info, final Configuration conf, final FTable table, final RedoLog redoLog, FServerServices services) throws IOException { return createEntityGroup(info, conf, table, true, services); } /** * Convenience method creating new EntityGroups. Used by createTable. The * {@link com.alibaba.wasp.fserver.redo.RedoLog} for the created entityGroup needs to be closed explicitly, * if it is not null. Use {@link EntityGroup#getLog()} to get access. * * @param info * Info for entityGroup to create. * @param conf * @param table * @param redoLog * shared RedoLog * @param initialize * - true to initialize the entityGroup * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup createEntityGroup(final EntityGroupInfo info, final Configuration conf, final FTable table, final RedoLog redoLog, final boolean initialize, FServerServices services) throws IOException { return createEntityGroup(info, conf, table, initialize, services); } /** * Convenience method creating new EntityGroups. Used by createTable. The * {@link com.alibaba.wasp.fserver.redo.RedoLog} for the created entityGroup needs to be closed explicitly, * if it is not null. Use {@link EntityGroup#getLog()} to get access. * * @param info * Info for entityGroup to create. * @param conf * @param table * @param initialize * - true to initialize the entityGroup * @param services * @return new EntityGroup * * @throws java.io.IOException */ public static EntityGroup createEntityGroup(final EntityGroupInfo info, final Configuration conf, final FTable table, final boolean initialize, final FServerServices services) throws IOException { LOG.info("creating EntityGroup " + info.getTableNameAsString() + " Table == " + table + " Table name == " + info.getTableNameAsString()); EntityGroup entityGroup = EntityGroup.newEntityGroup(conf, info, table, services); if (initialize) { entityGroup.initialize(); } return entityGroup; } public long getWriteRequestsCount() { return writeRequestsCount.get(); } public long getReadRequestsCount() { return readRequestsCount.get(); } public int getTransactionLogSize() { if (redo != null) { return redo.size(); } else { return 0; } } @Override public boolean equals(Object o) { if (!(o instanceof EntityGroup)) { return false; } return Bytes.equals(this.getEntityGroupName(), ((EntityGroup) o).getEntityGroupName()); } @Override public int hashCode() { return Bytes.hashCode(this.getEntityGroupName()); } /** Make sure this is a valid row for the EntityGroup */ void checkRow(final byte[] row, String op) throws IOException { if (!rowIsInRange(this.entityGroupInfo, row)) { throw new WrongEntityGroupException("Requested row out of range for " + op + " on EntityGroup " + this + ", startKey='" + Bytes.toStringBinary(this.entityGroupInfo.getStartKey()) + "', getEndKey()='" + Bytes.toStringBinary(this.entityGroupInfo.getEndKey()) + "', row='" + Bytes.toStringBinary(row) + "'"); } } /** * @see com.alibaba.wasp.fserver.EntityGroupServices#getFServerServices() */ @Override public FServerServices getFServerServices() { return this.services; } }