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  
20  package org.apache.hadoop.hbase.master.snapshot;
21  
22  import java.io.IOException;
23  import java.util.List;
24  import java.util.concurrent.CancellationException;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.classification.InterfaceAudience;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
34  import org.apache.hadoop.hbase.TableExistsException;
35  import org.apache.hadoop.hbase.catalog.MetaReader;
36  import org.apache.hadoop.hbase.errorhandling.ForeignException;
37  import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
38  import org.apache.hadoop.hbase.master.MasterServices;
39  import org.apache.hadoop.hbase.master.SnapshotSentinel;
40  import org.apache.hadoop.hbase.master.handler.CreateTableHandler;
41  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
42  import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
43  import org.apache.hadoop.hbase.snapshot.RestoreSnapshotHelper;
44  import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
45  import org.apache.hadoop.hbase.util.Bytes;
46  
47  import com.google.common.base.Preconditions;
48  
49  /**
50   * Handler to Clone a snapshot.
51   *
52   * <p>Uses {@link RestoreSnapshotHelper} to create a new table with the same
53   * content of the specified snapshot.
54   */
55  @InterfaceAudience.Private
56  public class CloneSnapshotHandler extends CreateTableHandler implements SnapshotSentinel {
57    private static final Log LOG = LogFactory.getLog(CloneSnapshotHandler.class);
58  
59    private final static String NAME = "Master CloneSnapshotHandler";
60  
61    private final SnapshotDescription snapshot;
62  
63    private final ForeignExceptionDispatcher monitor;
64  
65    private volatile boolean stopped = false;
66  
67    public CloneSnapshotHandler(final MasterServices masterServices,
68        final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor)
69        throws NotAllMetaRegionsOnlineException, TableExistsException, IOException {
70      super(masterServices, masterServices.getMasterFileSystem(), 
71        masterServices.getServerManager(), hTableDescriptor,
72        masterServices.getConfiguration(), null, masterServices.getCatalogTracker(),
73        masterServices.getAssignmentManager());
74  
75      // Snapshot information
76      this.snapshot = snapshot;
77  
78      // Monitor
79      this.monitor = new ForeignExceptionDispatcher();
80    }
81  
82    /**
83     * Create the on-disk regions, using the tableRootDir provided by the CreateTableHandler.
84     * The cloned table will be created in a temp directory, and then the CreateTableHandler
85     * will be responsible to add the regions returned by this method to META and do the assignment.
86     */
87    @Override
88    protected List<HRegionInfo> handleCreateHdfsRegions(final Path tableRootDir, final String tableName)
89        throws IOException {
90      FileSystem fs = fileSystemManager.getFileSystem();
91      Path rootDir = fileSystemManager.getRootDir();
92      Path tableDir = new Path(tableRootDir, tableName);
93  
94      try {
95        // 1. Execute the on-disk Clone
96        Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
97        RestoreSnapshotHelper restoreHelper = new RestoreSnapshotHelper(conf, fs,
98            snapshot, snapshotDir, hTableDescriptor, tableDir, monitor);
99        RestoreSnapshotHelper.RestoreMetaChanges metaChanges = restoreHelper.restoreHdfsRegions();
100 
101       // Clone operation should not have stuff to restore or remove
102       Preconditions.checkArgument(!metaChanges.hasRegionsToRestore(),
103           "A clone should not have regions to restore");
104       Preconditions.checkArgument(!metaChanges.hasRegionsToRemove(),
105           "A clone should not have regions to remove");
106 
107       // At this point the clone is complete. Next step is enabling the table.
108       LOG.info("Clone snapshot=" + snapshot.getName() + " on table=" + tableName + " completed!");
109 
110       // 2. let the CreateTableHandler add the regions to meta
111       return metaChanges.getRegionsToAdd();
112     } catch (Exception e) {
113       String msg = "clone snapshot=" + SnapshotDescriptionUtils.toString(snapshot) + " failed";
114       LOG.error(msg, e);
115       IOException rse = new RestoreSnapshotException(msg, e, snapshot);
116 
117       // these handlers aren't futures so we need to register the error here.
118       this.monitor.receive(new ForeignException(NAME, rse));
119       throw rse;
120     }
121   }
122 
123   @Override
124   protected void completed(final Throwable exception) {
125     this.stopped = true;
126   }
127 
128   @Override
129   public boolean isFinished() {
130     return this.stopped;
131   }
132 
133   @Override
134   public SnapshotDescription getSnapshot() {
135     return snapshot;
136   }
137 
138   @Override
139   public void cancel(String why) {
140     if (this.stopped) return;
141     this.stopped = true;
142     LOG.info("Stopping clone snapshot=" + snapshot + " because: " + why);
143     this.monitor.receive(new ForeignException(NAME, new CancellationException(why)));
144   }
145 
146   @Override
147   public ForeignException getExceptionIfFailed() {
148     return this.monitor.getException();
149   }
150 }