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( 075 final GetProcedureResultRequest request) throws IOException { 076 getProcedureResultCalled = true; 077 return GetProcedureResultResponse.newBuilder() 078 .setState(GetProcedureResultResponse.State.FINISHED) 079 .build(); 080 } 081 082 @Override 083 protected Void convertResult(final GetProcedureResultResponse response) throws IOException { 084 convertResultCalled = true; 085 return null; 086 } 087 088 @Override 089 protected Void waitOperationResult(final long deadlineTs) 090 throws IOException, TimeoutException { 091 waitOperationResultCalled = true; 092 return null; 093 } 094 095 @Override 096 protected Void postOperationResult(final Void result, final long deadlineTs) 097 throws IOException, TimeoutException { 098 postOperationResultCalled = true; 099 return result; 100 } 101 } 102 103 /** 104 * When a master return a result with procId, 105 * we are skipping the waitOperationResult() call, 106 * since we are getting the procedure result. 107 */ 108 @Test 109 public void testWithProcId() throws Exception { 110 HBaseAdmin admin = Mockito.mock(HBaseAdmin.class); 111 TestFuture f = new TestFuture(admin, 100L); 112 f.get(1, TimeUnit.MINUTES); 113 114 assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled()); 115 assertTrue("expected convertResult() to be called", f.wasConvertResultCalled()); 116 assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled()); 117 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 118 } 119 120 /** 121 * Verify that the spin loop for the procedure running works. 122 */ 123 @Test 124 public void testWithProcIdAndSpinning() throws Exception { 125 final AtomicInteger spinCount = new AtomicInteger(0); 126 HBaseAdmin admin = Mockito.mock(HBaseAdmin.class); 127 TestFuture f = new TestFuture(admin, 100L) { 128 @Override 129 protected GetProcedureResultResponse getProcedureResult( 130 final GetProcedureResultRequest request) throws IOException { 131 boolean done = spinCount.incrementAndGet() >= 10; 132 return GetProcedureResultResponse.newBuilder() 133 .setState(done ? GetProcedureResultResponse.State.FINISHED : 134 GetProcedureResultResponse.State.RUNNING) 135 .build(); 136 } 137 }; 138 f.get(1, TimeUnit.MINUTES); 139 140 assertEquals(10, spinCount.get()); 141 assertTrue("expected convertResult() to be called", f.wasConvertResultCalled()); 142 assertFalse("unexpected waitOperationResult() called", f.wasWaitOperationResultCalled()); 143 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 144 } 145 146 /** 147 * When a master return a result without procId, 148 * we are skipping the getProcedureResult() call. 149 */ 150 @Test 151 public void testWithoutProcId() throws Exception { 152 HBaseAdmin admin = Mockito.mock(HBaseAdmin.class); 153 TestFuture f = new TestFuture(admin, null); 154 f.get(1, TimeUnit.MINUTES); 155 156 assertFalse("unexpected getProcedureResult() called", f.wasGetProcedureResultCalled()); 157 assertFalse("unexpected convertResult() called", f.wasConvertResultCalled()); 158 assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled()); 159 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 160 } 161 162 /** 163 * When a new client with procedure support tries to ask an old-master without proc-support 164 * the procedure result we get a DoNotRetryIOException (which is an UnsupportedOperationException) 165 * The future should trap that and fallback to the waitOperationResult(). 166 * 167 * This happens when the operation calls happens on a "new master" but while we are waiting 168 * the operation to be completed, we failover on an "old master". 169 */ 170 @Test 171 public void testOnServerWithNoProcedureSupport() throws Exception { 172 HBaseAdmin admin = Mockito.mock(HBaseAdmin.class); 173 TestFuture f = new TestFuture(admin, 100L) { 174 @Override 175 protected GetProcedureResultResponse getProcedureResult( 176 final GetProcedureResultRequest request) throws IOException { 177 super.getProcedureResult(request); 178 throw new DoNotRetryIOException(new UnsupportedOperationException("getProcedureResult")); 179 } 180 }; 181 f.get(1, TimeUnit.MINUTES); 182 183 assertTrue("expected getProcedureResult() to be called", f.wasGetProcedureResultCalled()); 184 assertFalse("unexpected convertResult() called", f.wasConvertResultCalled()); 185 assertTrue("expected waitOperationResult() to be called", f.wasWaitOperationResultCalled()); 186 assertTrue("expected postOperationResult() to be called", f.wasPostOperationResultCalled()); 187 } 188}