~ubuntu-branches/ubuntu/precise/hbase/precise

« back to all changes in this revision

Viewing changes to src/java/org/apache/hadoop/hbase/regionserver/StoreFile.java

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Koch
  • Date: 2010-05-06 14:20:42 UTC
  • Revision ID: james.westby@ubuntu.com-20100506142042-r3hlvgxdcpb8tynl
Tags: upstream-0.20.4+dfsg1
ImportĀ upstreamĀ versionĀ 0.20.4+dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.FileNotFoundException;
 
23
import java.io.IOException;
 
24
import java.lang.management.ManagementFactory;
 
25
import java.lang.management.MemoryUsage;
 
26
import java.util.Map;
 
27
import java.util.Random;
 
28
import java.util.concurrent.atomic.AtomicBoolean;
 
29
import java.util.regex.Matcher;
 
30
import java.util.regex.Pattern;
 
31
 
 
32
import org.apache.commons.logging.Log;
 
33
import org.apache.commons.logging.LogFactory;
 
34
import org.apache.hadoop.fs.FileSystem;
 
35
import org.apache.hadoop.fs.Path;
 
36
import org.apache.hadoop.hbase.HBaseConfiguration;
 
37
import org.apache.hadoop.hbase.HConstants;
 
38
import org.apache.hadoop.hbase.KeyValue;
 
39
import org.apache.hadoop.hbase.io.HalfHFileReader;
 
40
import org.apache.hadoop.hbase.io.Reference;
 
41
import org.apache.hadoop.hbase.io.hfile.BlockCache;
 
42
import org.apache.hadoop.hbase.io.hfile.Compression;
 
43
import org.apache.hadoop.hbase.io.hfile.HFile;
 
44
import org.apache.hadoop.hbase.io.hfile.LruBlockCache;
 
45
import org.apache.hadoop.hbase.io.hfile.HFile.Reader;
 
46
import org.apache.hadoop.hbase.util.Bytes;
 
47
import org.apache.hadoop.util.StringUtils;
 
48
 
 
49
/**
 
50
 * A Store data file.  Stores usually have one or more of these files.  They
 
51
 * are produced by flushing the memstore to disk.  To
 
52
 * create, call {@link #getWriter(FileSystem, Path)} and append data.  Be
 
53
 * sure to add any metadata before calling close on the Writer
 
54
 * (Use the appendMetadata convenience methods). On close, a StoreFile is
 
55
 * sitting in the Filesystem.  To refer to it, create a StoreFile instance
 
56
 * passing filesystem and path.  To read, call {@link #getReader()}.
 
57
 * <p>StoreFiles may also reference store files in another Store.
 
58
 */
 
