Skip to content

Commit f1cda10

Browse files
committed
inserts and searches work; logic to dump layers works
1 parent 687f562 commit f1cda10

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

fdb-extensions/src/main/java/com/apple/foundationdb/async/hnsw/CompactStorageAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public Iterable<Node<NodeReference>> scanLayer(@Nonnull final ReadTransaction re
165165
return AsyncUtil.mapIterable(itemsIterable, keyValue -> {
166166
final byte[] key = keyValue.getKey();
167167
final byte[] value = keyValue.getValue();
168-
final Tuple primaryKey = getDataSubspace().unpack(key);
168+
final Tuple primaryKey = getDataSubspace().unpack(key).getNestedTuple(1);
169169
return nodeFromRaw(primaryKey, key, value);
170170
});
171171
}

fdb-extensions/src/main/java/com/apple/foundationdb/async/hnsw/HNSW.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,8 @@ private <N extends NodeReference> CompletableFuture<List<NodeReferenceWithDistan
796796
new InsertNeighborsChangeSet<>(new BaseNeighborsChangeSet<>(ImmutableList.of()),
797797
newNode.getNeighbors());
798798

799+
storageAdapter.writeNode(transaction, newNode, layer, newNodeChangeSet);
800+
799801
// create change sets for each selected neighbor and insert new node into them
800802
final Map<Tuple /* primaryKey */, NeighborsChangeSet<N>> neighborChangeSetMap =
801803
Maps.newLinkedHashMap();
@@ -824,7 +826,6 @@ private <N extends NodeReference> CompletableFuture<List<NodeReferenceWithDistan
824826
});
825827
}, MAX_CONCURRENT_NEIGHBOR_FETCHES, getExecutor())
826828
.thenApply(changeSets -> {
827-
storageAdapter.writeNode(transaction, newNode, layer, newNodeChangeSet);
828829
for (int i = 0; i < selectedNeighbors.size(); i++) {
829830
final NodeReferenceAndNode<N> selectedNeighbor = selectedNeighbors.get(i);
830831
final NeighborsChangeSet<N> changeSet = changeSets.get(i);
@@ -895,6 +896,8 @@ private <N extends NodeReference> CompletableFuture<List<NodeReferenceAndNode<N>
895896
if (selectedNeighborNode.getNeighbors().size() < mMax) {
896897
return CompletableFuture.completedFuture(null);
897898
} else {
899+
debug(l -> l.debug("pruning neighborhood of key={} which has numNeighbors={} out of mMax={}",
900+
selectedNeighborNode.getPrimaryKey(), selectedNeighborNode.getNeighbors().size(), mMax));
898901
return fetchNeighborhood(storageAdapter, transaction, layer, neighborChangeSet.merge(), nodeCache)
899902
.thenCompose(nodeReferenceWithVectors -> {
900903
final ImmutableList.Builder<NodeReferenceWithDistance> nodeReferencesWithDistancesBuilder =
@@ -1079,7 +1082,7 @@ public void scanLayer(@Nonnull final Database db,
10791082

10801083
@Nonnull
10811084
private StorageAdapter<? extends NodeReference> getStorageAdapterForLayer(final int layer) {
1082-
return layer > 0
1085+
return false && layer > 0
10831086
? new InliningStorageAdapter(getConfig(), InliningNode.factory(), getSubspace(), getOnWriteListener(), getOnReadListener())
10841087
: new CompactStorageAdapter(getConfig(), CompactNode.factory(), getSubspace(), getOnWriteListener(), getOnReadListener());
10851088
}

fdb-extensions/src/test/java/com/apple/foundationdb/async/hnsw/HNSWModificationTest.java

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@
4242
import org.slf4j.LoggerFactory;
4343

4444
import javax.annotation.Nonnull;
45+
import java.io.BufferedWriter;
46+
import java.io.FileWriter;
47+
import java.io.IOException;
4548
import java.util.ArrayList;
4649
import java.util.Comparator;
4750
import java.util.Random;
51+
import java.util.concurrent.atomic.AtomicLong;
4852

4953
/**
5054
* Tests testing insert/update/deletes of data into/in/from {@link RTree}s.
@@ -147,31 +151,68 @@ public void testInliningSerialization() {
147151
@Test
148152
public void testBasicInsert() {
149153
final Random random = new Random(0);
154+
final AtomicLong nextNodeId = new AtomicLong(0L);
150155
final HNSW hnsw = new HNSW(rtSubspace.getSubspace(), TestExecutors.defaultThreadPool());
151156

152157
db.run(tr -> {
153158
for (int i = 0; i < 10; i ++) {
154-
hnsw.insert(tr, createRandomPrimaryKey(random), createRandomVector(random, 728)).join();
159+
hnsw.insert(tr, createNextPrimaryKey(nextNodeId), createRandomVector(random, 728)).join();
155160
}
156161
return null;
157162
});
158163
}
159164

160165
@Test
161-
public void testBasicInsertAndScanLayer() {
166+
public void testBasicInsertAndScanLayer() throws Exception {
162167
final Random random = new Random(0);
163-
final HNSW hnsw = new HNSW(rtSubspace.getSubspace(), TestExecutors.defaultThreadPool());
168+
final AtomicLong nextNodeId = new AtomicLong(0L);
169+
final HNSW hnsw = new HNSW(rtSubspace.getSubspace(), TestExecutors.defaultThreadPool(),
170+
HNSW.DEFAULT_CONFIG.toBuilder().setM(4).setMMax(4).setMMax0(10).build(),
171+
OnWriteListener.NOOP, OnReadListener.NOOP);
164172

165173
db.run(tr -> {
166-
for (int i = 0; i < 20; i ++) {
167-
hnsw.insert(tr, createRandomPrimaryKey(random), createRandomVector(random, 728)).join();
174+
for (int i = 0; i < 100; i ++) {
175+
hnsw.insert(tr, createNextPrimaryKey(nextNodeId), createRandomVector(random, 2)).join();
168176
}
169177
return null;
170178
});
171179

172-
hnsw.scanLayer(db, 0, 100, node -> {
173-
System.out.println(node);
174-
});
180+
int layer = 0;
181+
while (true) {
182+
if (!dumpLayer(hnsw, layer++)) {
183+
break;
184+
}
185+
}
186+
}
187+
188+
private boolean dumpLayer(final HNSW hnsw, final int layer) throws IOException {
189+
final String verticesFileName = "/Users/nseemann/Downloads/vertices-" + layer + ".csv";
190+
final String edgesFileName = "/Users/nseemann/Downloads/edges-" + layer + ".csv";
191+
192+
final AtomicLong numReadAtomic = new AtomicLong(0L);
193+
try (final BufferedWriter verticesWriter = new BufferedWriter(new FileWriter(verticesFileName));
194+
final BufferedWriter edgesWriter = new BufferedWriter(new FileWriter(edgesFileName))) {
195+
hnsw.scanLayer(db, layer, 100, node -> {
196+
final CompactNode compactNode = node.asCompactNode();
197+
final Vector<Half> vector = compactNode.getVector();
198+
try {
199+
verticesWriter.write(compactNode.getPrimaryKey().getLong(0) + "," +
200+
vector.getComponent(0) + "," +
201+
vector.getComponent(1));
202+
verticesWriter.newLine();
203+
204+
for (final var neighbor : compactNode.getNeighbors()) {
205+
edgesWriter.write(compactNode.getPrimaryKey().getLong(0) + "," +
206+
neighbor.getPrimaryKey().getLong(0));
207+
edgesWriter.newLine();
208+
}
209+
numReadAtomic.getAndIncrement();
210+
} catch (final IOException e) {
211+
throw new RuntimeException("unable to write to file", e);
212+
}
213+
});
214+
}
215+
return numReadAtomic.get() != 0;
175216
}
176217

177218
private <N extends NodeReference> void writeNode(@Nonnull final Transaction transaction,
@@ -227,6 +268,11 @@ private static Tuple createRandomPrimaryKey(final @Nonnull Random random) {
227268
return Tuple.from(random.nextLong());
228269
}
229270

271+
@Nonnull
272+
private static Tuple createNextPrimaryKey(@Nonnull final AtomicLong nextIdAtomic) {
273+
return Tuple.from(nextIdAtomic.getAndIncrement());
274+
}
275+
230276
@Nonnull
231277
private Vector.HalfVector createRandomVector(@Nonnull final Random random, final int dimensionality) {
232278
final Half[] components = new Half[dimensionality];

0 commit comments

Comments
 (0)