View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.ByteArrayInputStream;
28  import java.io.ByteArrayOutputStream;
29  import java.io.DataInputStream;
30  import java.io.DataOutputStream;
31  import java.io.IOException;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.NavigableSet;
35  import java.util.Set;
36  
37  import org.apache.hadoop.hbase.client.Get;
38  import org.apache.hadoop.hbase.client.Scan;
39  import org.apache.hadoop.hbase.exceptions.DeserializationException;
40  import org.apache.hadoop.hbase.filter.BinaryComparator;
41  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
42  import org.apache.hadoop.hbase.filter.Filter;
43  import org.apache.hadoop.hbase.filter.PrefixFilter;
44  import org.apache.hadoop.hbase.filter.RowFilter;
45  import org.apache.hadoop.hbase.io.TimeRange;
46  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
47  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
48  import org.apache.hadoop.hbase.util.Bytes;
49  import org.apache.hadoop.hbase.util.Writables;
50  import org.apache.hadoop.io.DataInputBuffer;
51  import org.junit.Test;
52  import org.junit.experimental.categories.Category;
53  
54  /**
55   * Test HBase Writables serializations
56   */
57  @Category(SmallTests.class)
58  public class TestSerialization {
59    @Test public void testKeyValue() throws Exception {
60      final String name = "testKeyValue2";
61      byte[] row = name.getBytes();
62      byte[] fam = "fam".getBytes();
63      byte[] qf = "qf".getBytes();
64      long ts = System.currentTimeMillis();
65      byte[] val = "val".getBytes();
66      KeyValue kv = new KeyValue(row, fam, qf, ts, val);
67      ByteArrayOutputStream baos = new ByteArrayOutputStream();
68      DataOutputStream dos = new DataOutputStream(baos);
69      long l = KeyValue.write(kv, dos);
70      dos.close();
71      byte [] mb = baos.toByteArray();
72      ByteArrayInputStream bais = new ByteArrayInputStream(mb);
73      DataInputStream dis = new DataInputStream(bais);
74      KeyValue deserializedKv = KeyValue.create(dis);
75      assertTrue(Bytes.equals(kv.getBuffer(), deserializedKv.getBuffer()));
76      assertEquals(kv.getOffset(), deserializedKv.getOffset());
77      assertEquals(kv.getLength(), deserializedKv.getLength());
78    }
79  
80    @Test public void testCreateKeyValueInvalidNegativeLength() {
81  
82      KeyValue kv_0 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"),       // 51 bytes
83                                   Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my12345"));
84  
85      KeyValue kv_1 = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"),       // 49 bytes
86                                   Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("my123"));
87  
88      ByteArrayOutputStream baos = new ByteArrayOutputStream();
89      DataOutputStream dos = new DataOutputStream(baos);
90  
91      long l = 0;
92      try {
93        l  = KeyValue.oswrite(kv_0, dos);
94        l += KeyValue.oswrite(kv_1, dos);
95        assertEquals(100L, l);
96      } catch (IOException e) {
97        fail("Unexpected IOException" + e.getMessage());
98      }
99  
100     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
101     DataInputStream dis = new DataInputStream(bais);
102 
103     try {
104       KeyValue.create(dis);
105       assertTrue(kv_0.equals(kv_1));
106     } catch (Exception e) {
107       fail("Unexpected Exception" + e.getMessage());
108     }
109 
110     // length -1
111     try {
112       // even if we have a good kv now in dis we will just pass length with -1 for simplicity
113       KeyValue.create(-1, dis);
114       fail("Expected corrupt stream");
115     } catch (Exception e) {
116       assertEquals("Failed read -1 bytes, stream corrupt?", e.getMessage());
117     }
118 
119   }
120 
121   @Test
122   public void testSplitLogTask() throws DeserializationException {
123     SplitLogTask slt = new SplitLogTask.Unassigned(ServerName.valueOf("mgr,1,1"));
124     byte [] bytes = slt.toByteArray();
125     SplitLogTask sltDeserialized = SplitLogTask.parseFrom(bytes);
126     assertTrue(slt.equals(sltDeserialized));
127   }
128 
129   @Test public void testCompareFilter() throws Exception {
130     Filter f = new RowFilter(CompareOp.EQUAL,
131       new BinaryComparator(Bytes.toBytes("testRowOne-2")));
132     byte [] bytes = f.toByteArray();
133     Filter ff = RowFilter.parseFrom(bytes);
134     assertNotNull(ff);
135   }
136 
137   @Test public void testTableDescriptor() throws Exception {
138     final String name = "testTableDescriptor";
139     HTableDescriptor htd = createTableDescriptor(name);
140     byte [] mb = Writables.getBytes(htd);
141     HTableDescriptor deserializedHtd =
142       (HTableDescriptor)Writables.getWritable(mb, new HTableDescriptor());
143     assertEquals(htd.getTableName(), deserializedHtd.getTableName());
144   }
145 
146   /**
147    * Test RegionInfo serialization
148    * @throws Exception
149    */
150   @Test public void testRegionInfo() throws Exception {
151     HRegionInfo hri = createRandomRegion("testRegionInfo");
152 
153     //test toByteArray()
154     byte [] hrib = hri.toByteArray();
155     HRegionInfo deserializedHri = HRegionInfo.parseFrom(hrib);
156     assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName());
157     assertEquals(hri, deserializedHri);
158 
159     //test toDelimitedByteArray()
160     hrib = hri.toDelimitedByteArray();
161     DataInputBuffer buf = new DataInputBuffer();
162     try {
163       buf.reset(hrib, hrib.length);
164       deserializedHri = HRegionInfo.parseFrom(buf);
165       assertEquals(hri.getEncodedName(), deserializedHri.getEncodedName());
166       assertEquals(hri, deserializedHri);
167     } finally {
168       buf.close();
169     }
170   }
171 
172   @Test public void testRegionInfos() throws Exception {
173     HRegionInfo hri = createRandomRegion("testRegionInfos");
174     byte[] triple = HRegionInfo.toDelimitedByteArray(hri, hri, hri);
175     List<HRegionInfo> regions = HRegionInfo.parseDelimitedFrom(triple, 0, triple.length);
176     assertTrue(regions.size() == 3);
177     assertTrue(regions.get(0).equals(regions.get(1)));
178     assertTrue(regions.get(0).equals(regions.get(2)));
179   }
180 
181   private HRegionInfo createRandomRegion(final String name) {
182     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
183     String [] families = new String [] {"info", "anchor"};
184     for (int i = 0; i < families.length; i++) {
185       htd.addFamily(new HColumnDescriptor(families[i]));
186     }
187     return new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW,
188       HConstants.EMPTY_END_ROW);
189   }
190 
191   /*
192    * TODO
193   @Test public void testPut() throws Exception{
194     byte[] row = "row".getBytes();
195     byte[] fam = "fam".getBytes();
196     byte[] qf1 = "qf1".getBytes();
197     byte[] qf2 = "qf2".getBytes();
198     byte[] qf3 = "qf3".getBytes();
199     byte[] qf4 = "qf4".getBytes();
200     byte[] qf5 = "qf5".getBytes();
201     byte[] qf6 = "qf6".getBytes();
202     byte[] qf7 = "qf7".getBytes();
203     byte[] qf8 = "qf8".getBytes();
204 
205     long ts = System.currentTimeMillis();
206     byte[] val = "val".getBytes();
207 
208     Put put = new Put(row);
209     put.setWriteToWAL(false);
210     put.add(fam, qf1, ts, val);
211     put.add(fam, qf2, ts, val);
212     put.add(fam, qf3, ts, val);
213     put.add(fam, qf4, ts, val);
214     put.add(fam, qf5, ts, val);
215     put.add(fam, qf6, ts, val);
216     put.add(fam, qf7, ts, val);
217     put.add(fam, qf8, ts, val);
218 
219     byte[] sb = Writables.getBytes(put);
220     Put desPut = (Put)Writables.getWritable(sb, new Put());
221 
222     //Timing test
223 //    long start = System.nanoTime();
224 //    desPut = (Put)Writables.getWritable(sb, new Put());
225 //    long stop = System.nanoTime();
226 //    System.out.println("timer " +(stop-start));
227 
228     assertTrue(Bytes.equals(put.getRow(), desPut.getRow()));
229     List<KeyValue> list = null;
230     List<KeyValue> desList = null;
231     for(Map.Entry<byte[], List<KeyValue>> entry : put.getFamilyMap().entrySet()){
232       assertTrue(desPut.getFamilyMap().containsKey(entry.getKey()));
233       list = entry.getValue();
234       desList = desPut.getFamilyMap().get(entry.getKey());
235       for(int i=0; i<list.size(); i++){
236         assertTrue(list.get(i).equals(desList.get(i)));
237       }
238     }
239   }
240 
241 
242   @Test public void testPut2() throws Exception{
243     byte[] row = "testAbort,,1243116656250".getBytes();
244     byte[] fam = "historian".getBytes();
245     byte[] qf1 = "creation".getBytes();
246 
247     long ts = 9223372036854775807L;
248     byte[] val = "dont-care".getBytes();
249 
250     Put put = new Put(row);
251     put.add(fam, qf1, ts, val);
252 
253     byte[] sb = Writables.getBytes(put);
254     Put desPut = (Put)Writables.getWritable(sb, new Put());
255 
256     assertTrue(Bytes.equals(put.getRow(), desPut.getRow()));
257     List<KeyValue> list = null;
258     List<KeyValue> desList = null;
259     for(Map.Entry<byte[], List<KeyValue>> entry : put.getFamilyMap().entrySet()){
260       assertTrue(desPut.getFamilyMap().containsKey(entry.getKey()));
261       list = entry.getValue();
262       desList = desPut.getFamilyMap().get(entry.getKey());
263       for(int i=0; i<list.size(); i++){
264         assertTrue(list.get(i).equals(desList.get(i)));
265       }
266     }
267   }
268 
269 
270   @Test public void testDelete() throws Exception{
271     byte[] row = "row".getBytes();
272     byte[] fam = "fam".getBytes();
273     byte[] qf1 = "qf1".getBytes();
274 
275     long ts = System.currentTimeMillis();
276 
277     Delete delete = new Delete(row);
278     delete.deleteColumn(fam, qf1, ts);
279 
280     byte[] sb = Writables.getBytes(delete);
281     Delete desDelete = (Delete)Writables.getWritable(sb, new Delete());
282 
283     assertTrue(Bytes.equals(delete.getRow(), desDelete.getRow()));
284     List<KeyValue> list = null;
285     List<KeyValue> desList = null;
286     for(Map.Entry<byte[], List<KeyValue>> entry :
287         delete.getFamilyMap().entrySet()){
288       assertTrue(desDelete.getFamilyMap().containsKey(entry.getKey()));
289       list = entry.getValue();
290       desList = desDelete.getFamilyMap().get(entry.getKey());
291       for(int i=0; i<list.size(); i++){
292         assertTrue(list.get(i).equals(desList.get(i)));
293       }
294     }
295   }
296   */
297 
298   @Test public void testGet() throws Exception{
299     byte[] row = "row".getBytes();
300     byte[] fam = "fam".getBytes();
301     byte[] qf1 = "qf1".getBytes();
302 
303     long ts = System.currentTimeMillis();
304     int maxVersions = 2;
305 
306     Get get = new Get(row);
307     get.addColumn(fam, qf1);
308     get.setTimeRange(ts, ts+1);
309     get.setMaxVersions(maxVersions);
310 
311     ClientProtos.Get getProto = ProtobufUtil.toGet(get);
312     Get desGet = ProtobufUtil.toGet(getProto);
313 
314     assertTrue(Bytes.equals(get.getRow(), desGet.getRow()));
315     Set<byte[]> set = null;
316     Set<byte[]> desSet = null;
317 
318     for(Map.Entry<byte[], NavigableSet<byte[]>> entry :
319         get.getFamilyMap().entrySet()){
320       assertTrue(desGet.getFamilyMap().containsKey(entry.getKey()));
321       set = entry.getValue();
322       desSet = desGet.getFamilyMap().get(entry.getKey());
323       for(byte [] qualifier : set){
324         assertTrue(desSet.contains(qualifier));
325       }
326     }
327 
328     assertEquals(get.getMaxVersions(), desGet.getMaxVersions());
329     TimeRange tr = get.getTimeRange();
330     TimeRange desTr = desGet.getTimeRange();
331     assertEquals(tr.getMax(), desTr.getMax());
332     assertEquals(tr.getMin(), desTr.getMin());
333   }
334 
335 
336   @Test public void testScan() throws Exception {
337 
338     byte[] startRow = "startRow".getBytes();
339     byte[] stopRow  = "stopRow".getBytes();
340     byte[] fam = "fam".getBytes();
341     byte[] qf1 = "qf1".getBytes();
342 
343     long ts = System.currentTimeMillis();
344     int maxVersions = 2;
345 
346     Scan scan = new Scan(startRow, stopRow);
347     scan.addColumn(fam, qf1);
348     scan.setTimeRange(ts, ts+1);
349     scan.setMaxVersions(maxVersions);
350 
351     ClientProtos.Scan scanProto = ProtobufUtil.toScan(scan);
352     Scan desScan = ProtobufUtil.toScan(scanProto);
353 
354     assertTrue(Bytes.equals(scan.getStartRow(), desScan.getStartRow()));
355     assertTrue(Bytes.equals(scan.getStopRow(), desScan.getStopRow()));
356     assertEquals(scan.getCacheBlocks(), desScan.getCacheBlocks());
357     Set<byte[]> set = null;
358     Set<byte[]> desSet = null;
359 
360     for(Map.Entry<byte[], NavigableSet<byte[]>> entry :
361         scan.getFamilyMap().entrySet()){
362       assertTrue(desScan.getFamilyMap().containsKey(entry.getKey()));
363       set = entry.getValue();
364       desSet = desScan.getFamilyMap().get(entry.getKey());
365       for(byte[] column : set){
366         assertTrue(desSet.contains(column));
367       }
368 
369       // Test filters are serialized properly.
370       scan = new Scan(startRow);
371       final String name = "testScan";
372       byte [] prefix = Bytes.toBytes(name);
373       scan.setFilter(new PrefixFilter(prefix));
374       scanProto = ProtobufUtil.toScan(scan);
375       desScan = ProtobufUtil.toScan(scanProto);
376       Filter f = desScan.getFilter();
377       assertTrue(f instanceof PrefixFilter);
378     }
379 
380     assertEquals(scan.getMaxVersions(), desScan.getMaxVersions());
381     TimeRange tr = scan.getTimeRange();
382     TimeRange desTr = desScan.getTimeRange();
383     assertEquals(tr.getMax(), desTr.getMax());
384     assertEquals(tr.getMin(), desTr.getMin());
385   }
386 
387   /*
388    * TODO
389   @Test public void testResultEmpty() throws Exception {
390     List<KeyValue> keys = new ArrayList<KeyValue>();
391     Result r = Result.newResult(keys);
392     assertTrue(r.isEmpty());
393     byte [] rb = Writables.getBytes(r);
394     Result deserializedR = (Result)Writables.getWritable(rb, new Result());
395     assertTrue(deserializedR.isEmpty());
396   }
397 
398 
399   @Test public void testResult() throws Exception {
400     byte [] rowA = Bytes.toBytes("rowA");
401     byte [] famA = Bytes.toBytes("famA");
402     byte [] qfA = Bytes.toBytes("qfA");
403     byte [] valueA = Bytes.toBytes("valueA");
404 
405     byte [] rowB = Bytes.toBytes("rowB");
406     byte [] famB = Bytes.toBytes("famB");
407     byte [] qfB = Bytes.toBytes("qfB");
408     byte [] valueB = Bytes.toBytes("valueB");
409 
410     KeyValue kvA = new KeyValue(rowA, famA, qfA, valueA);
411     KeyValue kvB = new KeyValue(rowB, famB, qfB, valueB);
412 
413     Result result = Result.newResult(new KeyValue[]{kvA, kvB});
414 
415     byte [] rb = Writables.getBytes(result);
416     Result deResult = (Result)Writables.getWritable(rb, new Result());
417 
418     assertTrue("results are not equivalent, first key mismatch",
419         result.raw()[0].equals(deResult.raw()[0]));
420 
421     assertTrue("results are not equivalent, second key mismatch",
422         result.raw()[1].equals(deResult.raw()[1]));
423 
424     // Test empty Result
425     Result r = new Result();
426     byte [] b = Writables.getBytes(r);
427     Result deserialized = (Result)Writables.getWritable(b, new Result());
428     assertEquals(r.size(), deserialized.size());
429   }
430 
431   @Test public void testResultDynamicBuild() throws Exception {
432     byte [] rowA = Bytes.toBytes("rowA");
433     byte [] famA = Bytes.toBytes("famA");
434     byte [] qfA = Bytes.toBytes("qfA");
435     byte [] valueA = Bytes.toBytes("valueA");
436 
437     byte [] rowB = Bytes.toBytes("rowB");
438     byte [] famB = Bytes.toBytes("famB");
439     byte [] qfB = Bytes.toBytes("qfB");
440     byte [] valueB = Bytes.toBytes("valueB");
441 
442     KeyValue kvA = new KeyValue(rowA, famA, qfA, valueA);
443     KeyValue kvB = new KeyValue(rowB, famB, qfB, valueB);
444 
445     Result result = Result.newResult(new KeyValue[]{kvA, kvB});
446 
447     byte [] rb = Writables.getBytes(result);
448 
449 
450     // Call getRow() first
451     Result deResult = (Result)Writables.getWritable(rb, new Result());
452     byte [] row = deResult.getRow();
453     assertTrue(Bytes.equals(row, rowA));
454 
455     // Call sorted() first
456     deResult = (Result)Writables.getWritable(rb, new Result());
457     assertTrue("results are not equivalent, first key mismatch",
458         result.raw()[0].equals(deResult.raw()[0]));
459     assertTrue("results are not equivalent, second key mismatch",
460         result.raw()[1].equals(deResult.raw()[1]));
461 
462     // Call raw() first
463     deResult = (Result)Writables.getWritable(rb, new Result());
464     assertTrue("results are not equivalent, first key mismatch",
465         result.raw()[0].equals(deResult.raw()[0]));
466     assertTrue("results are not equivalent, second key mismatch",
467         result.raw()[1].equals(deResult.raw()[1]));
468 
469 
470   }
471 
472   @Test public void testResultArray() throws Exception {
473     byte [] rowA = Bytes.toBytes("rowA");
474     byte [] famA = Bytes.toBytes("famA");
475     byte [] qfA = Bytes.toBytes("qfA");
476     byte [] valueA = Bytes.toBytes("valueA");
477 
478     byte [] rowB = Bytes.toBytes("rowB");
479     byte [] famB = Bytes.toBytes("famB");
480     byte [] qfB = Bytes.toBytes("qfB");
481     byte [] valueB = Bytes.toBytes("valueB");
482 
483     KeyValue kvA = new KeyValue(rowA, famA, qfA, valueA);
484     KeyValue kvB = new KeyValue(rowB, famB, qfB, valueB);
485 
486 
487     Result result1 = Result.newResult(new KeyValue[]{kvA, kvB});
488     Result result2 = Result.newResult(new KeyValue[]{kvB});
489     Result result3 = Result.newResult(new KeyValue[]{kvB});
490 
491     Result [] results = new Result [] {result1, result2, result3};
492 
493     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
494     DataOutputStream out = new DataOutputStream(byteStream);
495     Result.writeArray(out, results);
496 
497     byte [] rb = byteStream.toByteArray();
498 
499     DataInputBuffer in = new DataInputBuffer();
500     in.reset(rb, 0, rb.length);
501 
502     Result [] deResults = Result.readArray(in);
503 
504     assertTrue(results.length == deResults.length);
505 
506     for(int i=0;i<results.length;i++) {
507       KeyValue [] keysA = results[i].raw();
508       KeyValue [] keysB = deResults[i].raw();
509       assertTrue(keysA.length == keysB.length);
510       for(int j=0;j<keysA.length;j++) {
511         assertTrue("Expected equivalent keys but found:\n" +
512             "KeyA : " + keysA[j].toString() + "\n" +
513             "KeyB : " + keysB[j].toString() + "\n" +
514             keysA.length + " total keys, " + i + "th so far"
515             ,keysA[j].equals(keysB[j]));
516       }
517     }
518 
519   }
520 
521   @Test public void testResultArrayEmpty() throws Exception {
522     List<KeyValue> keys = new ArrayList<KeyValue>();
523     Result r = Result.newResult(keys);
524     Result [] results = new Result [] {r};
525 
526     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
527     DataOutputStream out = new DataOutputStream(byteStream);
528 
529     Result.writeArray(out, results);
530 
531     results = null;
532 
533     byteStream = new ByteArrayOutputStream();
534     out = new DataOutputStream(byteStream);
535     Result.writeArray(out, results);
536 
537     byte [] rb = byteStream.toByteArray();
538 
539     DataInputBuffer in = new DataInputBuffer();
540     in.reset(rb, 0, rb.length);
541 
542     Result [] deResults = Result.readArray(in);
543 
544     assertTrue(deResults.length == 0);
545 
546     results = new Result[0];
547 
548     byteStream = new ByteArrayOutputStream();
549     out = new DataOutputStream(byteStream);
550     Result.writeArray(out, results);
551 
552     rb = byteStream.toByteArray();
553 
554     in = new DataInputBuffer();
555     in.reset(rb, 0, rb.length);
556 
557     deResults = Result.readArray(in);
558 
559     assertTrue(deResults.length == 0);
560 
561   }
562   */
563 
564   protected static final int MAXVERSIONS = 3;
565   protected final static byte [] fam1 = Bytes.toBytes("colfamily1");
566   protected final static byte [] fam2 = Bytes.toBytes("colfamily2");
567   protected final static byte [] fam3 = Bytes.toBytes("colfamily3");
568   protected static final byte [][] COLUMNS = {fam1, fam2, fam3};
569 
570   /**
571    * Create a table of name <code>name</code> with {@link COLUMNS} for
572    * families.
573    * @param name Name to give table.
574    * @return Column descriptor.
575    */
576   protected HTableDescriptor createTableDescriptor(final String name) {
577     return createTableDescriptor(name, MAXVERSIONS);
578   }
579 
580   /**
581    * Create a table of name <code>name</code> with {@link COLUMNS} for
582    * families.
583    * @param name Name to give table.
584    * @param versions How many versions to allow per column.
585    * @return Column descriptor.
586    */
587   protected HTableDescriptor createTableDescriptor(final String name,
588       final int versions) {
589     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
590     htd.addFamily(new HColumnDescriptor(fam1)
591         .setMaxVersions(versions)
592         .setBlockCacheEnabled(false)
593     );
594     htd.addFamily(new HColumnDescriptor(fam2)
595         .setMaxVersions(versions)
596         .setBlockCacheEnabled(false)
597     );
598     htd.addFamily(new HColumnDescriptor(fam3)
599         .setMaxVersions(versions)
600         .setBlockCacheEnabled(false)
601     );
602     return htd;
603   }
604 }