/** * Fallback implementation of getSplits, {@link SplitsUtil#primitiveGetSplits(int, byte[], byte[])}. * Ideally should be overridden by subclasses. * * @param numSplits Desired number of splits. If greater than zero, at most this many splits will be returned. * If less or equal to zero, any number of splits can be returned. * @param start If non-null, the returned splits will only cover keys that are greater or equal. * @param stop If non-null, the returned splits will only cover keys that are less. * @return list of {@link Split} */ @Override public List<Split> getSplits(int numSplits, byte[] start, byte[] stop) { ensureTransactionIsStarted(); List<KeyRange> keyRanges = SplitsUtil.primitiveGetSplits(numSplits, start, stop); return Lists.transform(keyRanges, new Function<KeyRange, Split>() { @Nullable @Override public Split apply(@Nullable KeyRange input) { return new TableSplit(input == null ? null : input.getStart(), input == null ? null : input.getStop()); } }); }
/** * Fallback implementation of getSplits, {@link SplitsUtil#primitiveGetSplits(int, byte[], byte[])}. * Ideally should be overridden by subclasses. * * @param numSplits Desired number of splits. If greater than zero, at most this many splits will be returned. * If less or equal to zero, any number of splits can be returned. * @param start If non-null, the returned splits will only cover keys that are greater or equal. * @param stop If non-null, the returned splits will only cover keys that are less. * @return list of {@link Split} */ @Override public List<Split> getSplits(int numSplits, byte[] start, byte[] stop) { ensureTransactionIsStarted(); List<KeyRange> keyRanges = SplitsUtil.primitiveGetSplits(numSplits, start, stop); return Lists.transform(keyRanges, new Function<KeyRange, Split>() { @Nullable @Override public Split apply(@Nullable KeyRange input) { return new TableSplit(input == null ? null : input.getStart(), input == null ? null : input.getStop()); } }); }
@ReadWrite @Override public Row incrementAndGet(byte[] row, byte[][] columns, long[] amounts) { ensureTransactionIsStarted(); return internalIncrementAndGet(row, columns, amounts); }
@ReadWrite @Override public Row incrementAndGet(byte[] row, byte[][] columns, long[] amounts) { ensureTransactionIsStarted(); return internalIncrementAndGet(row, columns, amounts); }
/** * NOTE: Depending on the use-case, calling this method may be much less efficient than calling same method * with columns as parameters because it will require a round trip to persistent store. */ @WriteOnly @Override public void delete(byte[] row) { ensureTransactionIsStarted(); // this is going to be expensive, but the only we can do as delete implementation act on per-column level try { Map<byte[], byte[]> rowMap = getRowMap(row); delete(row, rowMap.keySet().toArray(new byte[rowMap.keySet().size()][])); // "0" because we don't know what gets deleted reportWrite(1, 0); } catch (Exception e) { LOG.debug("delete failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("delete failed", e); } }
/** * NOTE: Depending on the use-case, calling this method may be much less efficient than calling same method * with columns as parameters because it will require a round trip to persistent store. */ @WriteOnly @Override public void delete(byte[] row) { ensureTransactionIsStarted(); // this is going to be expensive, but the only we can do as delete implementation act on per-column level try { Map<byte[], byte[]> rowMap = getRowMap(row); delete(row, rowMap.keySet().toArray(new byte[rowMap.keySet().size()][])); // "0" because we don't know what gets deleted reportWrite(1, 0); } catch (Exception e) { LOG.debug("delete failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("delete failed", e); } }
@WriteOnly @Override public void increment(byte[] row, byte[][] columns, long[] amounts) { ensureTransactionIsStarted(); if (enableReadlessIncrements) { NavigableMap<byte[], Update> colVals = buff.get(row); if (colVals == null) { colVals = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); buff.put(row, colVals); } for (int i = 0; i < columns.length; i++) { colVals.put(columns[i], Updates.mergeUpdates(colVals.get(columns[i]), new IncrementValue(amounts[i]))); } reportWrite(1, getSize(row) + getSize(columns) + getSize(amounts)); } else { internalIncrementAndGet(row, columns, amounts); } }
@WriteOnly @Override public void increment(byte[] row, byte[][] columns, long[] amounts) { ensureTransactionIsStarted(); if (enableReadlessIncrements) { NavigableMap<byte[], Update> colVals = buff.get(row); if (colVals == null) { colVals = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); buff.put(row, colVals); } for (int i = 0; i < columns.length; i++) { colVals.put(columns[i], Updates.mergeUpdates(colVals.get(columns[i]), new IncrementValue(amounts[i]))); } reportWrite(1, getSize(row) + getSize(columns) + getSize(amounts)); } else { internalIncrementAndGet(row, columns, amounts); } }
@ReadOnly @Override public Row get(byte[] row, byte[][] columns) { ensureTransactionIsStarted(); reportRead(1); try { return new Result(row, getRowMap(row, columns)); } catch (Exception e) { LOG.debug("get failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("get failed", e); } }
@WriteOnly @Override public void delete(byte[] row, byte[][] columns) { ensureTransactionIsStarted(); if (columns == null) { delete(row); return; } // Do not delete anything when columns list is empty. Return-fast shortcut if (columns.length == 0) { return; } // same as writing null for every column // ANDREAS: shouldn't this be DELETE_MARKER? putInternal(row, columns, new byte[columns.length][]); // "0" because we don't know what gets deleted reportWrite(1, 0); }
@WriteOnly @Override public void delete(byte[] row, byte[][] columns) { ensureTransactionIsStarted(); if (columns == null) { delete(row); return; } // Do not delete anything when columns list is empty. Return-fast shortcut if (columns.length == 0) { return; } // same as writing null for every column // ANDREAS: shouldn't this be DELETE_MARKER? putInternal(row, columns, new byte[columns.length][]); // "0" because we don't know what gets deleted reportWrite(1, 0); }
@ReadOnly @Override public Row get(byte[] row, byte[][] columns) { ensureTransactionIsStarted(); reportRead(1); try { return new Result(row, getRowMap(row, columns)); } catch (Exception e) { LOG.debug("get failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("get failed", e); } }
@ReadOnly @Override public Scanner scan(Scan scan) { ensureTransactionIsStarted(); NavigableMap<byte[], NavigableMap<byte[], Update>> bufferMap = scanBuffer(scan); try { return new BufferingScanner(bufferMap, scanPersisted(scan)); } catch (Exception e) { LOG.debug("scan failed for table: " + getTransactionAwareName() + ", scan: " + scan.toString(), e); throw new DataSetException("scan failed", e); } }
@ReadOnly @Override public Scanner scan(Scan scan) { ensureTransactionIsStarted(); NavigableMap<byte[], NavigableMap<byte[], Update>> bufferMap = scanBuffer(scan); try { return new BufferingScanner(bufferMap, scanPersisted(scan)); } catch (Exception e) { LOG.debug("scan failed for table: " + getTransactionAwareName() + ", scan: " + scan.toString(), e); throw new DataSetException("scan failed", e); } }
/** * NOTE: Depending on the use-case, calling this method may be much less * efficient than calling same method with columns as parameters because it may always require round trip to * persistent store */ @ReadOnly @Override public Row get(byte[] row) { ensureTransactionIsStarted(); reportRead(1); try { return new Result(row, getRowMap(row)); } catch (Exception e) { LOG.debug("get failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("get failed", e); } }
/** * NOTE: if value is null corresponded column is deleted. It will not be in result set when reading. * * Also see {@link co.cask.cdap.api.dataset.table.Table#put(byte[], byte[][], byte[][])}. */ @WriteOnly @Override public void put(byte[] row, byte[][] columns, byte[][] values) { ensureTransactionIsStarted(); putInternal(row, columns, values); // report metrics _after_ write was performed reportWrite(1, getSize(row) + getSize(columns) + getSize(values)); }
/** * NOTE: Depending on the use-case, calling this method may be much less * efficient than calling same method with columns as parameters because it may always require round trip to * persistent store */ @ReadOnly @Override public Row get(byte[] row) { ensureTransactionIsStarted(); reportRead(1); try { return new Result(row, getRowMap(row)); } catch (Exception e) { LOG.debug("get failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("get failed", e); } }
/** * NOTE: if value is null corresponded column is deleted. It will not be in result set when reading. * * Also see {@link co.cask.cdap.api.dataset.table.Table#put(byte[], byte[][], byte[][])}. */ @WriteOnly @Override public void put(byte[] row, byte[][] columns, byte[][] values) { ensureTransactionIsStarted(); putInternal(row, columns, values); // report metrics _after_ write was performed reportWrite(1, getSize(row) + getSize(columns) + getSize(values)); }
@ReadWrite @Override public boolean compareAndSwap(byte[] row, byte[] column, byte[] expectedValue, byte[] newValue) { ensureTransactionIsStarted(); // TODO: add support for empty values; see https://issues.cask.co/browse/TEPHRA-45 for details. if (newValue != null && newValue.length == 0) { warnAboutEmptyValue(column); } // NOTE: there is more efficient way to do it, but for now we want more simple implementation, not over-optimizing byte[][] columns = new byte[][]{column}; try { byte[] currentValue = getRowMap(row, columns).get(column); reportRead(1); if (Arrays.equals(expectedValue, currentValue)) { putInternal(row, columns, new byte[][]{newValue}); reportWrite(1, getSize(row) + getSize(column) + getSize(newValue)); return true; } } catch (Exception e) { LOG.debug("compareAndSwap failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("compareAndSwap failed", e); } return false; }
@ReadWrite @Override public boolean compareAndSwap(byte[] row, byte[] column, byte[] expectedValue, byte[] newValue) { ensureTransactionIsStarted(); // TODO: add support for empty values; see https://issues.cask.co/browse/TEPHRA-45 for details. if (newValue != null && newValue.length == 0) { warnAboutEmptyValue(column); } // NOTE: there is more efficient way to do it, but for now we want more simple implementation, not over-optimizing byte[][] columns = new byte[][]{column}; try { byte[] currentValue = getRowMap(row, columns).get(column); reportRead(1); if (Arrays.equals(expectedValue, currentValue)) { putInternal(row, columns, new byte[][]{newValue}); reportWrite(1, getSize(row) + getSize(column) + getSize(newValue)); return true; } } catch (Exception e) { LOG.debug("compareAndSwap failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("compareAndSwap failed", e); } return false; }