1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21
22 import static org.junit.Assert.assertTrue;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.CountDownLatch;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.Cell;
35 import org.apache.hadoop.hbase.CellScanner;
36 import org.apache.hadoop.hbase.HBaseTestingUtility;
37 import org.apache.hadoop.hbase.HConstants;
38 import org.apache.hadoop.hbase.HTableDescriptor;
39 import org.apache.hadoop.hbase.Server;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.client.Durability;
42 import org.apache.hadoop.hbase.client.Put;
43 import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
44 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
45 import org.apache.hadoop.hbase.testclassification.MediumTests;
46 import org.apache.hadoop.hbase.util.Bytes;
47 import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
48 import org.apache.hadoop.hbase.util.Threads;
49 import org.apache.hadoop.hbase.wal.WAL;
50 import org.apache.hadoop.hbase.wal.WALKey;
51 import org.apache.hadoop.hbase.wal.WALProvider.Writer;
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.experimental.categories.Category;
57 import org.junit.rules.TestName;
58 import org.mockito.Mockito;
59
60
61
62
63
64 @Category({MediumTests.class})
65 public class TestWALLockup {
66 private static final Log LOG = LogFactory.getLog(TestWALLockup.class);
67 @Rule public TestName name = new TestName();
68
69 private static final String COLUMN_FAMILY = "MyCF";
70 private static final byte [] COLUMN_FAMILY_BYTES = Bytes.toBytes(COLUMN_FAMILY);
71
72 HRegion region = null;
73
74 private static HBaseTestingUtility TEST_UTIL;
75 private static Configuration CONF ;
76 private String dir;
77
78
79 protected TableName tableName;
80
81 @Before
82 public void setup() throws IOException {
83 TEST_UTIL = HBaseTestingUtility.createLocalHTU();
84 CONF = TEST_UTIL.getConfiguration();
85
86 CONF.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f);
87 dir = TEST_UTIL.getDataTestDir("TestHRegion").toString();
88 tableName = TableName.valueOf(name.getMethodName());
89 }
90
91 @After
92 public void tearDown() throws Exception {
93 EnvironmentEdgeManagerTestHelper.reset();
94 LOG.info("Cleaning test directory: " + TEST_UTIL.getDataTestDir());
95 TEST_UTIL.cleanupTestDir();
96 }
97
98 String getName() {
99 return name.getMethodName();
100 }
101
102
103
104
105
106
107
108
109 @Test (timeout=30000)
110 public void testLockupWhenSyncInMiddleOfZigZagSetup() throws IOException {
111
112 class DodgyFSLog extends FSHLog {
113
114 volatile boolean throwException = false;
115
116
117 CountDownLatch latch = new CountDownLatch(1);
118
119 public DodgyFSLog(FileSystem fs, Path root, String logDir, Configuration conf)
120 throws IOException {
121 super(fs, root, logDir, conf);
122 }
123
124 @Override
125 protected void afterCreatingZigZagLatch() {
126
127
128
129
130 if (throwException) {
131 try {
132 LOG.info("LATCHED");
133 this.latch.await();
134 } catch (InterruptedException e) {
135
136 e.printStackTrace();
137 }
138 }
139 }
140
141 @Override
142 protected void beforeWaitOnSafePoint() {
143 if (throwException) {
144 LOG.info("COUNTDOWN");
145
146
147
148 while (this.latch.getCount() <= 0) Threads.sleep(1);
149 this.latch.countDown();
150 }
151 }
152
153 @Override
154 protected Writer createWriterInstance(Path path) throws IOException {
155 final Writer w = super.createWriterInstance(path);
156 return new Writer() {
157 @Override
158 public void close() throws IOException {
159 w.close();
160 }
161
162 @Override
163 public void sync() throws IOException {
164 if (throwException) {
165 throw new IOException("FAKE! Failed to replace a bad datanode...SYNC");
166 }
167 w.sync();
168 }
169
170 @Override
171 public void append(Entry entry) throws IOException {
172 if (throwException) {
173 throw new IOException("FAKE! Failed to replace a bad datanode...APPEND");
174 }
175 w.append(entry);
176 }
177
178 @Override
179 public long getLength() throws IOException {
180 return w.getLength();
181 }
182 };
183 }
184 }
185
186
187 Server server = Mockito.mock(Server.class);
188 Mockito.when(server.getConfiguration()).thenReturn(CONF);
189 Mockito.when(server.isStopped()).thenReturn(false);
190 Mockito.when(server.isAborted()).thenReturn(false);
191 RegionServerServices services = Mockito.mock(RegionServerServices.class);
192
193
194 FileSystem fs = FileSystem.get(CONF);
195 Path rootDir = new Path(dir + getName());
196 DodgyFSLog dodgyWAL = new DodgyFSLog(fs, rootDir, getName(), CONF);
197 Path originalWAL = dodgyWAL.getCurrentFileName();
198
199 LogRoller logRoller = new LogRoller(server, services);
200 logRoller.addWAL(dodgyWAL);
201
202 logRoller.start();
203
204 HTableDescriptor htd = new HTableDescriptor(TableName.META_TABLE_NAME);
205 final HRegion region = initHRegion(tableName, null, null, dodgyWAL);
206 byte [] bytes = Bytes.toBytes(getName());
207 try {
208
209
210
211 Put put = new Put(bytes);
212 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("1"), bytes);
213 WALKey key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(), htd.getTableName());
214 WALEdit edit = new WALEdit();
215 List<Cell> cells = new ArrayList<Cell>();
216 for (CellScanner cs = put.cellScanner(); cs.advance();) {
217 edit.add(cs.current());
218 cells.add(cs.current());
219 }
220
221
222 for (int i = 0; i < 1000; i++) {
223 dodgyWAL.append(htd, region.getRegionInfo(), key, edit, region.getSequenceId(), true,
224 cells);
225 }
226
227 dodgyWAL.throwException = true;
228
229 dodgyWAL.append(htd, region.getRegionInfo(), key, edit, region.getSequenceId(), true, cells);
230 boolean exception = false;
231 try {
232 dodgyWAL.sync();
233 } catch (Exception e) {
234 exception = true;
235 }
236 assertTrue("Did not get sync exception", exception);
237
238
239
240
241 Thread t = new Thread ("flusher") {
242 public void run() {
243 try {
244 region.flush(false);
245 } catch (IOException e) {
246
247 e.printStackTrace();
248 }
249 };
250 };
251 t.setDaemon(true);
252 t.start();
253
254 while (!region.writestate.flushing) Threads.sleep(1);
255
256 assertTrue(originalWAL != dodgyWAL.getCurrentFileName());
257
258 dodgyWAL.throwException = false;
259 region.put(put);
260 } finally {
261
262 Mockito.when(server.isStopped()).thenReturn(true);
263 if (logRoller != null) logRoller.interrupt();
264 if (region != null) region.close();
265 if (dodgyWAL != null) dodgyWAL.close();
266 }
267 }
268
269
270
271
272
273 public HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, WAL wal)
274 throws IOException {
275 return TEST_UTIL.createLocalHRegion(tableName.getName(), startKey, stopKey,
276 getName(), CONF, false, Durability.SYNC_WAL,
277 wal, COLUMN_FAMILY_BYTES);
278 }
279 }