001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.procedure2; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022 023import java.io.IOException; 024import org.apache.hadoop.fs.FileSystem; 025import org.apache.hadoop.fs.Path; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 028import org.apache.hadoop.hbase.procedure2.store.ProcedureStore; 029import org.apache.hadoop.hbase.testclassification.MasterTests; 030import org.apache.hadoop.hbase.testclassification.SmallTests; 031import org.junit.After; 032import org.junit.Before; 033import org.junit.ClassRule; 034import org.junit.Test; 035import org.junit.experimental.categories.Category; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039@Category({ MasterTests.class, SmallTests.class }) 040public class TestChildProcedures { 041 042 @ClassRule 043 public static final HBaseClassTestRule CLASS_RULE = 044 HBaseClassTestRule.forClass(TestChildProcedures.class); 045 046 private static final Logger LOG = LoggerFactory.getLogger(TestChildProcedures.class); 047 048 private static final int PROCEDURE_EXECUTOR_SLOTS = 1; 049 050 private static TestProcEnv procEnv; 051 private static ProcedureExecutor<TestProcEnv> procExecutor; 052 private static ProcedureStore procStore; 053 054 private HBaseCommonTestingUtil htu; 055 private FileSystem fs; 056 private Path testDir; 057 private Path logDir; 058 059 @Before 060 public void setUp() throws IOException { 061 htu = new HBaseCommonTestingUtil(); 062 testDir = htu.getDataTestDir(); 063 fs = testDir.getFileSystem(htu.getConfiguration()); 064 assertTrue(testDir.depth() > 1); 065 066 logDir = new Path(testDir, "proc-logs"); 067 procEnv = new TestProcEnv(); 068 procStore = ProcedureTestingUtility.createStore(htu.getConfiguration(), logDir); 069 procExecutor = new ProcedureExecutor<>(htu.getConfiguration(), procEnv, procStore); 070 procExecutor.testing = new ProcedureExecutor.Testing(); 071 procStore.start(PROCEDURE_EXECUTOR_SLOTS); 072 ProcedureTestingUtility.initAndStartWorkers(procExecutor, PROCEDURE_EXECUTOR_SLOTS, true); 073 } 074 075 @After 076 public void tearDown() throws IOException { 077 procExecutor.stop(); 078 procStore.stop(false); 079 fs.delete(logDir, true); 080 } 081 082 @Test 083 public void testChildLoad() throws Exception { 084 procEnv.toggleKillBeforeStoreUpdate = false; 085 086 TestRootProcedure proc = new TestRootProcedure(); 087 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 088 ProcedureTestingUtility.restart(procExecutor); 089 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 090 091 assertTrue("expected completed proc", procExecutor.isFinished(procId)); 092 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 093 } 094 095 @Test 096 public void testChildLoadWithSteppedRestart() throws Exception { 097 procEnv.toggleKillBeforeStoreUpdate = true; 098 099 TestRootProcedure proc = new TestRootProcedure(); 100 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 101 int restartCount = 0; 102 while (!procExecutor.isFinished(procId)) { 103 ProcedureTestingUtility.restart(procExecutor); 104 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 105 restartCount++; 106 } 107 assertEquals(3, restartCount); 108 assertTrue("expected completed proc", procExecutor.isFinished(procId)); 109 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 110 } 111 112 /** 113 * Test the state setting that happens after store to WAL; in particular the bit where we set the 114 * parent runnable again after its children have all completed successfully. See HBASE-20978. 115 */ 116 @Test 117 public void testChildLoadWithRestartAfterChildSuccess() throws Exception { 118 procEnv.toggleKillAfterStoreUpdate = true; 119 120 TestRootProcedure proc = new TestRootProcedure(); 121 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 122 int restartCount = 0; 123 while (!procExecutor.isFinished(procId)) { 124 ProcedureTestingUtility.restart(procExecutor); 125 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 126 restartCount++; 127 } 128 assertEquals(4, restartCount); 129 assertTrue("expected completed proc", procExecutor.isFinished(procId)); 130 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 131 } 132 133 @Test 134 public void testChildRollbackLoad() throws Exception { 135 procEnv.toggleKillBeforeStoreUpdate = false; 136 procEnv.triggerRollbackOnChild = true; 137 138 TestRootProcedure proc = new TestRootProcedure(); 139 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 140 ProcedureTestingUtility.restart(procExecutor); 141 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 142 143 assertProcFailed(procId); 144 } 145 146 @Test 147 public void testChildRollbackLoadWithSteppedRestart() throws Exception { 148 procEnv.toggleKillBeforeStoreUpdate = true; 149 procEnv.triggerRollbackOnChild = true; 150 151 TestRootProcedure proc = new TestRootProcedure(); 152 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 153 int restartCount = 0; 154 while (!procExecutor.isFinished(procId)) { 155 ProcedureTestingUtility.restart(procExecutor); 156 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 157 restartCount++; 158 } 159 assertEquals(2, restartCount); 160 assertProcFailed(procId); 161 } 162 163 private void assertProcFailed(long procId) { 164 assertTrue("expected completed proc", procExecutor.isFinished(procId)); 165 Procedure<?> result = procExecutor.getResult(procId); 166 assertEquals(true, result.isFailed()); 167 LOG.info(result.getException().getMessage()); 168 } 169 170 public static class TestRootProcedure extends SequentialProcedure<TestProcEnv> { 171 public TestRootProcedure() { 172 } 173 174 @Override 175 public Procedure[] execute(TestProcEnv env) { 176 if (env.toggleKillBeforeStoreUpdate) { 177 ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor); 178 } 179 if (env.toggleKillAfterStoreUpdate) { 180 ProcedureTestingUtility.toggleKillAfterStoreUpdate(procExecutor); 181 } 182 return new Procedure[] { new TestChildProcedure(), new TestChildProcedure() }; 183 } 184 185 @Override 186 public void rollback(TestProcEnv env) { 187 } 188 189 @Override 190 public boolean abort(TestProcEnv env) { 191 return false; 192 } 193 } 194 195 public static class TestChildProcedure extends SequentialProcedure<TestProcEnv> { 196 public TestChildProcedure() { 197 } 198 199 @Override 200 public Procedure[] execute(TestProcEnv env) { 201 if (env.toggleKillBeforeStoreUpdate) { 202 ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor); 203 } 204 if (env.triggerRollbackOnChild) { 205 setFailure("test", new Exception("test")); 206 } 207 return null; 208 } 209 210 @Override 211 public void rollback(TestProcEnv env) { 212 } 213 214 @Override 215 public boolean abort(TestProcEnv env) { 216 return false; 217 } 218 } 219 220 private static class TestProcEnv { 221 public boolean toggleKillBeforeStoreUpdate = false; 222 public boolean toggleKillAfterStoreUpdate = false; 223 public boolean triggerRollbackOnChild = false; 224 } 225}