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   * 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}