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 TestFuture(final HBaseAdmin admin, final Long procId, final boolean waitForOpResult) {
55        super(admin, procId, waitForOpResult);
56      }
57  
58      public boolean wasPostOperationResultCalled() {
59        return postOperationResultCalled;
60      }
61  
62      public boolean wasWaitOperationResultCalled() {
63        return waitOperationResultCalled;
64      }
65  
66      public boolean wasGetProcedureResultCalled() {
67        return getProcedureResultCalled;
68      }
69  
70      public boolean wasConvertResultCalled() {
71        return convertResultCalled;
72      }
73  
74      @Override
75      protected GetProcedureResultResponse getProcedureResult(
76          final GetProcedureResultRequest request) throws IOException {
77        getProcedureResultCalled = true;
78        return GetProcedureResultResponse.newBuilder()
79                .setState(GetProcedureResultResponse.State.FINISHED)
80                .build();
81      }
82  
83      @Override
84      protected Void convertResult(final GetProcedureResultResponse response) throws IOException {
85        convertResultCalled = true;
86        return null;
87      }
88  
89      @Override
90      protected Void waitOperationResult(final long deadlineTs)
91          throws IOException, TimeoutException {
92        waitOperationResultCalled = true;
93        return null;
94      }
95  
96      @Override
97      protected Void postOperationResult(final Void result, final long deadlineTs)
98          throws IOException, TimeoutException {
99        postOperationResultCalled = true;
100       return result;
101     }
102   }
103 
104   /**
105    * When a master return a result with procId,
106    * we are skipping the waitOperationResult() call,
107    * since we are getting the procedure result.
108    */
109   @Test(timeout=60000)
110   public void testWithProcId() throws Exception {
111     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
112     TestFuture f = new TestFuture(admin, 100L);
113     f.get(1, TimeUnit.MINUTES);
114 
115     assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled());
116     assertTrue("expected convertResult() to be called", f.wasConvertResultCalled());
117     assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled());
118     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
119   }
120 
121   /**
122    * Verify that the spin loop for the procedure running works.
123    */
124   @Test(timeout=60000)
125   public void testWithProcIdAndSpinning() throws Exception {
126     final AtomicInteger spinCount = new AtomicInteger(0);
127     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
128     TestFuture f = new TestFuture(admin, 100L) {
129       @Override
130       protected GetProcedureResultResponse getProcedureResult(
131           final GetProcedureResultRequest request) throws IOException {
132         boolean done = spinCount.incrementAndGet() >= 10;
133         return GetProcedureResultResponse.newBuilder()
134               .setState(done ? GetProcedureResultResponse.State.FINISHED :
135                 GetProcedureResultResponse.State.RUNNING)
136               .build();
137       }
138     };
139     f.get(1, TimeUnit.MINUTES);
140 
141     assertEquals(10, spinCount.get());
142     assertTrue("expected convertResult() to be called", f.wasConvertResultCalled());
143     assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled());
144     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
145   }
146 
147   /**
148    * When a master return a result without procId,
149    * we are skipping the getProcedureResult() call.
150    */
151   @Test(timeout=60000)
152   public void testWithoutProcId() throws Exception {
153     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
154     TestFuture f = new TestFuture(admin, null);
155     f.get(1, TimeUnit.MINUTES);
156 
157     assertFalse("unexpected getProcedureResult() called", f.wasGetProcedureResultCalled());
158     assertFalse("unexpected convertResult() called", f.wasConvertResultCalled());
159     assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled());
160     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
161   }
162 
163   /**
164    * When a new client with procedure support tries to ask an old-master without proc-support
165    * the procedure result we get a DoNotRetryIOException (which is an UnsupportedOperationException)
166    * The future should trap that and fallback to the waitOperationResult().
167    *
168    * This happens when the operation calls happens on a "new master" but while we are waiting
169    * the operation to be completed, we failover on an "old master".
170    */
171   @Test(timeout=60000)
172   public void testOnServerWithNoProcedureSupport() throws Exception {
173     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
174     TestFuture f = new TestFuture(admin, 100L) {
175       @Override
176       protected GetProcedureResultResponse getProcedureResult(
177         final GetProcedureResultRequest request) throws IOException {
178         super.getProcedureResult(request);
179         throw new DoNotRetryIOException(new UnsupportedOperationException("getProcedureResult"));
180       }
181     };
182     f.get(1, TimeUnit.MINUTES);
183 
184     assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled());
185     assertFalse("unexpected convertResult() called", f.wasConvertResultCalled());
186     assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled());
187     assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled());
188   }
189 
190   /**
191    * When master return a result by submitting the request asynchronously. we are skipping the
192    * waitOperationResult() call, since we are getting the procedure result.
193    */
194   @Test(timeout = 60000)
195   public void testWaitOperationResult() throws Exception {
196     HBaseAdmin admin = Mockito.mock(HBaseAdmin.class);
197     TestFuture f = new TestFuture(admin, 100L, true) {
198       @Override
199       protected GetProcedureResultResponse
200           getProcedureResult(final GetProcedureResultRequest request) throws IOException {
201         return GetProcedureResultResponse.newBuilder()
202             .setState(GetProcedureResultResponse.State.FINISHED).build();
203       }
204     };
205     f.get(1, TimeUnit.MINUTES);
206 
207     assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled());
208   }
209 }