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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022 023import java.io.IOException; 024import org.apache.hadoop.fs.FileSystem; 025import org.apache.hadoop.fs.Path; 026import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 027import org.apache.hadoop.hbase.procedure2.store.ProcedureStore; 028import org.apache.hadoop.hbase.testclassification.MasterTests; 029import org.apache.hadoop.hbase.testclassification.SmallTests; 030import org.junit.jupiter.api.AfterEach; 031import org.junit.jupiter.api.BeforeEach; 032import org.junit.jupiter.api.Tag; 033import org.junit.jupiter.api.Test; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037@Tag(MasterTests.TAG) 038@Tag(SmallTests.TAG) 039public class TestChildProcedures { 040 041 private static final Logger LOG = LoggerFactory.getLogger(TestChildProcedures.class); 042 043 private static final int PROCEDURE_EXECUTOR_SLOTS = 1; 044 045 private static TestProcEnv procEnv; 046 private static ProcedureExecutor<TestProcEnv> procExecutor; 047 private static ProcedureStore procStore; 048 049 private HBaseCommonTestingUtil htu; 050 private FileSystem fs; 051 private Path testDir; 052 private Path logDir; 053 054 @BeforeEach 055 public void setUp() throws IOException { 056 htu = new HBaseCommonTestingUtil(); 057 testDir = htu.getDataTestDir(); 058 fs = testDir.getFileSystem(htu.getConfiguration()); 059 assertTrue(testDir.depth() > 1); 060 061 logDir = new Path(testDir, "proc-logs"); 062 procEnv = new TestProcEnv(); 063 procStore = ProcedureTestingUtility.createStore(htu.getConfiguration(), logDir); 064 procExecutor = new ProcedureExecutor<>(htu.getConfiguration(), procEnv, procStore); 065 procExecutor.testing = new ProcedureExecutor.Testing(); 066 procStore.start(PROCEDURE_EXECUTOR_SLOTS); 067 ProcedureTestingUtility.initAndStartWorkers(procExecutor, PROCEDURE_EXECUTOR_SLOTS, true); 068 } 069 070 @AfterEach 071 public void tearDown() throws IOException { 072 procExecutor.stop(); 073 procStore.stop(false); 074 fs.delete(logDir, true); 075 } 076 077 @Test 078 public void testChildLoad() throws Exception { 079 procEnv.toggleKillBeforeStoreUpdate = false; 080 081 TestRootProcedure proc = new TestRootProcedure(); 082 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 083 ProcedureTestingUtility.restart(procExecutor); 084 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 085 086 assertTrue(procExecutor.isFinished(procId), "expected completed proc"); 087 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 088 } 089 090 @Test 091 public void testChildLoadWithSteppedRestart() throws Exception { 092 procEnv.toggleKillBeforeStoreUpdate = true; 093 094 TestRootProcedure proc = new TestRootProcedure(); 095 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 096 int restartCount = 0; 097 while (!procExecutor.isFinished(procId)) { 098 ProcedureTestingUtility.restart(procExecutor); 099 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 100 restartCount++; 101 } 102 assertEquals(3, restartCount); 103 assertTrue(procExecutor.isFinished(procId), "expected completed proc"); 104 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 105 } 106 107 /** 108 * Test the state setting that happens after store to WAL; in particular the bit where we set the 109 * parent runnable again after its children have all completed successfully. See HBASE-20978. 110 */ 111 @Test 112 public void testChildLoadWithRestartAfterChildSuccess() throws Exception { 113 procEnv.toggleKillAfterStoreUpdate = true; 114 115 TestRootProcedure proc = new TestRootProcedure(); 116 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 117 int restartCount = 0; 118 while (!procExecutor.isFinished(procId)) { 119 ProcedureTestingUtility.restart(procExecutor); 120 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 121 restartCount++; 122 } 123 assertEquals(4, restartCount); 124 assertTrue(procExecutor.isFinished(procId), "expected completed proc"); 125 ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId); 126 } 127 128 @Test 129 public void testChildRollbackLoad() throws Exception { 130 procEnv.toggleKillBeforeStoreUpdate = false; 131 procEnv.triggerRollbackOnChild = true; 132 133 TestRootProcedure proc = new TestRootProcedure(); 134 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 135 ProcedureTestingUtility.restart(procExecutor); 136 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 137 138 assertProcFailed(procId); 139 } 140 141 @Test 142 public void testChildRollbackLoadWithSteppedRestart() throws Exception { 143 procEnv.toggleKillBeforeStoreUpdate = true; 144 procEnv.triggerRollbackOnChild = true; 145 146 TestRootProcedure proc = new TestRootProcedure(); 147 long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc); 148 int restartCount = 0; 149 while (!procExecutor.isFinished(procId)) { 150 ProcedureTestingUtility.restart(procExecutor); 151 ProcedureTestingUtility.waitProcedure(procExecutor, proc); 152 restartCount++; 153 } 154 assertEquals(2, restartCount); 155 assertProcFailed(procId); 156 } 157 158 private void assertProcFailed(long procId) { 159 assertTrue(procExecutor.isFinished(procId), "expected completed proc"); 160 Procedure<?> result = procExecutor.getResult(procId); 161 assertTrue(result.isFailed()); 162 LOG.info(result.getException().getMessage()); 163 } 164 165 public static class TestRootProcedure extends SequentialProcedure<TestProcEnv> { 166 public TestRootProcedure() { 167 } 168 169 @Override 170 public Procedure[] execute(TestProcEnv env) { 171 if (env.toggleKillBeforeStoreUpdate) { 172 ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor); 173 } 174 if (env.toggleKillAfterStoreUpdate) { 175 ProcedureTestingUtility.toggleKillAfterStoreUpdate(procExecutor); 176 } 177 return new Procedure[] { new TestChildProcedure(), new TestChildProcedure() }; 178 } 179 180 @Override 181 public void rollback(TestProcEnv env) { 182 } 183 184 @Override 185 public boolean abort(TestProcEnv env) { 186 return false; 187 } 188 } 189 190 public static class TestChildProcedure extends SequentialProcedure<TestProcEnv> { 191 public TestChildProcedure() { 192 } 193 194 @Override 195 public Procedure[] execute(TestProcEnv env) { 196 if (env.toggleKillBeforeStoreUpdate) { 197 ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor); 198 } 199 if (env.triggerRollbackOnChild) { 200 setFailure("test", new Exception("test")); 201 } 202 return null; 203 } 204 205 @Override 206 public void rollback(TestProcEnv env) { 207 } 208 209 @Override 210 public boolean abort(TestProcEnv env) { 211 return false; 212 } 213 } 214 215 private static class TestProcEnv { 216 public boolean toggleKillBeforeStoreUpdate = false; 217 public boolean toggleKillAfterStoreUpdate = false; 218 public boolean triggerRollbackOnChild = false; 219 } 220}