cfDescriptors = new ArrayList<>();
- this.compactionFilterFactory = new ConsumeQueueCompactionFilterFactory(messageStore::getMinPhyOffset);
-
- ColumnFamilyOptions cqCfOptions = RocksDBOptionsFactory.createCQCFOptions(this.messageStore, this.compactionFilterFactory);
+ ColumnFamilyOptions cqCfOptions = RocksDBOptionsFactory.createCQCFOptions(this.messageStore);
this.cfOptions.add(cqCfOptions);
cfDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cqCfOptions));
ColumnFamilyOptions offsetCfOptions = RocksDBOptionsFactory.createOffsetCFOptions();
this.cfOptions.add(offsetCfOptions);
cfDescriptors.add(new ColumnFamilyDescriptor(OFFSET_COLUMN_FAMILY, offsetCfOptions));
+
+ if (CqCompactionFilterJni.isLoaded()) {
+ CqCompactionFilterJni.createAndSetFilter(cqCfOptions);
+ CqCompactionFilterJni.setMinPhyOffset(messageStore.getMinPhyOffset());
+ log.info("CqCompactionFilter created and set, minPhyOffset: {}", messageStore.getMinPhyOffset());
+ } else {
+ log.warn("CqCompactionFilterJni native library not loaded, compaction filter will not be installed");
+ }
+
open(cfDescriptors);
this.defaultCFHandle = cfHandles.get(0);
this.offsetCFHandle = cfHandles.get(1);
} catch (final Exception e) {
- LOGGER.error("postLoad Failed. {}", this.dbPath, e);
+ log.error("postLoad Failed. {}", this.dbPath, e);
return false;
}
return true;
@@ -91,11 +102,6 @@ protected void preShutdown() {
if (this.offsetCFHandle != null) {
this.offsetCFHandle.close();
}
-
- if (this.compactionFilterFactory != null) {
- this.compactionFilterFactory.close();
- }
-
}
public byte[] getCQ(final byte[] keyBytes) throws RocksDBException {
@@ -116,10 +122,13 @@ public void batchPut(final WriteBatch batch) throws RocksDBException {
}
public void manualCompaction(final long minPhyOffset) {
+ if (CqCompactionFilterJni.isLoaded()) {
+ CqCompactionFilterJni.setMinPhyOffset(minPhyOffset);
+ }
try {
- manualCompaction(minPhyOffset, this.compactRangeOptions);
+ super.manualCompaction(this.compactRangeOptions);
} catch (Exception e) {
- LOGGER.error("manualCompaction Failed. minPhyOffset: {}", minPhyOffset, e);
+ log.error("manualCompaction Failed. minPhyOffset: {}", minPhyOffset, e);
}
}
@@ -130,4 +139,41 @@ public RocksIterator seekOffsetCF() {
public ColumnFamilyHandle getOffsetCFHandle() {
return this.offsetCFHandle;
}
+
+ /**
+ * Synchronously trigger compaction with an updated compaction filter threshold.
+ * This method updates the native compaction filter's minPhyOffset and then
+ * performs a full compaction on the default column family.
+ */
+ public void triggerCompactionSync(long minPhyOffset) throws RocksDBException {
+ if (CqCompactionFilterJni.isLoaded()) {
+ CqCompactionFilterJni.setMinPhyOffset(minPhyOffset);
+ }
+ db.compactRange(this.defaultCFHandle);
+ }
+
+ /**
+ * Flush all memtables to SST files.
+ */
+ public void flushAll() throws RocksDBException {
+ try (FlushOptions flushOpts = new FlushOptions()) {
+ flushOpts.setWaitForFlush(true);
+ flush(flushOpts);
+ }
+ }
+
+ /**
+ * Count all entries in the default column family by iterating. O(N), use only in tests.
+ */
+ public long countEntries() {
+ long count = 0;
+ try (RocksIterator iter = db.newIterator(this.defaultCFHandle)) {
+ iter.seekToFirst();
+ while (iter.isValid()) {
+ count++;
+ iter.next();
+ }
+ }
+ return count;
+ }
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/rocksdb/CqCompactionFilterJni.java b/store/src/main/java/org/apache/rocketmq/store/rocksdb/CqCompactionFilterJni.java
new file mode 100644
index 00000000000..d78579f37c1
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/rocksdb/CqCompactionFilterJni.java
@@ -0,0 +1,224 @@
+/*
+ * 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.rocketmq.store.rocksdb;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.rocksdb.ColumnFamilyOptions;
+import org.apache.rocketmq.logging.org.slf4j.Logger;
+import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
+
+public class CqCompactionFilterJni {
+
+ private static final Logger log = LoggerFactory.getLogger(LoggerName.ROCKSDB_LOGGER_NAME);
+
+ private static final AtomicLong NATIVE_FILTER_PTR = new AtomicLong(0);
+ private static volatile boolean loaded = false;
+
+ /** Platform-specific shim library name and extension. */
+ private static final String SHIM_LIB_NAME;
+ private static final String SHIM_LIB_EXTENSION;
+ private static final String ROCKSDB_JNI_LIB_NAME;
+
+ static {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.contains("mac") || os.contains("darwin") || os.contains("osx")) {
+ String arch = System.getProperty("os.arch");
+ SHIM_LIB_NAME = "libcq_compaction_filter.dylib";
+ SHIM_LIB_EXTENSION = ".dylib";
+ ROCKSDB_JNI_LIB_NAME = arch.contains("aarch") || arch.contains("arm")
+ ? "librocksdbjni-osx-aarch64"
+ : "librocksdbjni-osx-x86_64";
+ } else if (os.contains("win")) {
+ SHIM_LIB_NAME = "cq_compaction_filter.dll";
+ SHIM_LIB_EXTENSION = ".dll";
+ ROCKSDB_JNI_LIB_NAME = "librocksdbjni-win64.dll";
+ } else {
+ SHIM_LIB_NAME = "libcq_compaction_filter.so";
+ SHIM_LIB_EXTENSION = ".so";
+ ROCKSDB_JNI_LIB_NAME = "librocksdbjni-linux64.so";
+ }
+ }
+
+ static {
+ loadNativeShim();
+ }
+
+ private static synchronized void loadNativeShim() {
+ if (loaded) {
+ return;
+ }
+
+ // Preload RocksDB's native library so that linked symbols are available
+ // when our compaction filter shim is loaded.
+ String rocksdbDir = ensureRocksDBNativeLoaded();
+
+ String libName = SHIM_LIB_NAME;
+ try (InputStream is = CqCompactionFilterJni.class
+ .getClassLoader().getResourceAsStream("native/" + libName)) {
+ if (is == null) {
+ log.error("[CqCompactionFilterJni] Native library '{}' not found on classpath", libName);
+ return;
+ }
+ File tempLib;
+ if (rocksdbDir != null) {
+ // Extract our shim to the same temp directory as the RocksDB JNI library,
+ // so that the DT_NEEDED / LC_LOAD_DYLIB dependency can be resolved.
+ tempLib = new File(rocksdbDir, libName);
+ } else {
+ // RocksDB was loaded from java.library.path; our shim can go anywhere.
+ tempLib = File.createTempFile("cq_compaction_filter_", SHIM_LIB_EXTENSION);
+ }
+ Files.copy(is, tempLib.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ tempLib.deleteOnExit();
+ System.load(tempLib.getAbsolutePath());
+ loaded = true;
+ log.info("[CqCompactionFilterJni] Native library loaded from classpath: {}", tempLib.getAbsolutePath());
+ } catch (IOException e) {
+ log.error("[CqCompactionFilterJni] Failed to load native shim", e);
+ }
+ }
+
+ /**
+ * Returns whether the native compaction filter shim was successfully loaded.
+ */
+ public static boolean isLoaded() {
+ return loaded;
+ }
+
+ /**
+ * Locates and loads the RocksDB native JNI library, returning the temporary
+ * directory in which it was extracted (or null if loaded from java.library.path).
+ *
+ * This method deliberately uses {@code System.loadLibrary("rocksdbjni")}
+ * rather than {@code RocksDB.loadLibrary()} for the following reasons:
+ *
+ * - Avoid unnecessary side effects — {@code RocksDB.loadLibrary()}
+ * iterates over all compression types (snappy, lz4, zstd, bzip2, etc.)
+ * and attempts to load each one. Those libraries are not needed by this
+ * compaction filter, and the resulting {@code UnsatisfiedLinkError}s slow
+ * down startup and pollute logs.
+ * - Control the temp directory location — The caller needs to know
+ * the directory where the native JNI library was extracted so that
+ * {@code libcq_compaction_filter.so} can be placed alongside it. This is
+ * required for the dynamic linker to resolve the {@code DT_NEEDED}
+ * dependency of the custom shim. {@code RocksDB.loadLibrary()} extracts
+ * to an internal temp directory that is not exposed to callers.
+ * - Avoid class-loading coupling — {@code RocksDB.loadLibrary()}
+ * triggers the full initialization chain of the rocksdbjni Java bindings
+ * (including {@code CompressionType.values()} iteration and a singleton
+ * {@code NativeLibraryLoader} state machine). Loading the custom shim
+ * must complete before any RocksDB Java classes are exercised, to avoid
+ * native symbol resolution race conditions.
+ *
+ *
+ * @return the absolute path of the temporary directory containing the
+ * extracted RocksDB JNI library, or null if the library was loaded
+ * from {@code java.library.path} (in which case no temp directory
+ * is needed for the shim).
+ */
+ private static String ensureRocksDBNativeLoaded() {
+ // Try System.loadLibrary first (works if on java.library.path)
+ try {
+ System.loadLibrary("rocksdbjni");
+ // No temp dir needed since it's on java.library.path
+ return null;
+ } catch (UnsatisfiedLinkError ignored) {
+ // Not on java.library.path, try from JAR
+ }
+
+ // Determine the platform-specific JNI library name from RocksDB's Environment
+ String jniLibName;
+ try {
+ jniLibName = org.rocksdb.util.Environment.getJniLibraryFileName("rocksdb");
+ } catch (Exception e) {
+ jniLibName = ROCKSDB_JNI_LIB_NAME;
+ }
+
+ try (InputStream is = CqCompactionFilterJni.class.getClassLoader().getResourceAsStream(jniLibName)) {
+ if (is == null) {
+ log.error("[CqCompactionFilterJni] RocksDB native library '{}' not found on classpath", jniLibName);
+ return null;
+ }
+ // Create a temp directory and extract the library there.
+ // Our shim will be placed in the same directory so the DT_NEEDED
+ // dependency resolves correctly.
+ File tempDir = Files.createTempDirectory("rocksdb-native").toFile();
+ tempDir.deleteOnExit();
+ File tempLib = new File(tempDir, jniLibName);
+ Files.copy(is, tempLib.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ tempLib.deleteOnExit();
+ System.load(tempLib.getAbsolutePath());
+ return tempDir.getAbsolutePath();
+ } catch (IOException e) {
+ log.error("[CqCompactionFilterJni] Failed to extract RocksDB native library", e);
+ return null;
+ }
+ }
+
+ /**
+ * Create a native CqCompactionFilter instance.
+ * Returns the raw C++ pointer as a jlong.
+ */
+ public static native long createNativeFilter0();
+
+ /**
+ * Update the minPhyOffset threshold on an existing native filter.
+ */
+ public static native void setMinPhyOffset0(long filterPtr, long minPhyOffset);
+
+ /**
+ * Set the native compaction filter on the ColumnFamilyOptions via the
+ * public {@code setCompactionFilter} API.
+ *
+ * The wrapper uses {@code disOwnNativeHandle()} so that closing the
+ * ColumnFamilyOptions does not free the native filter — this prevents
+ * use-after-free when AbstractRocksDBStorage closes options before the DB.
+ */
+ public static void setNativeFilter(ColumnFamilyOptions options, long filterPtr) {
+ NativeCqCompactionFilter filter = new NativeCqCompactionFilter(filterPtr);
+ options.setCompactionFilter(filter);
+ }
+
+ /**
+ * Create the native filter and set it on the ColumnFamilyOptions.
+ * Returns the native pointer for later threshold updates.
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public static long createAndSetFilter(ColumnFamilyOptions options) {
+ long ptr = createNativeFilter0();
+ NATIVE_FILTER_PTR.set(ptr);
+ setNativeFilter(options, ptr);
+ return ptr;
+ }
+
+ /**
+ * Update the minPhyOffset on the current native filter.
+ */
+ public static void setMinPhyOffset(long minPhyOffset) {
+ long ptr = NATIVE_FILTER_PTR.get();
+ if (ptr != 0) {
+ setMinPhyOffset0(ptr, minPhyOffset);
+ log.info("CqCompactionFilter setMinPhyOffset={}", minPhyOffset);
+ }
+ }
+}
\ No newline at end of file
diff --git a/store/src/main/java/org/apache/rocketmq/store/rocksdb/NativeCqCompactionFilter.java b/store/src/main/java/org/apache/rocketmq/store/rocksdb/NativeCqCompactionFilter.java
new file mode 100644
index 00000000000..6a3101c261a
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/rocksdb/NativeCqCompactionFilter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.rocketmq.store.rocksdb;
+
+import org.rocksdb.AbstractCompactionFilter;
+import org.rocksdb.Slice;
+
+/**
+ * Thin Java wrapper around a native CqCompactionFilter C++ pointer.
+ *
+ * The native filter is allocated by {@link CqCompactionFilterJni#createNativeFilter0()}
+ * and its lifetime is managed externally (it lives for the entire JVM session).
+ * {@link #disOwnNativeHandle()} is called so that {@code close()} does not
+ * free the native memory — this is critical because {@code AbstractRocksDBStorage}
+ * closes {@code ColumnFamilyOptions} (which closes this filter) before closing
+ * the DB, while background compaction threads may still reference the filter.
+ */
+class NativeCqCompactionFilter extends AbstractCompactionFilter {
+
+ NativeCqCompactionFilter(long nativeHandle) {
+ super(nativeHandle);
+ disOwnNativeHandle();
+ }
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/rocksdb/RocksDBOptionsFactory.java b/store/src/main/java/org/apache/rocketmq/store/rocksdb/RocksDBOptionsFactory.java
index b74cf8c85d5..37eec67d357 100644
--- a/store/src/main/java/org/apache/rocketmq/store/rocksdb/RocksDBOptionsFactory.java
+++ b/store/src/main/java/org/apache/rocketmq/store/rocksdb/RocksDBOptionsFactory.java
@@ -41,8 +41,7 @@
public class RocksDBOptionsFactory {
- public static ColumnFamilyOptions createCQCFOptions(final MessageStore messageStore,
- ConsumeQueueCompactionFilterFactory consumeQueueCompactionFilterFactory) {
+ public static ColumnFamilyOptions createCQCFOptions(final MessageStore messageStore) {
BlockBasedTableConfig blockBasedTableConfig = new BlockBasedTableConfig().
setFormatVersion(5).
setIndexType(IndexType.kBinarySearch).
@@ -93,7 +92,6 @@ public static ColumnFamilyOptions createCQCFOptions(final MessageStore messageSt
setTargetFileSizeBase(256 * SizeUnit.MB).
setTargetFileSizeMultiplier(2).
setMergeOperator(new StringAppendOperator()).
- setCompactionFilterFactory(consumeQueueCompactionFilterFactory).
setReportBgIoStats(true).
setOptimizeFiltersForHits(true);
}
diff --git a/store/src/main/resources/native/cq_compaction_filter.cpp b/store/src/main/resources/native/cq_compaction_filter.cpp
new file mode 100644
index 00000000000..1d0bd84cd90
--- /dev/null
+++ b/store/src/main/resources/native/cq_compaction_filter.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+/*
+ * Native compaction filter for ConsumeQueue entries.
+ *
+ * Subclass rocksdb::CompactionFilter directly, create instances in C++,
+ * and pass the raw C++ pointer as a jlong to Java. Java's
+ * AbstractCompactionFilter(nativeHandle) wraps it seamlessly.
+ *
+ * All rocksdb symbols are declared weak so they resolve at runtime to the
+ * symbols already loaded by the JVM's ClassLoader.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "rocksdb/compaction_filter.h"
+#include "rocksdb/slice.h"
+
+/* ------------------------------------------------------------------ */
+/* Windows stub implementations */
+/* */
+/* On Linux/macOS, ELF/Mach-O shared libraries export all symbols by */
+/* default, so the shim resolves inherited virtual methods from */
+/* librocksdbjni at link time. On Windows, DLLs only export symbols */
+/* marked __declspec(dllexport) — rocksdbjni only exports JNI entry */
+/* points, not internal C++ class methods. We must provide stub */
+/* implementations for the Configurable/Customizable virtual methods */
+/* that appear in CompactionFilter's vtable. These stubs are never */
+/* called at runtime (RocksDB only invokes Filter() and Name() on */
+/* compaction filters), but the linker needs addresses for them. */
+/* ------------------------------------------------------------------ */
+
+#ifdef _WIN32
+
+#include "rocksdb/configurable.h"
+#include "rocksdb/customizable.h"
+#include
+#include
+
+namespace rocksdb {
+
+struct ConfigOptions;
+struct DBOptions;
+struct ColumnFamilyOptions;
+class OptionTypeInfo;
+
+// --- Configurable virtual methods (defined in options/configurable.cc) ---
+
+Status Configurable::GetOption(const ConfigOptions&, const std::string&,
+ std::string*) const {
+ return Status();
+}
+
+bool Configurable::AreEquivalent(const ConfigOptions&, const Configurable*,
+ std::string*) const {
+ return true;
+}
+
+Status Configurable::PrepareOptions(const ConfigOptions&) {
+ return Status();
+}
+
+Status Configurable::ValidateOptions(const DBOptions&,
+ const ColumnFamilyOptions&) const {
+ return Status();
+}
+
+const void* Configurable::GetOptionsPtr(const std::string&) const {
+ return nullptr;
+}
+
+Status Configurable::ParseStringOptions(const ConfigOptions&,
+ const std::string&) {
+ return Status();
+}
+
+Status Configurable::ConfigureOptions(
+ const ConfigOptions&,
+ const std::unordered_map&,
+ std::unordered_map*) {
+ return Status();
+}
+
+Status Configurable::ParseOption(const ConfigOptions&, const OptionTypeInfo&,
+ const std::string&, const std::string&,
+ void*) {
+ return Status();
+}
+
+bool Configurable::OptionsAreEqual(const ConfigOptions&, const OptionTypeInfo&,
+ const std::string&, const void*,
+ const void*, std::string*) const {
+ return true;
+}
+
+std::string Configurable::SerializeOptions(const ConfigOptions&,
+ const std::string&) const {
+ return "";
+}
+
+std::string Configurable::GetOptionName(const std::string& name) const {
+ return name;
+}
+
+// Non-virtual, but referenced by inline code paths
+void Configurable::RegisterOptions(const std::string&, void*,
+ const std::unordered_map*) {}
+
+Status Configurable::ConfigureFromMap(
+ const ConfigOptions&,
+ const std::unordered_map&) {
+ return Status();
+}
+
+Status Configurable::ConfigureFromMap(
+ const ConfigOptions&,
+ const std::unordered_map&,
+ std::unordered_map*) {
+ return Status();
+}
+
+Status Configurable::ConfigureOption(const ConfigOptions&, const std::string&,
+ const std::string&) {
+ return Status();
+}
+
+Status Configurable::ConfigureFromString(const ConfigOptions&,
+ const std::string&) {
+ return Status();
+}
+
+Status Configurable::GetOptionString(const ConfigOptions&,
+ std::string*) const {
+ return Status();
+}
+
+std::string Configurable::ToString(const ConfigOptions&,
+ const std::string&) const {
+ return "";
+}
+
+Status Configurable::GetOptionNames(const ConfigOptions&,
+ std::unordered_set*) const {
+ return Status();
+}
+
+Status Configurable::GetOptionsMap(
+ const std::string&, const std::string&, std::string*,
+ std::unordered_map*) {
+ return Status();
+}
+
+// --- Customizable virtual/override methods (defined in options/customizable.cc) ---
+
+Status Customizable::GetOption(const ConfigOptions&, const std::string&,
+ std::string*) const {
+ return Status();
+}
+
+bool Customizable::AreEquivalent(const ConfigOptions&, const Configurable*,
+ std::string*) const {
+ return true;
+}
+
+std::string Customizable::GetOptionName(const std::string& name) const {
+ return name;
+}
+
+std::string Customizable::SerializeOptions(const ConfigOptions&,
+ const std::string&) const {
+ return "";
+}
+
+std::string Customizable::GenerateIndividualId() const {
+ return "stub";
+}
+
+Status Customizable::GetOptionsMap(
+ const ConfigOptions&, const Customizable*, const std::string&,
+ std::string*, std::unordered_map*) {
+ return Status();
+}
+
+Status Customizable::ConfigureNewObject(
+ const ConfigOptions&, Customizable*,
+ const std::unordered_map&) {
+ return Status();
+}
+
+// --- Status methods (defined in util/status.cc) ---
+
+Status::Status(Code _code, SubCode _subcode, const Slice& msg,
+ const Slice& msg2, Severity sev)
+ : code_(_code), subcode_(_subcode), sev_(sev),
+ retryable_(false), data_loss_(false), scope_(0) {}
+
+std::unique_ptr Status::CopyState(const char* s) {
+ if (s == nullptr) return nullptr;
+ const size_t n = std::strlen(s) + 1;
+ char* result = new char[n];
+ std::memcpy(result, s, n);
+ return std::unique_ptr(result);
+}
+
+std::string Status::ToString() const {
+ return "OK";
+}
+
+} // namespace rocksdb
+
+#endif // _WIN32
+
+/* ------------------------------------------------------------------ */
+/* Our concrete compaction filter */
+/* ------------------------------------------------------------------ */
+
+class CqCompactionFilter : public rocksdb::CompactionFilter {
+public:
+ const char* Name() const override {
+ return "ConsumeQueueCompactionFilter";
+ }
+
+ bool Filter(int /*level*/, const rocksdb::Slice& /*key*/,
+ const rocksdb::Slice& existing_value, std::string* /*new_value*/,
+ bool* /*value_changed*/) const override {
+ static const int CQ_MIN_SIZE = 28;
+ if (existing_value.size() < static_cast(CQ_MIN_SIZE)) {
+ return false;
+ }
+ const unsigned char* data =
+ reinterpret_cast(existing_value.data());
+ int64_t phy_offset =
+ (static_cast(data[0]) << 56) |
+ (static_cast(data[1]) << 48) |
+ (static_cast(data[2]) << 40) |
+ (static_cast(data[3]) << 32) |
+ (static_cast(data[4]) << 24) |
+ (static_cast(data[5]) << 16) |
+ (static_cast(data[6]) << 8) |
+ (static_cast(data[7]));
+
+ int64_t min_offset = min_phy_offset_.load(std::memory_order_relaxed);
+ return phy_offset < min_offset;
+ }
+
+ void SetMinPhyOffset(int64_t offset) {
+ min_phy_offset_.store(offset, std::memory_order_relaxed);
+ }
+
+private:
+ std::atomic min_phy_offset_{0};
+};
+
+/* ------------------------------------------------------------------ */
+/* JNI bindings */
+/* ------------------------------------------------------------------ */
+
+#include
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL
+Java_org_apache_rocketmq_store_rocksdb_CqCompactionFilterJni_createNativeFilter0(
+ JNIEnv* env, jclass clazz) {
+ CqCompactionFilter* filter = new CqCompactionFilter();
+ return reinterpret_cast(filter);
+}
+
+JNIEXPORT void JNICALL
+Java_org_apache_rocketmq_store_rocksdb_CqCompactionFilterJni_setMinPhyOffset0(
+ JNIEnv* env, jclass clazz, jlong filterPtr, jlong minPhyOffset) {
+ CqCompactionFilter* filter = reinterpret_cast(filterPtr);
+ filter->SetMinPhyOffset(minPhyOffset);
+}
+
+} // extern "C"
diff --git a/store/src/main/resources/native/cq_compaction_filter.dll b/store/src/main/resources/native/cq_compaction_filter.dll
new file mode 100755
index 0000000000000000000000000000000000000000..2dc74834f41e6de650a06b5c48e527ded91ecc70
GIT binary patch
literal 136192
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