Search in sources :

Example 1 with ReadWrite

use of io.cdap.cdap.api.annotation.ReadWrite in project cdap by caskdata.

the class IndexedTable method incrementAndGet.

/**
 * Increments (atomically) the specified row and columns by the specified amounts, and returns the new values.
 * Note that performing this operation on an indexed column will generally have a negative impact on performance,
 * since up to three writes will need to be performed for every increment (one removing the index for the previous,
 * pre-increment value, one adding the index for the incremented value, and one for the increment itself).
 *
 * @see Table#incrementAndGet(byte[], byte[][], long[])
 */
@ReadWrite
@Override
public Row incrementAndGet(byte[] row, byte[][] columns, long[] amounts) {
    if (columns.length != amounts.length) {
        throw new IllegalArgumentException("Size of columns and amounts arguments must match");
    }
    Row existingRow = table.get(row, columns);
    byte[][] updatedValues = new byte[columns.length][];
    NavigableMap<byte[], byte[]> result = new TreeMap<>(Bytes.BYTES_COMPARATOR);
    for (int i = 0; i < columns.length; i++) {
        long existingValue = 0L;
        byte[] existingBytes = existingRow.get(columns[i]);
        if (existingBytes != null) {
            if (existingBytes.length != Bytes.SIZEOF_LONG) {
                throw new NumberFormatException("Attempted to increment a value that is not convertible to long," + " row: " + Bytes.toStringBinary(row) + " column: " + Bytes.toStringBinary(columns[i]));
            }
            existingValue = Bytes.toLong(existingBytes);
            if (indexedColumns.contains(columns[i])) {
                index.delete(createIndexKey(row, columns[i], existingBytes), IDX_COL);
            }
        }
        updatedValues[i] = Bytes.toBytes(existingValue + amounts[i]);
        result.put(columns[i], updatedValues[i]);
        if (indexedColumns.contains(columns[i])) {
            index.put(createIndexKey(row, columns[i], updatedValues[i]), IDX_COL, row);
        }
    }
    table.put(row, columns, updatedValues);
    return new Result(row, result);
}
Also used : Row(io.cdap.cdap.api.dataset.table.Row) TreeMap(java.util.TreeMap) Result(io.cdap.cdap.api.dataset.table.Result) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Example 2 with ReadWrite

use of io.cdap.cdap.api.annotation.ReadWrite in project cdap by caskdata.

the class PartitionedFileSetDataset method consumePartitions.

// PartitionConsumerState consists of two things:
// 1) A list of transaction IDs representing the list of transactions in progress during the previous call.
// Each of these transaction IDs need to be checked for new partitions because there may be partitions created by
// those partitions since the previous call.
// 2) A transaction ID from which to start scanning for new partitions. This is an exclusive end range that the
// previous call stopped scanning partitions at.
// Note that each of the transactions IDs in (1) will be smaller than the transactionId in (2).
@ReadWrite
@Override
public PartitionConsumerResult consumePartitions(PartitionConsumerState partitionConsumerState, int limit, Predicate<PartitionDetail> predicate) {
    List<Long> previousInProgress = partitionConsumerState.getVersionsToCheck();
    Set<Long> noLongerInProgress = setDiff(previousInProgress, tx.getInProgress());
    List<PartitionDetail> partitions = Lists.newArrayList();
    Iterator<Long> iter = noLongerInProgress.iterator();
    while (iter.hasNext()) {
        Long txId = iter.next();
        if (partitions.size() >= limit) {
            break;
        }
        try (Scanner scanner = partitionsTable.readByIndex(WRITE_PTR_COL, Bytes.toBytes(txId))) {
            scannerToPartitions(scanner, partitions, limit, predicate);
        }
        // remove the txIds as they are added to the partitions list already
        // if they're not removed, they will be persisted in the state for the next scan
        iter.remove();
    }
    // exclusive scan end, to be used as the start for a next call to consumePartitions
    long scanUpTo;
    if (partitions.size() < limit) {
        // no read your own writes (partitions)
        scanUpTo = Math.min(tx.getWritePointer(), tx.getReadPointer() + 1);
        Long endTxId;
        try (Scanner scanner = partitionsTable.scanByIndex(WRITE_PTR_COL, Bytes.toBytes(partitionConsumerState.getStartVersion()), Bytes.toBytes(scanUpTo))) {
            endTxId = scannerToPartitions(scanner, partitions, limit, predicate);
        }
        if (endTxId != null) {
            // nonnull means that the scanner was not exhausted
            scanUpTo = endTxId;
        }
    } else {
        // if we have already hit the limit, don't scan; instead, use the startVersion as the startVersion to the next
        // call to consumePartitions
        scanUpTo = partitionConsumerState.getStartVersion();
    }
    List<Long> inProgressBeforeScanEnd = Lists.newArrayList(noLongerInProgress);
    for (long txId : tx.getInProgress()) {
        if (txId >= scanUpTo) {
            break;
        }
        inProgressBeforeScanEnd.add(txId);
    }
    return new PartitionConsumerResult(new PartitionConsumerState(scanUpTo, inProgressBeforeScanEnd), partitions);
}
Also used : Scanner(io.cdap.cdap.api.dataset.table.Scanner) PartitionConsumerResult(io.cdap.cdap.api.dataset.lib.PartitionConsumerResult) PartitionConsumerState(io.cdap.cdap.api.dataset.lib.PartitionConsumerState) AtomicLong(java.util.concurrent.atomic.AtomicLong) PartitionDetail(io.cdap.cdap.api.dataset.lib.PartitionDetail) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Example 3 with ReadWrite

