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.mob.mapreduce;
20  
21  import java.io.IOException;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.fs.FileSystem;
28  import org.apache.hadoop.fs.Path;
29  import org.apache.hadoop.hbase.Cell;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.KeyValue;
33  import org.apache.hadoop.hbase.KeyValueUtil;
34  import org.apache.hadoop.hbase.Tag;
35  import org.apache.hadoop.hbase.TagType;
36  import org.apache.hadoop.hbase.client.HTable;
37  import org.apache.hadoop.hbase.client.Put;
38  import org.apache.hadoop.hbase.io.hfile.CacheConfig;
39  import org.apache.hadoop.hbase.mob.MobConstants;
40  import org.apache.hadoop.hbase.mob.MobUtils;
41  import org.apache.hadoop.hbase.mob.mapreduce.SweepJob.SweepCounter;
42  import org.apache.hadoop.hbase.mob.mapreduce.SweepReducer.SweepPartitionId;
43  import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
44  import org.apache.hadoop.hbase.regionserver.MemStore;
45  import org.apache.hadoop.hbase.regionserver.MemStoreSnapshot;
46  import org.apache.hadoop.hbase.regionserver.StoreFile;
47  import org.apache.hadoop.hbase.util.Bytes;
48  import org.apache.hadoop.mapreduce.Reducer.Context;
49  
50  /**
51   * The wrapper of a DefaultMemStore.
52   * This wrapper is used in the sweep reducer to buffer and sort the cells written from
53   * the invalid and small mob files.
54   * It's flushed when it's full, the mob data are written to the mob files, and their file names
55   * are written back to store files of HBase.
56   * This memStore is used to sort the cells in mob files.
57   * In a reducer of sweep tool, the mob files are grouped by the same prefix (start key and date),
58   * in each group, the reducer iterates the files and read the cells to a new and bigger mob file.
59   * The cells in the same mob file are ordered, but cells across mob files are not.
60   * So we need this MemStoreWrapper to sort those cells come from different mob files before
61   * flushing them to the disk, when the memStore is big enough it's flushed as a new mob file.
62   */
63  @InterfaceAudience.Private
64  public class MemStoreWrapper {
65  
66    private static final Log LOG = LogFactory.getLog(MemStoreWrapper.class);
67  
68    private MemStore memstore;
69    private long flushSize;
70    private SweepPartitionId partitionId;
71    private Context context;
72    private Configuration conf;
73    private HTable table;
74    private HColumnDescriptor hcd;
75    private Path mobFamilyDir;
76    private FileSystem fs;
77    private CacheConfig cacheConfig;
78  
79    public MemStoreWrapper(Context context, FileSystem fs, HTable table, HColumnDescriptor hcd,
80        MemStore memstore, CacheConfig cacheConfig) throws IOException {
81      this.memstore = memstore;
82      this.context = context;
83      this.fs = fs;
84      this.table = table;
85      this.hcd = hcd;
86      this.conf = context.getConfiguration();
87      this.cacheConfig = cacheConfig;
88      flushSize = this.conf.getLong(MobConstants.MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE,
89          MobConstants.DEFAULT_MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE);
90      mobFamilyDir = MobUtils.getMobFamilyPath(conf, table.getName(), hcd.getNameAsString());
91    }
92  
93    public void setPartitionId(SweepPartitionId partitionId) {
94      this.partitionId = partitionId;
95    }
96  
97    /**
98     * Flushes the memstore if the size is large enough.
99     * @throws IOException
100    */
101   private void flushMemStoreIfNecessary() throws IOException {
102     if (memstore.heapSize() >= flushSize) {
103       flushMemStore();
104     }
105   }
106 
107   /**
108    * Flushes the memstore anyway.
109    * @throws IOException
110    */
111   public void flushMemStore() throws IOException {
112     MemStoreSnapshot snapshot = memstore.snapshot();
113     internalFlushCache(snapshot);
114     memstore.clearSnapshot(snapshot.getId());
115   }
116 
117   /**
118    * Flushes the snapshot of the memstore.
119    * Flushes the mob data to the mob files, and flushes the name of these mob files to HBase.
120    * @param snapshot The snapshot of the memstore.
121    * @throws IOException
122    */
123   private void internalFlushCache(final MemStoreSnapshot snapshot)
124       throws IOException {
125     if (snapshot.getCellsCount() == 0) {
126       return;
127     }
128     // generate the files into a temp directory.
129     String tempPathString = context.getConfiguration().get(SweepJob.WORKING_FILES_DIR_KEY);
130     StoreFile.Writer mobFileWriter = MobUtils.createWriter(conf, fs, hcd,
131         partitionId.getDate(), new Path(tempPathString), snapshot.getCellsCount(),
132         hcd.getCompactionCompression(), partitionId.getStartKey(), cacheConfig);
133 
134     String relativePath = mobFileWriter.getPath().getName();
135     LOG.info("Create files under a temp directory " + mobFileWriter.getPath().toString());
136 
137     byte[] referenceValue = Bytes.toBytes(relativePath);
138     KeyValueScanner scanner = snapshot.getScanner();
139     Cell cell = null;
140     while (null != (cell = scanner.next())) {
141       KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
142       mobFileWriter.append(kv);
143     }
144     scanner.close();
145     // Write out the log sequence number that corresponds to this output
146     // hfile. The hfile is current up to and including logCacheFlushId.
147     mobFileWriter.appendMetadata(Long.MAX_VALUE, false, snapshot.getCellsCount());
148     mobFileWriter.close();
149 
150     MobUtils.commitFile(conf, fs, mobFileWriter.getPath(), mobFamilyDir, cacheConfig);
151     context.getCounter(SweepCounter.FILE_AFTER_MERGE_OR_CLEAN).increment(1);
152     // write reference/fileName back to the store files of HBase.
153     scanner = snapshot.getScanner();
154     scanner.seek(KeyValueUtil.createFirstOnRow(HConstants.EMPTY_START_ROW));
155     cell = null;
156     Tag tableNameTag = new Tag(TagType.MOB_TABLE_NAME_TAG_TYPE, this.table.getTableName());
157     while (null != (cell = scanner.next())) {
158       KeyValue reference = MobUtils.createMobRefKeyValue(cell, referenceValue, tableNameTag);
159       Put put =
160           new Put(reference.getRowArray(), reference.getRowOffset(), reference.getRowLength());
161       put.add(reference);
162       table.put(put);
163       context.getCounter(SweepCounter.RECORDS_UPDATED).increment(1);
164     }
165     table.flushCommits();
166     scanner.close();
167   }
168 
169   /**
170    * Adds a KeyValue into the memstore.
171    * @param kv The KeyValue to be added.
172    * @throws IOException
173    */
174   public void addToMemstore(KeyValue kv) throws IOException {
175     memstore.add(kv);
176     // flush the memstore if it's full.
177     flushMemStoreIfNecessary();
178   }
179 
180 }