public SweepTableIndices(KeyValueService kvs) { this(new IdsToNames(kvs), new NamesToIds(kvs)); }
static SweepTableIdentifier identified(int identifier) { return ImmutableSweepTableIdentifier.of(identifier, false); } }
@Override default byte[] persistToBytes() { return EncodingUtils.encodeSignedVarLong((isPending() ? -1 : 1) * identifier()); }
private int loadUncached(TableReference table) { while (true) { Optional<SweepTableIdentifier> identifier = namesToIds.currentMapping(table); if (identifier.isPresent() && !identifier.get().isPending()) { return identifier.get().identifier(); } log.info("Assigning table {} an identifier", LoggingArgs.tableRef(table)); // note - the second time through the loop this will fail, since on those iterations we're // doing our updates as CAS (at the bottom) not PUE, but it doubles as a get SweepTableIdentifier afterPendingPut = namesToIds.storeAsPending(table, idToNames.getNextId()); if (!afterPendingPut.isPending()) { log.info("Assigned table {} to id {}", LoggingArgs.tableRef(table), SafeArg.of("id", afterPendingPut.identifier())); return afterPendingPut.identifier(); } boolean assigmentWasSuccessful = idToNames.storeNewMapping(table, afterPendingPut.identifier()); if (assigmentWasSuccessful) { namesToIds.moveToComplete(table, afterPendingPut.identifier()); log.info("Assigned table {} to id {}", LoggingArgs.tableRef(table), SafeArg.of("id", afterPendingPut.identifier())); return afterPendingPut.identifier(); } // B namesToIds.storeAsPending(table, afterPendingPut.identifier(), idToNames.getNextId()); } } }
@Test public void testWorkflow() { assertThat(namesToIds.storeAsPending(TABLE, 1)) .isEqualTo(SweepTableIdentifier.pending(1)); namesToIds.storeAsPending(TABLE, 1, 2); assertThat(namesToIds.currentMapping(TABLE)).contains(SweepTableIdentifier.pending(2)); namesToIds.moveToComplete(TABLE, 2); assertThat(namesToIds.currentMapping(TABLE)).contains(SweepTableIdentifier.identified(2)); }
@Test public void testConcurrentCreationOfMultipleTables() { namesToIds.storeAsPending(table(1), 1); idsToNames.storeNewMapping(table(2), 1); assertThat(tableIndices.getTableId(table(1))).isEqualTo(2); }
@Test public void testWorkflow() { assertThat(idsToNames.getNextId()).isEqualTo(1); assertThat(idsToNames.get(1)).isEmpty(); assertThat(idsToNames.storeNewMapping(tableRef(0), 1)).isTrue(); assertThat(idsToNames.storeNewMapping(tableRef(1), 1)).isFalse(); assertThat(idsToNames.get(1)).contains(tableRef(0)); assertThat(idsToNames.getNextId()).isEqualTo(2); assertThat(idsToNames.storeNewMapping(tableRef(1), 2)).isTrue(); assertThat(idsToNames.getNextId()).isEqualTo(3); }
SweepTableIdentifier storeAsPending(TableReference table, int nextId) { SweepNameToIdRow row = SweepNameToIdRow.of(table.getQualifiedName()); Cell cell = Cell.create(row.persistToBytes(), SweepNameToIdNamedColumn.ID.getShortName()); SweepTableIdentifier newValue = SweepTableIdentifier.pending(nextId); CheckAndSetRequest cas = CheckAndSetRequest.newCell(NAME_TO_ID, cell, newValue.persistToBytes()); try { kvs.checkAndSet(cas); return newValue; } catch (CheckAndSetException e) { return currentMapping(table).get(); } }
@Test public void testConcurrentCreationOfSameTable() { namesToIds.storeAsPending(table(1), 2); assertThat(tableIndices.getTableId(table(1))).isEqualTo(2); }
@Test public void testConcurrentCreationOfDifferentTable() { idsToNames.storeNewMapping(table(2), 1); assertThat(tableIndices.getTableId(table(1))).isEqualTo(2); }
void storeAsPending(TableReference table, int lastAttempt, int thisAttempt) { SweepNameToIdRow row = SweepNameToIdRow.of(table.getQualifiedName()); Cell cell = Cell.create(row.persistToBytes(), SweepNameToIdNamedColumn.ID.getShortName()); byte[] oldValue = SweepTableIdentifier.pending(lastAttempt).persistToBytes(); byte[] newValue = SweepTableIdentifier.pending(thisAttempt).persistToBytes(); CheckAndSetRequest cas = CheckAndSetRequest.singleCell(NAME_TO_ID, cell, oldValue, newValue); try { kvs.checkAndSet(cas); } catch (CheckAndSetException e) { // ignored; we're already spinning } }
@Test public void testSucceedsOnFirstAttempt() { assertThat(tableIndices.getTableId(table(1))).isEqualTo(1); assertThat(tableIndices.getTableReference(1)).isEqualTo(table(1)); }
@Override public byte[] persistValue() { byte[] bytes = value.persistToBytes(); return CompressionUtils.compress(bytes, Compression.NONE); }
@Value.Check default void check() { checkState(identifier() != 0, "Identifier must not be zero"); }
private TableReference getTableReferenceUncached(int tableId) { return idToNames.get(tableId) .orElseThrow(() -> new NoSuchElementException("Id " + tableId + " does not exist")); }
public StoredWriteReference persist(WriteReference writeReference) { byte[] tableId = EncodingUtils.encodeUnsignedVarLong(tableIndices.getTableId(writeReference.tableRef())); byte[] row = EncodingUtils.encodeSizedBytes(writeReference.cell().getRowName()); byte[] column = EncodingUtils.encodeSizedBytes(writeReference.cell().getColumnName()); byte[] isTombstone = EncodingUtils.encodeUnsignedVarLong(writeReference.isTombstone() ? 1 : 0); return ImmutableStoredWriteReference.of(EncodingUtils.add(writePrefix, tableId, row, column, isTombstone)); } }
public SweepableCells( KeyValueService kvs, WriteInfoPartitioner partitioner, TargetedSweepMetrics metrics, TransactionService transactionService) { super(kvs, TargetedSweepTableFactory.of().getSweepableCellsTable(null).getTableRef(), partitioner, metrics); this.commitTsCache = CommitTsCache.create(transactionService); this.writeReferencePersister = new WriteReferencePersister(new SweepTableIndices(kvs)); }
@Test public void testThrowsIfUnknownTableId() { assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(() -> tableIndices.getTableReference(3)) .withMessage("Id 3 does not exist"); }
@Test public void testConflicts() { assertThat(namesToIds.storeAsPending(TABLE, 1)) .isEqualTo(SweepTableIdentifier.pending(1)); assertThat(namesToIds.storeAsPending(TABLE, 2)) .isEqualTo(SweepTableIdentifier.pending(1)); namesToIds.storeAsPending(TABLE, 2, 3); assertThat(namesToIds.currentMapping(TABLE)).contains(SweepTableIdentifier.pending(1)); assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(() -> namesToIds.moveToComplete(TABLE, 2)) .withMessage("Unexpectedly we state changed from pending(id) to not(identified(id)) after " + "identifying id as the correct value"); assertThat(namesToIds.currentMapping(TABLE)).contains(SweepTableIdentifier.pending(1)); namesToIds.moveToComplete(TABLE, 1); assertThat(namesToIds.storeAsPending(TABLE, 1)).isEqualTo(SweepTableIdentifier.identified(1)); } }
static SweepTableIdentifier pending(int identifier) { return ImmutableSweepTableIdentifier.of(identifier, true); }