This is an automated email from the ASF dual-hosted git repository.
vjasani pushed a commit to branch 5.2
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/5.2 by this push:
new 0d9a13812a PHOENIX-7625 Adding query plan information to connection
Activity Logger (#2253)
0d9a13812a is described below
commit 0d9a13812afa175226dcb8115a1ba154733c4a8a
Author: vikas meka <[email protected]>
AuthorDate: Thu Jul 31 14:13:49 2025 -0700
PHOENIX-7625 Adding query plan information to connection Activity Logger
(#2253)
---
.../org/apache/phoenix/jdbc/PhoenixStatement.java | 48 +++++++++++++++++++++
.../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, 153 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 2ea156e180..768f9c62f2 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
@@ -42,6 +42,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;
@@ -64,6 +65,8 @@ 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;
@@ -71,6 +74,7 @@ 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.Scan;
import org.apache.hadoop.hbase.util.Pair;
@@ -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;
@@ -288,12 +293,16 @@ 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();
public PhoenixStatement(PhoenixConnection connection) {
this.connection = connection;
this.queryTimeoutMillis = getDefaultQueryTimeoutMillis();
+ this.explainPlanLoggingEnabled =
connection.getQueryServices().getProps()
+ .getBoolean(CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED,
+
QueryServicesOptions.DEFAULT_CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED);
}
/**
@@ -388,6 +397,9 @@ public class PhoenixStatement implements
PhoenixMonitoredStatement, SQLCloseable
LOGGER.debug(LogUtil.addCustomAnnotations(
"Explain plan: " + explainPlan,
connection));
}
+ if (explainPlanLoggingEnabled) {
+ updateExplainPlanInformation(plan);
+ }
StatementContext context = plan.getContext();
context.setQueryLogger(queryLogger);
if (queryLogger.isDebugEnabled()) {
@@ -2605,4 +2617,40 @@ 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 3baf44754d..3fa9b2fb8a 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
@@ -34,7 +34,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 ee2a2a8af4..00e89eed26 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
@@ -147,4 +147,13 @@ public class ConnectionActivityLogger {
return this.logLevel != null && logLevel != LogLevel.OFF ?
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 ea95e05f2b..3df7893f06 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
@@ -304,6 +304,8 @@ public interface QueryServices extends SQLCloseable {
"phoenix.connection.activity.logging.enabled";
public static final String CONNECTION_ACTIVITY_LOGGING_INTERVAL =
"phoenix.connection.activity.logging.interval";
+ String CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED =
+ "phoenix.connection.activity.logging.explain.plan.enabled";
public static final String DEFAULT_COLUMN_ENCODED_BYTES_ATRRIB =
"phoenix.default.column.encoded.bytes.attrib";
public static final String DEFAULT_IMMUTABLE_STORAGE_SCHEME_ATTRIB =
"phoenix.default.immutable.storage.scheme";
public static final String
DEFAULT_MULTITENANT_IMMUTABLE_STORAGE_SCHEME_ATTRIB =
"phoenix.default.multitenant.immutable.storage.scheme";
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 5c9210ef20..4c01c85d31 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
@@ -29,6 +29,7 @@ import static
org.apache.phoenix.query.QueryServices.CLIENT_SPOOL_THRESHOLD_BYTE
import static
org.apache.phoenix.query.QueryServices.COLLECT_REQUEST_LEVEL_METRICS;
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_EXPLAIN_PLAN_LOGGING_ENABLED;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_ACTIVITY_LOGGING_INTERVAL;
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;
@@ -368,6 +369,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;
@@ -546,6 +548,8 @@ public class QueryServicesOptions {
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(CQSI_THREAD_POOL_ENABLED,
DEFAULT_CQSI_THREAD_POOL_ENABLED)
.setIfUnset(CQSI_THREAD_POOL_KEEP_ALIVE_SECONDS,
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 52e4f6aa0e..af8bf049b9 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
@@ -136,6 +136,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 87cf8649e3..451afc09df 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
@@ -20,6 +20,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;
@@ -159,4 +160,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 3a313f4973..9b02e17bc1 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
@@ -31,6 +31,7 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
+import java.sql.Statement;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.ArrayList;
@@ -199,6 +200,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, int numRows, int connNotClosed) throws Exception {
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 fb0ad2871d..0d20b20a4d 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));
return conf;
}
@@ -74,6 +75,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));
Configuration copy = new Configuration(conf);
copy.addResource(confToClone);
return copy;