This is an automated email from the ASF dual-hosted git repository.

jark pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new 657d19b25 [kv] Add RocksDB memory metrics by component type (#2392)
657d19b25 is described below

commit 657d19b25a4923df055359faecbccb7888b5a866
Author: Yang Wang <[email protected]>
AuthorDate: Sun Feb 1 20:21:09 2026 +0800

    [kv] Add RocksDB memory metrics by component type (#2392)
---
 .../java/org/apache/fluss/metrics/MetricNames.java | 21 ++++++
 .../fluss/server/kv/rocksdb/RocksDBStatistics.java | 82 ++++++++++++++++++++--
 .../server/metrics/group/TableMetricGroup.java     | 32 +++++++++
 .../org/apache/fluss/server/kv/KvTabletTest.java   | 46 ++++++++++++
 .../maintenance/observability/monitor-metrics.md   | 45 +++++++++++-
 5 files changed, 220 insertions(+), 6 deletions(-)

diff --git 
a/fluss-common/src/main/java/org/apache/fluss/metrics/MetricNames.java 
b/fluss-common/src/main/java/org/apache/fluss/metrics/MetricNames.java
index 60eb942e8..454b75bdc 100644
--- a/fluss-common/src/main/java/org/apache/fluss/metrics/MetricNames.java
+++ b/fluss-common/src/main/java/org/apache/fluss/metrics/MetricNames.java
@@ -179,6 +179,27 @@ public class MetricNames {
     /** Total memory usage across all RocksDB instances in this server (Sum 
aggregation). */
     public static final String ROCKSDB_MEMORY_USAGE_TOTAL = 
"rocksdbMemoryUsageTotal";
 
+    // Table-level RocksDB memory metrics (Sum aggregation)
+    /** Total memtable memory usage across all buckets of this table. */
+    public static final String ROCKSDB_MEMTABLE_MEMORY_USAGE_TOTAL =
+            "rocksdbMemTableMemoryUsageTotal";
+
+    /** Total unflushed memtable memory usage across all buckets of this 
table. */
+    public static final String ROCKSDB_MEMTABLE_UNFLUSHED_MEMORY_USAGE_TOTAL =
+            "rocksdbMemTableUnFlushedMemoryUsageTotal";
+
+    /** Total table readers (indexes and filters) memory usage across all 
buckets of this table. */
+    public static final String ROCKSDB_TABLE_READERS_MEMORY_USAGE_TOTAL =
+            "rocksdbTableReadersMemoryUsageTotal";
+
+    /** Total block cache memory usage across all buckets of this table. */
+    public static final String ROCKSDB_BLOCK_CACHE_MEMORY_USAGE_TOTAL =
+            "rocksdbBlockCacheMemoryUsageTotal";
+
+    /** Total pinned memory in block cache across all buckets of this table. */
+    public static final String ROCKSDB_BLOCK_CACHE_PINNED_USAGE_TOTAL =
+            "rocksdbBlockCachePinnedUsageTotal";
+
     // 
--------------------------------------------------------------------------------------------
     // metrics for table bucket
     // 
--------------------------------------------------------------------------------------------
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/kv/rocksdb/RocksDBStatistics.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/kv/rocksdb/RocksDBStatistics.java
index fd7a20cd7..3a4e4e5bf 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/kv/rocksdb/RocksDBStatistics.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/kv/rocksdb/RocksDBStatistics.java
@@ -226,8 +226,6 @@ public class RocksDBStatistics implements AutoCloseable {
                 return 0L;
             }
 
-            // Create cache set for memory usage calculation.
-            // If blockCache is null, pass null to MemoryUtil (will only count 
memtables, etc.)
             Set<Cache> caches = null;
             if (blockCache != null) {
                 caches = new HashSet<>();
@@ -239,9 +237,83 @@ public class RocksDBStatistics implements AutoCloseable {
                             Collections.singletonList(db), caches);
             return 
memoryUsage.values().stream().mapToLong(Long::longValue).sum();
         } catch (Exception e) {
-            LOG.debug(
-                    "Failed to get total memory usage from RocksDB (possibly 
closed or unavailable)",
-                    e);
+            LOG.debug("Failed to get total memory usage from RocksDB", e);
+            return 0L;
+        }
+    }
+
+    // ==================== Memory Metrics by Component ====================
+
+    /**
+     * Get memory usage for all memtables (active and immutable).
+     *
+     * @return memtable memory usage in bytes, or 0 if not available
+     */
+    public long getMemTableMemoryUsage() {
+        return getMemoryUsageByType(MemoryUsageType.kMemTableTotal);
+    }
+
+    /**
+     * Get memory usage for unflushed memtables.
+     *
+     * @return unflushed memtable memory usage in bytes, or 0 if not available
+     */
+    public long getMemTableUnFlushedMemoryUsage() {
+        return getMemoryUsageByType(MemoryUsageType.kMemTableUnFlushed);
+    }
+
+    /**
+     * Get memory usage for table readers (indexes and bloom filters).
+     *
+     * @return table readers memory usage in bytes, or 0 if not available
+     */
+    public long getTableReadersMemoryUsage() {
+        return getMemoryUsageByType(MemoryUsageType.kTableReadersTotal);
+    }
+
+    /**
+     * Get memory usage for block cache via MemoryUtil API.
+     *
+     * @return block cache memory usage in bytes, or 0 if not available
+     */
+    public long getBlockCacheMemoryUsage() {
+        return getMemoryUsageByType(MemoryUsageType.kCacheTotal);
+    }
+
+    /**
+     * Get pinned memory usage in block cache.
+     *
+     * @return pinned memory usage in bytes, or 0 if not available
+     */
+    public long getBlockCachePinnedUsage() {
+        try (ResourceGuard.Lease lease = resourceGuard.acquireResource()) {
+            if (blockCache != null) {
+                return blockCache.getPinnedUsage();
+            }
+        } catch (Exception e) {
+            LOG.debug("Failed to get pinned usage from RocksDB", e);
+        }
+        return 0L;
+    }
+
+    private long getMemoryUsageByType(MemoryUsageType type) {
+        try (ResourceGuard.Lease lease = resourceGuard.acquireResource()) {
+            if (db == null) {
+                return 0L;
+            }
+
+            Set<Cache> caches = null;
+            if (blockCache != null) {
+                caches = new HashSet<>();
+                caches.add(blockCache);
+            }
+
+            Map<MemoryUsageType, Long> memoryUsage =
+                    MemoryUtil.getApproximateMemoryUsageByType(
+                            Collections.singletonList(db), caches);
+            return memoryUsage.getOrDefault(type, 0L);
+        } catch (Exception e) {
+            LOG.debug("Failed to get memory usage for type {} from RocksDB", 
type, e);
             return 0L;
         }
     }
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/metrics/group/TableMetricGroup.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/metrics/group/TableMetricGroup.java
index fc40463f2..5f280c8e4 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/metrics/group/TableMetricGroup.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/metrics/group/TableMetricGroup.java
@@ -356,6 +356,38 @@ public class TableMetricGroup extends AbstractMetricGroup {
                         allRocksDBStatistics()
                                 
.mapToLong(RocksDBStatistics::getCompactionBytesWritten)
                                 .sum());
