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.assertNotEquals;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import org.apache.hadoop.fs.FileSystem;
026import org.apache.hadoop.fs.Path;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
029import org.apache.hadoop.hbase.procedure2.store.ProcedureStore;
030import org.apache.hadoop.hbase.testclassification.MasterTests;
031import org.apache.hadoop.hbase.testclassification.SmallTests;
032import org.junit.After;
033import org.junit.Before;
034import org.junit.ClassRule;
035import org.junit.Test;
036import org.junit.experimental.categories.Category;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040@Category({MasterTests.class, SmallTests.class})
041public class TestProcedureMetrics {
042  @ClassRule
043  public static final HBaseClassTestRule CLASS_RULE =
044      HBaseClassTestRule.forClass(TestProcedureMetrics.class);
045
046  private static final Logger LOG = LoggerFactory.getLogger(TestProcedureMetrics.class);
047
048  private static final int PROCEDURE_EXECUTOR_SLOTS = 1;
049
050  private TestProcEnv procEnv;
051  private static ProcedureExecutor<TestProcEnv> procExecutor;
052  private ProcedureStore procStore;
053
054  private HBaseCommonTestingUtility htu;
055  private FileSystem fs;
056  private Path testDir;
057  private Path logDir;
058
059  private static int beginCount = 0;
060  private static int successCount = 0;
061  private static int failedCount = 0;
062
063  @Before
064  public void setUp() throws IOException {
065    htu = new HBaseCommonTestingUtility();
066    testDir = htu.getDataTestDir();
067    fs = testDir.getFileSystem(htu.getConfiguration());
068    assertTrue(testDir.depth() > 1);
069
070    logDir = new Path(testDir, "proc-logs");
071    procEnv = new TestProcEnv();
072    procStore = ProcedureTestingUtility.createStore(htu.getConfiguration(), logDir);
073    procExecutor = new ProcedureExecutor<TestProcEnv>(htu.getConfiguration(), procEnv, procStore);
074    procExecutor.testing = new ProcedureExecutor.Testing();
075    procStore.start(PROCEDURE_EXECUTOR_SLOTS);
076    ProcedureTestingUtility.initAndStartWorkers(procExecutor, PROCEDURE_EXECUTOR_SLOTS, true);
077  }
078
079  @After
080  public void tearDown() throws IOException {
081    procExecutor.stop();
082    procStore.stop(false);
083    fs.delete(logDir, true);
084  }
085
086  @Test
087  public void testMetricForSimpleProcedure() throws Exception {
088    // procedure that executes successfully
089    ProcedureMetrics proc = new ProcedureMetrics(true);
090    long id = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
091    assertNotEquals("ProcId zero!", 0, id);
092    beginCount++;
093    successCount++;
094    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
095    assertEquals("beginCount doesn't match!", beginCount, proc.beginCount);
096    assertEquals("successCount doesn't match!", successCount, proc.successCount);
097    assertEquals("failedCont doesn't match!", failedCount, proc.failedCount);
098  }
099
100  @Test
101  public void testMetricsForFailedProcedure() throws Exception {
102    // procedure that fails
103    ProcedureMetrics proc = new ProcedureMetrics(false);
104    long id = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
105    assertNotEquals("ProcId zero!", 0, id);
106    beginCount++;
107    failedCount++;
108    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
109    assertEquals("beginCount doesn't match!", beginCount, proc.beginCount);
110    assertEquals("successCount doesn't match!", successCount, proc.successCount);
111    assertEquals("failedCont doesn't match!", failedCount, proc.failedCount);
112  }
113
114  @Test
115  public void testMetricForYieldProcedure() throws Exception {
116    // procedure that yields
117    ProcedureMetrics proc = new ProcedureMetrics(true, true);
118    long id = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
119    assertNotEquals("ProcId zero!", 0, id);
120    beginCount++;
121    successCount++;
122    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
123    assertEquals("beginCount doesn't match!", beginCount, proc.beginCount);
124    assertEquals("successCount doesn't match!", successCount, proc.successCount);
125    assertEquals("failedCont doesn't match!", failedCount, proc.failedCount);
126  }
127
128  @Test
129  public void testMetricForFailedYiledProcedure() {
130    // procedure that yields and fails
131    ProcedureMetrics proc = new ProcedureMetrics(false, true);
132    long id = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
133    assertNotEquals("ProcId zero!", 0, id);
134    beginCount++;
135    failedCount++;
136    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
137    assertEquals("beginCount doesn't match!", beginCount, proc.beginCount);
138    assertEquals("successCount doesn't match!", successCount, proc.successCount);
139    assertEquals("failedCont doesn't match!", failedCount, proc.failedCount);
140  }
141
142  @Test
143  public void testMetricForProcedureWithChildren() throws Exception {
144    // Procedure that yileds with one of the sub-procedures that fail
145    int subProcCount = 10;
146    int failChildIndex = 2;
147    int yiledChildIndex = -1;
148    ProcedureMetrics[] subprocs = new ProcedureMetrics[subProcCount];
149    for (int i = 0; i < subProcCount; ++i) {
150      subprocs[i] = new ProcedureMetrics(failChildIndex != i, yiledChildIndex == i, 3);
151    }
152
153    ProcedureMetrics proc = new ProcedureMetrics(true, true, 3, subprocs);
154    long id = ProcedureTestingUtility.submitAndWait(procExecutor, proc);
155    assertNotEquals("ProcId zero!", 0, id);
156    beginCount += subProcCount + 1;
157    successCount += subProcCount - (failChildIndex + 1);
158    if (failChildIndex >= 0) {
159      failedCount += subProcCount + 1;
160    } else {
161      successCount++;
162    }
163    ProcedureTestingUtility.waitProcedure(procExecutor, proc);
164    assertEquals("beginCount doesn't match!", beginCount, proc.beginCount);
165    assertEquals("successCount doesn't match!", successCount, proc.successCount);
166    assertEquals("failedCont doesn't match!", failedCount, proc.failedCount);
167  }
168
169  private static class TestProcEnv {
170    public boolean toggleKillBeforeStoreUpdate = false;
171    public boolean triggerRollbackOnChild = false;
172  }
173
174  public static class ProcedureMetrics extends SequentialProcedure<TestProcEnv> {
175    public static long beginCount = 0;
176    public static long successCount = 0;
177    public static long failedCount = 0;
178
179    private boolean success;
180    private boolean yield;
181    private int yieldCount;
182    private int yieldNum;
183
184    private ProcedureMetrics[] subprocs = null;
185
186    public ProcedureMetrics() {
187      this(true);
188    }
189
190    public ProcedureMetrics(boolean success) {
191      this(success, true);
192    }
193
194    public ProcedureMetrics(boolean success, boolean yield) {
195      this(success, yield, 1);
196    }
197
198    public ProcedureMetrics(boolean success, boolean yield, int yieldCount) {
199      this(success, yield, yieldCount, null);
200    }
201
202    public ProcedureMetrics(boolean success, ProcedureMetrics[] subprocs) {
203      this(success, false, 1, subprocs);
204    }
205
206    public ProcedureMetrics(boolean success, boolean yield, int yieldCount,
207                            ProcedureMetrics[] subprocs) {
208      this.success = success;
209      this.yield = yield;
210      this.yieldCount = yieldCount;
211      this.subprocs = subprocs;
212      yieldNum = 0;
213    }
214
215    @Override
216    protected void updateMetricsOnSubmit(TestProcEnv env) {
217      beginCount++;
218    }
219
220    @Override
221    protected Procedure[] execute(TestProcEnv env) throws ProcedureYieldException,
222        ProcedureSuspendedException, InterruptedException {
223      if (this.yield) {
224        if (yieldNum < yieldCount) {
225          yieldNum++;
226          throw new ProcedureYieldException();
227        }
228      }
229      if (!this.success) {
230        setFailure("Failed", new InterruptedException("Failed"));
231        return null;
232      }
233      return subprocs;
234    }
235
236    @Override
237    protected void rollback(TestProcEnv env) throws IOException, InterruptedException {
238    }
239
240    @Override
241    protected boolean abort(TestProcEnv env) {
242      return false;
243    }
244
245    @Override
246    protected void updateMetricsOnFinish(final TestProcEnv env, final long time, boolean success) {
247      if (success) {
248        successCount++;
249      } else {
250        failedCount++;
251      }
252    }
253  }
254}