Skip to content

Commit e7d33c2

Browse files
committed
Initial pass at KeySpacePath.importData
1 parent 8e813ed commit e7d33c2

File tree

5 files changed

+889
-0
lines changed

5 files changed

+889
-0
lines changed

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/KeySpacePath.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,4 +580,20 @@ default List<ResolvedKeySpacePath> listSubdirectory(@Nonnull FDBRecordContext co
580580
RecordCursor<DataInKeySpacePath> exportAllData(@Nonnull FDBRecordContext context,
581581
@Nullable byte[] continuation,
582582
@Nonnull ScanProperties scanProperties);
583+
584+
/**
585+
* Imports the provided data exported via {@link #exportAllData} into this {@code KeySpacePath}.
586+
* This will validate that any data provided in {@code dataToImport} has a path that should be in this path,
587+
* or one of the sub-directories, if not the future will complete exceptionally with
588+
* {@link RecordCoreIllegalImportDataException}.
589+
* If there is any data already existing under this path, the new data will overwrite if the keys are the same.
590+
* This will use the logical value in the {@link DataInKeySpacePath#getResolvedPath()} to determine the key, rather
591+
* than the raw key, meaning that this will work even if the data was exported from a different cluster.
592+
* @param context the transaction context in which to save the data
593+
* @param dataToImport the data to be saved to the database
594+
* @return a future to be completed once all data has been important.
595+
*/
596+
@API(API.Status.EXPERIMENTAL)
597+
CompletableFuture<Void> importData(@Nonnull FDBRecordContext context,
598+
@Nonnull Iterable<DataInKeySpacePath> dataToImport);
583599
}

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/KeySpacePathImpl.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.apple.foundationdb.subspace.Subspace;
3333
import com.apple.foundationdb.tuple.ByteArrayUtil;
3434
import com.apple.foundationdb.tuple.Tuple;
35+
import com.apple.foundationdb.tuple.TupleHelpers;
3536
import com.google.common.collect.Lists;
3637

3738
import javax.annotation.Nonnull;
@@ -356,6 +357,43 @@ public RecordCursor<DataInKeySpacePath> exportAllData(@Nonnull FDBRecordContext
356357
.map(keyValue -> new DataInKeySpacePath(this, keyValue, context));
357358
}
358359

360+
@Nonnull
361+
@Override
362+
public CompletableFuture<Void> importData(@Nonnull FDBRecordContext context,
363+
@Nonnull Iterable<DataInKeySpacePath> dataToImport) {
364+
return toTupleAsync(context).thenCompose(targetTuple -> {
365+
List<CompletableFuture<Void>> importFutures = new ArrayList<>();
366+
367+
for (DataInKeySpacePath dataItem : dataToImport) {
368+
CompletableFuture<Void> importFuture = dataItem.getResolvedPath().thenCompose(resolvedPath -> {
369+
// Validate that this data belongs under this path
370+
Tuple itemTuple = resolvedPath.toTuple();
371+
if (!TupleHelpers.isPrefix(targetTuple, itemTuple)) {
372+
throw new RecordCoreIllegalImportDataException(
373+
"Data item path does not belong under target path",
374+
"target", targetTuple, "item", itemTuple);
375+
}
376+
377+
// Reconstruct the key using logical values from the resolved path
378+
Tuple keyTuple = itemTuple;
379+
if (resolvedPath.getRemainder() != null) {
380+
keyTuple = keyTuple.addAll(resolvedPath.getRemainder());
381+
}
382+
383+
// Store the data
384+
byte[] keyBytes = keyTuple.pack();
385+
byte[] valueBytes = dataItem.getRawKeyValue().getValue();
386+
context.ensureActive().set(keyBytes, valueBytes);
387+
388+
return AsyncUtil.DONE;
389+
});
390+
importFutures.add(importFuture);
391+
}
392+
393+
return AsyncUtil.whenAll(importFutures);
394+
});
395+
}
396+
359397
/**
360398
* Returns this path properly wrapped in whatever implementation the directory the path is contained in dictates.
361399
*/

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/keyspace/KeySpacePathWrapper.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,11 @@ public RecordCursor<DataInKeySpacePath> exportAllData(@Nonnull FDBRecordContext
234234
@Nonnull ScanProperties scanProperties) {
235235
return inner.exportAllData(context, continuation, scanProperties);
236236
}
237+
238+
@Nonnull
239+
@Override
240+
public CompletableFuture<Void> importData(@Nonnull FDBRecordContext context,
241+
@Nonnull Iterable<DataInKeySpacePath> dataToImport) {
242+
return inner.importData(context, dataToImport);
243+
}
237244
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* RecordCoreIllegalImportDataException.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.record.provider.foundationdb.keyspace;
22+
23+
import com.apple.foundationdb.record.RecordCoreArgumentException;
24+
25+
import javax.annotation.Nonnull;
26+
27+
public class RecordCoreIllegalImportDataException extends RecordCoreArgumentException {
28+
private static final long serialVersionUID = 1L;
29+
30+
public RecordCoreIllegalImportDataException(@Nonnull final String msg, @Nonnull final Object... keyValue) {
31+
super(msg, keyValue);
32+
}
33+
}

0 commit comments

Comments
 (0)