use of io.cdap.cdap.api.annotation.ReadWrite in project cdap by caskdata.

the class BufferingTable method compareAndSwap.

@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;
}
Also used : DataSetException(io.cdap.cdap.api.dataset.DataSetException) DataSetException(io.cdap.cdap.api.dataset.DataSetException) IOException(java.io.IOException) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Example 4 with ReadWrite

use of io.cdap.cdap.api.annotation.ReadWrite in project cdap by caskdata.

the class IndexedTable method compareAndSwap.

/**
 * Perform a swap operation by primary key.
 * Parameters are as if they were on a non-indexed table.
 * Note that if the swap is on the secondary key column,
 * then the index must be updated; otherwise, this is a
 * pass-through to the underlying table.
 */
@ReadWrite
@Override
public boolean compareAndSwap(byte[] row, byte[] column, byte[] expected, byte[] newValue) {
    // is the same as the new value, then the index is not affected either.
    if (!indexedColumns.contains(column) || Arrays.equals(expected, newValue)) {
        return table.compareAndSwap(row, column, expected, newValue);
    }
    // the swap is on the index column. it will only succeed if the current
    // value matches the expected value of the swap. if that value is not null,
    // then we must remove the row key from the index for that value.
    Delete idxDelete = null;
    if (expected != null) {
        idxDelete = new Delete(createIndexKey(row, column, expected), IDX_COL);
    }
    // if the new value is not null, then we must add the rowkey to the index
    // for that value.
    Put idxPut = null;
    if (newValue != null) {
        idxPut = new Put(createIndexKey(row, column, newValue), IDX_COL, row);
    }
    // apply all operations to both tables
    boolean success = table.compareAndSwap(row, column, expected, newValue);
    if (!success) {
        // do nothing: no changes
        return false;
    }
    if (idxDelete != null) {
        index.delete(idxDelete);
    }
    if (idxPut != null) {
        index.put(idxPut);
    }
    return true;
}
Also used : Delete(io.cdap.cdap.api.dataset.table.Delete) Put(io.cdap.cdap.api.dataset.table.Put) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Example 5 with ReadWrite

use of io.cdap.cdap.api.annotation.ReadWrite in project cdap by caskdata.

the class BufferingTable method internalIncrementAndGet.

@ReadWrite
protected Row internalIncrementAndGet(byte[] row, byte[][] columns, long[] amounts) {
    // Logic:
    // * fetching current values
    // * updating values
    // * updating in-memory store
    // * returning updated values as result
    // NOTE: there is more efficient way to do it, but for now we want more simple implementation, not over-optimizing
    Map<byte[], byte[]> rowMap;
    try {
        rowMap = getRowMap(row, columns);
        reportRead(1);
    } catch (Exception e) {
        LOG.debug("incrementAndGet failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e);
        throw new DataSetException("incrementAndGet failed", e);
    }
    byte[][] updatedValues = new byte[columns.length][];
    NavigableMap<byte[], byte[]> result = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
    for (int i = 0; i < columns.length; i++) {
        byte[] column = columns[i];
        byte[] val = rowMap.get(column);
        // converting to long
        long longVal;
        if (val == null) {
            longVal = 0L;
        } else {
            if (val.length != Bytes.SIZEOF_LONG) {
                throw new NumberFormatException("Attempted to increment a value that is not convertible to long," + " row: " + Bytes.toStringBinary(row) + " column: " + Bytes.toStringBinary(column));
            }
            longVal = Bytes.toLong(val);
        }
        longVal += amounts[i];
        updatedValues[i] = Bytes.toBytes(longVal);
        result.put(column, updatedValues[i]);
    }
    putInternal(row, columns, updatedValues);
    reportWrite(1, getSize(row) + getSize(columns) + getSize(amounts));
    return new Result(row, result);
}
Also used : DataSetException(io.cdap.cdap.api.dataset.DataSetException) DataSetException(io.cdap.cdap.api.dataset.DataSetException) IOException(java.io.IOException) Result(io.cdap.cdap.api.dataset.table.Result) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Aggregations

ReadWrite (io.cdap.cdap.api.annotation.ReadWrite)5 DataSetException (io.cdap.cdap.api.dataset.DataSetException)2 Result (io.cdap.cdap.api.dataset.table.Result)2 IOException (java.io.IOException)2 PartitionConsumerResult (io.cdap.cdap.api.dataset.lib.PartitionConsumerResult)1 PartitionConsumerState (io.cdap.cdap.api.dataset.lib.PartitionConsumerState)1 PartitionDetail (io.cdap.cdap.api.dataset.lib.PartitionDetail)1 Delete (io.cdap.cdap.api.dataset.table.Delete)1 Put (io.cdap.cdap.api.dataset.table.Put)1 Row (io.cdap.cdap.api.dataset.table.Row)1 Scanner (io.cdap.cdap.api.dataset.table.Scanner)1 TreeMap (java.util.TreeMap)1 AtomicLong (java.util.concurrent.atomic.AtomicLong)1