+
+        // Fine-grained memory metrics - track memory usage by component type
+        gauge(
+                MetricNames.ROCKSDB_MEMTABLE_MEMORY_USAGE_TOTAL,
+                () ->
+                        allRocksDBStatistics()
+                                
.mapToLong(RocksDBStatistics::getMemTableMemoryUsage)
+                                .sum());
+        gauge(
+                MetricNames.ROCKSDB_MEMTABLE_UNFLUSHED_MEMORY_USAGE_TOTAL,
+                () ->
+                        allRocksDBStatistics()
+                                
.mapToLong(RocksDBStatistics::getMemTableUnFlushedMemoryUsage)
+                                .sum());
+        gauge(
+                MetricNames.ROCKSDB_TABLE_READERS_MEMORY_USAGE_TOTAL,
+                () ->
+                        allRocksDBStatistics()
+                                
.mapToLong(RocksDBStatistics::getTableReadersMemoryUsage)
+                                .sum());
+        gauge(
+                MetricNames.ROCKSDB_BLOCK_CACHE_MEMORY_USAGE_TOTAL,
+                () ->
+                        allRocksDBStatistics()
+                                
.mapToLong(RocksDBStatistics::getBlockCacheMemoryUsage)
+                                .sum());
+        gauge(
+                MetricNames.ROCKSDB_BLOCK_CACHE_PINNED_USAGE_TOTAL,
+                () ->
+                        allRocksDBStatistics()
+                                
.mapToLong(RocksDBStatistics::getBlockCachePinnedUsage)
+                                .sum());
     }
 
     /** Metric group for specific kind of tablet of a table. */
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/kv/KvTabletTest.java 
b/fluss-server/src/test/java/org/apache/fluss/server/kv/KvTabletTest.java
index 1f0a8ec06..582d27ece 100644
--- a/fluss-server/src/test/java/org/apache/fluss/server/kv/KvTabletTest.java
+++ b/fluss-server/src/test/java/org/apache/fluss/server/kv/KvTabletTest.java
@@ -1569,6 +1569,45 @@ class KvTabletTest {
                 .as("Total memory usage must be positive")
                 .isGreaterThan(0);
 
+        // ========== Phase 5.1: Verify Fine-Grained Memory Metrics ==========
+        // All fine-grained memory metrics should be non-negative
+        long memTableUsage = statistics.getMemTableMemoryUsage();
+        assertThat(memTableUsage)
+                .as("MemTable memory usage should be non-negative")
+                .isGreaterThanOrEqualTo(0);
+
+        long memTableUnFlushedUsage = 
statistics.getMemTableUnFlushedMemoryUsage();
+        assertThat(memTableUnFlushedUsage)
+                .as("Unflushed memtable memory usage should be non-negative")
+                .isGreaterThanOrEqualTo(0);
+
+        long tableReadersUsage = statistics.getTableReadersMemoryUsage();
+        assertThat(tableReadersUsage)
+                .as("Table readers memory usage should be non-negative")
+                .isGreaterThanOrEqualTo(0);
+
+        long blockCacheMemoryUsage = statistics.getBlockCacheMemoryUsage();
+        assertThat(blockCacheMemoryUsage)
+                .as("Block cache memory usage should be non-negative")
+                .isGreaterThanOrEqualTo(0);
+
+        long blockCachePinnedUsage = statistics.getBlockCachePinnedUsage();
+        assertThat(blockCachePinnedUsage)
+                .as("Block cache pinned usage should be non-negative")
+                .isGreaterThanOrEqualTo(0);
+
+        // Total memory usage should be at least as large as any individual 
component
+        long totalMemoryUsage = statistics.getTotalMemoryUsage();
+        assertThat(totalMemoryUsage)
+                .as("Total memory should be at least as large as memtable 
usage")
+                .isGreaterThanOrEqualTo(memTableUsage);
+        assertThat(totalMemoryUsage)
+                .as("Total memory should be at least as large as table readers 
usage")
+                .isGreaterThanOrEqualTo(tableReadersUsage);
+        assertThat(totalMemoryUsage)
+                .as("Total memory should be at least as large as block cache 
usage")
+                .isGreaterThanOrEqualTo(blockCacheMemoryUsage);
+
         // ========== Phase 6: Verify Metrics After Close ==========
         kvTablet.close();
 
@@ -1585,5 +1624,12 @@ class KvTabletTest {
         assertThat(statistics.getCompactionBytesWritten()).isEqualTo(0);
         assertThat(statistics.getCompactionTimeMicros()).isEqualTo(0);
         assertThat(statistics.getTotalMemoryUsage()).isEqualTo(0);
+
+        // Fine-grained memory metrics should also return 0 after close
+        assertThat(statistics.getMemTableMemoryUsage()).isEqualTo(0);
+        assertThat(statistics.getMemTableUnFlushedMemoryUsage()).isEqualTo(0);
+        assertThat(statistics.getTableReadersMemoryUsage()).isEqualTo(0);
+        assertThat(statistics.getBlockCacheMemoryUsage()).isEqualTo(0);
+        assertThat(statistics.getBlockCachePinnedUsage()).isEqualTo(0);
     }
 }
diff --git a/website/docs/maintenance/observability/monitor-metrics.md 
b/website/docs/maintenance/observability/monitor-metrics.md
index 65109551c..4b082b2f2 100644
--- a/website/docs/maintenance/observability/monitor-metrics.md
+++ b/website/docs/maintenance/observability/monitor-metrics.md
@@ -948,7 +948,50 @@ These metrics use Sum aggregation to show the total value 
across all tables in a
       <th rowspan="1"><strong>tabletserver</strong></th>
       <td style={{textAlign: 'center', verticalAlign: 'middle' }} 
rowspan="1">-</td>
       <td>rocksdbMemoryUsageTotal</td>
-      <td>Total memory usage across all RocksDB instances in this server (in 
bytes). This includes memory used by memtables, block cache, and other RocksDB 
internal structures.</td>
+      <td>Total memory usage across all RocksDB instances in this server (in 
bytes).</td>
+      <td>Gauge</td>
+    </tr>
+  </tbody>
+</table>
+
+#### Table-level RocksDB Memory Metrics (Sum Aggregation)
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style={{width: '30pt'}}>Scope</th>
+      <th class="text-left" style={{width: '150pt'}}>Infix</th>
+      <th class="text-left" style={{width: '80pt'}}>Metrics</th>
+      <th class="text-left" style={{width: '300pt'}}>Description</th>
+      <th class="text-left" style={{width: '40pt'}}>Type</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <th rowspan="6"><strong>tabletserver</strong></th>
+      <td rowspan="6">table</td>
+      <td>rocksdbMemTableMemoryUsageTotal</td>
+      <td>Total memtable memory usage across all buckets of this table (in 
bytes).</td>
+      <td>Gauge</td>
+    </tr>
+    <tr>
+      <td>rocksdbMemTableUnFlushedMemoryUsageTotal</td>
+      <td>Total unflushed memtable memory usage across all buckets of this 
table (in bytes).</td>
+      <td>Gauge</td>
+    </tr>
+    <tr>
+      <td>rocksdbTableReadersMemoryUsageTotal</td>
+      <td>Total table readers (indexes and filters) memory usage across all 
buckets of this table (in bytes).</td>
+      <td>Gauge</td>
+    </tr>
+    <tr>
+      <td>rocksdbBlockCacheMemoryUsageTotal</td>
+      <td>Total block cache memory usage across all buckets of this table (in 
bytes).</td>
+      <td>Gauge</td>
+    </tr>
+    <tr>
+      <td>rocksdbBlockCachePinnedUsageTotal</td>
+      <td>Total pinned memory in block cache across all buckets of this table 
(in bytes).</td>
       <td>Gauge</td>
     </tr>
   </tbody>

Reply via email to