1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.hadoop.hbase.client;
18
19 import static org.junit.Assert.*;
20
21 import org.apache.hadoop.hbase.HConstants;
22 import org.apache.hadoop.hbase.HRegionInfo;
23 import org.apache.hadoop.hbase.HRegionLocation;
24 import org.apache.hadoop.hbase.TableName;
25 import org.apache.hadoop.hbase.client.HTableMultiplexer.FlushWorker;
26 import org.apache.hadoop.hbase.client.HTableMultiplexer.PutStatus;
27 import org.apache.hadoop.hbase.testclassification.SmallTests;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.junit.experimental.categories.Category;
31 import org.mockito.invocation.InvocationOnMock;
32 import org.mockito.stubbing.Answer;
33
34 import java.io.IOException;
35 import java.util.concurrent.LinkedBlockingQueue;
36 import java.util.concurrent.ScheduledExecutorService;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.atomic.AtomicInteger;
39 import java.util.concurrent.atomic.AtomicLong;
40
41 import static java.nio.charset.StandardCharsets.UTF_8;
42 import static org.mockito.Matchers.any;
43 import static org.mockito.Matchers.anyBoolean;
44 import static org.mockito.Matchers.anyInt;
45 import static org.mockito.Mockito.doCallRealMethod;
46 import static org.mockito.Mockito.eq;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.times;
49 import static org.mockito.Mockito.verify;
50 import static org.mockito.Mockito.when;
51
52 @Category(SmallTests.class)
53 public class TestHTableMultiplexerViaMocks {
54
55 private static final int NUM_RETRIES = HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER;
56 private HTableMultiplexer mockMultiplexer;
57 private ClusterConnection mockConnection;
58 private HRegionLocation mockRegionLocation;
59 private HRegionInfo mockRegionInfo;
60
61 private TableName tableName;
62 private Put put;
63
64 @Before
65 public void setupTest() {
66 mockMultiplexer = mock(HTableMultiplexer.class);
67 mockConnection = mock(ClusterConnection.class);
68 mockRegionLocation = mock(HRegionLocation.class);
69 mockRegionInfo = mock(HRegionInfo.class);
70
71 tableName = TableName.valueOf("my_table");
72 put = new Put(getBytes("row1"));
73 put.addColumn(getBytes("f1"), getBytes("q1"), getBytes("v11"));
74 put.addColumn(getBytes("f1"), getBytes("q2"), getBytes("v12"));
75 put.addColumn(getBytes("f2"), getBytes("q1"), getBytes("v21"));
76
77
78 when(mockMultiplexer.put(any(TableName.class), any(Put.class), anyInt())).thenCallRealMethod();
79
80
81 when(mockMultiplexer.getConnection()).thenReturn(mockConnection);
82
83
84 when(mockRegionLocation.getRegionInfo()).thenReturn(mockRegionInfo);
85
86
87 when(mockRegionInfo.getTable()).thenReturn(tableName);
88 }
89
90 @Test public void useCacheOnInitialPut() throws Exception {
91 mockMultiplexer.put(tableName, put, NUM_RETRIES);
92
93 verify(mockMultiplexer)._put(tableName, put, NUM_RETRIES, false);
94 }
95
96 @Test public void nonNullLocationQueuesPut() throws Exception {
97 final LinkedBlockingQueue<PutStatus> queue = new LinkedBlockingQueue<>();
98
99
100 when(mockMultiplexer._put(any(TableName.class), any(Put.class), anyInt(), anyBoolean())).thenCallRealMethod();
101
102
103 when(mockConnection.getRegionLocation(tableName, put.getRow(), false)).thenReturn(mockRegionLocation);
104 when(mockMultiplexer.getQueue(mockRegionLocation)).thenReturn(queue);
105
106 assertTrue("Put should have been queued", mockMultiplexer.put(tableName, put, NUM_RETRIES));
107
108 assertEquals(1, queue.size());
109 final PutStatus ps = queue.take();
110 assertEquals(put, ps.put);
111 assertEquals(mockRegionInfo, ps.regionInfo);
112 }
113
114 @Test public void ignoreCacheOnRetriedPut() throws Exception {
115 FlushWorker mockFlushWorker = mock(FlushWorker.class);
116 ScheduledExecutorService mockExecutor = mock(ScheduledExecutorService.class);
117 final AtomicInteger retryInQueue = new AtomicInteger(0);
118 final AtomicLong totalFailedPuts = new AtomicLong(0L);
119 final int maxRetryInQueue = 20;
120 final long delay = 100L;
121
122 final PutStatus ps = new PutStatus(mockRegionInfo, put, NUM_RETRIES);
123
124
125 when(mockFlushWorker.resubmitFailedPut(any(PutStatus.class), any(HRegionLocation.class))).thenCallRealMethod();
126
127 when(mockMultiplexer._put(tableName, put, NUM_RETRIES - 1, true)).thenReturn(true);
128
129
130 when(mockFlushWorker.getExecutor()).thenReturn(mockExecutor);
131 when(mockFlushWorker.getNextDelay(anyInt())).thenReturn(delay);
132 when(mockFlushWorker.getMultiplexer()).thenReturn(mockMultiplexer);
133 when(mockFlushWorker.getRetryInQueue()).thenReturn(retryInQueue);
134 when(mockFlushWorker.getMaxRetryInQueue()).thenReturn(maxRetryInQueue);
135 when(mockFlushWorker.getTotalFailedPutCount()).thenReturn(totalFailedPuts);
136
137
138 when(mockExecutor.schedule(any(Runnable.class), eq(delay), eq(TimeUnit.MILLISECONDS))).thenAnswer(
139 new Answer<Void>() {
140 @Override
141 public Void answer(InvocationOnMock invocation) throws Throwable {
142
143 assertEquals(1L, retryInQueue.get());
144
145 Object[] args = invocation.getArguments();
146 assertEquals(3, args.length);
147 assertTrue("Argument should be an instance of Runnable", args[0] instanceof Runnable);
148 Runnable runnable = (Runnable) args[0];
149 runnable.run();
150 return null;
151 }
152 });
153
154
155 assertTrue("Put should have been rescheduled", mockFlushWorker.resubmitFailedPut(ps, mockRegionLocation));
156
157 verify(mockMultiplexer)._put(tableName, put, NUM_RETRIES - 1, true);
158 assertEquals(0L, totalFailedPuts.get());
159
160 assertEquals(0L, retryInQueue.get());
161 }
162
163 @SuppressWarnings("deprecation")
164 @Test public void testConnectionClosing() throws IOException {
165 doCallRealMethod().when(mockMultiplexer).close();
166
167 when(mockConnection.isClosed()).thenReturn(false);
168
169 mockMultiplexer.close();
170
171
172 verify(mockConnection).close();
173 }
174
175 @SuppressWarnings("deprecation")
176 @Test public void testClosingAlreadyClosedConnection() throws IOException {
177 doCallRealMethod().when(mockMultiplexer).close();
178
179 when(mockConnection.isClosed()).thenReturn(true);
180
181 mockMultiplexer.close();
182
183
184 verify(mockConnection, times(0)).close();
185 }
186
187
188
189
190 private static byte[] getBytes(String str) {
191 return str.getBytes(UTF_8);
192 }
193 }