1   /**
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.fs.FileSystem;
29  import org.apache.hadoop.fs.Path;
30  import org.apache.hadoop.hbase.*;
31  import org.apache.hadoop.hbase.client.Delete;
32  import org.apache.hadoop.hbase.client.Put;
33  import org.apache.hadoop.hbase.client.Result;
34  import org.apache.hadoop.hbase.client.Scan;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.apache.hadoop.hbase.util.Writables;
37  import org.apache.hadoop.hdfs.MiniDFSCluster;
38  import org.junit.experimental.categories.Category;
39  
40  /**
41   * {@link TestGet} is a medley of tests of get all done up as a single test.
42   * This class
43   */
44  @Category(SmallTests.class)
45  public class TestGetClosestAtOrBefore extends HBaseTestCase {
46    private static final Log LOG = LogFactory.getLog(TestGetClosestAtOrBefore.class);
47  
48    private static final byte[] T00 = Bytes.toBytes("000");
49    private static final byte[] T10 = Bytes.toBytes("010");
50    private static final byte[] T11 = Bytes.toBytes("011");
51    private static final byte[] T12 = Bytes.toBytes("012");
52    private static final byte[] T20 = Bytes.toBytes("020");
53    private static final byte[] T30 = Bytes.toBytes("030");
54    private static final byte[] T31 = Bytes.toBytes("031");
55    private static final byte[] T35 = Bytes.toBytes("035");
56    private static final byte[] T40 = Bytes.toBytes("040");
57  
58  
59  
60    public void testUsingMetaAndBinary() throws IOException {
61      FileSystem filesystem = FileSystem.get(conf);
62      Path rootdir = testDir;
63      // Up flush size else we bind up when we use default catalog flush of 16k.
64      HTableDescriptor.META_TABLEDESC.setMemStoreFlushSize(64 * 1024 * 1024);
65  
66      HRegion mr = HRegion.createHRegion(HRegionInfo.FIRST_META_REGIONINFO,
67        rootdir, this.conf, HTableDescriptor.META_TABLEDESC);
68      try {
69      // Write rows for three tables 'A', 'B', and 'C'.
70      for (char c = 'A'; c < 'D'; c++) {
71        HTableDescriptor htd = new HTableDescriptor("" + c);
72        final int last = 128;
73        final int interval = 2;
74        for (int i = 0; i <= last; i += interval) {
75          HRegionInfo hri = new HRegionInfo(htd.getName(),
76            i == 0? HConstants.EMPTY_BYTE_ARRAY: Bytes.toBytes((byte)i),
77            i == last? HConstants.EMPTY_BYTE_ARRAY: Bytes.toBytes((byte)i + interval));
78          Put put = new Put(hri.getRegionName());
79          put.setWriteToWAL(false);
80          put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
81                  Writables.getBytes(hri));
82          mr.put(put, false);
83        }
84      }
85      InternalScanner s = mr.getScanner(new Scan());
86      try {
87        List<KeyValue> keys = new ArrayList<KeyValue>();
88        while(s.next(keys)) {
89          LOG.info(keys);
90          keys.clear();
91        }
92      } finally {
93        s.close();
94      }
95      findRow(mr, 'C', 44, 44);
96      findRow(mr, 'C', 45, 44);
97      findRow(mr, 'C', 46, 46);
98      findRow(mr, 'C', 43, 42);
99      mr.flushcache();
100     findRow(mr, 'C', 44, 44);
101     findRow(mr, 'C', 45, 44);
102     findRow(mr, 'C', 46, 46);
103     findRow(mr, 'C', 43, 42);
104     // Now delete 'C' and make sure I don't get entries from 'B'.
105     byte [] firstRowInC = HRegionInfo.createRegionName(Bytes.toBytes("" + 'C'),
106       HConstants.EMPTY_BYTE_ARRAY, HConstants.ZEROES, false);
107     Scan scan = new Scan(firstRowInC);
108     s = mr.getScanner(scan);
109     try {
110       List<KeyValue> keys = new ArrayList<KeyValue>();
111       while (s.next(keys)) {
112         mr.delete(new Delete(keys.get(0).getRow()), null, false);
113         keys.clear();
114       }
115     } finally {
116       s.close();
117     }
118     // Assert we get null back (pass -1).
119     findRow(mr, 'C', 44, -1);
120     findRow(mr, 'C', 45, -1);
121     findRow(mr, 'C', 46, -1);
122     findRow(mr, 'C', 43, -1);
123     mr.flushcache();
124     findRow(mr, 'C', 44, -1);
125     findRow(mr, 'C', 45, -1);
126     findRow(mr, 'C', 46, -1);
127     findRow(mr, 'C', 43, -1);
128     } finally {
129       if (mr != null) {
130         try {
131           mr.close();
132         } catch (Exception e) {
133           e.printStackTrace();
134         }
135         mr.getLog().closeAndDelete();
136       }
137     }
138   }
139 
140   /*
141    * @param mr
142    * @param table
143    * @param rowToFind
144    * @param answer Pass -1 if we're not to find anything.
145    * @return Row found.
146    * @throws IOException
147    */
148   private byte [] findRow(final HRegion mr, final char table,
149     final int rowToFind, final int answer)
150   throws IOException {
151     byte [] tableb = Bytes.toBytes("" + table);
152     // Find the row.
153     byte [] tofindBytes = Bytes.toBytes((short)rowToFind);
154     byte [] metaKey = HRegionInfo.createRegionName(tableb, tofindBytes,
155       HConstants.NINES, false);
156     LOG.info("find=" + new String(metaKey));
157     Result r = mr.getClosestRowBefore(metaKey);
158     if (answer == -1) {
159       assertNull(r);
160       return null;
161     }
162     assertTrue(Bytes.compareTo(Bytes.toBytes((short)answer),
163       extractRowFromMetaRow(r.getRow())) == 0);
164     return r.getRow();
165   }
166 
167   private byte [] extractRowFromMetaRow(final byte [] b) {
168     int firstDelimiter = KeyValue.getDelimiter(b, 0, b.length,
169       HRegionInfo.DELIMITER);
170     int lastDelimiter = KeyValue.getDelimiterInReverse(b, 0, b.length,
171       HRegionInfo.DELIMITER);
172     int length = lastDelimiter - firstDelimiter - 1;
173     byte [] row = new byte[length];
174     System.arraycopy(b, firstDelimiter + 1, row, 0, length);
175     return row;
176   }
177 
178   /**
179    * Test file of multiple deletes and with deletes as final key.
180    * @see <a href="https://issues.apache.org/jira/browse/HBASE-751">HBASE-751</a>
181    */
182   public void testGetClosestRowBefore3() throws IOException{
183     HRegion region = null;
184     byte [] c0 = COLUMNS[0];
185     byte [] c1 = COLUMNS[1];
186     try {
187       HTableDescriptor htd = createTableDescriptor(getName());
188       region = createNewHRegion(htd, null, null);
189 
190       Put p = new Put(T00);
191       p.add(c0, c0, T00);
192       region.put(p);
193 
194       p = new Put(T10);
195       p.add(c0, c0, T10);
196       region.put(p);
197 
198       p = new Put(T20);
199       p.add(c0, c0, T20);
200       region.put(p);
201 
202       Result r = region.getClosestRowBefore(T20, c0);
203       assertTrue(Bytes.equals(T20, r.getRow()));
204 
205       Delete d = new Delete(T20);
206       d.deleteColumn(c0, c0);
207       region.delete(d, null, false);
208 
209       r = region.getClosestRowBefore(T20, c0);
210       assertTrue(Bytes.equals(T10, r.getRow()));
211 
212       p = new Put(T30);
213       p.add(c0, c0, T30);
214       region.put(p);
215 
216       r = region.getClosestRowBefore(T30, c0);
217       assertTrue(Bytes.equals(T30, r.getRow()));
218 
219       d = new Delete(T30);
220       d.deleteColumn(c0, c0);
221       region.delete(d, null, false);
222 
223       r = region.getClosestRowBefore(T30, c0);
224       assertTrue(Bytes.equals(T10, r.getRow()));
225       r = region.getClosestRowBefore(T31, c0);
226       assertTrue(Bytes.equals(T10, r.getRow()));
227 
228       region.flushcache();
229 
230       // try finding "010" after flush
231       r = region.getClosestRowBefore(T30, c0);
232       assertTrue(Bytes.equals(T10, r.getRow()));
233       r = region.getClosestRowBefore(T31, c0);
234       assertTrue(Bytes.equals(T10, r.getRow()));
235 
236       // Put into a different column family.  Should make it so I still get t10
237       p = new Put(T20);
238       p.add(c1, c1, T20);
239       region.put(p);
240 
241       r = region.getClosestRowBefore(T30, c0);
242       assertTrue(Bytes.equals(T10, r.getRow()));
243       r = region.getClosestRowBefore(T31, c0);
244       assertTrue(Bytes.equals(T10, r.getRow()));
245 
246       region.flushcache();
247 
248       r = region.getClosestRowBefore(T30, c0);
249       assertTrue(Bytes.equals(T10, r.getRow()));
250       r = region.getClosestRowBefore(T31, c0);
251       assertTrue(Bytes.equals(T10, r.getRow()));
252 
253       // Now try combo of memcache and mapfiles.  Delete the t20 COLUMS[1]
254       // in memory; make sure we get back t10 again.
255       d = new Delete(T20);
256       d.deleteColumn(c1, c1);
257       region.delete(d, null, false);
258       r = region.getClosestRowBefore(T30, c0);
259       assertTrue(Bytes.equals(T10, r.getRow()));
260 
261       // Ask for a value off the end of the file.  Should return t10.
262       r = region.getClosestRowBefore(T31, c0);
263       assertTrue(Bytes.equals(T10, r.getRow()));
264       region.flushcache();
265       r = region.getClosestRowBefore(T31, c0);
266       assertTrue(Bytes.equals(T10, r.getRow()));
267 
268       // Ok.  Let the candidate come out of hfile but have delete of
269       // the candidate be in memory.
270       p = new Put(T11);
271       p.add(c0, c0, T11);
272       region.put(p);
273       d = new Delete(T10);
274       d.deleteColumn(c1, c1);
275       r = region.getClosestRowBefore(T12, c0);
276       assertTrue(Bytes.equals(T11, r.getRow()));
277     } finally {
278       if (region != null) {
279         try {
280           region.close();
281         } catch (Exception e) {
282           e.printStackTrace();
283         }
284         region.getLog().closeAndDelete();
285       }
286     }
287   }
288 
289   /** For HBASE-694 */
290   public void testGetClosestRowBefore2() throws IOException{
291     HRegion region = null;
292     byte [] c0 = COLUMNS[0];
293     try {
294       HTableDescriptor htd = createTableDescriptor(getName());
295       region = createNewHRegion(htd, null, null);
296 
297       Put p = new Put(T10);
298       p.add(c0, c0, T10);
299       region.put(p);
300 
301       p = new Put(T30);
302       p.add(c0, c0, T30);
303       region.put(p);
304 
305       p = new Put(T40);
306       p.add(c0, c0, T40);
307       region.put(p);
308 
309       // try finding "035"
310       Result r = region.getClosestRowBefore(T35, c0);
311       assertTrue(Bytes.equals(T30, r.getRow()));
312 
313       region.flushcache();
314 
315       // try finding "035"
316       r = region.getClosestRowBefore(T35, c0);
317       assertTrue(Bytes.equals(T30, r.getRow()));
318 
319       p = new Put(T20);
320       p.add(c0, c0, T20);
321       region.put(p);
322 
323       // try finding "035"
324       r = region.getClosestRowBefore(T35, c0);
325       assertTrue(Bytes.equals(T30, r.getRow()));
326 
327       region.flushcache();
328 
329       // try finding "035"
330       r = region.getClosestRowBefore(T35, c0);
331       assertTrue(Bytes.equals(T30, r.getRow()));
332     } finally {
333       if (region != null) {
334         try {
335           region.close();
336         } catch (Exception e) {
337           e.printStackTrace();
338         }
339         region.getLog().closeAndDelete();
340       }
341     }
342   }
343 
344   @org.junit.Rule
345   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
346     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
347 }
348