use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method loadSyntheticRecord.
/**
* Load a {@link FDBSyntheticRecord synthetic record} by loading its stored constituent records and synthesizing it from them.
* @param primaryKey the primary key of the synthetic record, which includes the primary keys of the constituents
* @return a future which completes to the synthesized record
*/
@Nonnull
@API(API.Status.EXPERIMENTAL)
public CompletableFuture<FDBSyntheticRecord> loadSyntheticRecord(@Nonnull Tuple primaryKey) {
SyntheticRecordType<?> syntheticRecordType = getRecordMetaData().getSyntheticRecordTypeFromRecordTypeKey(primaryKey.get(0));
int nconstituents = syntheticRecordType.getConstituents().size();
if (nconstituents != primaryKey.size() - 1) {
throw recordCoreException("Primary key does not have correct number of nested keys: " + primaryKey);
}
final Map<String, FDBStoredRecord<? extends Message>> constituents = new ConcurrentHashMap<>(nconstituents);
final CompletableFuture<?>[] futures = new CompletableFuture<?>[nconstituents];
for (int i = 0; i < nconstituents; i++) {
final SyntheticRecordType.Constituent constituent = syntheticRecordType.getConstituents().get(i);
final Tuple constituentKey = primaryKey.getNestedTuple(i + 1);
if (constituentKey == null) {
futures[i] = AsyncUtil.DONE;
} else {
futures[i] = loadRecordAsync(constituentKey).thenApply(rec -> {
if (rec == null) {
throw new RecordDoesNotExistException("constituent record not found: " + constituent.getName());
}
constituents.put(constituent.getName(), rec);
return null;
});
}
}
return CompletableFuture.allOf(futures).thenApply(vignore -> FDBSyntheticRecord.of(syntheticRecordType, constituents));
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method getRecordCountForRebuildIndexes.
/**
* Get count of records to pass to a {@link UserVersionChecker} to decide whether to build right away. If all of the
* new indexes are over a single type and that type has a record key prefix, then this count will only be over the
* record type being indexed. If not, it will be the count of all records of all types, as in that case, the indexer
* will need to scan the entire store to build each index. If determining the record count would be too costly (such
* as if there is not an appropriate {@linkplain IndexTypes#COUNT count} index defined), this function may return
* {@link Long#MAX_VALUE} to indicate that an unknown and unbounded number of records would have to be scanned
* to build the index.
*
* @param newStore {@code true} if this is a brand new store
* @param rebuildRecordCounts {@code true} if there is a record count key that needs to be rebuilt
* @param indexes indexes that need to be built
* @param singleRecordTypeWithPrefixKey either a single record type prefixed by the record type key or {@code null}
* @return a future that completes to the record count for the version checker
*/
@Nonnull
@SuppressWarnings("PMD.EmptyCatchBlock")
protected CompletableFuture<Long> getRecordCountForRebuildIndexes(boolean newStore, boolean rebuildRecordCounts, @Nonnull Map<Index, List<RecordType>> indexes, @Nullable RecordType singleRecordTypeWithPrefixKey) {
// Do this with the new indexes in write-only mode to avoid using one of them
// when evaluating the snapshot record count.
MutableRecordStoreState writeOnlyState = recordStoreStateRef.get().withWriteOnlyIndexes(indexes.keySet().stream().map(Index::getName).collect(Collectors.toList()));
if (singleRecordTypeWithPrefixKey != null) {
// Get a count for just those records, either from a COUNT index on just that type or from a universal COUNT index grouped by record type.
MutableRecordStoreState saveState = recordStoreStateRef.get();
try {
recordStoreStateRef.set(writeOnlyState);
return getSnapshotRecordCountForRecordType(singleRecordTypeWithPrefixKey.getName());
} catch (RecordCoreException ex) {
// No such index; have to use total record count.
} finally {
recordStoreStateRef.set(saveState);
}
}
if (!rebuildRecordCounts) {
MutableRecordStoreState saveState = recordStoreStateRef.get();
try {
recordStoreStateRef.set(writeOnlyState);
// See: FDBRecordStoreBase.checkPossiblyRebuild() could take a long time if the record count index is split into many groups (https://github.com/FoundationDB/fdb-record-layer/issues/7)
return getSnapshotRecordCount();
} catch (RecordCoreException ex) {
// Probably this was from the lack of appropriate index on count; treat like rebuildRecordCounts = true.
} finally {
recordStoreStateRef.set(saveState);
}
}
// Do a scan (limited to a single record) to see if the store is empty.
final ExecuteProperties executeProperties = ExecuteProperties.newBuilder().setReturnedRowLimit(1).setIsolationLevel(IsolationLevel.SNAPSHOT).build();
final ScanProperties scanProperties = new ScanProperties(executeProperties);
final RecordCursor<FDBStoredRecord<Message>> records;
if (singleRecordTypeWithPrefixKey == null) {
records = scanRecords(null, scanProperties);
} else {
records = scanRecords(TupleRange.allOf(singleRecordTypeWithPrefixKey.getRecordTypeKeyTuple()), null, scanProperties);
}
return records.onNext().thenApply(result -> {
if (result.hasNext()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(KeyValueLogMessage.of("version check scan found non-empty store", subspaceProvider.logKey(), subspaceProvider.toString(context)));
}
return Long.MAX_VALUE;
} else {
if (newStore ? LOGGER.isDebugEnabled() : LOGGER.isInfoEnabled()) {
KeyValueLogMessage msg = KeyValueLogMessage.build("version check scan found empty store", subspaceProvider.logKey(), subspaceProvider.toString(context));
if (newStore) {
LOGGER.debug(msg.toString());
} else {
LOGGER.info(msg.toString());
}
}
return 0L;
}
});
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class FDBRecordVersion method complete.
/**
* Create a <code>FDBRecordVersion</code> that is complete and has the given
* global and local versions. It can then be serialized into bytes by
* combining these bytes in the appropriate way.
*
* @param globalVersion the global version associated with this version
* @param localVersion the local version associated with this version
* @return a new complete <code>FDBRecordVersion</code>
*/
@Nonnull
public static FDBRecordVersion complete(@Nonnull byte[] globalVersion, int localVersion) {
if (globalVersion.length != GLOBAL_VERSION_LENGTH) {
throw new RecordCoreException("Specified global version has invalid length " + globalVersion.length);
}
if (Arrays.equals(INCOMPLETE_GLOBAL_VERSION, globalVersion)) {
throw new RecordCoreException("Specified version has incomplete global version");
}
validateLocalVersion(localVersion);
ByteBuffer buffer = ByteBuffer.allocate(VERSION_LENGTH).order(ByteOrder.BIG_ENDIAN);
buffer.put(globalVersion);
buffer.putShort((short) localVersion);
return new FDBRecordVersion(true, buffer.array(), false);
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class FDBReverseDirectoryCache method putToSubspace.
private CompletableFuture<Void> putToSubspace(@Nonnull FDBRecordContext context, @Nonnull Subspace reverseCacheSubspace, @Nonnull ScopedValue<String> scopedPathString, @Nonnull Long pathValue) {
String pathString = scopedPathString.getData();
Transaction transaction = context.ensureActive();
return transaction.snapshot().get(reverseCacheSubspace.pack(pathValue)).thenApply(valueBytes -> {
if (valueBytes != null) {
String readValue = Tuple.fromBytes(valueBytes).getString(0);
if (!readValue.equals(pathString)) {
throw new RecordCoreException("Provided value for path key does not match existing value in reverse directory layer cache").addLogInfo(LogMessageKeys.RESOLVER, scopedPathString.getScope()).addLogInfo(LogMessageKeys.RESOLVER_PATH, pathString).addLogInfo(LogMessageKeys.RESOLVER_KEY, pathValue).addLogInfo(LogMessageKeys.CACHED_KEY, readValue);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Put unnecessary, found path '" + readValue + "'' in reverse lookup for value '" + pathValue + "'");
}
persistentCacheHitCount.incrementAndGet();
logStatsToStoreTimer(context, FDBStoreTimer.Counts.REVERSE_DIR_PERSISTENT_CACHE_HIT_COUNT);
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding '" + pathValue + "' to reverse lookup with key " + pathString);
}
// Take care NOT to place the value in our cache. We don't own the calling context/transaction
// so it is possible it could fail/rollback leaving our cache inconsistent.
transaction.set(reverseCacheSubspace.pack(pathValue), Tuple.from(pathString).pack());
persistentCacheMissCount.incrementAndGet();
logStatsToStoreTimer(context, FDBStoreTimer.Counts.REVERSE_DIR_PERSISTENT_CACHE_MISS_COUNT);
}
return null;
});
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method addConvertRecordVersions.
private void addConvertRecordVersions(@Nonnull List<CompletableFuture<Void>> work) {
if (useOldVersionFormat()) {
throw recordCoreException("attempted to convert record versions when still using older format");
}
final Subspace legacyVersionSubspace = getLegacyVersionSubspace();
// Read all of the keys in the old record version location. For each
// record, copy its version to the new location within the primary record
// subspace. Then once they are all copied, delete the old subspace.
KeyValueCursor kvCursor = KeyValueCursor.Builder.withSubspace(legacyVersionSubspace).setContext(getRecordContext()).setScanProperties(ScanProperties.FORWARD_SCAN).build();
CompletableFuture<Void> workFuture = kvCursor.forEach(kv -> {
final Tuple primaryKey = legacyVersionSubspace.unpack(kv.getKey());
final FDBRecordVersion version = FDBRecordVersion.fromBytes(kv.getValue(), false);
final byte[] newKeyBytes = getSubspace().pack(recordVersionKey(primaryKey));
final byte[] newValueBytes = SplitHelper.packVersion(version);
ensureContextActive().set(newKeyBytes, newValueBytes);
}).thenAccept(ignore -> ensureContextActive().clear(legacyVersionSubspace.range()));
work.add(workFuture);
}
Aggregations