This is an automated email from the ASF dual-hosted git repository.
vjasani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push:
new 6e1812082b PHOENIX-7625 Adding query plan information to
ConnectionActivityLogger (#2246)
6e1812082b is described below
commit 6e1812082b410848d081b22dcbb28bf2f3c15a71
Author: vikas meka <[email protected]>
AuthorDate: Tue Jul 29 15:00:03 2025 -0700
PHOENIX-7625 Adding query plan information to ConnectionActivityLogger
(#2246)
---
.../org/apache/phoenix/jdbc/PhoenixStatement.java | 46 ++++++++++++++++++++
.../org/apache/phoenix/log/ActivityLogInfo.java | 3 +-
.../phoenix/log/ConnectionActivityLogger.java | 9 ++++
.../org/apache/phoenix/query/QueryServices.java | 2 +
.../apache/phoenix/query/QueryServicesOptions.java | 4 ++
.../java/org/apache/phoenix/util/QueryUtil.java | 2 +
.../org/apache/phoenix/end2end/ConnectionIT.java | 34 +++++++++++++++
.../phoenix/jdbc/LoggingConnectionLimiterIT.java | 50 ++++++++++++++++++++++
.../jdbc/LoggingSingleConnectionLimiterIT.java | 2 +
9 files changed, 151 insertions(+), 1 deletion(-)
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 4e4a34ae29..c0cb8a7339 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -43,6 +43,7 @@ import static
org.apache.phoenix.monitoring.MetricType.UPSERT_FAILED_SQL_COUNTER
import static org.apache.phoenix.monitoring.MetricType.UPSERT_SQL_COUNTER;
import static org.apache.phoenix.monitoring.MetricType.UPSERT_SQL_QUERY_TIME;
import static
org.apache.phoenix.monitoring.MetricType.UPSERT_SUCCESS_SQL_COUNTER;
+import static
org.apache.phoenix.query.QueryServices.CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED;
import java.io.File;
import java.io.IOException;
@@ -65,11 +66,14 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellComparator;
+import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.client.Consistency;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
@@ -87,6 +91,7 @@ import org.apache.phoenix.compile.DeclareCursorCompiler;
import org.apache.phoenix.compile.DeleteCompiler;
import org.apache.phoenix.compile.DropSequenceCompiler;
import org.apache.phoenix.compile.ExplainPlan;
+import org.apache.phoenix.compile.ExplainPlanAttributes;
import org.apache.phoenix.compile.ExpressionProjector;
import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
import org.apache.phoenix.compile.ListJarsQueryPlan;
@@ -292,6 +297,7 @@ public class PhoenixStatement implements
PhoenixMonitoredStatement, SQLCloseable
private int maxRows;
private int fetchSize = -1;
private int queryTimeoutMillis;
+ private boolean explainPlanLoggingEnabled;
// Caching per Statement
protected final Calendar localCalendar = Calendar.getInstance();
private boolean validateLastDdlTimestamp;
@@ -302,6 +308,9 @@ public class PhoenixStatement implements
PhoenixMonitoredStatement, SQLCloseable
this.queryTimeoutMillis = getDefaultQueryTimeoutMillis();
this.validateLastDdlTimestamp =
ValidateLastDDLTimestampUtil.getValidateLastDdlTimestampEnabled(this.connection);
+ this.explainPlanLoggingEnabled =
+
connection.getQueryServices().getProps().getBoolean(CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED,
+ QueryServicesOptions.DEFAULT_CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED);
}
/**
@@ -417,6 +426,9 @@ public class PhoenixStatement implements
PhoenixMonitoredStatement, SQLCloseable
LOGGER
.debug(LogUtil.addCustomAnnotations("Explain plan: " +
explainPlan, connection));
}
+ if (explainPlanLoggingEnabled) {
+ updateExplainPlanInformation(plan);
+ }
context.setQueryLogger(queryLogger);
if (queryLogger.isDebugEnabled()) {
queryLogger.log(QueryLogInfo.EXPLAIN_PLAN_I,
@@ -2999,4 +3011,38 @@ public class PhoenixStatement implements
PhoenixMonitoredStatement, SQLCloseable
return localCalendar;
}
+ private void updateExplainPlanInformation(QueryPlan plan) throws
SQLException {
+ if (
+ plan == null || !getConnection().getActivityLogger()
+ .isLevelEnabled(ActivityLogInfo.EXPLAIN_PLAN.getLogLevel())
+ ) {
+ return;
+ }
+
+ ExplainPlan explainPlan = plan.getExplainPlan();
+ ExplainPlanAttributes explainPlanAttributes =
explainPlan.getPlanStepsAsAttributes();
+
+ List<HRegionLocation> location =
explainPlanAttributes.getRegionLocations();
+ String regionInfo = getRegionInfo(location);
+
+ String sb = Stream.of(explainPlanAttributes.getExplainScanType(),
regionInfo)
+ .collect(Collectors.joining(","));
+ updateActivityOnConnection(ActivityLogInfo.EXPLAIN_PLAN, sb);
+ }
+
+ private String getRegionInfo(List<HRegionLocation> location) {
+ if (location == null || location.isEmpty()) {
+ return "";
+ }
+
+ String regions =
+ location.stream().map(regionLocation ->
regionLocation.getRegion().getEncodedName())
+ .collect(Collectors.joining(","));
+
+ String hostnames =
+
location.stream().map(HRegionLocation::getHostname).collect(Collectors.joining(","));
+
+ return QueryUtil.REGIONS + "={" + regions + "}," + QueryUtil.HOSTNAMES +
"={" + hostnames + "}";
+ }
+
}
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/log/ActivityLogInfo.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/log/ActivityLogInfo.java
index 9ad6f9e80a..3fff30b41b 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/log/ActivityLogInfo.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/log/ActivityLogInfo.java
@@ -31,7 +31,8 @@ public enum ActivityLogInfo {
REQUEST_ID("r", LogLevel.INFO, PVarchar.INSTANCE),
TABLE_NAME("n", LogLevel.INFO, PVarchar.INSTANCE),
OP_NAME("o", LogLevel.INFO, PVarchar.INSTANCE),
- OP_STMTS("#", LogLevel.INFO, PInteger.INSTANCE);
+ OP_STMTS("#", LogLevel.INFO, PInteger.INSTANCE),
+ EXPLAIN_PLAN("ep", LogLevel.INFO, PVarchar.INSTANCE);
public final String shortName;
public final LogLevel logLevel;
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/log/ConnectionActivityLogger.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/log/ConnectionActivityLogger.java
index 211a129234..8d1eae9f98 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/log/ConnectionActivityLogger.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/log/ConnectionActivityLogger.java
@@ -154,4 +154,13 @@ public class ConnectionActivityLogger {
? logLevel.ordinal() <= this.logLevel.ordinal()
: false;
}
+
+ /**
+ * Get the Explain plan information.
+ */
+ public String getExplainPlanInfo() {
+ return isLevelEnabled(ActivityLogInfo.EXPLAIN_PLAN.getLogLevel())
+ ? activityList.get(ActivityLogInfo.EXPLAIN_PLAN.ordinal())
+ : null;
+ }
}
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
index 60efb91310..94f8574361 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -347,6 +347,8 @@ public interface QueryServices extends SQLCloseable {
"phoenix.internal.connection.max.allowed.connections";
public static final String CONNECTION_ACTIVITY_LOGGING_ENABLED =
"phoenix.connection.activity.logging.enabled";
+ String CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED =
+ "phoenix.connection.activity.logging.explain.plan.enabled";
public static final String CONNECTION_ACTIVITY_LOGGING_INTERVAL =
"phoenix.connection.activity.logging.interval";
public static final String DEFAULT_COLUMN_ENCODED_BYTES_ATRRIB =
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index 18da8b2ddd..16c1c28709 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -33,6 +33,7 @@ import static
org.apache.phoenix.query.QueryServices.COLLECT_REQUEST_LEVEL_METRI
import static org.apache.phoenix.query.QueryServices.COMMIT_STATS_ASYNC;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_ACTIVITY_LOGGING_ENABLED;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_ACTIVITY_LOGGING_INTERVAL;
+import static
org.apache.phoenix.query.QueryServices.CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_QUERY_SERVICE_HISTOGRAM_SIZE_RANGES;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_QUERY_SERVICE_METRICS_ENABLED;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_QUERY_SERVICE_METRICS_PUBLISHER_CLASSNAME;
@@ -388,6 +389,7 @@ public class QueryServicesOptions {
public static final int DEFAULT_INTERNAL_CONNECTION_MAX_ALLOWED_CONNECTIONS
= 0;
public static final boolean DEFAULT_CONNECTION_ACTIVITY_LOGGING_ENABLED =
false;
+ public static final boolean DEFAULT_CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED
= false;
public static final int DEFAULT_CONNECTION_ACTIVITY_LOGGING_INTERVAL_IN_MINS
= 15;
public static final boolean DEFAULT_STATS_COLLECTION_ENABLED = true;
public static final boolean DEFAULT_USE_STATS_FOR_PARALLELIZATION = true;
@@ -597,6 +599,8 @@ public class QueryServicesOptions {
.setIfUnset(SERVER_MERGE_FOR_UNCOVERED_INDEX,
DEFAULT_SERVER_MERGE_FOR_UNCOVERED_INDEX)
.setIfUnset(MAX_IN_LIST_SKIP_SCAN_SIZE,
DEFAULT_MAX_IN_LIST_SKIP_SCAN_SIZE)
.setIfUnset(CONNECTION_ACTIVITY_LOGGING_ENABLED,
DEFAULT_CONNECTION_ACTIVITY_LOGGING_ENABLED)
+ .setIfUnset(CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED,
+ DEFAULT_CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED)
.setIfUnset(CONNECTION_ACTIVITY_LOGGING_INTERVAL,
DEFAULT_CONNECTION_ACTIVITY_LOGGING_INTERVAL_IN_MINS)
.setIfUnset(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED,
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/QueryUtil.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/QueryUtil.java
index 6b0ccc643a..e02fe57423 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/util/QueryUtil.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/util/QueryUtil.java
@@ -137,6 +137,8 @@ public final class QueryUtil {
public static final int DATA_TYPE_NAME_POSITION = 6;
public static final String IS_SERVER_CONNECTION = "IS_SERVER_CONNECTION";
+ public static final String REGIONS = "regions";
+ public static final String HOSTNAMES = "hostnames";
private static final String SELECT = "SELECT";
private static final String FROM = "FROM";
private static final String WHERE = "WHERE";
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ConnectionIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ConnectionIT.java
index ff872bd639..24d5811e38 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ConnectionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ConnectionIT.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.end2end;
import static org.apache.phoenix.query.BaseTest.setUpConfigForMiniCluster;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeTrue;
import java.sql.Connection;
@@ -158,4 +159,37 @@ public class ConnectionIT {
}
}
+
+ @Test
+ public void testQueryPlanIsNullByDefault() throws SQLException {
+ // Test that query plan is null by default even after data operations
+ String tableName = "TEST_QUERY_PLAN_" + tableCounter++;
+
+ try (PhoenixConnection conn = (PhoenixConnection)
DriverManager.getConnection("jdbc:phoenix")) {
+ // Initially, query plan should be null (disabled by default)
+ String initialQueryPlan = conn.getActivityLogger().getExplainPlanInfo();
+ assertNull("Query plan should be null by default (disabled)",
initialQueryPlan);
+
+ // Create table and load data
+ try (Statement stmt = conn.createStatement()) {
+ stmt.execute("CREATE TABLE " + tableName + " (id INTEGER PRIMARY KEY,
name VARCHAR)");
+ stmt.execute("UPSERT INTO " + tableName + " VALUES (1, 'test1')");
+ stmt.execute("UPSERT INTO " + tableName + " VALUES (2, 'test2')");
+ conn.commit();
+ }
+
+ // Execute a query - query plan should still be null since it's disabled
by default
+ try (Statement stmt = conn.createStatement()) {
+ ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName);
+ while (rs.next()) {
+ // Consume the result set
+ }
+
+ // Query plan should still be null since it's disabled by default
+ String queryPlan = conn.getActivityLogger().getExplainPlanInfo();
+ assertNull("Query plan should be null after query execution (disabled
by default)",
+ queryPlan);
+ }
+ }
+ }
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingConnectionLimiterIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingConnectionLimiterIT.java
index 289939c5a4..dd59730ce1 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingConnectionLimiterIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingConnectionLimiterIT.java
@@ -23,6 +23,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.sql.Statement;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
@@ -186,6 +187,55 @@ public abstract class LoggingConnectionLimiterIT extends
BaseTest {
}
+ @Test
+ public void testQueryExplainPlan() throws Exception {
+
+ String query = "SELECT * FROM " + tableName; // FULL SCAN
+
+ if (getConnection() instanceof PhoenixConnection) {
+ try (PhoenixConnection pconn =
getConnection().unwrap(PhoenixConnection.class);
+ Statement stmt = pconn.createStatement()) {
+ ResultSet rs = stmt.executeQuery(query);
+ while (rs.next()) {
+ // do nothing
+ }
+ boolean queryPlanFound = false;
+ String queryPlan = pconn.getActivityLogger().getExplainPlanInfo();
+ if (
+ queryPlan != null && queryPlan.contains("FULL SCAN") &&
queryPlan.contains("regions=")
+ && queryPlan.contains("hostnames=")
+ ) {
+ queryPlanFound = true;
+
+ // Extract regions and hostnames efficiently
+ String regions = extractBetweenBraces(queryPlan, "regions={");
+ String hostnames = extractBetweenBraces(queryPlan, "hostnames={");
+
+ assertFalse("Regions should not be empty", regions.trim().isEmpty());
+ assertFalse("Hostnames should not be empty",
hostnames.trim().isEmpty());
+
+ }
+ assertTrue("Query plan should contain FULL SCAN, regions, and
hostnames", queryPlanFound);
+ }
+ try (PhoenixConnection pconn =
getConnection().unwrap(PhoenixConnection.class);) {
+ loadData(pconn, "PhoenixTest", "1", 10, 2);
+ String queryPlan = pconn.getActivityLogger().getExplainPlanInfo();
+ assertTrue("Query plan should be empty for non-query operations",
queryPlan.isEmpty());
+ }
+ } else {
+ // for HA case, ignoring as parallelPhoenixConnection object doesn't
have activity Logger
+ // object defined.
+ assertTrue(getConnection() instanceof ParallelPhoenixConnection);
+ }
+
+ }
+
+ private String extractBetweenBraces(String text, String marker) {
+ int start = text.indexOf(marker) + marker.length();
+ int end = text.indexOf("}", start);
+ return text.substring(start, end);
+ }
+
protected abstract ConnectionLimiter getConnectionLimiter() throws Exception;
protected int runSampleActivity(ActivityType activityType, int clientPool,
int clientQueue,
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingSingleConnectionLimiterIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingSingleConnectionLimiterIT.java
index 1c37e4f58b..11798b3986 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingSingleConnectionLimiterIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/LoggingSingleConnectionLimiterIT.java
@@ -64,6 +64,7 @@ public class LoggingSingleConnectionLimiterIT extends
LoggingConnectionLimiterIT
conf.set(QueryServices.INTERNAL_CONNECTION_MAX_ALLOWED_CONNECTIONS,
String.valueOf(20));
conf.set(PhoenixHAExecutorServiceProvider.HA_MAX_POOL_SIZE,
String.valueOf(5));
conf.set(PhoenixHAExecutorServiceProvider.HA_MAX_QUEUE_SIZE,
String.valueOf(30));
+ conf.set(QueryServices.CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED,
String.valueOf(true));
conf.set(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY,
ZKConnectionInfo.ZK_REGISTRY_NAME);
return conf;
@@ -77,6 +78,7 @@ public class LoggingSingleConnectionLimiterIT extends
LoggingConnectionLimiterIT
conf.set(QueryServices.INTERNAL_CONNECTION_MAX_ALLOWED_CONNECTIONS,
String.valueOf(20));
conf.set(PhoenixHAExecutorServiceProvider.HA_MAX_POOL_SIZE,
String.valueOf(5));
conf.set(PhoenixHAExecutorServiceProvider.HA_MAX_QUEUE_SIZE,
String.valueOf(30));
+ conf.set(QueryServices.CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED,
String.valueOf(true));
conf.set(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY,
ZKConnectionInfo.ZK_REGISTRY_NAME);
Configuration copy = new Configuration(conf);