View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import java.io.IOException;
23  import java.util.concurrent.atomic.AtomicInteger;
24  import java.util.concurrent.TimeUnit;
25  import java.util.concurrent.TimeoutException;
26  
27  import org.apache.hadoop.hbase.testclassification.SmallTests;
28  import org.apache.hadoop.hbase.DoNotRetryIOException;
29  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultRequest;
30  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultResponse;
31  
32  import org.junit.Test;
33  import org.junit.experimental.categories.Category;
34  
35  import org.mockito.Mockito;
36  
37  import static org.junit.Assert.assertEquals;
38  import static org.junit.Assert.assertFalse;
39  import static org.junit.Assert.assertTrue;
40  import static org.junit.Assert.fail;
41  
42  @Category(SmallTests.class)
43  public class TestProcedureFuture {
44    private static class TestFuture extends HBaseAdmin.ProcedureFuture<Void> {
45      private boolean postOperationResultCalled = false;
46      private boolean waitOperationResultCalled = false;
47      private boolean getProcedureResultCalled = false;
48      private boolean convertResultCalled = false;
49  
50      public TestFuture(final HBaseAdmin admin, final Long procId) {
51        super(admin, procId);
52      }
53  
54      public boolean wasPostOperationResultCalled() {
55        return postOperationResultCalled;
56      }
57  
58      public boolean wasWaitOperationResultCalled() {
59        return waitOperationResultCalled;
60      }
61  
62      public boolean wasGetProcedureResultCalled() {
63        return getProcedureResultCalled;
64      }
65  
66      public boolean wasConvertResultCalled() {
67        return convertResultCalled;
68      }
69  
70      @Override
71      protected GetProcedureResultResponse getProcedureResult(
72          final GetProcedureResultRequest request) throws IOException {
73        getProcedureResultCalled = true;
74        return GetProcedureResultResponse.newBuilder()
75                .setState(GetProcedureResultResponse.State.FINISHED)
76                .build();
77      }
78  
79      @Override
80      protected Void convertResult(final GetProcedureResultResponse response) throws IOException {
81        convertResultCalled = true;
82        return null;
83      }
84  
85      @Override
86      protected Void waitOperationResult(final long deadlineTs)
87          throws IOException, TimeoutException {
88        waitOperationResultCalled = true;
89        return null;
90      }
91  
92      @Override
93      protected Void postOperationResult(final Void result, final long deadlineTs)
94          throws IOException, TimeoutException {
95        postOperationResultCalled = true;
96        return result;
97      }
98    }
99  
100   /**
101    * When a master return a result with procId,
102    * we are skipping the waitOperationResult() call,
103    * since we are getting the procedure result.
104    */
105   @Test(timeout=60000)
106   public void testWithProcId() throws Exception {
107     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
108     TestFuture f = new TestFuture(admin, 100L);
109     f.get(1, TimeUnit.MINUTES);
110 
111     assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled());
112     assertTrue("expected convertResult() to be called", f.wasConvertResultCalled());
113     assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled());
114     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
115   }
116 
117   /**
118    * Verify that the spin loop for the procedure running works.
119    */
120   @Test(timeout=60000)
121   public void testWithProcIdAndSpinning() throws Exception {
122     final AtomicInteger spinCount = new AtomicInteger(0);
123     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
124     TestFuture f = new TestFuture(admin, 100L) {
125       @Override
126       protected GetProcedureResultResponse getProcedureResult(
127           final GetProcedureResultRequest request) throws IOException {
128         boolean done = spinCount.incrementAndGet() >= 10;
129         return GetProcedureResultResponse.newBuilder()
130               .setState(done ? GetProcedureResultResponse.State.FINISHED :
131                 GetProcedureResultResponse.State.RUNNING)
132               .build();
133       }
134     };
135     f.get(1, TimeUnit.MINUTES);
136 
137     assertEquals(10, spinCount.get());
138     assertTrue("expected convertResult() to be called", f.wasConvertResultCalled());
139     assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled());
140     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
141   }
142 
143   /**
144    * When a master return a result without procId,
145    * we are skipping the getProcedureResult() call.
146    */
147   @Test(timeout=60000)
148   public void testWithoutProcId() throws Exception {
149     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
150     TestFuture f = new TestFuture(admin, null);
151     f.get(1, TimeUnit.MINUTES);
152 
153     assertFalse("unexpected getProcedureResult() called", f.wasGetProcedureResultCalled());
154     assertFalse("unexpected convertResult() called", f.wasConvertResultCalled());
155     assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled());
156     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
157   }
158 
159   /**
160    * When a new client with procedure support tries to ask an old-master without proc-support
161    * the procedure result we get a DoNotRetryIOException (which is an UnsupportedOperationException)
162    * The future should trap that and fallback to the waitOperationResult().
163    *
164    * This happens when the operation calls happens on a "new master" but while we are waiting
165    * the operation to be completed, we failover on an "old master".
166    */
167   @Test(timeout=60000)
168   public void testOnServerWithNoProcedureSupport() throws Exception {
169     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
170     TestFuture f = new TestFuture(admin, 100L) {
171       @Override
172       protected GetProcedureResultResponse getProcedureResult(
173         final GetProcedureResultRequest request) throws IOException {
174         super.getProcedureResult(request);
175         throw new DoNotRetryIOException(new UnsupportedOperationException("getProcedureResult"));
176       }
177     };
178     f.get(1, TimeUnit.MINUTES);
179 
180     assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled());
181     assertFalse("unexpected convertResult() called", f.wasConvertResultCalled());
182     assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled());
183     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
184   }
185 }