This is an automated email from the ASF dual-hosted git repository.
stigahuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
The following commit(s) were added to refs/heads/master by this push:
new 036c4fdf0 IMPALA-13863: Add metric to track number of loaded tables in
catalogd
036c4fdf0 is described below
commit 036c4fdf04a1fb14838e2acaf5b2af2719d67a43
Author: Arnab Karmakar <[email protected]>
AuthorDate: Tue Dec 23 03:04:52 2025 -0800
IMPALA-13863: Add metric to track number of loaded tables in catalogd
Implements a real-time counter that tracks the number of loaded tables
(non-IncompleteTable instances) in the catalog. This helps monitor
catalog memory pressure and query performance impact from implicit table
invalidation mechanisms.
The counter uses AtomicInteger for thread-safety and is updated across
all table state transitions:
- Incremented when IncompleteTable is replaced with a loaded table
- Decremented when tables are invalidated, dropped, or aged out
- Reset to 0 on global INVALIDATE METADATA
Testing:
Manual verification and automated tests confirm correct
behavior across load, invalidate, drop, and timeout scenarios.
Change-Id: I5aa54f9f7507709b654df22e24592799811e8b6c
Reviewed-on: http://gerrit.cloudera.org:8080/23804
Reviewed-by: Impala Public Jenkins <[email protected]>
Tested-by: Impala Public Jenkins <[email protected]>
---
be/src/catalog/catalog-server.cc | 3 +
be/src/catalog/catalog-server.h | 3 +
common/thrift/JniCatalog.thrift | 3 +
common/thrift/metrics.json | 10 ++
.../java/org/apache/impala/catalog/Catalog.java | 55 +++++++++++
.../apache/impala/catalog/CatalogObjectCache.java | 31 ++++++
.../impala/catalog/CatalogServiceCatalog.java | 6 ++
fe/src/main/java/org/apache/impala/catalog/Db.java | 11 ++-
.../java/org/apache/impala/service/JniCatalog.java | 1 +
.../custom_cluster/test_automatic_invalidation.py | 108 ++++++++++++++++++++-
tests/webserver/test_web_pages.py | 1 +
11 files changed, 228 insertions(+), 4 deletions(-)
diff --git a/be/src/catalog/catalog-server.cc b/be/src/catalog/catalog-server.cc
index 9e9ece243..88a0eb571 100644
--- a/be/src/catalog/catalog-server.cc
+++ b/be/src/catalog/catalog-server.cc
@@ -366,6 +366,7 @@ const string CATALOG_NUM_TABLES_WAITING_FOR_ASYNC_LOADING =
const string CATALOG_NUM_DBS = "catalog.num-databases";
const string CATALOG_NUM_TABLES = "catalog.num-tables";
const string CATALOG_NUM_FUNCTIONS = "catalog.num-functions";
+const string CATALOG_NUM_LOADED_TABLES = "catalog.num-loaded-tables";
const string CATALOG_NUM_HMS_CLIENTS_IDLE = "catalog.hms-client-pool.num-idle";
const string CATALOG_NUM_HMS_CLIENTS_IN_USE =
"catalog.hms-client-pool.num-in-use";
@@ -645,6 +646,7 @@ CatalogServer::CatalogServer(MetricGroup* metrics)
num_dbs_metric_ = metrics->AddGauge(CATALOG_NUM_DBS, 0);
num_tables_metric_ = metrics->AddGauge(CATALOG_NUM_TABLES, 0);
num_functions_metric_ = metrics->AddGauge(CATALOG_NUM_FUNCTIONS, 0);
+ num_loaded_tables_metric_ = metrics->AddGauge(CATALOG_NUM_LOADED_TABLES, 0);
num_hms_clients_idle_metric_ =
metrics->AddGauge(CATALOG_NUM_HMS_CLIENTS_IDLE, 0);
num_hms_clients_in_use_metric_ =
metrics->AddGauge(CATALOG_NUM_HMS_CLIENTS_IN_USE, 0);
@@ -1125,6 +1127,7 @@ void CatalogServer::MarkPendingMetadataReset(const
unique_lock<std::mutex>& lock
num_dbs_metric_->SetValue(response.catalog_num_dbs);
num_tables_metric_->SetValue(response.catalog_num_tables);
num_functions_metric_->SetValue(response.catalog_num_functions);
+ num_loaded_tables_metric_->SetValue(response.catalog_num_loaded_tables);
num_hms_clients_idle_metric_->SetValue(response.catalog_num_hms_clients_idle);
num_hms_clients_in_use_metric_->SetValue(response.catalog_num_hms_clients_in_use);
TEventProcessorMetrics eventProcessorMetrics = response.event_metrics;
diff --git a/be/src/catalog/catalog-server.h b/be/src/catalog/catalog-server.h
index cb6b637a8..3b574baec 100644
--- a/be/src/catalog/catalog-server.h
+++ b/be/src/catalog/catalog-server.h
@@ -206,6 +206,9 @@ class CatalogServer {
IntGauge* num_tables_metric_;
IntGauge* num_functions_metric_;
+ /// Metric that tracks the number of loaded tables.
+ IntGauge* num_loaded_tables_metric_;
+
/// Metrics that track the number of HMS clients
IntGauge* num_hms_clients_idle_metric_;
IntGauge* num_hms_clients_in_use_metric_;
diff --git a/common/thrift/JniCatalog.thrift b/common/thrift/JniCatalog.thrift
index 766f8cd11..53a55bcd2 100644
--- a/common/thrift/JniCatalog.thrift
+++ b/common/thrift/JniCatalog.thrift
@@ -1093,6 +1093,9 @@ struct TGetCatalogServerMetricsResponse {
// Metrics of HMS clients
13: optional i32 catalog_num_hms_clients_idle
14: optional i32 catalog_num_hms_clients_in_use
+
+ // Number of loaded tables (i.e. not IncompleteTable)
+ 15: optional i32 catalog_num_loaded_tables
}
// Request to copy the generated testcase from a given input path.
diff --git a/common/thrift/metrics.json b/common/thrift/metrics.json
index 182919de8..b5954b2e3 100644
--- a/common/thrift/metrics.json
+++ b/common/thrift/metrics.json
@@ -571,6 +571,16 @@
"kind": "GAUGE",
"key": "catalog.num-functions"
},
+ {
+ "description": "The number of loaded tables in the catalog.",
+ "contexts": [
+ "CATALOGSERVER"
+ ],
+ "label": "Loaded Tables",
+ "units": "NONE",
+ "kind": "GAUGE",
+ "key": "catalog.num-loaded-tables"
+ },
{
"description": "The number of Hive Metastore Clients that are idle.",
"contexts": [
diff --git a/fe/src/main/java/org/apache/impala/catalog/Catalog.java
b/fe/src/main/java/org/apache/impala/catalog/Catalog.java
index 387cd3c2f..d1da330b8 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Catalog.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Catalog.java
@@ -25,6 +25,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import javax.annotation.Nullable;
@@ -52,6 +55,8 @@ import org.apache.impala.thrift.TTableName;
import org.apache.impala.thrift.TUniqueId;
import org.apache.impala.util.EventSequence;
import org.apache.impala.util.PatternMatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
@@ -76,6 +81,8 @@ import com.google.common.base.Preconditions;
* Builtins are populated on startup in initBuiltins().
*/
public abstract class Catalog implements AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(Catalog.class);
+
// Initial catalog version and ID.
public final static long INITIAL_CATALOG_VERSION = 0L;
public static final TUniqueId INITIAL_CATALOG_SERVICE_ID = new TUniqueId(0L,
0L);
@@ -111,6 +118,47 @@ public abstract class Catalog implements AutoCloseable {
protected final CatalogObjectCache<AuthzCacheInvalidation>
authzCacheInvalidation_ =
new CatalogObjectCache<>();
+ // Number of loaded tables (i.e. not IncompleteTable) in the catalog cache.
+ // This is updated in real-time when tables are loaded or invalidated.
+ // IMPALA-13863: Track loaded tables to help monitor catalog memory pressure.
+ // This is static because it tracks loaded tables across all catalog
instances.
+ protected static final AtomicInteger numLoadedTables_ = new AtomicInteger(0);
+
+ // Update function for tracking table additions/replacements in the loaded
tables
+ // counter. This is passed to Db#addTable() to centralize the counter update
logic.
+ public static final BiConsumer<Table, Table> TABLE_ADD_UPDATE_FUNC =
+ (existingTbl, newTbl) -> {
+ if (existingTbl != null) {
+ boolean wasLoaded = !(existingTbl instanceof IncompleteTable);
+ boolean isLoaded = !(newTbl instanceof IncompleteTable);
+
+ if (wasLoaded && !isLoaded) {
+ numLoadedTables_.decrementAndGet();
+ LOG.trace("Replaced loaded table {} with IncompleteTable, " +
+ "loaded tables count: {}", newTbl.getFullName(),
numLoadedTables_.get());
+ } else if (!wasLoaded && isLoaded) {
+ numLoadedTables_.incrementAndGet();
+ LOG.trace("Replaced IncompleteTable with loaded table {}, " +
+ "loaded tables count: {}", newTbl.getFullName(),
numLoadedTables_.get());
+ }
+ } else if (!(newTbl instanceof IncompleteTable)) {
+ // New table being added and it's loaded
+ numLoadedTables_.incrementAndGet();
+ LOG.trace("Added new loaded table {}, loaded tables count: {}",
+ newTbl.getFullName(), numLoadedTables_.get());
+ }
+ };
+
+ // Update function for tracking table removals in the loaded tables counter.
+ // This is passed to Db#removeTable() to centralize the counter update logic.
+ public static final Consumer<Table> TABLE_REMOVE_UPDATE_FUNC = (removedTbl)
-> {
+ if (!(removedTbl instanceof IncompleteTable)) {
+ numLoadedTables_.decrementAndGet();
+ LOG.trace("Removed loaded table {}, loaded tables count: {}",
+ removedTbl.getFullName(), numLoadedTables_.get());
+ }
+ };
+
// This member is responsible for heartbeating HMS locks and transactions.
private TransactionKeepalive transactionKeepalive_;
@@ -299,6 +347,13 @@ public abstract class Catalog implements AutoCloseable {
return dataSources_.getValues();
}
+ /**
+ * Returns the number of loaded tables (not IncompleteTable) in the catalog.
+ */
+ public int getNumLoadedTables() {
+ return numLoadedTables_.get();
+ }
+
/**
* Returns a list of data sources names that match pattern.
*
diff --git a/fe/src/main/java/org/apache/impala/catalog/CatalogObjectCache.java
b/fe/src/main/java/org/apache/impala/catalog/CatalogObjectCache.java
index dd33ab3f4..7467a81f5 100644
--- a/fe/src/main/java/org/apache/impala/catalog/CatalogObjectCache.java
+++ b/fe/src/main/java/org/apache/impala/catalog/CatalogObjectCache.java
@@ -22,6 +22,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -69,6 +71,16 @@ public class CatalogObjectCache<T extends CatalogObject>
implements Iterable<T>
* Returns true if this item was added or false if the existing value was
preserved.
*/
public synchronized boolean add(T catalogObject) {
+ return add(catalogObject, null);
+ }
+
+ /**
+ * Extends the base class method to call the update function if provided.
+ * The updateFunc is called with (existingItem, newItem) when an item is
added or
+ * replaced, allowing the caller to track state transitions.
+ * Returns true if this item was added or false if the existing value was
preserved.
+ */
+ public synchronized boolean add(T catalogObject, BiConsumer<T, T>
updateFunc) {
Preconditions.checkNotNull(catalogObject);
String key = catalogObject.getName();
if (caseInsensitiveKeys_) key = key.toLowerCase();
@@ -76,6 +88,9 @@ public class CatalogObjectCache<T extends CatalogObject>
implements Iterable<T>
if (existingItem == null) {
CatalogObjectVersionSet.INSTANCE.addVersion(
catalogObject.getCatalogVersion());
+ if (updateFunc != null) {
+ updateFunc.accept(null, catalogObject);
+ }
return true;
}
@@ -86,6 +101,9 @@ public class CatalogObjectCache<T extends CatalogObject>
implements Iterable<T>
metadataCache_.put(key, catalogObject);
CatalogObjectVersionSet.INSTANCE.updateVersions(
existingItem.getCatalogVersion(), catalogObject.getCatalogVersion());
+ if (updateFunc != null) {
+ updateFunc.accept(existingItem, catalogObject);
+ }
return true;
}
return false;
@@ -96,11 +114,24 @@ public class CatalogObjectCache<T extends CatalogObject>
implements Iterable<T>
* if no item was removed.
*/
public synchronized T remove(String name) {
+ return remove(name, null);
+ }
+
+ /**
+ * Removes an item from the metadata cache with an optional update function.
+ * The updateFunc is called with the removed item if it was successfully
removed,
+ * allowing the caller to track the removal.
+ * Returns the removed item, or null if no item was removed.
+ */
+ public synchronized T remove(String name, Consumer<T> updateFunc) {
if (caseInsensitiveKeys_) name = name.toLowerCase();
T removedObject = metadataCache_.remove(name);
if (removedObject != null) {
CatalogObjectVersionSet.INSTANCE.removeVersion(
removedObject.getCatalogVersion());
+ if (updateFunc != null) {
+ updateFunc.accept(removedObject);
+ }
}
return removedObject;
}
diff --git
a/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
b/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
index 46c434615..6706bc7ec 100644
--- a/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
+++ b/fe/src/main/java/org/apache/impala/catalog/CatalogServiceCatalog.java
@@ -2510,6 +2510,10 @@ public class CatalogServiceCatalog extends Catalog {
catalogTimeline.markEvent(GOT_CATALOG_VERSION_WRITE_LOCK);
catalogVersion_++;
+ // Reset loaded tables counter since all tables will be replaced with
+ // IncompleteTable during global invalidate.
+ numLoadedTables_.set(0);
+
// Update data source, db and table metadata.
// First, refresh DataSource objects from HMS and assign new versions.
refreshDataSources();
@@ -2743,6 +2747,8 @@ public class CatalogServiceCatalog extends Catalog {
Preconditions.checkState(versionLock_.isWriteLockedByCurrentThread());
if (!db.isSystemDb()) {
for (Table tbl: db.getTables()) {
+ // Update loaded tables counter using the centralized update function.
+ Catalog.TABLE_REMOVE_UPDATE_FUNC.accept(tbl);
tbl.setCatalogVersion(incrementAndGetCatalogVersion());
deleteLog_.addRemovedObject(tbl.toMinimalTCatalogObject());
}
diff --git a/fe/src/main/java/org/apache/impala/catalog/Db.java
b/fe/src/main/java/org/apache/impala/catalog/Db.java
index efd7efb51..904ae6ae0 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Db.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Db.java
@@ -211,9 +211,12 @@ public class Db extends CatalogObjectImpl implements FeDb {
public TCatalogObjectType getCatalogObjectType() { return
TCatalogObjectType.DATABASE; }
/**
- * Adds a table to the table cache.
+ * Adds a table to the table cache. The static Catalog.TABLE_ADD_UPDATE_FUNC
is used
+ * to track loaded tables count changes.
*/
- public void addTable(Table table) { tableCache_.add(table); }
+ public void addTable(Table table) {
+ tableCache_.add(table, Catalog.TABLE_ADD_UPDATE_FUNC);
+ }
@Override
public List<String> getAllTableNames() {
@@ -267,9 +270,11 @@ public class Db extends CatalogObjectImpl implements FeDb {
/**
* Removes the table name and any cached metadata from the Table cache.
+ * The static Catalog.TABLE_REMOVE_UPDATE_FUNC is used to track loaded tables
+ * count changes.
*/
public Table removeTable(String tableName) {
- return tableCache_.remove(tableName.toLowerCase());
+ return tableCache_.remove(tableName.toLowerCase(),
Catalog.TABLE_REMOVE_UPDATE_FUNC);
}
@Override
diff --git a/fe/src/main/java/org/apache/impala/service/JniCatalog.java
b/fe/src/main/java/org/apache/impala/service/JniCatalog.java
index eb1b8c561..9481761af 100644
--- a/fe/src/main/java/org/apache/impala/service/JniCatalog.java
+++ b/fe/src/main/java/org/apache/impala/service/JniCatalog.java
@@ -589,6 +589,7 @@ public class JniCatalog {
response.setCatalog_num_functions(catalog_.getNumFunctions());
response.setCatalog_num_hms_clients_idle(catalog_.getNumHmsClientsIdle());
response.setCatalog_num_hms_clients_in_use(catalog_.getNumHmsClientsInUse());
+ response.setCatalog_num_loaded_tables(catalog_.getNumLoadedTables());
response.setEvent_metrics(catalog_.getEventProcessorMetrics());
return response;
});
diff --git a/tests/custom_cluster/test_automatic_invalidation.py
b/tests/custom_cluster/test_automatic_invalidation.py
index 6ab68066f..2203b7578 100644
--- a/tests/custom_cluster/test_automatic_invalidation.py
+++ b/tests/custom_cluster/test_automatic_invalidation.py
@@ -25,7 +25,8 @@ from tests.util.filesystem_utils import IS_HDFS, IS_LOCAL
from tests.common.custom_cluster_test_suite import CustomClusterTestSuite
-
+from tests.common.impala_test_suite import ImpalaTestSuite
+from tests.util.event_processor_utils import EventProcessorUtils
class TestAutomaticCatalogInvalidation(CustomClusterTestSuite):
""" Test that tables are cached in the catalogd after usage for the
configured time
@@ -101,3 +102,108 @@ class
TestAutomaticCatalogInvalidation(CustomClusterTestSuite):
# seconds to reduce flakiness.
time.sleep(10)
assert self.metadata_cache_string not in self._get_catalog_object()
+
+ @pytest.mark.execute_serially
+ @CustomClusterTestSuite.with_args(catalogd_args=timeout_flag,
impalad_args=timeout_flag)
+ def test_loaded_tables_metric(self, unique_database):
+ """Test IMPALA-13863: catalog.num-loaded-tables metric tracks loaded tables
+ correctly across various metadata operations including loading,
invalidation,
+ refresh, rename, and removal."""
+ metric_name = "catalog.num-loaded-tables"
+ catalogd = self.cluster.catalogd.service
+
+ # Test 1: Loading increases counter
+ self.execute_query("invalidate metadata")
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ self.execute_query(self.query)
+ catalogd.wait_for_metric_value(metric_name, 1)
+
+ # Test 2: Single table INVALIDATE METADATA decreases counter
+ self.execute_query("invalidate metadata functional.alltypes")
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ # Test 3: REFRESH loaded table (counter should stay same)
+ self.execute_query(self.query)
+ catalogd.wait_for_metric_value(metric_name, 1)
+ count_before_refresh = catalogd.get_metric_value(metric_name)
+
+ self.execute_query("refresh functional.alltypes")
+ # Wait for one metrics refresh cycle (REFRESH_METRICS_INTERVAL_MS)
+ # to ensure the metric is updated
+ time.sleep(1)
+ count_after_refresh = catalogd.get_metric_value(metric_name)
+ assert count_after_refresh == count_before_refresh, (
+ "Count should stay same after REFRESH of loaded table (was %d, now %d)"
+ % (count_before_refresh, count_after_refresh))
+
+ # Test 4: ALTER TABLE RENAME (counter decreases because old loaded table is
+ # removed and new table starts as IncompleteTable)
+ self.execute_query("create table %s.test_rename_tbl (id int)" %
unique_database)
+ self.execute_query("select * from %s.test_rename_tbl" % unique_database)
+ catalogd.wait_for_metric_value(metric_name, 2)
+
+ self.execute_query("alter table %s.test_rename_tbl rename \
+ to %s.test_renamed_tbl" % (unique_database, unique_database))
+ catalogd.wait_for_metric_value(metric_name, 1)
+
+ # Verify that accessing the renamed table increments the counter
+ self.execute_query("select * from %s.test_renamed_tbl" % unique_database)
+ catalogd.wait_for_metric_value(metric_name, 2)
+
+ # Test 5: Load another table, then global INVALIDATE METADATA
+ self.execute_query("select count(*) from functional.alltypessmall")
+ catalogd.wait_for_metric_value(metric_name, 3)
+
+ self.execute_query("invalidate metadata")
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ # Test 6: CREATE TABLE, load it, then DROP TABLE
+ self.execute_query("create table %s.test_metric_tbl (id int)" %
unique_database)
+ # Wait for one metrics refresh cycle (REFRESH_METRICS_INTERVAL_MS)
+ # to ensure the metric is updated
+ time.sleep(1)
+ count_after_create = catalogd.get_metric_value(metric_name)
+ assert count_after_create == 0, (
+ "Count should be 0 after creating table (got %d)" % count_after_create)
+
+ self.execute_query("select * from %s.test_metric_tbl" % unique_database)
+ catalogd.wait_for_metric_value(metric_name, 1)
+
+ self.execute_query("drop table %s.test_metric_tbl" % unique_database)
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ # Test 7: Hive-side DROP TABLE processed via events
+ self.execute_query("create table %s.hive_drop_tbl (id int, val string)"
+ % unique_database)
+ self.execute_query("select * from %s.hive_drop_tbl" % unique_database)
+ catalogd.wait_for_metric_value(metric_name, 1)
+
+ # Drop table from Hive side
+ self.run_stmt_in_hive("drop table %s.hive_drop_tbl" % unique_database)
+ EventProcessorUtils.wait_for_event_processing(self)
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ # Test 8: DROP DATABASE CASCADE with loaded table
+ test_db = ImpalaTestSuite.get_random_name("test_db_")
+ try:
+ self.execute_query("create database if not exists %s" % test_db)
+ self.execute_query("create table %s.t1 (id int, name string)" % test_db)
+ self.execute_query("insert into %s.t1 values (1, 'test')" % test_db)
+ self.execute_query("select * from %s.t1" % test_db)
+ catalogd.wait_for_metric_value(metric_name, 1)
+ finally:
+ self.execute_query("drop database %s cascade" % test_db)
+ catalogd.wait_for_metric_value(metric_name, 0)
+
+ # Test 9: Automatic timeout-based invalidation
+ self.execute_query(self.query)
+ catalogd.wait_for_metric_value(metric_name, 1)
+ assert self.metadata_cache_string in self._get_catalog_object()
+
+ # Wait for automatic timeout-based invalidation to complete and metric to
update
+ # Timeout is 2x the invalidation timeout to account for background
processing delays
+ catalogd.wait_for_metric_value(metric_name, 0, timeout=self.timeout * 2)
+ # Verify that the table metadata was actually invalidated
+ assert self.metadata_cache_string not in self._get_catalog_object(), \
+ "Table metadata should be invalidated after timeout"
diff --git a/tests/webserver/test_web_pages.py
b/tests/webserver/test_web_pages.py
index 63f5d2b51..1b0658d7b 100644
--- a/tests/webserver/test_web_pages.py
+++ b/tests/webserver/test_web_pages.py
@@ -1101,6 +1101,7 @@ class TestWebPage(ImpalaTestSuite):
assert "catalog.num-functions" in metric_keys
assert "catalog.hms-client-pool.num-idle" in metric_keys
assert "catalog.hms-client-pool.num-in-use" in metric_keys
+ assert "catalog.num-loaded-tables" in metric_keys
def test_iceberg_table_metrics(self):
assert '23448' == self.__get_table_metric(