Skip to content

Commit af18670

Browse files
committed
HBASE-24805 HBaseTestingUtility.getConnection should be threadsafe
* refactor how we use connection to rely on the access method * refactor initialization and cleanup of the shared connection * incompatibly change HCTU's Configuration member variable to be final so it can be safely accessed from multiple threads. Closes #2188 adapted for jdk7 Signed-off-by: Viraj Jasani <vjasani@apache.org> (cherry picked from commit 86ebbdd) (cherry picked from commit 0806349)
1 parent 1f0abf8 commit af18670

2 files changed

Lines changed: 39 additions & 19 deletions

File tree

hbase-common/src/test/java/org/apache/hadoop/hbase/HBaseCommonTestingUtility.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
public class HBaseCommonTestingUtility {
4141
protected static final Log LOG = LogFactory.getLog(HBaseCommonTestingUtility.class);
4242

43-
protected Configuration conf;
43+
protected final Configuration conf;
4444

4545
public HBaseCommonTestingUtility() {
4646
this(null);

hbase-server/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.util.Set;
4747
import java.util.TreeSet;
4848
import java.util.UUID;
49+
import java.util.concurrent.atomic.AtomicReference;
4950
import java.util.concurrent.TimeUnit;
5051

5152
import org.apache.commons.io.FileUtils;
@@ -190,10 +191,7 @@ public class HBaseTestingUtility extends HBaseCommonTestingUtility {
190191
* HBaseTestingUtility*/
191192
private Path dataTestDirOnTestFS = null;
192193

193-
/**
194-
* Shared cluster connection.
195-
*/
196-
private volatile Connection connection;
194+
private final AtomicReference<Connection> connectionRef = new AtomicReference<>();
197195

198196
/**
199197
* System property key to get test directory value.
@@ -1170,10 +1168,6 @@ public MiniHBaseCluster getMiniHBaseCluster() {
11701168
*/
11711169
public void shutdownMiniCluster() throws Exception {
11721170
LOG.info("Shutting down minicluster");
1173-
if (this.connection != null && !this.connection.isClosed()) {
1174-
this.connection.close();
1175-
this.connection = null;
1176-
}
11771171
shutdownMiniHBaseCluster();
11781172
if (!this.passedZkCluster){
11791173
shutdownMiniZKCluster();
@@ -1203,10 +1197,7 @@ public boolean cleanupTestDir() {
12031197
* @throws IOException
12041198
*/
12051199
public void shutdownMiniHBaseCluster() throws IOException {
1206-
if (hbaseAdmin != null) {
1207-
hbaseAdmin.close0();
1208-
hbaseAdmin = null;
1209-
}
1200+
closeConnection();
12101201

12111202
// unset the configuration for MIN and MAX RS to start
12121203
conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1);
@@ -3020,16 +3011,26 @@ public HBaseCluster getHBaseClusterInterface() {
30203011
}
30213012

30223013
/**
3023-
* Get a Connection to the cluster.
3024-
* Not thread-safe (This class needs a lot of work to make it thread-safe).
3014+
* Get a shared Connection to the cluster.
3015+
* this method is threadsafe.
30253016
* @return A Connection that can be shared. Don't close. Will be closed on shutdown of cluster.
30263017
* @throws IOException
30273018
*/
30283019
public Connection getConnection() throws IOException {
3029-
if (this.connection == null) {
3030-
this.connection = ConnectionFactory.createConnection(this.conf);
3020+
Connection connection = this.connectionRef.get();
3021+
while (connection == null) {
3022+
connection = ConnectionFactory.createConnection(this.conf);
3023+
if (! this.connectionRef.compareAndSet(null, connection)) {
3024+
try {
3025+
connection.close();
3026+
} catch (IOException exception) {
3027+
LOG.debug("Ignored failure while closing connection on contended connection creation.",
3028+
exception);
3029+
}
3030+
connection = this.connectionRef.get();
3031+
}
30313032
}
3032-
return this.connection;
3033+
return connection;
30333034
}
30343035

30353036
/**
@@ -3067,6 +3068,25 @@ private synchronized void close0() throws IOException {
30673068
}
30683069
}
30693070

3071+
public void closeConnection() throws IOException {
3072+
if (hbaseAdmin != null) {
3073+
try {
3074+
hbaseAdmin.close0();
3075+
} catch (IOException exception) {
3076+
LOG.debug("Ignored failure while closing admin.", exception);
3077+
}
3078+
hbaseAdmin = null;
3079+
}
3080+
Connection connection = this.connectionRef.getAndSet(null);
3081+
if (connection != null) {
3082+
try {
3083+
connection.close();
3084+
} catch (IOException exception) {
3085+
LOG.debug("Ignored failure while closing connection.", exception);
3086+
}
3087+
}
3088+
}
3089+
30703090
/**
30713091
* Returns a ZooKeeperWatcher instance.
30723092
* This instance is shared between HBaseTestingUtility instance users.
@@ -3240,7 +3260,7 @@ public String explainTableAvailability(TableName tableName) throws IOException {
32403260
.getRegionAssignments();
32413261
final List<Pair<HRegionInfo, ServerName>> metaLocations =
32423262
MetaTableAccessor
3243-
.getTableRegionsAndLocations(getZooKeeperWatcher(), connection, tableName);
3263+
.getTableRegionsAndLocations(getZooKeeperWatcher(), getConnection(), tableName);
32443264
for (Pair<HRegionInfo, ServerName> metaLocation : metaLocations) {
32453265
HRegionInfo hri = metaLocation.getFirst();
32463266
ServerName sn = metaLocation.getSecond();

0 commit comments

Comments
 (0)