Skip to content

Commit 4143909

Browse files
author
Hernan Gelaf-Romer
committed
SnapshotProcedure and EnableTableProcedure can cause a deadlock
1 parent a71288f commit 4143909

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.apache.hadoop.hbase.monitoring.MonitoredTask;
4343
import org.apache.hadoop.hbase.monitoring.TaskMonitor;
4444
import org.apache.hadoop.hbase.procedure2.Procedure;
45+
import org.apache.hadoop.hbase.procedure2.ProcedureAbortedException;
4546
import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
4647
import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
4748
import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
@@ -143,13 +144,18 @@ protected Flow executeFromState(MasterProcedureEnv env, SnapshotState state)
143144
setNextState(SnapshotState.SNAPSHOT_WRITE_SNAPSHOT_INFO);
144145
return Flow.HAS_MORE_STATE;
145146
case SNAPSHOT_WRITE_SNAPSHOT_INFO:
146-
SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, workingDir, workingDirFS);
147147
TableState tableState =
148148
env.getMasterServices().getTableStateManager().getTableState(snapshotTable);
149149
if (tableState.isEnabled()) {
150+
SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, workingDir, workingDirFS);
150151
setNextState(SnapshotState.SNAPSHOT_SNAPSHOT_ONLINE_REGIONS);
151152
} else if (tableState.isDisabled()) {
153+
SnapshotDescriptionUtils.writeSnapshotInfo(snapshot, workingDir, workingDirFS);
152154
setNextState(SnapshotState.SNAPSHOT_SNAPSHOT_CLOSED_REGIONS);
155+
} else {
156+
setState(ProcedureState.FAILED);
157+
throw new ProcedureAbortedException(
158+
"Table " + snapshotTable + " is not in a valid state for snapshot: " + tableState);
153159
}
154160
return Flow.HAS_MORE_STATE;
155161
case SNAPSHOT_SNAPSHOT_ONLINE_REGIONS:

hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSnapshotProcedureConcurrently.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@
2020
import static org.junit.Assert.assertEquals;
2121
import static org.junit.Assert.assertFalse;
2222
import static org.junit.Assert.fail;
23-
2423
import java.io.IOException;
2524
import java.util.Arrays;
2625
import java.util.Comparator;
2726
import java.util.List;
27+
import java.util.concurrent.atomic.AtomicBoolean;
2828
import java.util.stream.Collectors;
2929
import org.apache.hadoop.hbase.HBaseClassTestRule;
30+
import org.apache.hadoop.hbase.TableName;
3031
import org.apache.hadoop.hbase.client.SnapshotDescription;
3132
import org.apache.hadoop.hbase.client.SnapshotType;
33+
import org.apache.hadoop.hbase.client.TableState;
3234
import org.apache.hadoop.hbase.master.snapshot.SnapshotManager;
3335
import org.apache.hadoop.hbase.procedure2.Procedure;
3436
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
@@ -39,7 +41,6 @@
3941
import org.junit.ClassRule;
4042
import org.junit.Test;
4143
import org.junit.experimental.categories.Category;
42-
4344
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
4445
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
4546
import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
@@ -136,4 +137,46 @@ public void run() {
136137
SnapshotTestingUtils.confirmSnapshotValid(TEST_UTIL, snapshotProto, TABLE_NAME, CF);
137138
SnapshotTestingUtils.confirmSnapshotValid(TEST_UTIL, snapshotOnSameTableProto, TABLE_NAME, CF);
138139
}
140+
141+
@Test
142+
public void testItFailsIfTableIsNotDisabledOrEnabled() throws Exception {
143+
master.getTableStateManager().setTableState(TABLE_NAME, TableState.State.ENABLING);
144+
final AtomicBoolean failed = new AtomicBoolean(false);
145+
Thread snapshotThread = new Thread("snapshot") {
146+
@Override
147+
public void run() {
148+
try {
149+
TEST_UTIL.getAdmin().snapshot(snapshot);
150+
} catch (IOException e) {
151+
failed.set(true);
152+
}
153+
}
154+
};
155+
snapshotThread.start();
156+
SnapshotManager sm = master.getSnapshotManager();
157+
TEST_UTIL.waitFor(60000, 50,
158+
() -> !sm.isTakingSnapshot(TABLE_NAME) && sm.isTableTakingAnySnapshot(TABLE_NAME));
159+
160+
TestEnableTableProcedure enableTable =
161+
new TestEnableTableProcedure(master.getMasterProcedureExecutor().getEnvironment(), TABLE_NAME,
162+
MasterProcedureProtos.EnableTableState.ENABLE_TABLE_SET_ENABLED_TABLE_STATE);
163+
master.getMasterProcedureExecutor().submitProcedure(enableTable);
164+
TEST_UTIL.waitFor(60000,
165+
() -> master.getTableStateManager().getTableState(TABLE_NAME).isEnabled() && failed.get());
166+
TEST_UTIL.waitFor(60000, failed::get);
167+
}
168+
169+
// Needs to be publicly accessible for Procedure validation
170+
public static class TestEnableTableProcedure extends EnableTableProcedure {
171+
// Necessary for Procedure validation
172+
public TestEnableTableProcedure() {
173+
}
174+
175+
public TestEnableTableProcedure(MasterProcedureEnv env, TableName tableName,
176+
MasterProcedureProtos.EnableTableState state) {
177+
super(env, tableName);
178+
this.setNextState(state);
179+
}
180+
181+
}
139182
}

0 commit comments

Comments
 (0)