1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import static org.junit.Assert.*;
21  
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.util.Arrays;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.fs.FileStatus;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.hbase.HBaseTestingUtility;
32  import org.apache.hadoop.hbase.HColumnDescriptor;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.HTableDescriptor;
35  import org.apache.hadoop.hbase.TableDescriptors;
36  import org.apache.hadoop.hbase.TableExistsException;
37  import org.junit.Test;
38  
39  
40  /**
41   * Tests for {@link FSTableDescriptors}.
42   */
43  public class TestFSTableDescriptors {
44    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
45    private static final Log LOG = LogFactory.getLog(TestFSTableDescriptors.class);
46  
47    @Test (expected=IllegalArgumentException.class)
48    public void testRegexAgainstOldStyleTableInfo() {
49      Path p = new Path("/tmp", FSTableDescriptors.TABLEINFO_NAME);
50      int i = FSTableDescriptors.getTableInfoSequenceid(p);
51      assertEquals(0, i);
52      // Assert it won't eat garbage -- that it fails
53      p = new Path("/tmp", "abc");
54      FSTableDescriptors.getTableInfoSequenceid(p);
55    }
56  
57    @Test
58    public void testCreateAndUpdate() throws IOException {
59      Path testdir = UTIL.getDataTestDir();
60      HTableDescriptor htd = new HTableDescriptor("testCreate");
61      FileSystem fs = FileSystem.get(UTIL.getConfiguration());
62      assertTrue(FSTableDescriptors.createTableDescriptor(fs, testdir, htd));
63      assertFalse(FSTableDescriptors.createTableDescriptor(fs, testdir, htd));
64      FileStatus [] statuses = fs.listStatus(testdir);
65      assertTrue(statuses.length == 1);
66      for (int i = 0; i < 10; i++) {
67        FSTableDescriptors.updateHTableDescriptor(fs, testdir, htd);
68      }
69      statuses = fs.listStatus(testdir);
70      assertTrue(statuses.length == 1);
71      Path tmpTableDir = new Path(FSUtils.getTablePath(testdir, htd.getName()), ".tmp");
72      statuses = fs.listStatus(tmpTableDir);
73      assertTrue(statuses.length == 0);
74    }
75  
76    @Test
77    public void testSequenceidAdvancesOnTableInfo() throws IOException {
78      Path testdir = UTIL.getDataTestDir("testSequenceidAdvancesOnTableInfo");
79      HTableDescriptor htd = new HTableDescriptor("testSequenceidAdvancesOnTableInfo");
80      FileSystem fs = FileSystem.get(UTIL.getConfiguration());
81      Path p0 = FSTableDescriptors.updateHTableDescriptor(fs, testdir, htd);
82      int i0 = FSTableDescriptors.getTableInfoSequenceid(p0);
83      Path p1 = FSTableDescriptors.updateHTableDescriptor(fs, testdir, htd);
84      // Assert we cleaned up the old file.
85      assertTrue(!fs.exists(p0));
86      int i1 = FSTableDescriptors.getTableInfoSequenceid(p1);
87      assertTrue(i1 == i0 + 1);
88      Path p2 = FSTableDescriptors.updateHTableDescriptor(fs, testdir, htd);
89      // Assert we cleaned up the old file.
90      assertTrue(!fs.exists(p1));
91      int i2 = FSTableDescriptors.getTableInfoSequenceid(p2);
92      assertTrue(i2 == i1 + 1);
93    }
94  
95    @Test
96    public void testFormatTableInfoSequenceId() {
97      Path p0 = assertWriteAndReadSequenceid(0);
98      // Assert p0 has format we expect.
99      StringBuilder sb = new StringBuilder();
100     for (int i = 0; i < FSTableDescriptors.WIDTH_OF_SEQUENCE_ID; i++) {
101       sb.append("0");
102     }
103     assertEquals(FSTableDescriptors.TABLEINFO_NAME + "." + sb.toString(),
104       p0.getName());
105     // Check a few more.
106     Path p2 = assertWriteAndReadSequenceid(2);
107     Path p10000 = assertWriteAndReadSequenceid(10000);
108     // Get a .tablinfo that has no sequenceid suffix.
109     Path p = new Path(p0.getParent(), FSTableDescriptors.TABLEINFO_NAME);
110     FileStatus fs = new FileStatus(0, false, 0, 0, 0, p);
111     FileStatus fs0 = new FileStatus(0, false, 0, 0, 0, p0);
112     FileStatus fs2 = new FileStatus(0, false, 0, 0, 0, p2);
113     FileStatus fs10000 = new FileStatus(0, false, 0, 0, 0, p10000);
114     FSTableDescriptors.FileStatusFileNameComparator comparator =
115       new FSTableDescriptors.FileStatusFileNameComparator();
116     assertTrue(comparator.compare(fs, fs0) > 0);
117     assertTrue(comparator.compare(fs0, fs2) > 0);
118     assertTrue(comparator.compare(fs2, fs10000) > 0);
119   }
120 
121   private Path assertWriteAndReadSequenceid(final int i) {
122     Path p = FSTableDescriptors.getTableInfoFileName(new Path("/tmp"), i);
123     int ii = FSTableDescriptors.getTableInfoSequenceid(p);
124     assertEquals(i, ii);
125     return p;
126   }
127 
128   @Test
129   public void testRemoves() throws IOException {
130     final String name = "testRemoves";
131     FileSystem fs = FileSystem.get(UTIL.getConfiguration());
132     // Cleanup old tests if any detrius laying around.
133     Path rootdir = new Path(UTIL.getDataTestDir(), name);
134     TableDescriptors htds = new FSTableDescriptors(fs, rootdir);
135     HTableDescriptor htd = new HTableDescriptor(name);
136     htds.add(htd);
137     assertNotNull(htds.remove(htd.getNameAsString()));
138     assertNull(htds.remove(htd.getNameAsString()));
139   }
140 
141   @Test public void testReadingHTDFromFS() throws IOException {
142     final String name = "testReadingHTDFromFS";
143     FileSystem fs = FileSystem.get(UTIL.getConfiguration());
144     HTableDescriptor htd = new HTableDescriptor(name);
145     Path rootdir = UTIL.getDataTestDir(name);
146     createHTDInFS(fs, rootdir, htd);
147     HTableDescriptor htd2 =
148       FSTableDescriptors.getTableDescriptor(fs, rootdir, htd.getNameAsString());
149     assertTrue(htd.equals(htd2));
150   }
151 
152   private void createHTDInFS(final FileSystem fs, Path rootdir,
153       final HTableDescriptor htd)
154   throws IOException {
155     FSTableDescriptors.createTableDescriptor(fs, rootdir, htd);
156   }
157 
158   @Test public void testHTableDescriptors()
159   throws IOException, InterruptedException {
160     final String name = "testHTableDescriptors";
161     FileSystem fs = FileSystem.get(UTIL.getConfiguration());
162     // Cleanup old tests if any debris laying around.
163     Path rootdir = new Path(UTIL.getDataTestDir(), name);
164     final int count = 10;
165     // Write out table infos.
166     for (int i = 0; i < count; i++) {
167       HTableDescriptor htd = new HTableDescriptor(name + i);
168       createHTDInFS(fs, rootdir, htd);
169     }
170     FSTableDescriptors htds = new FSTableDescriptors(fs, rootdir) {
171       @Override
172       public HTableDescriptor get(byte[] tablename)
173           throws TableExistsException, FileNotFoundException, IOException {
174         LOG.info(Bytes.toString(tablename) + ", cachehits=" + this.cachehits);
175         return super.get(tablename);
176       }
177     };
178     for (int i = 0; i < count; i++) {
179       assertTrue(htds.get(Bytes.toBytes(name + i)) !=  null);
180     }
181     for (int i = 0; i < count; i++) {
182       assertTrue(htds.get(Bytes.toBytes(name + i)) !=  null);
183     }
184     // Update the table infos
185     for (int i = 0; i < count; i++) {
186       HTableDescriptor htd = new HTableDescriptor(name + i);
187       htd.addFamily(new HColumnDescriptor("" + i));
188       FSTableDescriptors.updateHTableDescriptor(fs, rootdir, htd);
189     }
190     // Wait a while so mod time we write is for sure different.
191     Thread.sleep(100);
192     for (int i = 0; i < count; i++) {
193       assertTrue(htds.get(Bytes.toBytes(name + i)) !=  null);
194     }
195     for (int i = 0; i < count; i++) {
196       assertTrue(htds.get(Bytes.toBytes(name + i)) !=  null);
197     }
198     assertEquals(count * 4, htds.invocations);
199     assertTrue("expected=" + (count * 2) + ", actual=" + htds.cachehits,
200       htds.cachehits >= (count * 2));
201     assertTrue(htds.get(HConstants.ROOT_TABLE_NAME) != null);
202     assertEquals(htds.invocations, count * 4 + 1);
203     assertTrue("expected=" + ((count * 2) + 1) + ", actual=" + htds.cachehits,
204       htds.cachehits >= ((count * 2) + 1));
205   }
206 
207   @Test (expected=org.apache.hadoop.hbase.TableExistsException.class)
208   public void testNoSuchTable() throws IOException {
209     final String name = "testNoSuchTable";
210     FileSystem fs = FileSystem.get(UTIL.getConfiguration());
211     // Cleanup old tests if any detrius laying around.
212     Path rootdir = new Path(UTIL.getDataTestDir(), name);
213     TableDescriptors htds = new FSTableDescriptors(fs, rootdir);
214     htds.get("NoSuchTable");
215   }
216 
217   @Test
218   public void testUpdates() throws IOException {
219     final String name = "testUpdates";
220     FileSystem fs = FileSystem.get(UTIL.getConfiguration());
221     // Cleanup old tests if any detrius laying around.
222     Path rootdir = new Path(UTIL.getDataTestDir(), name);
223     TableDescriptors htds = new FSTableDescriptors(fs, rootdir);
224     HTableDescriptor htd = new HTableDescriptor(name);
225     htds.add(htd);
226     htds.add(htd);
227     htds.add(htd);
228   }
229 
230   @Test
231   public void testTableInfoFileStatusComparator() {
232     FileStatus bare =
233       new FileStatus(0, false, 0, 0, -1, new Path("/tmp", FSTableDescriptors.TABLEINFO_NAME));
234     FileStatus future =
235       new FileStatus(0, false, 0, 0, -1,
236         new Path("/tmp/tablinfo." + System.currentTimeMillis()));
237     FileStatus farFuture =
238       new FileStatus(0, false, 0, 0, -1,
239         new Path("/tmp/tablinfo." + System.currentTimeMillis() + 1000));
240     FileStatus [] alist = {bare, future, farFuture};
241     FileStatus [] blist = {bare, farFuture, future};
242     FileStatus [] clist = {farFuture, bare, future};
243     FSTableDescriptors.FileStatusFileNameComparator c =
244       new FSTableDescriptors.FileStatusFileNameComparator();
245     Arrays.sort(alist, c);
246     Arrays.sort(blist, c);
247     Arrays.sort(clist, c);
248     // Now assert all sorted same in way we want.
249     for (int i = 0; i < alist.length; i++) {
250       assertTrue(alist[i].equals(blist[i]));
251       assertTrue(blist[i].equals(clist[i]));
252       assertTrue(clist[i].equals(i == 0? farFuture: i == 1? future: bare));
253     }
254   }
255 }