/** * Converts the tuple produced for this path to a subspace. * * @param context the context in which to resolve the path * @return The subspace from the resolved path. */ default Subspace toSubspace(FDBRecordContext context) { return new Subspace(toTuple(context)); }
@Override public CompletableFuture<Subspace> createOrOpen(Tuple key) throws PersistenceException { Subspace subspace = subspaceByTuple.computeIfAbsent(key, k -> new Subspace(allocateUnusedShortPrefix())); return CompletableFuture.completedFuture(subspace); }
private static CompletableFuture<Boolean> hasConflictAtRoot(Transaction context, Long value) { return hasConflictInSubspace(context, new Subspace(), value); }
public FDBMetaDataStore(@Nonnull FDBRecordContext context, @Nonnull KeySpacePath path) { this(context, new Subspace(path.toTuple(context)), null); }
private ScopedInterningLayer(@Nonnull FDBDatabase database, @Nullable KeySpacePath path, @Nonnull Supplier<Tuple> pathTuple) { super(database, path); if (path == null) { this.baseSubspace = new Subspace(); this.nodeSubspace = new Subspace(GLOBAL_SCOPE_PREFIX_BYTES); this.interningLayer = new StringInterningLayer(nodeSubspace, true); this.infoString = "ScopedInterningLayer:GLOBAL"; } else { this.baseSubspace = new Subspace(pathTuple.get()); this.nodeSubspace = baseSubspace; this.interningLayer = new StringInterningLayer(nodeSubspace, false); this.infoString = "ScopedInterningLayer:" + path.toString(); } this.stateSubspace = nodeSubspace.get(STATE_SUBSPACE_KEY_SUFFIX); this.hashCode = Objects.hash(ScopedInterningLayer.class, baseSubspace, database); }
private ScopedDirectoryLayer(@Nonnull FDBDatabase database, @Nullable KeySpacePath path, @Nonnull Supplier<Tuple> pathTuple) { super(database, path); if (path == null) { this.baseSubspace = new Subspace(); this.contentSubspace = new Subspace(); this.infoString = "ScopedDirectoryLayer:GLOBAL"; } else { this.baseSubspace = new Subspace(pathTuple.get()); this.contentSubspace = new Subspace(RESERVED_CONTENT_SUBSPACE_PREFIX); this.infoString = "ScopedDirectoryLayer:" + path.toString(); } this.nodeSubspace = new Subspace(Bytes.concat(baseSubspace.getKey(), DirectoryLayer.DEFAULT_NODE_SUBSPACE.getKey())); this.directoryLayer = new DirectoryLayer(nodeSubspace, contentSubspace); this.stateSubspace = nodeSubspace.get(STATE_SUBSPACE_KEY_SUFFIX); this.hashCode = Objects.hash(ScopedDirectoryLayer.class, baseSubspace, database); }
private ExtendedDirectoryLayer(@Nonnull FDBDatabase database, @Nullable KeySpacePath path, @Nonnull Supplier<Tuple> pathTuple) { super(database, path); if (path == null) { this.isRootLevel = true; this.baseSubspace = DEFAULT_BASE_SUBSPACE; this.nodeSubspace = DEFAULT_NODE_SUBSPACE; this.contentSubspace = DEFAULT_CONTENT_SUBSPACE; this.infoString = "ExtendedDirectoryLayer:GLOBAL"; } else { this.isRootLevel = false; this.baseSubspace = new Subspace(pathTuple.get()); this.nodeSubspace = new Subspace(Bytes.concat(baseSubspace.getKey(), DirectoryLayer.DEFAULT_NODE_SUBSPACE.getKey())); this.contentSubspace = new Subspace(RESERVED_CONTENT_SUBSPACE_PREFIX); this.infoString = "ExtendedDirectoryLayer:" + path.toString(); } this.stateSubspace = nodeSubspace.get(STATE_SUBSPACE_KEY_SUFFIX); this.hashCode = Objects.hash(ExtendedDirectoryLayer.class, baseSubspace, database); }
public static void deleteStore(FDBRecordContext context, KeySpacePath path) { final Subspace subspace = new Subspace(path.toTuple(context)); deleteStore(context, subspace); }
@Override public JsonPersistence initialize(String defaultNamespace, Map<String, String> configuration, Set<String> managedDomains) { FDB fdb = FDB.selectAPIVersion(520); Database db = fdb.open(); String nodePrefixHex = configuration.get("foundationdb.directory.node-prefix.hex"); if (nodePrefixHex == null || nodePrefixHex.isBlank()) { nodePrefixHex = "0x23"; // default } String contentPrefixHex = configuration.get("foundationdb.directory.content-prefix.hex"); if (contentPrefixHex == null || contentPrefixHex.isBlank()) { contentPrefixHex = "0x24"; // default } int fragmentCapacityBytes = Integer.parseInt(ofNullable(configuration.get("persistence.fragment.capacity")).orElse("8192")); byte[] nodePrefix = hexToBytes(nodePrefixHex); byte[] contentPrefix = hexToBytes(contentPrefixHex); Directory directory = new DirectoryLayer(new Subspace(nodePrefix), new Subspace(contentPrefix)); FoundationDBPersistence persistence = new FoundationDBPersistence(new FoundationDBTransactionFactory(db), new DefaultFoundationDBDirectory(db, directory)); return new BufferedJsonPersistence(new DefaultFlattenedPersistence(persistence, fragmentCapacityBytes), fragmentCapacityBytes); }
private void populate(final FDBRecordContext initialContext, Subspace directory) { // Range for the directory layer. WARNING, this assumes a bunch about the internals of the // directory layer and may want to be re-worked at some point using the DirectoryLayer API's. final byte[] prefix = {(byte) 0xFE}; final Subspace subdirs = new Subspace(Tuple.from(prefix, 0L), prefix); fdb.asyncToSync(initialContext.getTimer(), FDBStoreTimer.Waits.WAIT_REVERSE_DIRECTORY_SCAN, populate(initialContext, subdirs, directory, null)); }
/** * Load serialized byte array that may be split among several keys. * @param tr read transaction * @param context transaction context * @param subspace subspace containing serialized value * @param key key within subspace * @param splitLongRecords <code>true</code> if multiple keys should be used; if <code>false</code>, <code>serialized</code> must fit in a single key * @param missingUnsplitRecordSuffix if <code>splitLongRecords</code> is <code>false</code> and this is <code>true</code>, this will assume keys are missing a suffix for backwards compatibility reasons * @param sizeInfo optional size information to populate * @return the merged byte array */ public static CompletableFuture<FDBRawRecord> loadWithSplit(@Nonnull final ReadTransaction tr, @Nonnull final FDBRecordContext context, @Nonnull final Subspace subspace, @Nonnull final Tuple key, final boolean splitLongRecords, final boolean missingUnsplitRecordSuffix, @Nullable SizeInfo sizeInfo) { if (!splitLongRecords && missingUnsplitRecordSuffix) { return loadUnsplitLegacy(tr, context, subspace, key, sizeInfo); } // Even if long records are not split, then unless we are using the old format, it might be the case // that there is a record version associated with this key, hence the range read. // It is still better to do a range read in that case than two point reads (probably). final long startTime = System.nanoTime(); final byte[] keyBytes = subspace.pack(key); final AsyncIterable<KeyValue> rangeScan = tr.getRange(Range.startsWith(keyBytes), ReadTransaction.ROW_LIMIT_UNLIMITED, false, StreamingMode.WANT_ALL); final AsyncIterator<KeyValue> rangeIter = rangeScan.iterator(); context.instrument(FDBStoreTimer.DetailEvents.GET_RECORD_RANGE_RAW_FIRST_CHUNK, rangeIter.onHasNext(), startTime); return new SingleKeyUnsplitter(context, key, new Subspace(keyBytes), rangeIter, sizeInfo).run(context.getExecutor()); }
@Test public void serializeKey() { List<Tuple> sortedTuples = TEST_TUPLES.stream().sorted().collect(Collectors.toList()); List<Tuple> sortedKeys = TEST_TUPLES.stream() .map(serializer::serializeKey) .sorted(ByteArrayUtil::compareUnsigned) .map(serializer::deserializeKey) .collect(Collectors.toList()); assertEquals(sortedTuples, sortedKeys); // Add a subspace and make sure unpacking by length works. Subspace subspace = new Subspace(Tuple.from("fake", "subspace")); List<Tuple> prefixedTuples = TEST_TUPLES.stream() .map(serializer::serializeKey) .map(b -> ByteArrayUtil.join(subspace.getKey(), b)) .map(data -> serializer.deserializeKey(data, subspace.getKey().length)) .collect(Collectors.toList()); assertEquals(TEST_TUPLES, prefixedTuples); }
? CompletableFuture.completedFuture(new Subspace()) : listFrom.toSubspaceAsync(context);
@Test public void testLocatableDirectoryResolver() { KeySpace keySpace = new KeySpace( new KeySpaceDirectory("path", KeyType.STRING, "path") .addSubdirectory(new KeySpaceDirectory("to", KeyType.STRING, "to") .addSubdirectory(new KeySpaceDirectory("dirLayer", KeyType.STRING, "dirLayer")) ) ); FDBRecordContext context = database.openContext(); KeySpacePath path = keySpace.pathFromKey(context, Tuple.from("path", "to", "dirLayer")); LocatableResolver resolver = scopedDirectoryGenerator.apply(context, path); Long value = resolver.resolve(context.getTimer(), "foo").join(); DirectoryLayer directoryLayer = new DirectoryLayer( new Subspace(Bytes.concat(path.toTuple(context).pack(), DirectoryLayer.DEFAULT_NODE_SUBSPACE.getKey())), path.toSubspace(context)); context = database.openContext(); validate(context, resolver, directoryLayer, "foo", value); DirectoryLayer defaultDirectoryLayer = DirectoryLayer.getDefault(); List<String> defaultDirectories = defaultDirectoryLayer.list(context.ensureActive()).join(); assertThat("entry is not in the default directory layer", defaultDirectories, not(hasItem("foo"))); }