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.wal;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertNotSame;
023import static org.junit.jupiter.api.Assertions.assertNull;
024import static org.junit.jupiter.api.Assertions.assertThrows;
025import static org.junit.jupiter.api.Assertions.assertTrue;
026
027import java.io.IOException;
028import java.util.NavigableSet;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.fs.FSDataOutputStream;
031import org.apache.hadoop.fs.FileSystem;
032import org.apache.hadoop.fs.Path;
033import org.apache.hadoop.hbase.HBaseTestingUtil;
034import org.apache.hadoop.hbase.HConstants;
035import org.apache.hadoop.hbase.KeyValueTestUtil;
036import org.apache.hadoop.hbase.ServerName;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.testclassification.RegionServerTests;
039import org.apache.hadoop.hbase.testclassification.SmallTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.apache.hadoop.hbase.util.CommonFSUtils;
042import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
043import org.apache.hadoop.hbase.wal.WALSplitter.PipelineController;
044import org.junit.jupiter.api.Tag;
045import org.junit.jupiter.api.Test;
046
047/**
048 * Simple testing of a few WAL methods.
049 */
050@Tag(RegionServerTests.TAG)
051@Tag(SmallTests.TAG)
052public class TestWALMethods {
053
054  private static final byte[] TEST_REGION = Bytes.toBytes("test_region");
055  private static final TableName TEST_TABLE = TableName.valueOf("test_table");
056
057  private final HBaseTestingUtil util = new HBaseTestingUtil();
058
059  @Test
060  public void testServerNameFromWAL() throws Exception {
061    Path walPath = new Path("/hbase/WALs/regionserver-2.example.com,22101,1487767381290",
062      "regionserver-2.example.com%2C22101%2C1487767381290.null0.1487785392316");
063    ServerName name = AbstractFSWALProvider.getServerNameFromWALDirectoryName(walPath);
064    assertEquals(ServerName.valueOf("regionserver-2.example.com", 22101, 1487767381290L), name);
065  }
066
067  @Test
068  public void testServerNameFromTestWAL() throws Exception {
069    Path walPath = new Path(
070      "/user/example/test-data/12ff1404-68c6-4715-a4b9-775e763842bc/WALs/TestWALRecordReader",
071      "TestWALRecordReader.default.1487787939118");
072    ServerName name = AbstractFSWALProvider.getServerNameFromWALDirectoryName(walPath);
073    assertNull(name);
074  }
075
076  /**
077   * Assert that getSplitEditFilesSorted returns files in expected order and that it skips
078   * moved-aside files.
079   */
080  @Test
081  public void testGetSplitEditFilesSorted() throws IOException {
082    FileSystem fs = FileSystem.get(util.getConfiguration());
083    Path regiondir = util.getDataTestDir("regiondir");
084    fs.delete(regiondir, true);
085    fs.mkdirs(regiondir);
086    Path recoverededits = WALSplitUtil.getRegionDirRecoveredEditsDir(regiondir);
087    String first = WALSplitUtil.formatRecoveredEditsFileName(-1);
088    createFile(fs, recoverededits, first);
089    createFile(fs, recoverededits, WALSplitUtil.formatRecoveredEditsFileName(0));
090    createFile(fs, recoverededits, WALSplitUtil.formatRecoveredEditsFileName(1));
091    createFile(fs, recoverededits, WALSplitUtil.formatRecoveredEditsFileName(11));
092    createFile(fs, recoverededits, WALSplitUtil.formatRecoveredEditsFileName(2));
093    createFile(fs, recoverededits, WALSplitUtil.formatRecoveredEditsFileName(50));
094    String last = WALSplitUtil.formatRecoveredEditsFileName(Long.MAX_VALUE);
095    createFile(fs, recoverededits, last);
096    createFile(fs, recoverededits,
097      Long.toString(Long.MAX_VALUE) + "." + EnvironmentEdgeManager.currentTime());
098
099    final Configuration walConf = new Configuration(util.getConfiguration());
100    CommonFSUtils.setRootDir(walConf, regiondir);
101    (new WALFactory(walConf, "dummyLogName")).getWAL(null);
102
103    NavigableSet<Path> files = WALSplitUtil.getSplitEditFilesSorted(fs, regiondir);
104    assertEquals(7, files.size());
105    assertEquals(files.pollFirst().getName(), first);
106    assertEquals(files.pollLast().getName(), last);
107    assertEquals(files.pollFirst().getName(), WALSplitUtil.formatRecoveredEditsFileName(0));
108    assertEquals(files.pollFirst().getName(), WALSplitUtil.formatRecoveredEditsFileName(1));
109    assertEquals(files.pollFirst().getName(), WALSplitUtil.formatRecoveredEditsFileName(2));
110    assertEquals(files.pollFirst().getName(), WALSplitUtil.formatRecoveredEditsFileName(11));
111  }
112
113  private void createFile(final FileSystem fs, final Path testdir, final String name)
114    throws IOException {
115    FSDataOutputStream fdos = fs.create(new Path(testdir, name), true);
116    fdos.close();
117  }
118
119  @Test
120  public void testRegionEntryBuffer() throws Exception {
121    EntryBuffers.RegionEntryBuffer reb =
122      new EntryBuffers.RegionEntryBuffer(TEST_TABLE, TEST_REGION);
123    assertEquals(0, reb.heapSize());
124
125    reb.appendEntry(createTestLogEntry(1));
126    assertTrue(reb.heapSize() > 0);
127  }
128
129  @Test
130  public void testEntrySink() throws Exception {
131    EntryBuffers sink = new EntryBuffers(new PipelineController(), 1 * 1024 * 1024);
132    for (int i = 0; i < 1000; i++) {
133      WAL.Entry entry = createTestLogEntry(i);
134      sink.appendEntry(entry);
135    }
136
137    assertTrue(sink.totalBuffered > 0);
138    long amountInChunk = sink.totalBuffered;
139    // Get a chunk
140    EntryBuffers.RegionEntryBuffer chunk = sink.getChunkToWrite();
141    assertEquals(chunk.heapSize(), amountInChunk);
142
143    // Make sure it got marked that a thread is "working on this"
144    assertTrue(sink.isRegionCurrentlyWriting(TEST_REGION));
145
146    // Insert some more entries
147    for (int i = 0; i < 500; i++) {
148      WAL.Entry entry = createTestLogEntry(i);
149      sink.appendEntry(entry);
150    }
151    // Asking for another chunk shouldn't work since the first one
152    // is still writing
153    assertNull(sink.getChunkToWrite());
154
155    // If we say we're done writing the first chunk, then we should be able
156    // to get the second
157    sink.doneWriting(chunk);
158
159    EntryBuffers.RegionEntryBuffer chunk2 = sink.getChunkToWrite();
160    assertNotNull(chunk2);
161    assertNotSame(chunk, chunk2);
162    long amountInChunk2 = sink.totalBuffered;
163    // The second chunk had fewer rows than the first
164    assertTrue(amountInChunk2 < amountInChunk);
165
166    sink.doneWriting(chunk2);
167    assertEquals(0, sink.totalBuffered);
168  }
169
170  private WAL.Entry createTestLogEntry(int i) {
171    long seq = i;
172    long now = i * 1000;
173
174    WALEdit edit = new WALEdit();
175    edit.add(KeyValueTestUtil.create("row", "fam", "qual", 1234, "val"));
176    WALKeyImpl key =
177      new WALKeyImpl(TEST_REGION, TEST_TABLE, seq, now, HConstants.DEFAULT_CLUSTER_ID);
178    WAL.Entry entry = new WAL.Entry(key, edit);
179    return entry;
180  }
181
182  @Test
183  public void testParseServerNameFromWALName() {
184    assertEquals(ServerName.valueOf("abc,123,123"),
185      AbstractFSWALProvider.parseServerNameFromWALName("abc,123,123.1.12345.meta"));
186    assertEquals(ServerName.valueOf("abc,123,123"),
187      AbstractFSWALProvider.parseServerNameFromWALName("abc,123,123.12345"));
188    assertEquals(ServerName.valueOf("abc,123,123"),
189      AbstractFSWALProvider.parseServerNameFromWALName("abc,123,123"));
190    assertThrows(IllegalArgumentException.class,
191      () -> AbstractFSWALProvider.parseServerNameFromWALName("test,abc,123,123.12345"));
192    assertThrows(IllegalArgumentException.class,
193      () -> AbstractFSWALProvider.parseServerNameFromWALName("abc"));
194  }
195}