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.HBaseCommonTestingUtility;
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 HBaseCommonTestingUtility 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 HBaseCommonTestingUtility();
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, 0, false,
073            true);
074  }
075
076  @After
077  public void tearDown() throws IOException {
078    procExecutor.stop();
079    procStore.stop(false);
080    fs.delete(logDir, true);
081  }
082
083  @Test
084  public void testChildLoad() throws Exception {
085    procEnv.toggleKillBeforeStoreUpdate = false;
086
087    TestRootProcedure proc = new TestRootProcedure();
088    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
089    ProcedureTestingUtility.restart(procExecutor);
090    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
091
092    assertTrue("expected completed proc", procExecutor.isFinished(procId));
093    ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId);
094  }
095
096  @Test
097  public void testChildLoadWithSteppedRestart() throws Exception {
098    procEnv.toggleKillBeforeStoreUpdate = true;
099
100    TestRootProcedure proc = new TestRootProcedure();
101    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
102    int restartCount = 0;
103    while (!procExecutor.isFinished(procId)) {
104      ProcedureTestingUtility.restart(procExecutor);
105      ProcedureTestingUtility.waitProcedure(procExecutor, proc);
106      restartCount++;
107    }
108    assertEquals(3, restartCount);
109    assertTrue("expected completed proc", procExecutor.isFinished(procId));
110    ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId);
111  }
112
113
114  /**
115   * Test the state setting that happens after store to WAL; in particular the bit where we
116   * set the parent runnable again after its children have all completed successfully.
117   * See HBASE-20978.
118   */
119  @Test
120  public void testChildLoadWithRestartAfterChildSuccess() throws Exception {
121    procEnv.toggleKillAfterStoreUpdate = true;
122
123    TestRootProcedure proc = new TestRootProcedure();
124    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
125    int restartCount = 0;
126    while (!procExecutor.isFinished(procId)) {
127      ProcedureTestingUtility.restart(procExecutor);
128      ProcedureTestingUtility.waitProcedure(procExecutor, proc);
129      restartCount++;
130    }
131    assertEquals(4, restartCount);
132    assertTrue("expected completed proc", procExecutor.isFinished(procId));
133    ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId);
134  }
135
136  @Test
137  public void testChildRollbackLoad() throws Exception {
138    procEnv.toggleKillBeforeStoreUpdate = false;
139    procEnv.triggerRollbackOnChild = true;
140
141    TestRootProcedure proc = new TestRootProcedure();
142    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
143    ProcedureTestingUtility.restart(procExecutor);
144    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
145
146    assertProcFailed(procId);
147  }
148
149  @Test
150  public void testChildRollbackLoadWithSteppedRestart() throws Exception {
151    procEnv.toggleKillBeforeStoreUpdate = true;
152    procEnv.triggerRollbackOnChild = true;
153
154    TestRootProcedure proc = new TestRootProcedure();
155    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
156    int restartCount = 0;
157    while (!procExecutor.isFinished(procId)) {
158      ProcedureTestingUtility.restart(procExecutor);
159      ProcedureTestingUtility.waitProcedure(procExecutor, proc);
160      restartCount++;
161    }
162    assertEquals(2, restartCount);
163    assertProcFailed(procId);
164  }
165
166  private void assertProcFailed(long procId) {
167    assertTrue("expected completed proc", procExecutor.isFinished(procId));
168    Procedure<?> result = procExecutor.getResult(procId);
169    assertEquals(true, result.isFailed());
170    LOG.info(result.getException().getMessage());
171  }
172
173  public static class TestRootProcedure extends SequentialProcedure<TestProcEnv> {
174    public TestRootProcedure() {}
175
176    @Override
177    public Procedure[] execute(TestProcEnv env) {
178      if (env.toggleKillBeforeStoreUpdate) {
179        ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor);
180      }
181      if (env.toggleKillAfterStoreUpdate) {
182        ProcedureTestingUtility.toggleKillAfterStoreUpdate(procExecutor);
183      }
184      return new Procedure[] { new TestChildProcedure(), new TestChildProcedure() };
185    }
186
187    @Override
188    public void rollback(TestProcEnv env) {
189    }
190
191    @Override
192    public boolean abort(TestProcEnv env) {
193      return false;
194    }
195  }
196
197  public static class TestChildProcedure extends SequentialProcedure<TestProcEnv> {
198    public TestChildProcedure() {}
199
200    @Override
201    public Procedure[] execute(TestProcEnv env) {
202      if (env.toggleKillBeforeStoreUpdate) {
203        ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor);
204      }
205      if (env.triggerRollbackOnChild) {
206        setFailure("test", new Exception("test"));
207      }
208      return null;
209    }
210
211    @Override
212    public void rollback(TestProcEnv env) {
213    }
214
215    @Override
216    public boolean abort(TestProcEnv env) {
217      return false;
218    }
219  }
220
221  private static class TestProcEnv {
222    public boolean toggleKillBeforeStoreUpdate = false;
223    public boolean toggleKillAfterStoreUpdate = false;
224    public boolean triggerRollbackOnChild = false;
225  }
226}