[
https://issues.apache.org/jira/browse/PHOENIX-4906?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17696135#comment-17696135
]
ASF GitHub Bot commented on PHOENIX-4906:
-----------------------------------------
stoty commented on code in PR #1552:
URL: https://github.com/apache/phoenix/pull/1552#discussion_r1124380739
##########
phoenix-core/src/it/java/org/apache/phoenix/PhoenixMasterObserverIT.java:
##########
@@ -0,0 +1,107 @@
+package org.apache.phoenix;
+
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.apache.phoenix.util.TestUtil.getAllSplits;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Admin;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.client.RegionLocator;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.phoenix.coprocessor.PhoenixMasterObserver;
+import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest;
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(NeedsOwnMiniClusterTest.class)
+public class PhoenixMasterObserverIT extends ParallelStatsDisabledIT {
+
+ String create_table =
+ "CREATE TABLE IF NOT EXISTS %s(ID VARCHAR NOT NULL PRIMARY KEY,
VAL1 INTEGER, VAL2 INTEGER) SALT_BUCKETS=4";
+ String indexName = generateUniqueName();
+ String create_index = "CREATE INDEX " + indexName + " ON %s(VAL1 DESC)
INCLUDE (VAL2)";
+ String upsertStatement = "UPSERT INTO %s VALUES(?, ?, ?)";
+ String deleteTableName = generateUniqueName();
+
+ @BeforeClass
+ public static synchronized void doSetup() throws Exception {
+ Map<String, String> serverProps = Maps.newHashMapWithExpectedSize(3);
+ serverProps.put("hbase.coprocessor.master.classes",
PhoenixMasterObserver.class.getName());
Review Comment:
There is no way to automatically load a Master coprocessor on Phoenix
startup, right ?
##########
phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixMasterObserver.java:
##########
@@ -0,0 +1,114 @@
+package org.apache.phoenix.coprocessor;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
+import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
+import org.apache.hadoop.hbase.coprocessor.MasterObserver;
+import org.apache.hadoop.hbase.coprocessor.ObserverContext;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.schema.SaltingUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.SchemaUtil;
+
+public class PhoenixMasterObserver implements MasterObserver,
MasterCoprocessor {
+
+ @Override
+ public Optional<MasterObserver> getMasterObserver() {
+ return Optional.of(this);
+ }
+
+ @Override
+ public void preMergeRegions(final
ObserverContext<MasterCoprocessorEnvironment> ctx, RegionInfo[] regionsToMerge)
throws IOException {
+ try {
+ if (blockMergeForSaltedTables(ctx, regionsToMerge)) {
+ //different salt so block merge
+ throw new IOException("Don't merge regions with different salt
bits");
+ }
+ } catch (SQLException e) {
+ throw new IOException("SQLException while fetching data from
phoenix", e);
+ }
+ }
+
+ private boolean
blockMergeForSaltedTables(ObserverContext<MasterCoprocessorEnvironment> ctx,
+ RegionInfo[] regionsToMerge) throws SQLException {
+ TableName table = regionsToMerge[0].getTable();
+ int saltBuckets = getTableSalt(ctx, table.toString());
+ if(saltBuckets > 0 ) {
+ System.out.println("Number of buckets="+saltBuckets);
+ return !regionsHaveSameSalt(regionsToMerge);
+ }
+ // table is not salted so don't block merge
+ return false;
+ }
+
+ private int getTableSalt(ObserverContext<MasterCoprocessorEnvironment>
ctx, String table) throws SQLException {
+ int saltBucket = 0;
+ String schemaName = SchemaUtil.getSchemaNameFromFullName(table);
+ String tableName = SchemaUtil.getTableNameFromFullName(table);
+ String sql =
+ "SELECT salt_buckets FROM system.catalog WHERE table_name = ? "
+ + "AND (table_schem = ? OR table_schem IS NULL)";
+
+ if (schemaName == null || schemaName.isEmpty()) {
+ sql = sql + " and table_schem is null";
+ } else {
+ sql = sql + String.format(" and table_schem=%s", schemaName);
+ }
+
+ Configuration conf = ctx.getEnvironment().getConfiguration();
+ PreparedStatement stmt;
+ ResultSet resultSet;
+ try (Connection conn = QueryUtil.getConnectionOnServer(conf)) {
Review Comment:
I wonder if we should get the salted status via a PTable object from the
CQSI cache instead.
Wouldn't that be faster ?
> Abnormal query result due to merging regions of a salted table
> --------------------------------------------------------------
>
> Key: PHOENIX-4906
> URL: https://issues.apache.org/jira/browse/PHOENIX-4906
> Project: Phoenix
> Issue Type: Bug
> Affects Versions: 4.11.0, 4.14.0
> Reporter: JeongMin Ju
> Assignee: Aman Poonia
> Priority: Critical
> Attachments: SaltingWithRegionMergeIT.java,
> ScanRanges_intersectScan.png, TestSaltingWithRegionMerge.java,
> initial_salting_region.png, merged-region.png
>
>
> For a salted table, when a query is made for an entire data target, a
> different plan is created depending on the type of the query, and as a
> result, erroneous data is retrieved as a result.
> {code:java}
> // Actually, the schema of the table I used is different, but please ignore
> it.
> create table if not exists test.test_tale (
> rk1 varchar not null,
> rk2 varchar not null,
> column1 varchar
> constraint pk primary key (rk1, rk2)
> )
> ...
> SALT_BUCKETS=16...
> ;
> {code}
>
> I created a table with 16 salting regions and then wrote a lot of data.
> HBase automatically split the region and I did the merging regions for data
> balancing between the region servers.
> Then, when run the query, you can see that another plan is created according
> to the Where clause.
> * query1
> select count\(*) from test.test_table;
> {code:java}
> +-------------------------------------------------------------------------------------------------------+-----------------+----------------+
> | PLAN
> | EST_BYTES_READ | EST_ROWS_READ |
> +-------------------------------------------------------------------------------------------------------+-----------------+----------------+
> | CLIENT 1851-CHUNK 5005959292 ROWS 1944546675532 BYTES PARALLEL 11-WAY FULL
> SCAN OVER TEST:TEST_TABLE | 1944546675532 | 5005959292 |
> | SERVER FILTER BY FIRST KEY ONLY
> | 1944546675532 | 5005959292 |
> | SERVER AGGREGATE INTO SINGLE ROW
> | 1944546675532 | 5005959292 |
> +-------------------------------------------------------------------------------------------------------+-----------------+----------------+
> {code}
> * query2
> select count\(*) from test.test_table where rk2 = 'aa';
> {code}
> +-------------------------------------------------------------------------------------------------------------------+-----------------+----------------+
> | PLAN
> | EST_BYTES_READ | EST_ROWS_READ |
> +-------------------------------------------------------------------------------------------------------------------+-----------------+----------------+
> | CLIENT 1846-CHUNK 4992196444 ROWS 1939177965768 BYTES PARALLEL 11-WAY RANGE
> SCAN OVER TEST:TEST_TABLE [0] - [15] | 1939177965768 | 4992196444 |
> | SERVER FILTER BY FIRST KEY ONLY AND RK2 = 'aa'
> | 1939177965768 | 4992196444 |
> | SERVER AGGREGATE INTO SINGLE ROW
> | 1939177965768 | 4992196444 |
> +-------------------------------------------------------------------------------------------------------------------+-----------------+----------------+
> {code}
> Since rk2 used in the where clause of query2 is the second column of the PK,
> it must be a full scan query like query1.
> However, as you can see, query2 is created by range scan and the generated
> chunk is also less than five compared to query1.
> I added the log and printed out the startkey and endkey of the scan object
> generated by the plan.
> And I found 5 chunks missing by query2.
> All five missing chunks were found in regions where the originally generated
> region boundary value was not maintained through the merge operation.
> !initial_salting_region.png!
> After merging regions
> !merged-region.png!
> The code that caused the problem is this part.
> When a select query is executed, the
> [org.apache.phoenix.iterate.BaseResultIterators#getParallelScans|https://github.com/apache/phoenix/blob/v4.11.0-HBase-1.2/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java#L743-L744]
> method creates a Scan object based on the GuidePost in the statistics table.
> In the case of a GuidePost that contains a region boundary, it is split into
> two Scan objects. The code used here is
> [org.apache.phoenix.compile.ScanRanges#intersectScan|https://github.com/apache/phoenix/blob/v4.11.0-HBase-1.2/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java#L299-L303].
> !ScanRanges_intersectScan.png!
> In the case of a table that has been salted, the code compares it with the
> remainder after subtracting the salt(prefix) bytes.
> I can not be sure that this code is buggy or intended.
> In this case, I have merge the region directly, but it is likely to occur
> through HBase's Normalizer function.
> I wish other users did not merge the region manually or not the table
> property Normalization_enabled to true in their production cluster. If so,
> check to see if the initial Salting Region boundary is correct. If the
> boundary value has disappeared, you are seeing the wrong data.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)