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