This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-paimon.git
The following commit(s) were added to refs/heads/master by this push:
new fbc07327d [core] Introduce Context for LookupStoreFactory to avoid
writing metadata (#2817)
fbc07327d is described below
commit fbc07327d7796e4f1af57646e75f8540ad664e0f
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Jan 30 17:41:12 2024 +0800
[core] Introduce Context for LookupStoreFactory to avoid writing metadata
(#2817)
---
.../benchmark/LookupBloomFilterBenchmark.java | 3 +-
.../apache/paimon/lookup/LookupStoreFactory.java | 5 +-
.../apache/paimon/lookup/LookupStoreWriter.java | 7 +-
.../org/apache/paimon/lookup/hash/HashContext.java | 62 +++++++
.../paimon/lookup/hash/HashLookupStoreFactory.java | 4 +-
.../paimon/lookup/hash/HashLookupStoreReader.java | 129 +++----------
.../paimon/lookup/hash/HashLookupStoreWriter.java | 143 +++++++--------
.../java/org/apache/paimon/utils/BloomFilter.java | 12 +-
.../apache/paimon/utils/FileBasedBloomFilter.java | 6 +-
.../lookup/hash/HashLookupStoreFactoryTest.java | 200 ++++++++++-----------
.../apache/paimon/mergetree/ContainsLevels.java | 12 +-
.../org/apache/paimon/mergetree/LookupLevels.java | 12 +-
12 files changed, 276 insertions(+), 319 deletions(-)
diff --git
a/paimon-benchmark/paimon-micro-benchmarks/src/test/java/org/apache/paimon/benchmark/LookupBloomFilterBenchmark.java
b/paimon-benchmark/paimon-micro-benchmarks/src/test/java/org/apache/paimon/benchmark/LookupBloomFilterBenchmark.java
index 5b5ea766e..e878301d2 100644
---
a/paimon-benchmark/paimon-micro-benchmarks/src/test/java/org/apache/paimon/benchmark/LookupBloomFilterBenchmark.java
+++
b/paimon-benchmark/paimon-micro-benchmarks/src/test/java/org/apache/paimon/benchmark/LookupBloomFilterBenchmark.java
@@ -137,7 +137,6 @@ public class LookupBloomFilterBenchmark {
for (byte[] input : inputs) {
writer.put(input, value);
}
- writer.close();
- return factory.createReader(file);
+ return factory.createReader(file, writer.close());
}
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreFactory.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreFactory.java
index cde93fef0..c22b8605a 100644
---
a/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreFactory.java
+++
b/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreFactory.java
@@ -42,7 +42,7 @@ public interface LookupStoreFactory {
LookupStoreWriter createWriter(File file, @Nullable BloomFilter.Builder
bloomFilter)
throws IOException;
- LookupStoreReader createReader(File file) throws IOException;
+ LookupStoreReader createReader(File file, Context context) throws
IOException;
static Function<Long, BloomFilter.Builder> bfGenerator(Options options) {
Function<Long, BloomFilter.Builder> bfGenerator = rowCount -> null;
@@ -58,4 +58,7 @@ public interface LookupStoreFactory {
}
return bfGenerator;
}
+
+ /** Context between writer and reader. */
+ interface Context {}
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreWriter.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreWriter.java
index 607681204..60271ce40 100644
---
a/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreWriter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/lookup/LookupStoreWriter.java
@@ -18,12 +18,15 @@
package org.apache.paimon.lookup;
-import java.io.Closeable;
+import org.apache.paimon.lookup.LookupStoreFactory.Context;
+
import java.io.IOException;
/** Writer to prepare binary file. */
-public interface LookupStoreWriter extends Closeable {
+public interface LookupStoreWriter {
/** Put key value to store. */
void put(byte[] key, byte[] value) throws IOException;
+
+ Context close() throws IOException;
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashContext.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashContext.java
new file mode 100644
index 000000000..d7d90badd
--- /dev/null
+++ b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashContext.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.lookup.hash;
+
+import org.apache.paimon.lookup.LookupStoreFactory.Context;
+
+/** Context for {@link HashLookupStoreFactory}. */
+public class HashContext implements Context {
+
+ // is bloom filter enabled
+ final boolean bloomFilterEnabled;
+ // expected entries for bloom filter
+ final long bloomFilterExpectedEntries;
+ // bytes for bloom filter
+ final int bloomFilterBytes;
+
+ // Key count for each key length
+ final int[] keyCounts;
+ // Slot size for each key length
+ final int[] slotSizes;
+ // Number of slots for each key length
+ final int[] slots;
+ // Offset of the index for different key length
+ final int[] indexOffsets;
+ // Offset of the data for different key length
+ final long[] dataOffsets;
+
+ public HashContext(
+ boolean bloomFilterEnabled,
+ long bloomFilterExpectedEntries,
+ int bloomFilterBytes,
+ int[] keyCounts,
+ int[] slotSizes,
+ int[] slots,
+ int[] indexOffsets,
+ long[] dataOffsets) {
+ this.bloomFilterEnabled = bloomFilterEnabled;
+ this.bloomFilterExpectedEntries = bloomFilterExpectedEntries;
+ this.bloomFilterBytes = bloomFilterBytes;
+ this.keyCounts = keyCounts;
+ this.slotSizes = slotSizes;
+ this.slots = slots;
+ this.indexOffsets = indexOffsets;
+ this.dataOffsets = dataOffsets;
+ }
+}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreFactory.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreFactory.java
index e34e97a4e..c4dbcffb9 100644
---
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreFactory.java
+++
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreFactory.java
@@ -41,8 +41,8 @@ public class HashLookupStoreFactory implements
LookupStoreFactory {
}
@Override
- public HashLookupStoreReader createReader(File file) throws IOException {
- return new HashLookupStoreReader(cacheManager, cachePageSize, file);
+ public HashLookupStoreReader createReader(File file, Context context)
throws IOException {
+ return new HashLookupStoreReader(file, (HashContext) context,
cacheManager, cachePageSize);
}
@Override
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreReader.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreReader.java
index aae039f31..19849953a 100644
---
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreReader.java
+++
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreReader.java
@@ -30,16 +30,10 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
-import java.io.BufferedInputStream;
-import java.io.DataInputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
@@ -64,111 +58,41 @@ public class HashLookupStoreReader
private FileBasedRandomInputView inputView;
// Buffers
private final byte[] slotBuffer;
+
@Nullable private FileBasedBloomFilter bloomFilter;
- HashLookupStoreReader(CacheManager cacheManager, int cachePageSize, File
file)
+ HashLookupStoreReader(
+ File file, HashContext context, CacheManager cacheManager, int
cachePageSize)
throws IOException {
// File path
if (!file.exists()) {
throw new FileNotFoundException("File " + file.getAbsolutePath() +
" not found");
}
+
+ keyCounts = context.keyCounts;
+ slots = context.slots;
+ slotSizes = context.slotSizes;
+ int maxSlotSize = 0;
+ for (int slotSize : slotSizes) {
+ maxSlotSize = Math.max(maxSlotSize, slotSize);
+ }
+ slotBuffer = new byte[maxSlotSize];
+ indexOffsets = context.indexOffsets;
+ dataOffsets = context.dataOffsets;
+
LOG.info("Opening file {}", file.getName());
// Create Mapped file in read-only mode
inputView = new FileBasedRandomInputView(file, cacheManager,
cachePageSize);
- // Open file and read metadata
- long createdAt;
- FileInputStream inputStream = new FileInputStream(file);
- DataInputStream dataInputStream = new DataInputStream(new
BufferedInputStream(inputStream));
- // Offset of the index in the channel
- int keyCount;
- int indexOffset;
- long dataOffset;
- boolean bloomFilterEnabled;
- try {
- // Time
- createdAt = dataInputStream.readLong();
-
- // Metadata counters
- keyCount = dataInputStream.readInt();
- // Number of different key length
- int keyLengthCount = dataInputStream.readInt();
- // Max key length
- int maxKeyLength = dataInputStream.readInt();
- // BloomFilter
- bloomFilterEnabled = dataInputStream.readBoolean();
- if (bloomFilterEnabled) {
- long recordCount = dataInputStream.readLong();
- int bfBytes = dataInputStream.readInt();
- // Bloom filter buffer start index
- int bfStartIndex = dataInputStream.readInt();
- // Skip bloom filter buffers
- dataInputStream.skipBytes(bfBytes);
- bloomFilter =
- new FileBasedBloomFilter(
- inputView.file(), cacheManager, recordCount,
bfStartIndex, bfBytes);
- }
-
- // Read offset counts and keys
- indexOffsets = new int[maxKeyLength + 1];
- dataOffsets = new long[maxKeyLength + 1];
- keyCounts = new int[maxKeyLength + 1];
- slots = new int[maxKeyLength + 1];
- slotSizes = new int[maxKeyLength + 1];
-
- int maxSlotSize = 0;
- for (int i = 0; i < keyLengthCount; i++) {
- int keyLength = dataInputStream.readInt();
-
- keyCounts[keyLength] = dataInputStream.readInt();
- slots[keyLength] = dataInputStream.readInt();
- slotSizes[keyLength] = dataInputStream.readInt();
- indexOffsets[keyLength] = dataInputStream.readInt();
- dataOffsets[keyLength] = dataInputStream.readLong();
-
- maxSlotSize = Math.max(maxSlotSize, slotSizes[keyLength]);
- }
-
- slotBuffer = new byte[maxSlotSize];
-
- // Read index offset to resign indexOffsets
- indexOffset = dataInputStream.readInt();
- for (int i = 0; i < indexOffsets.length; i++) {
- indexOffsets[i] = indexOffset + indexOffsets[i];
- }
- // Read data offset to resign dataOffsets
- dataOffset = dataInputStream.readLong();
- for (int i = 0; i < dataOffsets.length; i++) {
- dataOffsets[i] = dataOffset + dataOffsets[i];
- }
- } finally {
- // Close metadata
- dataInputStream.close();
- inputStream.close();
+ if (context.bloomFilterEnabled) {
+ bloomFilter =
+ new FileBasedBloomFilter(
+ inputView.file(),
+ cacheManager,
+ context.bloomFilterExpectedEntries,
+ 0,
+ context.bloomFilterBytes);
}
-
- // logging
- DecimalFormat integerFormat = new DecimalFormat("#,##0.00");
- StringBuilder statMsg = new StringBuilder("Storage metadata\n");
- statMsg.append(" Created at:
").append(formatCreatedAt(createdAt)).append("\n");
- statMsg.append(" Key count: ").append(keyCount).append("\n");
- statMsg.append(" Bloom filter:
").append(bloomFilterEnabled).append("\n");
- for (int i = 0; i < keyCounts.length; i++) {
- if (keyCounts[i] > 0) {
- statMsg.append(" Key count for key length ")
- .append(i)
- .append(": ")
- .append(keyCounts[i])
- .append("\n");
- }
- }
- statMsg.append(" Index size: ")
- .append(integerFormat.format((dataOffset - indexOffset) /
(1024.0 * 1024.0)))
- .append(" Mb\n");
- statMsg.append(" Data size: ")
- .append(integerFormat.format((file.length() - dataOffset) /
(1024.0 * 1024.0)))
- .append(" Mb\n");
- LOG.info(statMsg.toString());
}
@Override
@@ -226,13 +150,6 @@ public class HashLookupStoreReader
return res;
}
- private String formatCreatedAt(long createdAt) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd G 'at'
HH:mm:ss z");
- Calendar cl = Calendar.getInstance();
- cl.setTimeInMillis(createdAt);
- return sdf.format(cl.getTime());
- }
-
@Override
public void close() throws IOException {
inputView.close();
diff --git
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreWriter.java
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreWriter.java
index 972066643..d0d00ba0e 100644
---
a/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreWriter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/lookup/hash/HashLookupStoreWriter.java
@@ -18,6 +18,7 @@
package org.apache.paimon.lookup.hash;
+import org.apache.paimon.lookup.LookupStoreFactory.Context;
import org.apache.paimon.lookup.LookupStoreWriter;
import org.apache.paimon.utils.BloomFilter;
import org.apache.paimon.utils.MurmurHashUtils;
@@ -68,8 +69,6 @@ public class HashLookupStoreWriter implements
LookupStoreWriter {
private int[] lastValuesLength;
// Data length
private long[] dataLengths;
- // Index length
- private long indexesLength;
// Max offset length
private int[] maxOffsetLengths;
// Number of keys
@@ -79,6 +78,7 @@ public class HashLookupStoreWriter implements
LookupStoreWriter {
private int valueCount;
// Number of collisions
private int collisions;
+
@Nullable private final BloomFilter.Builder bloomFilter;
HashLookupStoreWriter(double loadFactor, File file, @Nullable
BloomFilter.Builder bloomFilter)
@@ -157,7 +157,7 @@ public class HashLookupStoreWriter implements
LookupStoreWriter {
}
@Override
- public void close() throws IOException {
+ public Context close() throws IOException {
// Close the data and index streams
for (DataOutputStream dos : dataStreams) {
if (dos != null) {
@@ -177,17 +177,63 @@ public class HashLookupStoreWriter implements
LookupStoreWriter {
// Prepare files to merge
List<File> filesToMerge = new ArrayList<>();
- try {
+ int bloomFilterBytes = bloomFilter == null ? 0 :
bloomFilter.getBuffer().size();
+ HashContext context =
+ new HashContext(
+ bloomFilter != null,
+ bloomFilter == null ? 0 :
bloomFilter.expectedEntries(),
+ bloomFilterBytes,
+ new int[keyCounts.length],
+ new int[keyCounts.length],
+ new int[keyCounts.length],
+ new int[keyCounts.length],
+ new long[keyCounts.length]);
+
+ long indexesLength = bloomFilterBytes;
+ long datasLength = 0;
+ for (int i = 0; i < this.keyCounts.length; i++) {
+ if (this.keyCounts[i] > 0) {
+ // Write the key Count
+ context.keyCounts[i] = keyCounts[i];
+
+ // Write slot count
+ int slots = (int) Math.round(keyCounts[i] / loadFactor);
+ context.slots[i] = slots;
- // Write metadata file
- File metadataFile = new File(tempFolder, "metadata.dat");
- metadataFile.deleteOnExit();
- FileOutputStream metadataOutputStream = new
FileOutputStream(metadataFile);
- DataOutputStream metadataDataOutputStream = new
DataOutputStream(metadataOutputStream);
- writeMetadata(metadataDataOutputStream);
- metadataDataOutputStream.close();
- metadataOutputStream.close();
- filesToMerge.add(metadataFile);
+ // Write slot size
+ int offsetLength = maxOffsetLengths[i];
+ context.slotSizes[i] = i + offsetLength;
+
+ // Write index offset
+ context.indexOffsets[i] = (int) indexesLength;
+
+ // Increment index length
+ indexesLength += (long) (i + offsetLength) * slots;
+
+ // Write data length
+ context.dataOffsets[i] = datasLength;
+
+ // Increment data length
+ datasLength += dataLengths[i];
+ }
+ }
+
+ // adjust data offsets
+ for (int i = 0; i < context.dataOffsets.length; i++) {
+ context.dataOffsets[i] = indexesLength + context.dataOffsets[i];
+ }
+
+ try {
+ // Write bloom filter file
+ if (bloomFilter != null) {
+ File bloomFilterFile = new File(tempFolder, "bloomfilter.dat");
+ bloomFilterFile.deleteOnExit();
+ try (FileOutputStream bfOutputStream = new
FileOutputStream(bloomFilterFile)) {
+ bfOutputStream.write(bloomFilter.getBuffer().getArray());
+ LOG.info("Bloom filter size: {} bytes",
bloomFilter.getBuffer().size());
+ }
+ filesToMerge.add(bloomFilterFile);
+ }
// Build index file
for (int i = 0; i < indexFiles.length; i++) {
@@ -209,82 +255,13 @@ public class HashLookupStoreWriter implements
LookupStoreWriter {
// Merge and write to output
checkFreeDiskSpace(filesToMerge);
mergeFiles(filesToMerge, outputStream);
+ return context;
} finally {
outputStream.close();
cleanup(filesToMerge);
}
}
- private void writeMetadata(DataOutputStream dataOutputStream) throws
IOException {
- // Write time
- dataOutputStream.writeLong(System.currentTimeMillis());
-
- // Prepare
- int keyLengthCount = getNumKeyCount();
- int maxKeyLength = keyCounts.length - 1;
-
- // Write size (number of keys)
- dataOutputStream.writeInt(keyCount);
-
- // Write the number of different key length
- dataOutputStream.writeInt(keyLengthCount);
-
- // Write the max value for keyLength
- dataOutputStream.writeInt(maxKeyLength);
-
- // Write whether the bloom filter is enabled.
- dataOutputStream.writeBoolean(bloomFilter != null);
-
- if (bloomFilter != null) {
- dataOutputStream.writeLong(bloomFilter.getNumRecords());
- dataOutputStream.writeInt(bloomFilter.getBuffer().size());
-
- // Write the bloom filter start index
- dataOutputStream.writeInt(dataOutputStream.size() + Integer.SIZE /
Byte.SIZE);
-
- dataOutputStream.write(bloomFilter.getBuffer().getArray());
- LOG.info("Bloom filter size: {} bytes",
bloomFilter.getBuffer().size());
- }
-
- // For each keyLength
- long datasLength = 0L;
- for (int i = 0; i < keyCounts.length; i++) {
- if (keyCounts[i] > 0) {
- // Write the key length
- dataOutputStream.writeInt(i);
-
- // Write key count
- dataOutputStream.writeInt(keyCounts[i]);
-
- // Write slot count
- int slots = (int) Math.round(keyCounts[i] / loadFactor);
- dataOutputStream.writeInt(slots);
-
- // Write slot size
- int offsetLength = maxOffsetLengths[i];
- dataOutputStream.writeInt(i + offsetLength);
-
- // Write index offset
- dataOutputStream.writeInt((int) indexesLength);
-
- // Increment index length
- indexesLength += (long) (i + offsetLength) * slots;
-
- // Write data length
- dataOutputStream.writeLong(datasLength);
-
- // Increment data length
- datasLength += dataLengths[i];
- }
- }
-
- // Write the position of the index and the data
- int indexOffset =
- dataOutputStream.size() + (Integer.SIZE / Byte.SIZE) +
(Long.SIZE / Byte.SIZE);
- dataOutputStream.writeInt(indexOffset);
- dataOutputStream.writeLong(indexOffset + indexesLength);
- }
-
private File buildIndex(int keyLength) throws IOException {
long count = keyCounts[keyLength];
int slots = (int) Math.round(count / loadFactor);
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter.java
b/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter.java
index d4fee8ee5..3c5d4e3bc 100644
--- a/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter.java
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/BloomFilter.java
@@ -125,13 +125,13 @@ public class BloomFilter {
private final MemorySegment buffer;
private final BloomFilter filter;
- private final long numRecords;
+ private final long expectedEntries;
- Builder(MemorySegment buffer, long numRecords) {
+ Builder(MemorySegment buffer, long expectedEntries) {
this.buffer = buffer;
- this.filter = new BloomFilter(numRecords, buffer.size());
+ this.filter = new BloomFilter(expectedEntries, buffer.size());
filter.setMemorySegment(buffer, 0);
- this.numRecords = numRecords;
+ this.expectedEntries = expectedEntries;
}
public boolean testHash(int hash) {
@@ -146,8 +146,8 @@ public class BloomFilter {
return buffer;
}
- public long getNumRecords() {
- return numRecords;
+ public long expectedEntries() {
+ return expectedEntries;
}
@VisibleForTesting
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/FileBasedBloomFilter.java
b/paimon-common/src/main/java/org/apache/paimon/utils/FileBasedBloomFilter.java
index 84fa35cfc..d6ce54bf3 100644
---
a/paimon-common/src/main/java/org/apache/paimon/utils/FileBasedBloomFilter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/utils/FileBasedBloomFilter.java
@@ -43,13 +43,13 @@ public class FileBasedBloomFilter {
public FileBasedBloomFilter(
RandomAccessFile file,
CacheManager cacheManager,
- long numRecords,
+ long expectedEntries,
long readOffset,
int readLength) {
this.file = file;
this.cacheManager = cacheManager;
- checkArgument(numRecords >= 0);
- this.filter = new BloomFilter(numRecords, readLength);
+ checkArgument(expectedEntries >= 0);
+ this.filter = new BloomFilter(expectedEntries, readLength);
this.readOffset = readOffset;
this.readLength = readLength;
this.accessCount = 0;
diff --git
a/paimon-common/src/test/java/org/apache/paimon/lookup/hash/HashLookupStoreFactoryTest.java
b/paimon-common/src/test/java/org/apache/paimon/lookup/hash/HashLookupStoreFactoryTest.java
index a2633ff36..c7f386950 100644
---
a/paimon-common/src/test/java/org/apache/paimon/lookup/hash/HashLookupStoreFactoryTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/lookup/hash/HashLookupStoreFactoryTest.java
@@ -24,24 +24,29 @@ package org.apache.paimon.lookup.hash;
import org.apache.paimon.io.DataOutputSerializer;
import org.apache.paimon.io.cache.CacheManager;
+import org.apache.paimon.lookup.LookupStoreFactory.Context;
import org.apache.paimon.options.MemorySize;
+import
org.apache.paimon.testutils.junit.parameterized.ParameterizedTestExtension;
+import org.apache.paimon.testutils.junit.parameterized.Parameters;
import org.apache.paimon.utils.BloomFilter;
import org.apache.paimon.utils.MathUtils;
import org.apache.paimon.utils.VarLengthIntUtils;
import org.apache.commons.math3.random.RandomDataGenerator;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestTemplate;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -51,6 +56,7 @@ import java.util.concurrent.ThreadLocalRandom;
import static org.assertj.core.api.Assertions.assertThat;
/** Test for {@link HashLookupStoreFactory}. */
+@ExtendWith(ParameterizedTestExtension.class)
public class HashLookupStoreFactoryTest {
@TempDir Path tempDir;
@@ -58,9 +64,21 @@ public class HashLookupStoreFactoryTest {
private final RandomDataGenerator random = new RandomDataGenerator();
private final int pageSize = 1024;
+ private final boolean enableBloomFilter;
+
private File file;
private HashLookupStoreFactory factory;
+ public HashLookupStoreFactoryTest(List<Object> var) {
+ this.enableBloomFilter = (Boolean) var.get(0);
+ }
+
+ @SuppressWarnings("unused")
+ @Parameters(name = "enableBf-{0}")
+ public static List<List<Object>> getVarSeg() {
+ return Arrays.asList(Collections.singletonList(true),
Collections.singletonList(false));
+ }
+
@BeforeEach
public void setUp() throws IOException {
this.factory =
@@ -72,10 +90,6 @@ public class HashLookupStoreFactoryTest {
}
}
- public static Object[] enableBloomFilter() {
- return new Object[] {false, true};
- }
-
private BloomFilter.Builder createBloomFiler(boolean enabled) {
if (!enabled) {
return null;
@@ -91,46 +105,43 @@ public class HashLookupStoreFactoryTest {
return str.getBytes(StandardCharsets.UTF_8);
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testEmpty(boolean enableBloomFilter) throws IOException {
+ @TestTemplate
+ public void testEmpty() throws IOException {
HashLookupStoreWriter writer =
factory.createWriter(file,
createBloomFiler(enableBloomFilter));
- writer.close();
+ Context context = writer.close();
assertThat(file.exists()).isTrue();
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
assertThat(reader.lookup(toBytes(1))).isNull();
reader.close();
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testOneKey(boolean enableBloomFilter) throws IOException {
+ @TestTemplate
+ public void testOneKey() throws IOException {
HashLookupStoreWriter writer =
factory.createWriter(file,
createBloomFiler(enableBloomFilter));
writer.put(toBytes(1), toBytes("foo"));
- writer.close();
+ Context context = writer.close();
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
assertThat(reader.lookup(toBytes(1))).isEqualTo(toBytes("foo"));
reader.close();
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testTwoFirstKeyLength(boolean enableBloomFilter) throws
IOException {
+ @TestTemplate
+ public void testTwoFirstKeyLength() throws IOException {
int key1 = 1;
int key2 = 245;
// Write
- writeStore(enableBloomFilter, file, new Object[] {key1, key2}, new
Object[] {1, 6});
+ Context context = writeStore(file, new Object[] {key1, key2}, new
Object[] {1, 6});
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
assertThat(reader.lookup(toBytes(key1))).isEqualTo(toBytes(1));
assertThat(reader.lookup((toBytes(key2)))).isEqualTo(toBytes(6));
assertThat(reader.lookup(toBytes(0))).isNull();
@@ -140,17 +151,16 @@ public class HashLookupStoreFactoryTest {
assertThat(reader.lookup(toBytes(1245))).isNull();
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testKeyLengthGap(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testKeyLengthGap() throws IOException {
int key1 = 1;
int key2 = 2450;
// Write
- writeStore(enableBf, file, new Object[] {key1, key2}, new Object[] {1,
6});
+ Context context = writeStore(file, new Object[] {key1, key2}, new
Object[] {1, 6});
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
assertThat(reader.lookup(toBytes(key1))).isEqualTo(toBytes(1));
assertThat(reader.lookup((toBytes(key2)))).isEqualTo(toBytes(6));
assertThat(reader.lookup(toBytes(0))).isNull();
@@ -162,17 +172,16 @@ public class HashLookupStoreFactoryTest {
assertThat(reader.lookup(toBytes(2454441))).isNull();
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testKeyLengthStartTwo(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testKeyLengthStartTwo() throws IOException {
int key1 = 245;
int key2 = 2450;
// Write
- writeStore(enableBf, file, new Object[] {key1, key2}, new Object[] {1,
6});
+ Context context = writeStore(file, new Object[] {key1, key2}, new
Object[] {1, 6});
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
assertThat(reader.lookup(toBytes(key1))).isEqualTo(toBytes(1));
assertThat(reader.lookup((toBytes(key2)))).isEqualTo(toBytes(6));
assertThat(reader.lookup(toBytes(6))).isNull();
@@ -183,9 +192,8 @@ public class HashLookupStoreFactoryTest {
assertThat(reader.lookup(toBytes(2454441))).isNull();
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testDataOnTwoBuffers(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testDataOnTwoBuffers() throws IOException {
Object[] keys = new Object[] {1, 2, 3};
Object[] values =
new Object[] {
@@ -200,18 +208,17 @@ public class HashLookupStoreFactoryTest {
new HashLookupStoreFactory(
new CacheManager(MemorySize.ofMebiBytes(1)), pageSize,
0.75d);
- writeStore(factory, enableBf, file, keys, values);
+ Context context = writeStore(factory, file, keys, values);
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
for (int i = 0; i < keys.length; i++) {
assertThat(reader.lookup(toBytes(keys[i]))).isEqualTo(toBytes(values[i]));
}
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testDataSizeOnTwoBuffers(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testDataSizeOnTwoBuffers() throws IOException {
Object[] keys = new Object[] {1, 2, 3};
Object[] values =
new Object[] {
@@ -231,71 +238,61 @@ public class HashLookupStoreFactoryTest {
new HashLookupStoreFactory(
new CacheManager(MemorySize.ofMebiBytes(1)), pageSize,
0.75d);
- writeStore(enableBf, file, keys, values);
+ Context context = writeStore(file, keys, values);
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
for (int i = 0; i < keys.length; i++) {
assertThat(reader.lookup(toBytes(keys[i]))).isEqualTo(toBytes(values[i]));
}
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadStringToString(boolean enableBf) throws IOException {
- testReadKeyToString(generateStringKeys(100), enableBf);
+ @TestTemplate
+ public void testReadStringToString() throws IOException {
+ testReadKeyToString(generateStringKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadIntToString(boolean enableBf) throws IOException {
- testReadKeyToString(generateIntKeys(100), enableBf);
+ @TestTemplate
+ public void testReadIntToString() throws IOException {
+ testReadKeyToString(generateIntKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadDoubleToString(boolean enableBf) throws IOException {
- testReadKeyToString(generateDoubleKeys(100), enableBf);
+ @TestTemplate
+ public void testReadDoubleToString() throws IOException {
+ testReadKeyToString(generateDoubleKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadLongToString(boolean enableBf) throws IOException {
- testReadKeyToString(generateLongKeys(100), enableBf);
+ @TestTemplate
+ public void testReadLongToString() throws IOException {
+ testReadKeyToString(generateLongKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadStringToInt(boolean enableBf) throws IOException {
- testReadKeyToInt(generateStringKeys(100), enableBf);
+ @TestTemplate
+ public void testReadStringToInt() throws IOException {
+ testReadKeyToInt(generateStringKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadByteToInt(boolean enableBf) throws IOException {
- testReadKeyToInt(generateByteKeys(100), enableBf);
+ @TestTemplate
+ public void testReadByteToInt() throws IOException {
+ testReadKeyToInt(generateByteKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadIntToInt(boolean enableBf) throws IOException {
- testReadKeyToInt(generateIntKeys(100), enableBf);
+ @TestTemplate
+ public void testReadIntToInt() throws IOException {
+ testReadKeyToInt(generateIntKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadCompoundToString(boolean enableBf) throws IOException {
- testReadKeyToString(generateCompoundKeys(100), enableBf);
+ @TestTemplate
+ public void testReadCompoundToString() throws IOException {
+ testReadKeyToString(generateCompoundKeys(100));
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testReadCompoundByteToString(boolean enableBf) throws
IOException {
- testReadKeyToString(new Object[] {generateCompoundByteKey()},
enableBf);
+ @TestTemplate
+ public void testReadCompoundByteToString() throws IOException {
+ testReadKeyToString(new Object[] {generateCompoundByteKey()});
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testCacheExpiration(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testCacheExpiration() throws IOException {
int len = 1000;
ThreadLocalRandom rnd = ThreadLocalRandom.current();
Object[] keys = new Object[len];
@@ -306,32 +303,31 @@ public class HashLookupStoreFactoryTest {
}
// Write
- writeStore(enableBf, file, keys, values);
+ Context context = writeStore(file, keys, values);
// Read
factory =
new HashLookupStoreFactory(new CacheManager(new
MemorySize(8096)), pageSize, 0.75d);
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
for (int i = 0; i < keys.length; i++) {
assertThat(reader.lookup(toBytes(keys[i]))).isEqualTo(toBytes(values[i]));
}
}
- @ParameterizedTest
- @MethodSource(value = "enableBloomFilter")
- public void testIterate(boolean enableBf) throws IOException {
+ @TestTemplate
+ public void testIterate() throws IOException {
Integer[] keys = generateIntKeys(100);
String[] values = generateStringData(keys.length, 12);
// Write
- writeStore(enableBf, file, keys, values);
+ Context context = writeStore(file, keys, values);
// Sets
Set<Integer> keysSet = new HashSet<>(Arrays.asList(keys));
Set<String> valuesSet = new HashSet<>(Arrays.asList(values));
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
Iterator<Map.Entry<byte[], byte[]>> itr = reader.iterator();
for (int i = 0; i < keys.length; i++) {
assertThat(itr.hasNext()).isTrue();
@@ -351,13 +347,13 @@ public class HashLookupStoreFactoryTest {
// UTILITY
- private void testReadKeyToString(Object[] keys, boolean enableBf) throws
IOException {
+ private void testReadKeyToString(Object[] keys) throws IOException {
// Write
Object[] values = generateStringData(keys.length, 10);
- writeStore(enableBf, file, keys, values);
+ Context context = writeStore(file, keys, values);
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
for (int i = 0; i < keys.length; i++) {
assertThat(reader.lookup(toBytes(keys[i]))).isEqualTo(toBytes(values[i]));
@@ -366,13 +362,13 @@ public class HashLookupStoreFactoryTest {
reader.close();
}
- private void testReadKeyToInt(Object[] keys, boolean enableBf) throws
IOException {
+ private void testReadKeyToInt(Object[] keys) throws IOException {
// Write
Integer[] values = generateIntData(keys.length);
- writeStore(enableBf, file, keys, values);
+ Context context = writeStore(file, keys, values);
// Read
- HashLookupStoreReader reader = factory.createReader(file);
+ HashLookupStoreReader reader = factory.createReader(file, context);
for (int i = 0; i < keys.length; i++) {
assertThat(reader.lookup(toBytes(keys[i]))).isEqualTo(toBytes(values[i]));
@@ -381,23 +377,19 @@ public class HashLookupStoreFactoryTest {
reader.close();
}
- private void writeStore(boolean enableBf, File location, Object[] keys,
Object[] values)
- throws IOException {
- writeStore(factory, enableBf, location, keys, values);
+ private Context writeStore(File location, Object[] keys, Object[] values)
throws IOException {
+ return writeStore(factory, location, keys, values);
}
- private void writeStore(
- HashLookupStoreFactory factory,
- boolean enableBf,
- File location,
- Object[] keys,
- Object[] values)
+ private Context writeStore(
+ HashLookupStoreFactory factory, File location, Object[] keys,
Object[] values)
throws IOException {
- HashLookupStoreWriter writer = factory.createWriter(location,
createBloomFiler(enableBf));
+ HashLookupStoreWriter writer =
+ factory.createWriter(location,
createBloomFiler(enableBloomFilter));
for (int i = 0; i < keys.length; i++) {
writer.put(toBytes(keys[i]), toBytes(values[i]));
}
- writer.close();
+ return writer.close();
}
private Integer[] generateIntKeys(int count) {
diff --git
a/paimon-core/src/main/java/org/apache/paimon/mergetree/ContainsLevels.java
b/paimon-core/src/main/java/org/apache/paimon/mergetree/ContainsLevels.java
index 34f9947ba..cce18363c 100644
--- a/paimon-core/src/main/java/org/apache/paimon/mergetree/ContainsLevels.java
+++ b/paimon-core/src/main/java/org/apache/paimon/mergetree/ContainsLevels.java
@@ -154,10 +154,10 @@ public class ContainsLevels implements
Levels.DropFileCallback, Closeable {
if (!localFile.createNewFile()) {
throw new IOException("Can not create new file: " + localFile);
}
- try (LookupStoreWriter kvWriter =
- lookupStoreFactory.createWriter(
- localFile, bfGenerator.apply(file.rowCount()));
- RecordReader<KeyValue> reader = fileReaderFactory.apply(file))
{
+ LookupStoreWriter kvWriter =
+ lookupStoreFactory.createWriter(localFile,
bfGenerator.apply(file.rowCount()));
+ LookupStoreFactory.Context context;
+ try (RecordReader<KeyValue> reader = fileReaderFactory.apply(file)) {
RecordReader.RecordIterator<KeyValue> batch;
KeyValue kv;
while ((batch = reader.readBatch()) != null) {
@@ -170,9 +170,11 @@ public class ContainsLevels implements
Levels.DropFileCallback, Closeable {
} catch (IOException e) {
FileIOUtils.deleteFileOrDirectory(localFile);
throw e;
+ } finally {
+ context = kvWriter.close();
}
- return new ContainsFile(localFile,
lookupStoreFactory.createReader(localFile));
+ return new ContainsFile(localFile,
lookupStoreFactory.createReader(localFile, context));
}
@Override
diff --git
a/paimon-core/src/main/java/org/apache/paimon/mergetree/LookupLevels.java
b/paimon-core/src/main/java/org/apache/paimon/mergetree/LookupLevels.java
index 9f8b7a196..5420d42a9 100644
--- a/paimon-core/src/main/java/org/apache/paimon/mergetree/LookupLevels.java
+++ b/paimon-core/src/main/java/org/apache/paimon/mergetree/LookupLevels.java
@@ -171,10 +171,10 @@ public class LookupLevels implements
Levels.DropFileCallback, Closeable {
if (!localFile.createNewFile()) {
throw new IOException("Can not create new file: " + localFile);
}
- try (LookupStoreWriter kvWriter =
- lookupStoreFactory.createWriter(
- localFile, bfGenerator.apply(file.rowCount()));
- RecordReader<KeyValue> reader = fileReaderFactory.apply(file))
{
+ LookupStoreWriter kvWriter =
+ lookupStoreFactory.createWriter(localFile,
bfGenerator.apply(file.rowCount()));
+ LookupStoreFactory.Context context;
+ try (RecordReader<KeyValue> reader = fileReaderFactory.apply(file)) {
DataOutputSerializer valueOut = new DataOutputSerializer(32);
RecordReader.RecordIterator<KeyValue> batch;
KeyValue kv;
@@ -193,9 +193,11 @@ public class LookupLevels implements
Levels.DropFileCallback, Closeable {
} catch (IOException e) {
FileIOUtils.deleteFileOrDirectory(localFile);
throw e;
+ } finally {
+ context = kvWriter.close();
}
- return new LookupFile(localFile, file,
lookupStoreFactory.createReader(localFile));
+ return new LookupFile(localFile, file,
lookupStoreFactory.createReader(localFile, context));
}
@Override