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.store;
019
020import static org.junit.Assert.assertEquals;
021
022import java.io.IOException;
023import java.io.UncheckedIOException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027import java.util.stream.Collectors;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.procedure2.Procedure;
030import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
031import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
032import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
033import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
034import org.apache.hadoop.hbase.procedure2.store.ProcedureStore.ProcedureIterator;
035import org.apache.hadoop.hbase.testclassification.MasterTests;
036import org.apache.hadoop.hbase.testclassification.SmallTests;
037import org.junit.ClassRule;
038import org.junit.Test;
039import org.junit.experimental.categories.Category;
040
041import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos;
042
043@Category({ MasterTests.class, SmallTests.class })
044public class TestProcedureTree {
045
046  @ClassRule
047  public static final HBaseClassTestRule CLASS_RULE =
048    HBaseClassTestRule.forClass(TestProcedureTree.class);
049
050  public static final class TestProcedure extends Procedure<Void> {
051
052    @Override
053    public void setProcId(long procId) {
054      super.setProcId(procId);
055    }
056
057    @Override
058    public void setParentProcId(long parentProcId) {
059      super.setParentProcId(parentProcId);
060    }
061
062    @Override
063    public synchronized void addStackIndex(int index) {
064      super.addStackIndex(index);
065    }
066
067    @Override
068    protected Procedure<Void>[] execute(Void env)
069      throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException {
070      return null;
071    }
072
073    @Override
074    protected void rollback(Void env) throws IOException, InterruptedException {
075    }
076
077    @Override
078    protected boolean abort(Void env) {
079      return false;
080    }
081
082    @Override
083    protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
084    }
085
086    @Override
087    protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
088    }
089  }
090
091  private TestProcedure createProc(long procId, long parentProcId) {
092    TestProcedure proc = new TestProcedure();
093    proc.setProcId(procId);
094    if (parentProcId != Procedure.NO_PROC_ID) {
095      proc.setParentProcId(parentProcId);
096    }
097    return proc;
098  }
099
100  private List<ProcedureProtos.Procedure> toProtos(TestProcedure... procs) {
101    return Arrays.stream(procs).map(p -> {
102      try {
103        return ProcedureUtil.convertToProtoProcedure(p);
104      } catch (IOException e) {
105        throw new UncheckedIOException(e);
106      }
107    }).collect(Collectors.toList());
108  }
109
110  private List<TestProcedure> getProcs(ProcedureIterator iter) throws IOException {
111    List<TestProcedure> procs = new ArrayList<>();
112    while (iter.hasNext()) {
113      procs.add((TestProcedure) iter.next());
114    }
115    return procs;
116  }
117
118  @Test
119  public void testMissingStackId() throws IOException {
120    TestProcedure proc0 = createProc(1, Procedure.NO_PROC_ID);
121    proc0.addStackIndex(0);
122    TestProcedure proc1 = createProc(2, 1);
123    proc1.addStackIndex(1);
124    TestProcedure proc2 = createProc(3, 2);
125    proc2.addStackIndex(3);
126    ProcedureTree tree = ProcedureTree.build(toProtos(proc0, proc1, proc2));
127    List<TestProcedure> validProcs = getProcs(tree.getValidProcs());
128    assertEquals(0, validProcs.size());
129    List<TestProcedure> corruptedProcs = getProcs(tree.getCorruptedProcs());
130    assertEquals(3, corruptedProcs.size());
131    assertEquals(1, corruptedProcs.get(0).getProcId());
132    assertEquals(2, corruptedProcs.get(1).getProcId());
133    assertEquals(3, corruptedProcs.get(2).getProcId());
134  }
135
136  @Test
137  public void testDuplicatedStackId() throws IOException {
138    TestProcedure proc0 = createProc(1, Procedure.NO_PROC_ID);
139    proc0.addStackIndex(0);
140    TestProcedure proc1 = createProc(2, 1);
141    proc1.addStackIndex(1);
142    TestProcedure proc2 = createProc(3, 2);
143    proc2.addStackIndex(1);
144    ProcedureTree tree = ProcedureTree.build(toProtos(proc0, proc1, proc2));
145    List<TestProcedure> validProcs = getProcs(tree.getValidProcs());
146    assertEquals(0, validProcs.size());
147    List<TestProcedure> corruptedProcs = getProcs(tree.getCorruptedProcs());
148    assertEquals(3, corruptedProcs.size());
149    assertEquals(1, corruptedProcs.get(0).getProcId());
150    assertEquals(2, corruptedProcs.get(1).getProcId());
151    assertEquals(3, corruptedProcs.get(2).getProcId());
152  }
153
154  @Test
155  public void testOrphan() throws IOException {
156    TestProcedure proc0 = createProc(1, Procedure.NO_PROC_ID);
157    proc0.addStackIndex(0);
158    TestProcedure proc1 = createProc(2, 1);
159    proc1.addStackIndex(1);
160    TestProcedure proc2 = createProc(3, Procedure.NO_PROC_ID);
161    proc2.addStackIndex(0);
162    TestProcedure proc3 = createProc(5, 4);
163    proc3.addStackIndex(1);
164    ProcedureTree tree = ProcedureTree.build(toProtos(proc0, proc1, proc2, proc3));
165    List<TestProcedure> validProcs = getProcs(tree.getValidProcs());
166    assertEquals(3, validProcs.size());
167    List<TestProcedure> corruptedProcs = getProcs(tree.getCorruptedProcs());
168    assertEquals(1, corruptedProcs.size());
169    assertEquals(5, corruptedProcs.get(0).getProcId());
170    assertEquals(4, corruptedProcs.get(0).getParentProcId());
171  }
172
173}