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.client; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.concurrent.TimeUnit; 026import java.util.concurrent.TimeoutException; 027import java.util.concurrent.atomic.AtomicInteger; 028import org.apache.hadoop.hbase.DoNotRetryIOException; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.testclassification.ClientTests; 031import org.apache.hadoop.hbase.testclassification.SmallTests; 032import org.junit.ClassRule; 033import org.junit.Test; 034import org.junit.experimental.categories.Category; 035import org.mockito.Mockito; 036 037import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetProcedureResultRequest; 038import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetProcedureResultResponse; 039 040@Category({ ClientTests.class, SmallTests.class }) 041public class TestProcedureFuture { 042 043 @ClassRule 044 public static final HBaseClassTestRule CLASS_RULE = 045 HBaseClassTestRule.forClass(TestProcedureFuture.class); 046 047 private static class TestFuture extends HBaseAdmin.ProcedureFuture<Void> { 048 private boolean postOperationResultCalled = false; 049 private boolean waitOperationResultCalled = false; 050 private boolean getProcedureResultCalled = false; 051 private boolean convertResultCalled = false; 052 053 public TestFuture(final HBaseAdmin admin, final Long procId) { 054 super(admin, procId); 055 } 056 057 public boolean wasPostOperationResultCalled() { 058 return postOperationResultCalled; 059 } 060 061 public boolean wasWaitOperationResultCalled() { 062 return waitOperationResultCalled; 063 } 064 065 public boolean wasGetProcedureResultCalled() { 066 return getProcedureResultCalled; 067 } 068 069 public boolean wasConvertResultCalled() { 070 return convertResultCalled; 071 } 072 073 @Override 074 protected GetProcedureResultResponse getProcedureResult(final GetProcedureResultRequest request) 075 throws IOException { 076 getProcedureResultCalled = true; 077 return GetProcedureResultResponse.newBuilder() 078 .setState(GetProcedureResultResponse.State.FINISHED).build(); 079 } 080 081 @Override 082 protected Void convertResult(final GetProcedureResultResponse response) throws IOException { 083 convertResultCalled = true; 084 return null; 085 } 086 087 @Override 088 protected Void waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { 089 waitOperationResultCalled = true; 090 return null; 091 } 092 093 @Override 094 protected Void postOperationResult(final Void result, final long deadlineTs) 095 throws IOException, TimeoutException { 096 postOperationResultCalled = true; 097 return result; 098 } 099 } 100 101 /** 102 * When a master return a result with procId, we are skipping the waitOperationResult() call, 103 * since we are getting the procedure result. 104 */ 105 @Test 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 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 127 getProcedureResult(final GetProcedureResultRequest request) throws IOException { 128 boolean done = spinCount.incrementAndGet() >= 10; 129 return GetProcedureResultResponse.newBuilder() 130 .setState(done 131 ? GetProcedureResultResponse.State.FINISHED 132 : GetProcedureResultResponse.State.RUNNING) 133 .build(); 134 } 135 }; 136 f.get(1, TimeUnit.MINUTES); 137 138 assertEquals(10, spinCount.get()); 139 assertTrue("expected convertResult() to be called", f.wasConvertResultCalled()); 140 assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled()); 141 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 142 } 143 144 /** 145 * When a master return a result without procId, we are skipping the getProcedureResult() call. 146 */ 147 @Test 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 the 161 * procedure result we get a DoNotRetryIOException (which is an UnsupportedOperationException) The 162 * future should trap that and fallback to the waitOperationResult(). This happens when the 163 * operation calls happens on a "new master" but while we are waiting the operation to be 164 * completed, we failover on an "old master". 165 */ 166 @Test 167 public void testOnServerWithNoProcedureSupport() throws Exception { 168 HBaseAdmin admin = Mockito.mock(HBaseAdmin.class); 169 TestFuture f = new TestFuture(admin, 100L) { 170 @Override 171 protected GetProcedureResultResponse 172 getProcedureResult(final GetProcedureResultRequest request) throws IOException { 173 super.getProcedureResult(request); 174 throw new DoNotRetryIOException(new UnsupportedOperationException("getProcedureResult")); 175 } 176 }; 177 f.get(1, TimeUnit.MINUTES); 178 179 assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled()); 180 assertFalse("unexpected convertResult() called", f.wasConvertResultCalled()); 181 assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled()); 182 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 183 } 184}