/** * 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); } }
private Map<byte[], byte[]> getRowMap(byte[] row) throws Exception { NavigableMap<byte[], byte[]> result = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); // checking if the row was deleted inside this tx NavigableMap<byte[], Update> buffCols = buff.get(row); boolean rowDeleted = buffCols == null && buff.containsKey(row); if (rowDeleted) { return Collections.emptyMap(); } Map<byte[], byte[]> persisted = getPersisted(row, null); result.putAll(persisted); if (buffCols != null) { // buffered should override those returned from persistent store mergeToPersisted(result, buffCols, null); } return unwrapDeletes(result); }
@Override public boolean commitTx() throws Exception { if (!buff.isEmpty()) { // We first assume that all data will be persisted. So that if exception happen during persist we try to // rollback everything we had in in-memory buffer. toUndo = buff; // clearing up in-memory buffer by initializing new map. // NOTE: we want to init map here so that if no changes are made we re-use same instance of the map in next tx // NOTE: we could cache two maps and swap them to avoid creation of map instances, but code would be ugly buff = new ConcurrentSkipListMap<>(Bytes.BYTES_COMPARATOR); // TODO: tracking of persisted items can be optimized by returning a pair {succeededOrNot, persisted} which // tells if persisting succeeded and what was persisted (i.e. what we will have to undo in case of rollback) persist(toUndo); } return true; }
@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 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); } }
table.startTx(tx1); table.put(R1, a(C1, C2, C3), lb(1, 2, 3)); table.put(R2, a(C2, C3, C4), lb(3, 2, 1)); List<Row> rows = table.get(Lists.newArrayList(new Get(R1), new Get(R2))); Assert.assertEquals(2, rows.size()); TableAssert.assertRow(rows.get(0), R1, a(C1, C2, C3), lb(1, 2, 3)); TableAssert.assertRow(rows.get(1), R2, a(C2, C3, C4), lb(3, 2, 1)); rows = table.get(Lists.newArrayList(new Get(R1, C2, C3), new Get(R2, C2, C3), new Get(R3))); Assert.assertEquals(3, rows.size()); TableAssert.assertRow(rows.get(0), R1, a(C2, C3), lb(2, 3)); Collection<byte []> txChanges = table.getTxChanges(); txClient.canCommitOrThrow(tx1, txChanges); Assert.assertTrue(table.commitTx()); txClient.commitOrThrow(tx1); table.postTxCommit(); table.startTx(tx2); table.put(R1, a(C2, C3, C4), lb(4, 3, 2)); table.delete(R1, a(C1, C2)); table.delete(R2); table.put(R3, C5, L1); rows = table.get(Lists.newArrayList(new Get(R1), new Get(R2), new Get(R3))); Assert.assertEquals(3, rows.size());
table.startTx(tx); table.put(new byte[] {0}, new byte[] {9}, new byte[] {8}); table.commitTx(); txClient.commitOrThrow(tx); table.startTx(tx); byte[] colParam = new byte[] {2}; byte[] valParam = Bytes.toBytes(3L); table.put(rowParam, colParam, valParam); Row getRow = table.get(new byte[] {1}); Map<byte[], byte[]> getRowResult = getRow.getColumns(); Assert.assertEquals(1, getRowResult.size()); Row getColumnSetRow = table.get(new byte[] {1}); Map<byte[], byte[]> getColumnSetResult = getColumnSetRow.getColumns(); Assert.assertEquals(1, getColumnSetResult.size()); byte[] valFromGetColumn = table.get(new byte[] {1}, new byte[] {2}); Assert.assertArrayEquals(Bytes.toBytes(3L), valFromGetColumn); Scanner scan = table.scan(new byte[] {1}, null); Row next = scan.next(); Assert.assertNotNull(next); table.delete(rowParam, colParam);
table1.startTx(tx1); table1.put(R1, a(C1), a(V1)); Collection<byte []> tx1Changes = table1.getTxChanges(); txClient.canCommitOrThrow(tx1, tx1Changes); Assert.assertTrue(table1.commitTx()); txClient.commitOrThrow(tx1); table1.postTxCommit(); table2.startTx(tx2); table2.put(R1, a(C1), a(V1)); Collection<byte []> tx2Changes = table2.getTxChanges(); txClient.canCommitOrThrow(tx2, tx2Changes); Assert.assertTrue(table2.commitTx()); txClient.commitOrThrow(tx2); table1.postTxCommit(); String tx1ChangePrefix = new String(table1.getNameAsTxChangePrefix()); String tx2ChangePrefix = new String(table2.getNameAsTxChangePrefix()); String tx1Change = new String(((ArrayList<byte []>) tx1Changes).get(0)); String tx2Change = new String(((ArrayList<byte []>) tx2Changes).get(0));
@ReadOnly @Override public Row get(byte[] row, byte[] startColumn, byte[] stopColumn, int limit) { ensureTransactionIsStarted(); reportRead(1); // checking if the row was deleted inside this tx NavigableMap<byte[], Update> buffCols = buff.get(row); // NOTE: since we cannot tell the exact column set, we always have to go to persisted store. // potential improvement: do not fetch columns available in in-mem buffer (we know them at this point) try { Map<byte[], byte[]> persistedCols = getPersisted(row, startColumn, stopColumn, limit); // adding server cols, and then overriding with buffered values NavigableMap<byte[], byte[]> result = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); if (persistedCols != null) { result.putAll(persistedCols); } if (buffCols != null) { buffCols = getRange(buffCols, startColumn, stopColumn, limit); // null valued columns in in-memory buffer are deletes, so we need to delete them from the result list mergeToPersisted(result, buffCols, null); } // applying limit return new Result(row, head(result, limit)); } catch (Exception e) { LOG.debug("get failed for table: " + getTransactionAwareName() + ", row: " + Bytes.toStringBinary(row), e); throw new DataSetException("get failed", e); } }
BufferingTable myTable1 = new BufferingTableWithPersistingFailure(getTable(CONTEXT1, MY_TABLE)); myTable1.startTx(tx1); myTable1.put(R1, a(C1), a(V1)); myTable1.put(R2, a(C2), a(V2)); TableAssert.assertRow(a(C1, V1), myTable1.get(R1, a(C1))); TableAssert.assertRow(a(C2, V2), myTable1.get(R2, a(C2))); myTable1.commitTx(); Assert.assertFalse(true); } catch (Throwable th) { Assert.assertTrue(myTable1.rollbackTx());
@ReadOnly @Override public List<Row> get(List<Get> gets) { ensureTransactionIsStarted(); try { List<Map<byte[], byte[]>> persistedRows = getPersisted(gets); List<byte[]> getColumns = get.getColumns(); byte[][] columns = getColumns == null ? null : getColumns.toArray(new byte[getColumns.size()][]); mergeToPersisted(rowColumns, buffCols, columns); result.add(new Result(row, unwrapDeletes(rowColumns))); LOG.debug("multi-get failed for table: " + getTransactionAwareName(), e); throw new DataSetException("multi-get failed", e);
private void verify(BufferingTable table, byte[] row, byte[] col, byte[] val) throws Exception { // get column Assert.assertArrayEquals(val, table.get(row, col)); // get set of columns Row getColSetRow = table.get(row, new byte[][] {col}); Map<byte[], byte[]> getColSetResult = getColSetRow.getColumns(); Assert.assertEquals(1, getColSetResult.size()); Assert.assertArrayEquals(val, getColSetResult.get(col)); // get row Row getRow = table.get(row); Map<byte[], byte[]> getRowResult = getRow.getColumns(); Assert.assertEquals(1, getRowResult.size()); Assert.assertArrayEquals(val, getRowResult.get(col)); // scan Scanner scan = table.scan(row, null); Row next = scan.next(); Assert.assertNotNull(next); Assert.assertArrayEquals(row, next.getRow()); Assert.assertArrayEquals(val, next.get(col)); Assert.assertNull(scan.next()); }
@Override protected NavigableMap<byte[], byte[]> getPersisted(byte[] row, byte[] startColumn, byte[] stopColumn, int limit) throws Exception { return delegate.getPersisted(row, startColumn, stopColumn, limit); }
private Collection<byte[]> getRowChanges() { // we resolve conflicts on row level of individual table List<byte[]> changes = new ArrayList<>(buff.size()); for (byte[] changedRow : buff.keySet()) { changes.add(Bytes.add(getNameAsTxChangePrefix(), changedRow)); } return changes; }
@Override public void startTx(Transaction tx) { super.startTx(tx); // set this to null in case it was left over - for whatever reason - by the previous tx encodedTx = null; }
@Override public boolean rollbackTx() throws Exception { buff.clear(); if (toUndo != null) { undo(toUndo); toUndo = null; } tx = null; return true; }
@ReadOnly @Override public Scanner scan(byte[] startRow, byte[] stopRow) { return scan(new Scan(startRow, stopRow)); }
@Override public void postTxCommit() { super.postTxCommit(); // we don't do this on commitTx() because we may still need the tx for rollback encodedTx = null; }
@Override public boolean rollbackTx() throws Exception { boolean success = super.rollbackTx(); encodedTx = null; return success; }
@Override protected Scanner scanPersisted(Scan scan) throws Exception { return delegate.scanPersisted(scan); }