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