Skip to content

Commit d073b2b

Browse files
authored
HBASE-30058 Eliminate unnecessary connection creation in snapshot operations (#8030) (#8314)
Each snapshot operation created two short-lived connections in SnapshotDescriptionUtils.validate() — one for isSecurityAvailable() to check hbase:acl table existence, and another in writeAclToSnapshotDescription() to read ACL data. In Kerberos environments with ZKConnectionRegistry, each connection triggered a new ZK session with GSSAPI authentication and a TGS request to the KDC, causing excessive KDC load during batch snapshot operations. This patch reuses the caller's existing connection instead of creating new ones: - isSecurityAvailable() now accepts a Connection parameter - writeAclToSnapshotDescription() passes the shared connection's Table to PermissionStorage.getTablePermissions() - All callers (MasterRpcServices, RestoreSnapshotProcedure, CloneSnapshotProcedure) pass through their available connection The original validate(SnapshotDescription, Configuration) and isSecurityAvailable(Configuration) signatures are preserved as overloads that delegate to the new Connection-based methods, so existing callers (including tests) keep working unchanged. The new Connection-based overloads are documented as preferred for callers that already hold a Connection, to avoid the per-call connection setup cost. Co-authored-by: Jeongmin Ju <mini666@daum.net> Signed-off-by: Junegunn Choi <junegunn@apache.org>
1 parent 198589f commit d073b2b

5 files changed

Lines changed: 58 additions & 14 deletions

File tree

hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,8 +1701,8 @@ public SnapshotResponse snapshot(RpcController controller, SnapshotRequest reque
17011701
LOG.info(master.getClientIdAuditPrefix() + " snapshot request for:"
17021702
+ ClientSnapshotDescriptionUtils.toString(request.getSnapshot()));
17031703
// get the snapshot information
1704-
SnapshotDescription snapshot =
1705-
SnapshotDescriptionUtils.validate(request.getSnapshot(), master.getConfiguration());
1704+
SnapshotDescription snapshot = SnapshotDescriptionUtils.validate(master.getConnection(),
1705+
request.getSnapshot(), master.getConfiguration());
17061706
// send back the max amount of time the client should wait for the snapshot to complete
17071707
long waitTime = SnapshotDescriptionUtils.getMaxMasterTimeout(master.getConfiguration(),
17081708
snapshot.getType(), SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME);

hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/CloneSnapshotProcedure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ private void restoreSnapshotAcl(MasterProcedureEnv env) throws IOException {
133133
Configuration conf = env.getMasterServices().getConfiguration();
134134
if (
135135
restoreAcl && snapshot.hasUsersAndPermissions() && snapshot.getUsersAndPermissions() != null
136-
&& SnapshotDescriptionUtils.isSecurityAvailable(conf)
136+
&& SnapshotDescriptionUtils.isSecurityAvailable(env.getMasterServices().getConnection())
137137
) {
138138
RestoreSnapshotHelper.restoreSnapshotAcl(snapshot, tableDescriptor.getTableName(), conf);
139139
}

hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/RestoreSnapshotProcedure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ private void addRegionsToInMemoryStates(List<RegionInfo> regionInfos, MasterProc
555555
private void restoreSnapshotAcl(final MasterProcedureEnv env) throws IOException {
556556
if (
557557
restoreAcl && snapshot.hasUsersAndPermissions() && snapshot.getUsersAndPermissions() != null
558-
&& SnapshotDescriptionUtils.isSecurityAvailable(env.getMasterServices().getConfiguration())
558+
&& SnapshotDescriptionUtils.isSecurityAvailable(env.getMasterServices().getConnection())
559559
) {
560560
// restore acl of snapshot to table.
561561
RestoreSnapshotHelper.restoreSnapshotAcl(snapshot, TableName.valueOf(snapshot.getTable()),

hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/PermissionStorage.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,13 @@ static Map<byte[], ListMultimap<String, UserPermission>> loadAll(Configuration c
480480

481481
public static ListMultimap<String, UserPermission> getTablePermissions(Configuration conf,
482482
TableName tableName) throws IOException {
483-
return getPermissions(conf, tableName != null ? tableName.getName() : null, null, null, null,
484-
null, false);
483+
return getTablePermissions(conf, tableName, null);
484+
}
485+
486+
public static ListMultimap<String, UserPermission> getTablePermissions(Configuration conf,
487+
TableName tableName, Table t) throws IOException {
488+
return getPermissions(conf, tableName != null ? tableName.getName() : null, t, null, null, null,
489+
false);
485490
}
486491

487492
public static ListMultimap<String, UserPermission> getNamespacePermissions(Configuration conf,

hbase-server/src/main/java/org/apache/hadoop/hbase/snapshot/SnapshotDescriptionUtils.java

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.hadoop.hbase.client.Admin;
3535
import org.apache.hadoop.hbase.client.Connection;
3636
import org.apache.hadoop.hbase.client.ConnectionFactory;
37+
import org.apache.hadoop.hbase.client.Table;
3738
import org.apache.hadoop.hbase.ipc.RpcServer;
3839
import org.apache.hadoop.hbase.security.User;
3940
import org.apache.hadoop.hbase.security.access.AccessChecker;
@@ -317,10 +318,36 @@ private static Path getDefaultWorkingSnapshotDir(final Path rootDir) {
317318
*/
318319
public static SnapshotDescription validate(SnapshotDescription snapshot, Configuration conf)
319320
throws IllegalArgumentException, IOException {
321+
requireHasTable(snapshot);
322+
try (Connection conn = ConnectionFactory.createConnection(conf)) {
323+
return validate(conn, snapshot, conf);
324+
}
325+
}
326+
327+
private static void requireHasTable(SnapshotDescription snapshot) {
320328
if (!snapshot.hasTable()) {
321329
throw new IllegalArgumentException(
322330
"Descriptor doesn't apply to a table, so we can't build it.");
323331
}
332+
}
333+
334+
/**
335+
* Convert the passed snapshot description into a 'full' snapshot description based on default
336+
* parameters, if none have been supplied. This resolves any 'optional' parameters that aren't
337+
* supplied to their default values. Prefer this overload over
338+
* {@link #validate(SnapshotDescription, Configuration)} when the caller already holds a
339+
* {@link Connection}, to avoid the cost of creating a fresh connection (which involves a ZK
340+
* session and, in Kerberos environments, a TGS request to the KDC) for each call.
341+
* @param conn connection to use for reading ACL information when security is enabled
342+
* @param snapshot general snapshot descriptor
343+
* @param conf Configuration to read configured snapshot defaults if snapshot is not complete
344+
* @return a valid snapshot description
345+
* @throws IllegalArgumentException if the {@link SnapshotDescription} is not a complete
346+
* {@link SnapshotDescription}.
347+
*/
348+
public static SnapshotDescription validate(Connection conn, SnapshotDescription snapshot,
349+
Configuration conf) throws IllegalArgumentException, IOException {
350+
requireHasTable(snapshot);
324351

325352
SnapshotDescription.Builder builder = snapshot.toBuilder();
326353

@@ -365,8 +392,8 @@ public static SnapshotDescription validate(SnapshotDescription snapshot, Configu
365392
snapshot = builder.build();
366393

367394
// set the acl to snapshot if security feature is enabled.
368-
if (isSecurityAvailable(conf)) {
369-
snapshot = writeAclToSnapshotDescription(snapshot, conf);
395+
if (isSecurityAvailable(conn)) {
396+
snapshot = writeAclToSnapshotDescription(conn, snapshot, conf);
370397
}
371398
return snapshot;
372399
}
@@ -491,20 +518,32 @@ public static boolean isSnapshotOwner(org.apache.hadoop.hbase.client.SnapshotDes
491518
}
492519

493520
public static boolean isSecurityAvailable(Configuration conf) throws IOException {
494-
try (Connection conn = ConnectionFactory.createConnection(conf);
495-
Admin admin = conn.getAdmin()) {
521+
try (Connection conn = ConnectionFactory.createConnection(conf)) {
522+
return isSecurityAvailable(conn);
523+
}
524+
}
525+
526+
/**
527+
* Prefer this overload over {@link #isSecurityAvailable(Configuration)} when the caller already
528+
* holds a {@link Connection}, to avoid the cost of creating a fresh connection (which involves a
529+
* ZK session and, in Kerberos environments, a TGS request to the KDC) for each call.
530+
*/
531+
public static boolean isSecurityAvailable(Connection conn) throws IOException {
532+
try (Admin admin = conn.getAdmin()) {
496533
return admin.tableExists(PermissionStorage.ACL_TABLE_NAME);
497534
}
498535
}
499536

500-
private static SnapshotDescription writeAclToSnapshotDescription(SnapshotDescription snapshot,
501-
Configuration conf) throws IOException {
537+
private static SnapshotDescription writeAclToSnapshotDescription(Connection conn,
538+
SnapshotDescription snapshot, Configuration conf) throws IOException {
502539
ListMultimap<String, UserPermission> perms =
503540
User.runAsLoginUser(new PrivilegedExceptionAction<ListMultimap<String, UserPermission>>() {
504541
@Override
505542
public ListMultimap<String, UserPermission> run() throws Exception {
506-
return PermissionStorage.getTablePermissions(conf,
507-
TableName.valueOf(snapshot.getTable()));
543+
try (Table aclTable = conn.getTable(PermissionStorage.ACL_TABLE_NAME)) {
544+
return PermissionStorage.getTablePermissions(conf,
545+
TableName.valueOf(snapshot.getTable()), aclTable);
546+
}
508547
}
509548
});
510549
return snapshot.toBuilder()

0 commit comments

Comments
 (0)