59
public class StoreFile implements HConstants {
 
60
  static final Log LOG = LogFactory.getLog(StoreFile.class.getName());
 
61
 
 
62
  private static final String HFILE_CACHE_SIZE_KEY = "hfile.block.cache.size";
 
63
 
 
64
  private static BlockCache hfileBlockCache = null;
 
65
 
 
66
  // Make default block size for StoreFiles 8k while testing.  TODO: FIX!
 
67
  // Need to make it 8k for testing.
 
68
  private static final int DEFAULT_BLOCKSIZE_SMALL = 8 * 1024;
 
69
 
 
70
  private final FileSystem fs;
 
71
  // This file's path.
 
72
  private final Path path;
 
73
  // If this storefile references another, this is the reference instance.
 
74
  private Reference reference;
 
75
  // If this StoreFile references another, this is the other files path.
 
76
  private Path referencePath;
 
77
  // Should the block cache be used or not.
 
78
  private boolean blockcache;
 
79
  // Is this from an in-memory store
 
80
  private boolean inMemory;
 
81
  
 
82
  // Keys for metadata stored in backing HFile.
 
83
  private static final byte [] MAX_SEQ_ID_KEY = Bytes.toBytes("MAX_SEQ_ID_KEY");
 
84
  // Set when we obtain a Reader.
 
85
  private long sequenceid = -1;
 
86
 
 
87
  private static final byte [] MAJOR_COMPACTION_KEY =
 
88
    Bytes.toBytes("MAJOR_COMPACTION_KEY");
 
89
  // If true, this file was product of a major compaction.  Its then set
 
90
  // whenever you get a Reader.
 
91
  private AtomicBoolean majorCompaction = null;
 
92
  
 
93
  /*
 
94
   * Regex that will work for straight filenames and for reference names.
 
95
   * If reference, then the regex has more than just one group.  Group 1 is
 
96
   * this files id.  Group 2 the referenced region name, etc.
 
97
   */
 
98
  private static final Pattern REF_NAME_PARSER =
 
99
    Pattern.compile("^(\\d+)(?:\\.(.+))?$");
 
100
 
 
101
  private volatile HFile.Reader reader;
 
102
 
 
103
  // Used making file ids.
 
104
  private final static Random rand = new Random();
 
105
  private final HBaseConfiguration conf;
 
106
 
 
107
  /**
 
108
   * Constructor, loads a reader and it's indices, etc. May allocate a 
 
109
   * substantial amount of ram depending on the underlying files (10-20MB?).
 
110
   * 
 
111
   * @param fs  The current file system to use.
 
112
   * @param p  The path of the file.
 
113
   * @param blockcache  <code>true</code> if the block cache is enabled.
 
114
   * @param conf  The current configuration.
 
115
   * @throws IOException When opening the reader fails.
 
116
   */
 
117
  StoreFile(final FileSystem fs, final Path p, final boolean blockcache, 
 
118
      final HBaseConfiguration conf, final boolean inMemory) 
 
119
  throws IOException {
 
120
    this.conf = conf;
 
121
    this.fs = fs;
 
122
    this.path = p;
 
123
    this.blockcache = blockcache;
 
124
    this.inMemory = inMemory;
 
125
    if (isReference(p)) {
 
126
      this.reference = Reference.read(fs, p);
 
127
      this.referencePath = getReferredToFile(this.path);
 
128
    }
 
129
    this.reader = open();
 
130
  }
 
131
 
 
132
  /**
 
133
   * @return Path or null if this StoreFile was made with a Stream.
 
134
   */
 
135
  Path getPath() {
 
136
    return this.path;
 
137
  }
 
138
 
 
139
  /**
 
140
   * @return The Store/ColumnFamily this file belongs to.
 
141
   */
 
142
  byte [] getFamily() {
 
143
    return Bytes.toBytes(this.path.getParent().getName());
 
144
  }
 
145
 
 
146
  /**
 
147
   * @return True if this is a StoreFile Reference; call after {@link #open()}
 
148
   * else may get wrong answer.
 
149
   */
 
150
  boolean isReference() {
 
151
    return this.reference != null;
 
152
  }
 
153
 
 
154
  /**
 
155
   * @param p Path to check.
 
156
   * @return True if the path has format of a HStoreFile reference.
 
157
   */
 
158
  public static boolean isReference(final Path p) {
 
159
    return isReference(p, REF_NAME_PARSER.matcher(p.getName()));
 
160
  }
 
161
 
 
162
  /**
 
163
   * @param p Path to check.
 
164
   * @param m Matcher to use.
 
165
   * @return True if the path has format of a HStoreFile reference.
 
166
   */
 
167
  public static boolean isReference(final Path p, final Matcher m) {
 
168
    if (m == null || !m.matches()) {
 
169
      LOG.warn("Failed match of store file name " + p.toString());
 
170
      throw new RuntimeException("Failed match of store file name " +
 
171
          p.toString());
 
172
    }
 
173
    return m.groupCount() > 1 && m.group(2) != null;
 
174
  }
 
175
 
 
176
  /*
 
177
   * Return path to the file referred to by a Reference.  Presumes a directory
 
178
   * hierarchy of <code>${hbase.rootdir}/tablename/regionname/familyname</code>.
 
179
   * @param p Path to a Reference file.
 
180
   * @return Calculated path to parent region file.
 
181
   * @throws IOException
 
182
   */
 
183
  static Path getReferredToFile(final Path p) {
 
184
    Matcher m = REF_NAME_PARSER.matcher(p.getName());
 
185
    if (m == null || !m.matches()) {
 
186
      LOG.warn("Failed match of store file name " + p.toString());
 
187
      throw new RuntimeException("Failed match of store file name " +
 
188
          p.toString());
 
189
    }
 
190
    // Other region name is suffix on the passed Reference file name
 
191
    String otherRegion = m.group(2);
 
192
    // Tabledir is up two directories from where Reference was written.
 
193
    Path tableDir = p.getParent().getParent().getParent();
 
194
    String nameStrippedOfSuffix = m.group(1);
 
195
    // Build up new path with the referenced region in place of our current
 
196
    // region in the reference path.  Also strip regionname suffix from name.
 
197
    return new Path(new Path(new Path(tableDir, otherRegion),
 
198
      p.getParent().getName()), nameStrippedOfSuffix);
 
199
  }
 
200
 
 
201
  /**
 
202
   * @return True if this file was made by a major compaction.
 
203
   */
 
204
  boolean isMajorCompaction() {
 
205
    if (this.majorCompaction == null) {
 
206
      throw new NullPointerException("This has not been set yet");
 
207
    }
 
208
    return this.majorCompaction.get();
 
209
  }
 
210
 
 
211
  /**
 
212
   * @return This files maximum edit sequence id.
 
213
   */
 
214
  public long getMaxSequenceId() {
 
215
    if (this.sequenceid == -1) {
 
216
      throw new IllegalAccessError("Has not been initialized");
 
217
    }
 
218
    return this.sequenceid;
 
219
  }
 
220
 
 
221
  /**
 
222
   * Returns the block cache or <code>null</code> in case none should be used.
 
223
   * 
 
224
   * @param conf  The current configuration.
 
225
   * @return The block cache or <code>null</code>.
 
226
   */
 
227
  public static synchronized BlockCache getBlockCache(HBaseConfiguration conf) {
 
228
    if (hfileBlockCache != null) return hfileBlockCache;
 
229
 
 
230
    float cachePercentage = conf.getFloat(HFILE_CACHE_SIZE_KEY, 0.0f);
 
231
    // There should be a better way to optimize this. But oh well.
 
232
    if (cachePercentage == 0L) return null;
 
233
    if (cachePercentage > 1.0) {
 
234
      throw new IllegalArgumentException(HFILE_CACHE_SIZE_KEY +
 
235
        " must be between 0.0 and 1.0, not > 1.0");
 
236
    }
 
237
 
 
238
    // Calculate the amount of heap to give the heap.
 
239
    MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
 
240
    long cacheSize = (long)(mu.getMax() * cachePercentage);
 
241
    LOG.info("Allocating LruBlockCache with maximum size " +
 
242
      StringUtils.humanReadableInt(cacheSize));
 
243
    hfileBlockCache = new LruBlockCache(cacheSize, DEFAULT_BLOCKSIZE_SMALL);
 
244
    return hfileBlockCache;
 
245
  }
 
246
 
 
247
  /**
 
248
   * @return the blockcache
 
249
   */
 
250
  public BlockCache getBlockCache() {
 
251
    return blockcache ? getBlockCache(conf) : null;
 
252
  }
 
253
 
 
254
  /**
 
255
   * Opens reader on this store file.  Called by Constructor.
 
256
   * @return Reader for the store file.
 
257
   * @throws IOException
 
258
   * @see #close()
 
259
   */
 
260
  protected HFile.Reader open()
 
261
  throws IOException {
 
262
    if (this.reader != null) {
 
263
      throw new IllegalAccessError("Already open");
 
264
    }
 
265
    if (isReference()) {
 
266
      this.reader = new HalfHFileReader(this.fs, this.referencePath, 
 
267
          getBlockCache(), this.reference);
 
268
    } else {
 
269
      this.reader = new Reader(this.fs, this.path, getBlockCache(),
 
270
          this.inMemory);
 
271
    }
 
272
    // Load up indices and fileinfo.
 
273
    Map<byte [], byte []> map = this.reader.loadFileInfo();
 
274
    // Read in our metadata.
 
275
    byte [] b = map.get(MAX_SEQ_ID_KEY);
 
276
    if (b != null) {
 
277
      // By convention, if halfhfile, top half has a sequence number > bottom
 
278
      // half. Thats why we add one in below. Its done for case the two halves
 
279
      // are ever merged back together --rare.  Without it, on open of store,
 
280
      // since store files are distingushed by sequence id, the one half would
 
281
      // subsume the other.
 
282
      this.sequenceid = Bytes.toLong(b);
 
283
      if (isReference()) {
 
284
        if (Reference.isTopFileRegion(this.reference.getFileRegion())) {
 
285
          this.sequenceid += 1;
 
286
        }
 
287
      }
 
288
      
 
289
    }
 
290
    b = map.get(MAJOR_COMPACTION_KEY);
 
291
    if (b != null) {
 
292
      boolean mc = Bytes.toBoolean(b);
 
293
      if (this.majorCompaction == null) {
 
294
        this.majorCompaction = new AtomicBoolean(mc);
 
295
      } else {
 
296
        this.majorCompaction.set(mc);
 
297
      }
 
298
    }
 
299
 
 
300
    // TODO read in bloom filter here, ignore if the column family config says
 
301
    // "no bloom filter" even if there is one in the hfile.
 
302
    return this.reader;
 
303
  }
 
304
 
 
305
  /**
 
306
   * @return Current reader.  Must call open first else returns null.
 
307
   */
 
308
  public HFile.Reader getReader() {
 
309
    return this.reader;
 
310
  }
 
311
 
 
312
  /**
 
313
   * @throws IOException
 
314
   */
 
315
  public synchronized void close() throws IOException {
 
316
    if (this.reader != null) {
 
317
      this.reader.close();
 
318
      this.reader = null;
 
319
    }
 
320
  }
 
321
 
 
322
  @Override
 
323
  public String toString() {
 
324
    return this.path.toString() +
 
325
      (isReference()? "-" + this.referencePath + "-" + reference.toString(): "");
 
326
  }
 
327
 
 
328
  /**
 
329
   * Delete this file
 
330
   * @throws IOException 
 
331
   */
 
332
  public void delete() throws IOException {
 
333
    close();
 
334
    this.fs.delete(getPath(), true);
 
335
  }
 
336
 
 
337
  /**
 
338
   * Utility to help with rename.
 
339
   * @param fs
 
340
   * @param src
 
341
   * @param tgt
 
342
   * @return True if succeeded.
 
343
   * @throws IOException
 
344
   */
 
345
  public static Path rename(final FileSystem fs, final Path src,
 
346
      final Path tgt)
 
347
  throws IOException {
 
348
    if (!fs.exists(src)) {
 
349
      throw new FileNotFoundException(src.toString());
 
350
    }
 
351
    if (!fs.rename(src, tgt)) {
 
352
      throw new IOException("Failed rename of " + src + " to " + tgt);
 
353
    }
 
354
    return tgt;
 
355
  }
 
356
 
 
357
  /**
 
358
   * Get a store file writer. Client is responsible for closing file when done.
 
359
   * If metadata, add BEFORE closing using
 
360
   * {@link #appendMetadata(org.apache.hadoop.hbase.io.hfile.HFile.Writer, long)}.
 
361
   * @param fs
 
362
   * @param dir Path to family directory.  Makes the directory if doesn't exist.
 
363
   * Creates a file with a unique name in this directory.
 
364
   * @return HFile.Writer
 
365
   * @throws IOException
 
366
   */
 
367
  public static HFile.Writer getWriter(final FileSystem fs, final Path dir)
 
368
  throws IOException {
 
369
    return getWriter(fs, dir, DEFAULT_BLOCKSIZE_SMALL, null, null);
 
370
  }
 
371
 
 
372
  /**
 
373
   * Get a store file writer. Client is responsible for closing file when done.
 
374
   * If metadata, add BEFORE closing using
 
375
   * {@link #appendMetadata(org.apache.hadoop.hbase.io.hfile.HFile.Writer, long)}.
 
376
   * @param fs
 
377
   * @param dir Path to family directory.  Makes the directory if doesn't exist.
 
378
   * Creates a file with a unique name in this directory.
 
379
   * @param blocksize
 
380
   * @param algorithm Pass null to get default.
 
381
   * @param c Pass null to get default.
 
382
   * @return HFile.Writer
 
383
   * @throws IOException
 
384
   */
 
385
  public static HFile.Writer getWriter(final FileSystem fs, final Path dir,
 
386
                                       final int blocksize, final Compression.Algorithm algorithm,
 
387
                                       final KeyValue.KeyComparator c)
 
388
  throws IOException {
 
389
    if (!fs.exists(dir)) {
 
390
      fs.mkdirs(dir);
 
391
    }
 
392
    Path path = getUniqueFile(fs, dir);
 
393
    return new HFile.Writer(fs, path, blocksize,
 
394
      algorithm == null? HFile.DEFAULT_COMPRESSION_ALGORITHM: algorithm,
 
395
      c == null? KeyValue.KEY_COMPARATOR: c);
 
396
  }
 
397
 
 
398
  /**
 
399
   * @param fs
 
400
   * @param dir Directory to create file in.
 
401
   * @return random filename inside passed <code>dir</code>
 
402
   */
 
403
  public static Path getUniqueFile(final FileSystem fs, final Path dir)
 
404
  throws IOException {
 
405
    if (!fs.getFileStatus(dir).isDir()) {
 
406
      throw new IOException("Expecting " + dir.toString() +
 
407
        " to be a directory");
 
408
    }
 
409
    return fs.getFileStatus(dir).isDir()? getRandomFilename(fs, dir): dir;
 
410
  }
 
411
 
 
412
  /**
 
413
   *
 
414
   * @param fs
 
415
   * @param dir
 
416
   * @return Path to a file that doesn't exist at time of this invocation.
 
417
   * @throws IOException
 
418
   */
 
419
  static Path getRandomFilename(final FileSystem fs, final Path dir)
 
420
  throws IOException {
 
421
    return getRandomFilename(fs, dir, null);
 
422
  }
 
423
 
 
424
  /**
 
425
   *
 
426
   * @param fs
 
427
   * @param dir
 
428
   * @param suffix
 
429
   * @return Path to a file that doesn't exist at time of this invocation.
 
430
   * @throws IOException
 
431
   */
 
432
  static Path getRandomFilename(final FileSystem fs, final Path dir,
 
433
      final String suffix)
 
434
  throws IOException {
 
435
    long id = -1;
 
436
    Path p = null;
 
437
    do {
 
438
      id = Math.abs(rand.nextLong());
 
439
      p = new Path(dir, Long.toString(id) +
 
440
        ((suffix == null || suffix.length() <= 0)? "": suffix));
 
441
    } while(fs.exists(p));
 
442
    return p;
 
443
  }
 
444
 
 
445
  /**
 
446
   * Write file metadata.
 
447
   * Call before you call close on the passed <code>w</code> since its written
 
448
   * as metadata to that file.
 
449
   * 
 
450
   * @param w hfile writer
 
451
   * @param maxSequenceId Maximum sequence id.
 
452
   * @throws IOException
 
453
   */
 
454
  static void appendMetadata(final HFile.Writer w, final long maxSequenceId)
 
455
  throws IOException {
 
456
    appendMetadata(w, maxSequenceId, false);
 
457
  }
 
458
 
 
459
  /**
 
460
   * Writes metadata.
 
461
   * Call before you call close on the passed <code>w</code> since its written
 
462
   * as metadata to that file.
 
463
   * @param maxSequenceId Maximum sequence id.
 
464
   * @param mc True if this file is product of a major compaction
 
465
   * @throws IOException
 
466
   */
 
467
  public static void appendMetadata(final HFile.Writer w, final long maxSequenceId,
 
468
    final boolean mc)
 
469
  throws IOException {
 
470
    w.appendFileInfo(MAX_SEQ_ID_KEY, Bytes.toBytes(maxSequenceId));
 
471
    w.appendFileInfo(MAJOR_COMPACTION_KEY, Bytes.toBytes(mc));
 
472
  }
 
473
 
 
474
  /*
 
475
   * Write out a split reference.
 
476
   * @param fs
 
477
   * @param splitDir Presumes path format is actually
 
478
   * <code>SOME_DIRECTORY/REGIONNAME/FAMILY</code>.
 
479
   * @param f File to split.
 
480
   * @param splitRow
 
481
   * @param range
 
482
   * @return Path to created reference.
 
483
   * @throws IOException
 
484
   */
 
485
  static Path split(final FileSystem fs, final Path splitDir,
 
486
    final StoreFile f, final byte [] splitRow, final Reference.Range range)
 
487
  throws IOException {
 
488
    // A reference to the bottom half of the hsf store file.
 
489
    Reference r = new Reference(splitRow, range);
 
490
    // Add the referred-to regions name as a dot separated suffix. 
 
491
    // See REF_NAME_PARSER regex above.  The referred-to regions name is
 
492
    // up in the path of the passed in <code>f</code> -- parentdir is family,
 
493
    // then the directory above is the region name.
 
494
    String parentRegionName = f.getPath().getParent().getParent().getName();
 
495
    // Write reference with same file id only with the other region name as
 
496
    // suffix and into the new region location (under same family).
 
497
    Path p = new Path(splitDir, f.getPath().getName() + "." + parentRegionName);
 
498
    return r.write(fs, p);
 
499
  }
 
500
}