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, 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  /**
114   * Test the state setting that happens after store to WAL; in particular the bit where we
115   * set the parent runnable again after its children have all completed successfully.
116   * See HBASE-20978.
117   */
118  @Test
119  public void testChildLoadWithRestartAfterChildSuccess() throws Exception {
120    procEnv.toggleKillAfterStoreUpdate = true;
121
122    TestRootProcedure proc = new TestRootProcedure();
123    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
124    int restartCount = 0;
125    while (!procExecutor.isFinished(procId)) {
126      ProcedureTestingUtility.restart(procExecutor);
127      ProcedureTestingUtility.waitProcedure(procExecutor, proc);
128      restartCount++;
129    }
130    assertEquals(4, restartCount);
131    assertTrue("expected completed proc", procExecutor.isFinished(procId));
132    ProcedureTestingUtility.assertProcNotFailed(procExecutor, procId);
133  }
134
135  @Test
136  public void testChildRollbackLoad() throws Exception {
137    procEnv.toggleKillBeforeStoreUpdate = false;
138    procEnv.triggerRollbackOnChild = true;
139
140    TestRootProcedure proc = new TestRootProcedure();
141    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
142    ProcedureTestingUtility.restart(procExecutor);
143    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
144
145    assertProcFailed(procId);
146  }
147
148  @Test
149  public void testChildRollbackLoadWithSteppedRestart() throws Exception {
150    procEnv.toggleKillBeforeStoreUpdate = true;
151    procEnv.triggerRollbackOnChild = true;
152
153    TestRootProcedure proc = new TestRootProcedure();
154    long procId = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
155    int restartCount = 0;
156    while (!procExecutor.isFinished(procId)) {
157      ProcedureTestingUtility.restart(procExecutor);
158      ProcedureTestingUtility.waitProcedure(procExecutor, proc);
159      restartCount++;
160    }
161    assertEquals(2, restartCount);
162    assertProcFailed(procId);
163  }
164
165  private void assertProcFailed(long procId) {
166    assertTrue("expected completed proc", procExecutor.isFinished(procId));
167    Procedure<?> result = procExecutor.getResult(procId);
168    assertEquals(true, result.isFailed());
169    LOG.info(result.getException().getMessage());
170  }
171
172  public static class TestRootProcedure extends SequentialProcedure<TestProcEnv> {
173    public TestRootProcedure() {}
174
175    @Override
176    public Procedure[] execute(TestProcEnv env) {
177      if (env.toggleKillBeforeStoreUpdate) {
178        ProcedureTestingUtility.toggleKillBeforeStoreUpdate(procExecutor);
179      }
180      if (env.toggleKillAfterStoreUpdate) {
181        ProcedureTestingUtility.toggleKillAfterStoreUpdate(procExecutor);
182      }
183      return new Procedure[] { new TestChildProcedure(), new TestChildProcedure() };
184    }
185
186    @Override
187    public void rollback(TestProcEnv env) {
188    }
189
190    @Override
191    public boolean abort(TestProcEnv env) {
192      return false;
193    }
194  }
195
196  public static class TestChildProcedure extends SequentialProcedure<TestProcEnv> {
197    public TestChildProcedure() {}
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}