use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class RankIndexMaintainer method scan.
@Nonnull
@Override
public RecordCursor<IndexEntry> scan(@Nonnull IndexScanType scanType, @Nonnull TupleRange rankRange, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
if (scanType == IndexScanType.BY_VALUE) {
return scan(rankRange, continuation, scanProperties);
} else if (scanType != IndexScanType.BY_RANK) {
throw new RecordCoreException("Can only scan rank index by rank or by value.");
}
final Subspace extraSubspace = getSecondarySubspace();
final CompletableFuture<TupleRange> scoreRangeFuture = RankedSetIndexHelper.rankRangeToScoreRange(state, getGroupingCount(), extraSubspace, config, rankRange);
return RecordCursor.mapFuture(getExecutor(), scoreRangeFuture, continuation, (scoreRange, scoreContinuation) -> {
if (scoreRange == null) {
return RecordCursor.empty(getExecutor());
} else {
return scan(scoreRange, scoreContinuation, scanProperties);
}
});
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class BitmapValueIndexMaintainer method scan.
@Nonnull
@Override
public RecordCursor<IndexEntry> scan(@Nonnull IndexScanType scanType, @Nonnull TupleRange range, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
if (scanType != IndexScanType.BY_GROUP) {
throw new RecordCoreException("Can only scan bitmap index by group.");
}
final int groupPrefixSize = getGroupingCount();
final long startPosition;
if (range.getLow() != null && range.getLow().size() > groupPrefixSize && range.getLow().get(groupPrefixSize) != null) {
if (range.getLowEndpoint() == EndpointType.RANGE_EXCLUSIVE) {
startPosition = range.getLow().getLong(groupPrefixSize) + 1;
} else {
startPosition = range.getLow().getLong(groupPrefixSize);
}
if (startPosition % entrySize != 0) {
range = new TupleRange(range.getLow().popBack().add(startPosition - Math.floorMod(startPosition, (long) entrySize)), range.getHigh(), EndpointType.RANGE_INCLUSIVE, range.getHighEndpoint());
}
} else {
startPosition = Long.MIN_VALUE;
}
final long endPosition;
if (range.getHigh() != null && range.getHigh().size() > groupPrefixSize && range.getHigh().get(groupPrefixSize) != null) {
if (range.getHighEndpoint() == EndpointType.RANGE_INCLUSIVE) {
endPosition = range.getHigh().getLong(groupPrefixSize) + 1;
} else {
endPosition = range.getHigh().getLong(groupPrefixSize);
}
if (endPosition % entrySize != 0) {
range = new TupleRange(range.getLow(), range.getHigh().popBack().add(endPosition + Math.floorMod(entrySize - endPosition, (long) entrySize)), range.getLowEndpoint(), EndpointType.RANGE_INCLUSIVE);
}
} else {
endPosition = Long.MAX_VALUE;
}
return scan(range, continuation, scanProperties).map(indexEntry -> {
final long entryStart = indexEntry.getKey().getLong(groupPrefixSize);
final byte[] entryBitmap = indexEntry.getValue().getBytes(0);
final long entryEnd = entryStart + entryBitmap.length * 8;
if (entryStart < startPosition || entryEnd > endPosition) {
final long trimmedStart = Math.max(entryStart, startPosition);
final long trimmedEnd = Math.min(entryEnd, endPosition);
if (trimmedStart < trimmedEnd) {
final Tuple trimmedKey = indexEntry.getKey().popBack().add(trimmedStart);
final byte[] trimmedBitmap = new byte[((int) (trimmedEnd - trimmedStart) + 7) / 8];
for (long i = trimmedStart; i < trimmedEnd; i++) {
int offset = (int) (i - entryStart);
if ((entryBitmap[offset / 8] & (byte) (1 << (offset % 8))) != 0) {
int trimmedOffset = (int) (i - trimmedStart);
trimmedBitmap[trimmedOffset / 8] |= (byte) (1 << (trimmedOffset % 8));
}
}
final Tuple subValue = Tuple.from(trimmedBitmap);
return Optional.of(new IndexEntry(indexEntry.getIndex(), trimmedKey, subValue));
} else {
return Optional.<IndexEntry>empty();
}
} else {
return Optional.of(indexEntry);
}
}).filter(Optional::isPresent).map(Optional::get);
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class BitmapValueIndexMaintainer method updateIndexKeys.
@Override
@Nonnull
protected <M extends Message> CompletableFuture<Void> updateIndexKeys(@Nonnull final FDBIndexableRecord<M> savedRecord, final boolean remove, @Nonnull final List<IndexEntry> indexEntries) {
final int groupPrefixSize = getGroupingCount();
final List<CompletableFuture<Void>> futures = unique && !remove ? new ArrayList<>(indexEntries.size()) : null;
for (IndexEntry indexEntry : indexEntries) {
final long startTime = System.nanoTime();
final Tuple groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
Object positionObject = indexEntry.getKey().get(groupPrefixSize);
if (positionObject == null) {
continue;
}
if (!(positionObject instanceof Number)) {
// This should be prevented by the checks in the meta-data builder, but just in case
throw new RecordCoreException("position field in index entry is not a number").addLogInfo(LogMessageKeys.KEY, indexEntry.getKey(), LogMessageKeys.INDEX_NAME, state.index.getName(), LogMessageKeys.INDEX_TYPE, state.index.getType());
}
long position = ((Number) positionObject).longValue();
final int offset = (int) Math.floorMod(position, (long) entrySize);
position -= offset;
final byte[] key = state.indexSubspace.pack(groupKey.add(position));
// This has to be the same size every time, with all the unset bits, or else it gets truncated.
// We really could use a new mutation that took a linear bit position to set / clear and only did length extension or something like that.
final byte[] bitmap = new byte[(entrySize + 7) / 8];
if (remove) {
if (state.store.isIndexWriteOnly(state.index)) {
// If the index isn't built, it's possible this key wasn't reached.
// So initialize it to zeros (or leave it alone).
state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
}
// Otherwise the entry must already exist for us to be removing it,
// so there is no danger that this will store all (but one) ones in a new key.
Arrays.fill(bitmap, (byte) 0xFF);
bitmap[offset / 8] &= ~(byte) (1 << (offset % 8));
state.transaction.mutate(MutationType.BIT_AND, key, bitmap);
Arrays.fill(bitmap, (byte) 0x00);
state.transaction.mutate(MutationType.COMPARE_AND_CLEAR, key, bitmap);
} else {
if (unique) {
// Snapshot read to see if the bit is already set.
CompletableFuture<Void> future = state.transaction.snapshot().get(key).thenAccept(existing -> {
if (existing != null && (existing[offset / 8] & (byte) (1 << (offset % 8))) != 0) {
throw new RecordIndexUniquenessViolation(state.index, indexEntry, savedRecord.getPrimaryKey(), // Unfortunately, we don't know the other key.
null);
}
});
futures.add(future);
// Then arrange to conflict for the position with any concurrent update.
final byte[] conflictKey = new Subspace(key).pack(offset);
state.transaction.addReadConflictKey(conflictKey);
state.transaction.addWriteConflictKey(conflictKey);
}
bitmap[offset / 8] |= (byte) (1 << (offset % 8));
state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
}
if (state.store.getTimer() != null) {
state.store.getTimer().recordSinceNanoTime(FDBStoreTimer.Events.MUTATE_INDEX_ENTRY, startTime);
}
}
return futures != null ? AsyncUtil.whenAll(futures) : AsyncUtil.DONE;
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class ComparatorCursor method compareAllStates.
private boolean compareAllStates(final List<KeyedMergeCursorState<T>> cursorStates) {
final long startTime = System.nanoTime();
List<Object> referenceKey = getReferenceState(cursorStates).getComparisonKey();
for (KeyedMergeCursorState<T> cursorState : cursorStates) {
// No point comparing the reference key to itself
if (cursorState.getComparisonKey() == referenceKey) {
continue;
}
int compare = KeyComparisons.KEY_COMPARATOR.compare(cursorState.getComparisonKey(), referenceKey);
if (compare != 0) {
logComparisonFailure(referenceKey, cursorState.getComparisonKey());
if (abortOnComparisonFailure) {
throw new RecordCoreException("Comparison of plans failed").addLogInfo(LogMessageKeys.EXPECTED, referenceKey).addLogInfo(LogMessageKeys.ACTUAL, cursorState.getComparisonKey()).addLogInfo(LogMessageKeys.PLAN_HASH, planHashSupplier.get());
} else {
return false;
}
}
}
logCounters(cursorStates, startTime);
return true;
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class SyntheticRecordPlanner method forIndex.
/**
* Construct a plan for generating synthetic records for a given index.
*
* The generated records will be of indexed record types.
*
* Used by the {@link com.apple.foundationdb.record.provider.foundationdb.OnlineIndexer} to build from a full scan of stored records.
* @param index an index on synthetic record types
* @return a plan that can be applied to scanned records to generate synthetic records
*/
@Nonnull
public SyntheticRecordFromStoredRecordPlan forIndex(@Nonnull Index index) {
final Collection<RecordType> recordTypes = recordMetaData.recordTypesForIndex(index);
if (recordTypes.size() == 1) {
final RecordType recordType = recordTypes.iterator().next();
if (!recordType.isSynthetic()) {
throw new RecordCoreException("Index does not apply to synthetic record types " + index);
}
return forType((SyntheticRecordType<?>) recordType);
}
Multimap<String, SyntheticRecordFromStoredRecordPlan> byType = ArrayListMultimap.create();
for (RecordType recordType : recordTypes) {
if (!(recordType instanceof JoinedRecordType)) {
throw unknownSyntheticType(recordType);
}
JoinedRecordType joinedRecordType = (JoinedRecordType) recordType;
Optional<JoinedRecordType.JoinConstituent> maybeConstituent = joinedRecordType.getConstituents().stream().filter(c -> !c.isOuterJoined()).findFirst();
if (maybeConstituent.isPresent()) {
addToByType(byType, joinedRecordType, maybeConstituent.get());
} else {
for (JoinedRecordType.JoinConstituent joinConstituent : joinedRecordType.getConstituents()) {
addToByType(byType, joinedRecordType, joinConstituent);
}
}
}
return createByType(byType);
}
Aggregations