1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.master.snapshot;
19
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ThreadPoolExecutor;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.hbase.classification.InterfaceAudience;
35 import org.apache.hadoop.hbase.classification.InterfaceStability;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.fs.FSDataInputStream;
38 import org.apache.hadoop.fs.FileStatus;
39 import org.apache.hadoop.fs.FileSystem;
40 import org.apache.hadoop.fs.Path;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
43 import org.apache.hadoop.hbase.HConstants;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.Stoppable;
46 import org.apache.hadoop.hbase.MetaTableAccessor;
47 import org.apache.hadoop.hbase.errorhandling.ForeignException;
48 import org.apache.hadoop.hbase.executor.ExecutorService;
49 import org.apache.hadoop.hbase.ipc.RpcServer;
50 import org.apache.hadoop.hbase.master.AssignmentManager;
51 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
52 import org.apache.hadoop.hbase.master.MasterFileSystem;
53 import org.apache.hadoop.hbase.master.MasterServices;
54 import org.apache.hadoop.hbase.master.MetricsMaster;
55 import org.apache.hadoop.hbase.master.SnapshotSentinel;
56 import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
57 import org.apache.hadoop.hbase.master.cleaner.HFileLinkCleaner;
58 import org.apache.hadoop.hbase.procedure.MasterProcedureManager;
59 import org.apache.hadoop.hbase.procedure.Procedure;
60 import org.apache.hadoop.hbase.procedure.ProcedureCoordinator;
61 import org.apache.hadoop.hbase.procedure.ProcedureCoordinatorRpcs;
62 import org.apache.hadoop.hbase.procedure.ZKProcedureCoordinatorRpcs;
63 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
64 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription;
65 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
66 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription.Type;
67 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
68 import org.apache.hadoop.hbase.security.AccessDeniedException;
69 import org.apache.hadoop.hbase.security.User;
70 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
71 import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
72 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
73 import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
74 import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
75 import org.apache.hadoop.hbase.snapshot.SnapshotDoesNotExistException;
76 import org.apache.hadoop.hbase.snapshot.SnapshotExistsException;
77 import org.apache.hadoop.hbase.snapshot.SnapshotManifest;
78 import org.apache.hadoop.hbase.snapshot.SnapshotReferenceUtil;
79 import org.apache.hadoop.hbase.snapshot.TablePartiallyOpenException;
80 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
81 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
82 import org.apache.hadoop.hbase.util.FSUtils;
83 import org.apache.zookeeper.KeeperException;
84
85
86
87
88
89
90
91
92
93
94 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
95 @InterfaceStability.Unstable
96 public class SnapshotManager extends MasterProcedureManager implements Stoppable {
97 private static final Log LOG = LogFactory.getLog(SnapshotManager.class);
98
99
100 private static final int SNAPSHOT_WAKE_MILLIS_DEFAULT = 500;
101
102
103
104
105
106
107
108
109
110
111
112
113 private static final int SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT = 60 * 1000;
114
115
116 public static final String HBASE_SNAPSHOT_ENABLED = "hbase.snapshot.enabled";
117
118
119
120
121
122 private static final String SNAPSHOT_WAKE_MILLIS_KEY = "hbase.snapshot.master.wakeMillis";
123
124
125 public static final String ONLINE_SNAPSHOT_CONTROLLER_DESCRIPTION = "online-snapshot";
126
127
128 private static final String SNAPSHOT_POOL_THREADS_KEY = "hbase.snapshot.master.threads";
129
130
131 private static final int SNAPSHOT_POOL_THREADS_DEFAULT = 1;
132
133 private boolean stopped;
134 private MasterServices master;
135 private ProcedureCoordinator coordinator;
136
137
138 private boolean isSnapshotSupported = false;
139
140
141
142
143
144 private Map<TableName, SnapshotSentinel> snapshotHandlers =
145 new HashMap<TableName, SnapshotSentinel>();
146
147
148
149
150
151 private Map<TableName, SnapshotSentinel> restoreHandlers =
152 new HashMap<TableName, SnapshotSentinel>();
153
154 private Path rootDir;
155 private ExecutorService executorService;
156
157 public SnapshotManager() {}
158
159
160
161
162
163
164
165 public SnapshotManager(final MasterServices master, final MetricsMaster metricsMaster,
166 ProcedureCoordinator coordinator, ExecutorService pool)
167 throws IOException, UnsupportedOperationException {
168 this.master = master;
169
170 this.rootDir = master.getMasterFileSystem().getRootDir();
171 checkSnapshotSupport(master.getConfiguration(), master.getMasterFileSystem());
172
173 this.coordinator = coordinator;
174 this.executorService = pool;
175 resetTempDir();
176 }
177
178
179
180
181
182
183 public List<SnapshotDescription> getCompletedSnapshots() throws IOException {
184 return getCompletedSnapshots(SnapshotDescriptionUtils.getSnapshotsDir(rootDir));
185 }
186
187
188
189
190
191
192
193 private List<SnapshotDescription> getCompletedSnapshots(Path snapshotDir) throws IOException {
194 List<SnapshotDescription> snapshotDescs = new ArrayList<SnapshotDescription>();
195
196 FileSystem fs = master.getMasterFileSystem().getFileSystem();
197 if (snapshotDir == null) snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(rootDir);
198
199
200 if (!fs.exists(snapshotDir)) {
201 return snapshotDescs;
202 }
203
204
205 FileStatus[] snapshots = fs.listStatus(snapshotDir,
206 new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
207 MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
208
209 for (FileStatus snapshot : snapshots) {
210 Path info = new Path(snapshot.getPath(), SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
211
212 if (!fs.exists(info)) {
213 LOG.error("Snapshot information for " + snapshot.getPath() + " doesn't exist");
214 continue;
215 }
216 FSDataInputStream in = null;
217 try {
218 in = fs.open(info);
219 SnapshotDescription desc = SnapshotDescription.parseFrom(in);
220 if (cpHost != null) {
221 try {
222 cpHost.preListSnapshot(desc);
223 } catch (AccessDeniedException e) {
224 LOG.warn("Current user does not have access to " + desc.getName() + " snapshot. "
225 + "Either you should be owner of this snapshot or admin user.");
226
227 continue;
228 }
229 }
230 snapshotDescs.add(desc);
231
232
233 if (cpHost != null) {
234 cpHost.postListSnapshot(desc);
235 }
236 } catch (IOException e) {
237 LOG.warn("Found a corrupted snapshot " + snapshot.getPath(), e);
238 } finally {
239 if (in != null) {
240 in.close();
241 }
242 }
243 }
244 return snapshotDescs;
245 }
246
247
248
249
250
251
252
253 void resetTempDir() throws IOException {
254
255 Path tmpdir = SnapshotDescriptionUtils.getWorkingSnapshotDir(rootDir);
256 if (master.getMasterFileSystem().getFileSystem().exists(tmpdir)) {
257 if (!master.getMasterFileSystem().getFileSystem().delete(tmpdir, true)) {
258 LOG.warn("Couldn't delete working snapshot directory: " + tmpdir);
259 }
260 }
261 }
262
263
264
265
266
267
268
269 public void deleteSnapshot(SnapshotDescription snapshot) throws SnapshotDoesNotExistException, IOException {
270
271 if (!isSnapshotCompleted(snapshot)) {
272 throw new SnapshotDoesNotExistException(snapshot);
273 }
274
275 String snapshotName = snapshot.getName();
276
277 FileSystem fs = master.getMasterFileSystem().getFileSystem();
278 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName, rootDir);
279
280
281 snapshot = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
282
283
284 MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
285 if (cpHost != null) {
286 cpHost.preDeleteSnapshot(snapshot);
287 }
288
289 LOG.debug("Deleting snapshot: " + snapshotName);
290
291 if (!fs.delete(snapshotDir, true)) {
292 throw new HBaseSnapshotException("Failed to delete snapshot directory: " + snapshotDir);
293 }
294
295
296 if (cpHost != null) {
297 cpHost.postDeleteSnapshot(snapshot);
298 }
299
300 }
301
302
303
304
305
306
307
308
309
310 public boolean isSnapshotDone(SnapshotDescription expected) throws IOException {
311
312 if (expected == null) {
313 throw new UnknownSnapshotException(
314 "No snapshot name passed in request, can't figure out which snapshot you want to check.");
315 }
316
317 String ssString = ClientSnapshotDescriptionUtils.toString(expected);
318
319
320
321 SnapshotSentinel handler = removeSentinelIfFinished(this.snapshotHandlers, expected);
322
323
324 cleanupSentinels();
325
326 if (handler == null) {
327
328
329
330
331
332
333 if (!isSnapshotCompleted(expected)) {
334 throw new UnknownSnapshotException("Snapshot " + ssString
335 + " is not currently running or one of the known completed snapshots.");
336 }
337
338 return true;
339 }
340
341
342 try {
343 handler.rethrowExceptionIfFailed();
344 } catch (ForeignException e) {
345
346 String status;
347 Procedure p = coordinator.getProcedure(expected.getName());
348 if (p != null) {
349 status = p.getStatus();
350 } else {
351 status = expected.getName() + " not found in proclist " + coordinator.getProcedureNames();
352 }
353 throw new HBaseSnapshotException("Snapshot " + ssString + " had an error. " + status, e,
354 expected);
355 }
356
357
358 if (handler.isFinished()) {
359 LOG.debug("Snapshot '" + ssString + "' has completed, notifying client.");
360 return true;
361 } else if (LOG.isDebugEnabled()) {
362 LOG.debug("Snapshoting '" + ssString + "' is still in progress!");
363 }
364 return false;
365 }
366
367
368
369
370
371
372
373
374
375 synchronized boolean isTakingSnapshot(final SnapshotDescription snapshot) {
376 TableName snapshotTable = TableName.valueOf(snapshot.getTable());
377 if (isTakingSnapshot(snapshotTable)) {
378 return true;
379 }
380 Iterator<Map.Entry<TableName, SnapshotSentinel>> it = this.snapshotHandlers.entrySet().iterator();
381 while (it.hasNext()) {
382 Map.Entry<TableName, SnapshotSentinel> entry = it.next();
383 SnapshotSentinel sentinel = entry.getValue();
384 if (snapshot.getName().equals(sentinel.getSnapshot().getName()) && !sentinel.isFinished()) {
385 return true;
386 }
387 }
388 return false;
389 }
390
391
392
393
394
395
396
397 synchronized boolean isTakingSnapshot(final TableName tableName) {
398 SnapshotSentinel handler = this.snapshotHandlers.get(tableName);
399 return handler != null && !handler.isFinished();
400 }
401
402
403
404
405
406
407
408 private synchronized void prepareToTakeSnapshot(SnapshotDescription snapshot)
409 throws HBaseSnapshotException {
410 FileSystem fs = master.getMasterFileSystem().getFileSystem();
411 Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(snapshot, rootDir);
412 TableName snapshotTable =
413 TableName.valueOf(snapshot.getTable());
414
415
416 if (isTakingSnapshot(snapshot)) {
417 SnapshotSentinel handler = this.snapshotHandlers.get(snapshotTable);
418 throw new SnapshotCreationException("Rejected taking "
419 + ClientSnapshotDescriptionUtils.toString(snapshot)
420 + " because we are already running another snapshot "
421 + (handler != null ? ("on the same table " +
422 ClientSnapshotDescriptionUtils.toString(handler.getSnapshot()))
423 : "with the same name"), snapshot);
424 }
425
426
427 if (isRestoringTable(snapshotTable)) {
428 SnapshotSentinel handler = restoreHandlers.get(snapshotTable);
429 throw new SnapshotCreationException("Rejected taking "
430 + ClientSnapshotDescriptionUtils.toString(snapshot)
431 + " because we are already have a restore in progress on the same snapshot "
432 + ClientSnapshotDescriptionUtils.toString(handler.getSnapshot()), snapshot);
433 }
434
435 try {
436
437
438 fs.delete(workingDir, true);
439
440
441 if (!fs.mkdirs(workingDir)) {
442 throw new SnapshotCreationException("Couldn't create working directory (" + workingDir
443 + ") for snapshot" , snapshot);
444 }
445 } catch (HBaseSnapshotException e) {
446 throw e;
447 } catch (IOException e) {
448 throw new SnapshotCreationException(
449 "Exception while checking to see if snapshot could be started.", e, snapshot);
450 }
451 }
452
453
454
455
456
457
458 private synchronized void snapshotDisabledTable(SnapshotDescription snapshot)
459 throws HBaseSnapshotException {
460
461 prepareToTakeSnapshot(snapshot);
462
463
464 snapshot = snapshot.toBuilder().setType(Type.DISABLED).build();
465
466
467 DisabledTableSnapshotHandler handler =
468 new DisabledTableSnapshotHandler(snapshot, master);
469 snapshotTable(snapshot, handler);
470 }
471
472
473
474
475
476
477 private synchronized void snapshotEnabledTable(SnapshotDescription snapshot)
478 throws HBaseSnapshotException {
479
480 prepareToTakeSnapshot(snapshot);
481
482
483 EnabledTableSnapshotHandler handler =
484 new EnabledTableSnapshotHandler(snapshot, master, this);
485 snapshotTable(snapshot, handler);
486 }
487
488
489
490
491
492
493
494
495
496 private synchronized void snapshotTable(SnapshotDescription snapshot,
497 final TakeSnapshotHandler handler) throws HBaseSnapshotException {
498 try {
499 handler.prepare();
500 this.executorService.submit(handler);
501 this.snapshotHandlers.put(TableName.valueOf(snapshot.getTable()), handler);
502 } catch (Exception e) {
503
504 Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(snapshot, rootDir);
505 try {
506 if (!this.master.getMasterFileSystem().getFileSystem().delete(workingDir, true)) {
507 LOG.error("Couldn't delete working directory (" + workingDir + " for snapshot:" +
508 ClientSnapshotDescriptionUtils.toString(snapshot));
509 }
510 } catch (IOException e1) {
511 LOG.error("Couldn't delete working directory (" + workingDir + " for snapshot:" +
512 ClientSnapshotDescriptionUtils.toString(snapshot));
513 }
514
515 throw new SnapshotCreationException("Could not build snapshot handler", e, snapshot);
516 }
517 }
518
519
520
521
522
523
524
525
526 public void takeSnapshot(SnapshotDescription snapshot) throws IOException {
527
528 if (isSnapshotCompleted(snapshot)) {
529 throw new SnapshotExistsException("Snapshot '" + snapshot.getName()
530 + "' already stored on the filesystem.", snapshot);
531 }
532
533 LOG.debug("No existing snapshot, attempting snapshot...");
534
535
536 cleanupSentinels();
537
538
539 HTableDescriptor desc = null;
540 try {
541 desc = master.getTableDescriptors().get(
542 TableName.valueOf(snapshot.getTable()));
543 } catch (FileNotFoundException e) {
544 String msg = "Table:" + snapshot.getTable() + " info doesn't exist!";
545 LOG.error(msg);
546 throw new SnapshotCreationException(msg, e, snapshot);
547 } catch (IOException e) {
548 throw new SnapshotCreationException("Error while geting table description for table "
549 + snapshot.getTable(), e, snapshot);
550 }
551 if (desc == null) {
552 throw new SnapshotCreationException("Table '" + snapshot.getTable()
553 + "' doesn't exist, can't take snapshot.", snapshot);
554 }
555 SnapshotDescription.Builder builder = snapshot.toBuilder();
556
557 if (!snapshot.hasVersion()) {
558 builder.setVersion(SnapshotDescriptionUtils.SNAPSHOT_LAYOUT_VERSION);
559 }
560 User user = RpcServer.getRequestUser();
561 if (User.isHBaseSecurityEnabled(master.getConfiguration()) && user != null) {
562 builder.setOwner(user.getShortName());
563 }
564 snapshot = builder.build();
565
566
567 MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
568 if (cpHost != null) {
569 cpHost.preSnapshot(snapshot, desc);
570 }
571
572
573 TableName snapshotTable = TableName.valueOf(snapshot.getTable());
574 AssignmentManager assignmentMgr = master.getAssignmentManager();
575 if (assignmentMgr.getTableStateManager().isTableState(snapshotTable,
576 ZooKeeperProtos.Table.State.ENABLED)) {
577 LOG.debug("Table enabled, starting distributed snapshot.");
578 snapshotEnabledTable(snapshot);
579 LOG.debug("Started snapshot: " + ClientSnapshotDescriptionUtils.toString(snapshot));
580 }
581
582 else if (assignmentMgr.getTableStateManager().isTableState(snapshotTable,
583 ZooKeeperProtos.Table.State.DISABLED)) {
584 LOG.debug("Table is disabled, running snapshot entirely on master.");
585 snapshotDisabledTable(snapshot);
586 LOG.debug("Started snapshot: " + ClientSnapshotDescriptionUtils.toString(snapshot));
587 } else {
588 LOG.error("Can't snapshot table '" + snapshot.getTable()
589 + "', isn't open or closed, we don't know what to do!");
590 TablePartiallyOpenException tpoe = new TablePartiallyOpenException(snapshot.getTable()
591 + " isn't fully open.");
592 throw new SnapshotCreationException("Table is not entirely open or closed", tpoe, snapshot);
593 }
594
595
596 if (cpHost != null) {
597 cpHost.postSnapshot(snapshot, desc);
598 }
599 }
600
601
602
603
604
605
606
607
608
609
610 public synchronized void setSnapshotHandlerForTesting(
611 final TableName tableName,
612 final SnapshotSentinel handler) {
613 if (handler != null) {
614 this.snapshotHandlers.put(tableName, handler);
615 } else {
616 this.snapshotHandlers.remove(tableName);
617 }
618 }
619
620
621
622
623 ProcedureCoordinator getCoordinator() {
624 return coordinator;
625 }
626
627
628
629
630
631
632
633
634
635
636
637 private boolean isSnapshotCompleted(SnapshotDescription snapshot) throws IOException {
638 try {
639 final Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshot, rootDir);
640 FileSystem fs = master.getMasterFileSystem().getFileSystem();
641
642 return fs.exists(snapshotDir);
643 } catch (IllegalArgumentException iae) {
644 throw new UnknownSnapshotException("Unexpected exception thrown", iae);
645 }
646 }
647
648
649
650
651
652
653
654
655 synchronized void cloneSnapshot(final SnapshotDescription snapshot,
656 final HTableDescriptor hTableDescriptor) throws HBaseSnapshotException {
657 TableName tableName = hTableDescriptor.getTableName();
658
659
660 if (isTakingSnapshot(tableName)) {
661 throw new RestoreSnapshotException("Snapshot in progress on the restore table=" + tableName);
662 }
663
664
665 if (isRestoringTable(tableName)) {
666 throw new RestoreSnapshotException("Restore already in progress on the table=" + tableName);
667 }
668
669 try {
670 CloneSnapshotHandler handler =
671 new CloneSnapshotHandler(master, snapshot, hTableDescriptor).prepare();
672 this.executorService.submit(handler);
673 this.restoreHandlers.put(tableName, handler);
674 } catch (Exception e) {
675 String msg = "Couldn't clone the snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot) +
676 " on table=" + tableName;
677 LOG.error(msg, e);
678 throw new RestoreSnapshotException(msg, e);
679 }
680 }
681
682
683
684
685
686
687 public void restoreSnapshot(SnapshotDescription reqSnapshot) throws IOException {
688 FileSystem fs = master.getMasterFileSystem().getFileSystem();
689 Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(reqSnapshot, rootDir);
690 MasterCoprocessorHost cpHost = master.getMasterCoprocessorHost();
691
692
693 if (!fs.exists(snapshotDir)) {
694 LOG.error("A Snapshot named '" + reqSnapshot.getName() + "' does not exist.");
695 throw new SnapshotDoesNotExistException(reqSnapshot);
696 }
697
698
699
700
701 SnapshotDescription snapshot = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
702 SnapshotManifest manifest = SnapshotManifest.open(master.getConfiguration(), fs,
703 snapshotDir, snapshot);
704 HTableDescriptor snapshotTableDesc = manifest.getTableDescriptor();
705 TableName tableName = TableName.valueOf(reqSnapshot.getTable());
706
707
708 cleanupSentinels();
709
710
711 SnapshotReferenceUtil.verifySnapshot(master.getConfiguration(), fs, manifest);
712
713
714 if (MetaTableAccessor.tableExists(master.getConnection(), tableName)) {
715 if (master.getAssignmentManager().getTableStateManager().isTableState(
716 TableName.valueOf(snapshot.getTable()), ZooKeeperProtos.Table.State.ENABLED)) {
717 throw new UnsupportedOperationException("Table '" +
718 TableName.valueOf(snapshot.getTable()) + "' must be disabled in order to " +
719 "perform a restore operation" +
720 ".");
721 }
722
723
724 if (cpHost != null) {
725 cpHost.preRestoreSnapshot(reqSnapshot, snapshotTableDesc);
726 }
727 try {
728
729 checkAndUpdateNamespaceRegionQuota(manifest, tableName);
730 restoreSnapshot(snapshot, snapshotTableDesc);
731 } catch (IOException e) {
732 this.master.getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
733 LOG.error("Exception occurred while restoring the snapshot " + snapshot.getName()
734 + " as table " + tableName.getNameAsString(), e);
735 throw e;
736 }
737 LOG.info("Restore snapshot=" + snapshot.getName() + " as table=" + tableName);
738
739 if (cpHost != null) {
740 cpHost.postRestoreSnapshot(reqSnapshot, snapshotTableDesc);
741 }
742 } else {
743 HTableDescriptor htd = new HTableDescriptor(tableName, snapshotTableDesc);
744 if (cpHost != null) {
745 cpHost.preCloneSnapshot(reqSnapshot, htd);
746 }
747 try {
748 checkAndUpdateNamespaceQuota(manifest, tableName);
749 cloneSnapshot(snapshot, htd);
750 } catch (IOException e) {
751 this.master.getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
752 LOG.error("Exception occurred while cloning the snapshot " + snapshot.getName()
753 + " as table " + tableName.getNameAsString(), e);
754 throw e;
755 }
756 LOG.info("Clone snapshot=" + snapshot.getName() + " as table=" + tableName);
757
758 if (cpHost != null) {
759 cpHost.postCloneSnapshot(reqSnapshot, htd);
760 }
761 }
762 }
763
764 private void checkAndUpdateNamespaceQuota(SnapshotManifest manifest, TableName tableName)
765 throws IOException {
766 if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
767 this.master.getMasterQuotaManager().checkNamespaceTableAndRegionQuota(tableName,
768 manifest.getRegionManifestsMap().size());
769 }
770 }
771
772 private void checkAndUpdateNamespaceRegionQuota(SnapshotManifest manifest, TableName tableName)
773 throws IOException {
774 if (this.master.getMasterQuotaManager().isQuotaEnabled()) {
775 this.master.getMasterQuotaManager().checkAndUpdateNamespaceRegionQuota(tableName,
776 manifest.getRegionManifestsMap().size());
777 }
778 }
779
780
781
782
783
784
785
786
787 private synchronized void restoreSnapshot(final SnapshotDescription snapshot,
788 final HTableDescriptor hTableDescriptor) throws HBaseSnapshotException {
789 TableName tableName = hTableDescriptor.getTableName();
790
791
792 if (isTakingSnapshot(tableName)) {
793 throw new RestoreSnapshotException("Snapshot in progress on the restore table=" + tableName);
794 }
795
796
797 if (isRestoringTable(tableName)) {
798 throw new RestoreSnapshotException("Restore already in progress on the table=" + tableName);
799 }
800
801 try {
802 RestoreSnapshotHandler handler =
803 new RestoreSnapshotHandler(master, snapshot, hTableDescriptor).prepare();
804 this.executorService.submit(handler);
805 restoreHandlers.put(tableName, handler);
806 } catch (Exception e) {
807 String msg = "Couldn't restore the snapshot=" + ClientSnapshotDescriptionUtils.toString(
808 snapshot) +
809 " on table=" + tableName;
810 LOG.error(msg, e);
811 throw new RestoreSnapshotException(msg, e);
812 }
813 }
814
815
816
817
818
819
820
821 private synchronized boolean isRestoringTable(final TableName tableName) {
822 SnapshotSentinel sentinel = this.restoreHandlers.get(tableName);
823 return(sentinel != null && !sentinel.isFinished());
824 }
825
826
827
828
829
830
831
832
833
834 public boolean isRestoreDone(final SnapshotDescription snapshot) throws IOException {
835
836
837 SnapshotSentinel sentinel = removeSentinelIfFinished(this.restoreHandlers, snapshot);
838
839
840 cleanupSentinels();
841
842 if (sentinel == null) {
843
844 return true;
845 }
846
847 LOG.debug("Verify snapshot=" + snapshot.getName() + " against="
848 + sentinel.getSnapshot().getName() + " table=" +
849 TableName.valueOf(snapshot.getTable()));
850
851
852 sentinel.rethrowExceptionIfFailed();
853
854
855 if (sentinel.isFinished()) {
856 LOG.debug("Restore snapshot=" + ClientSnapshotDescriptionUtils.toString(snapshot) +
857 " has completed. Notifying the client.");
858 return true;
859 }
860
861 if (LOG.isDebugEnabled()) {
862 LOG.debug("Sentinel is not yet finished with restoring snapshot=" +
863 ClientSnapshotDescriptionUtils.toString(snapshot));
864 }
865 return false;
866 }
867
868
869
870
871
872
873
874
875 private synchronized SnapshotSentinel removeSentinelIfFinished(
876 final Map<TableName, SnapshotSentinel> sentinels,
877 final SnapshotDescription snapshot) {
878 if (!snapshot.hasTable()) {
879 return null;
880 }
881
882 TableName snapshotTable = TableName.valueOf(snapshot.getTable());
883 SnapshotSentinel h = sentinels.get(snapshotTable);
884 if (h == null) {
885 return null;
886 }
887
888 if (!h.getSnapshot().getName().equals(snapshot.getName())) {
889
890 return null;
891 }
892
893
894 if (h.isFinished()) {
895 sentinels.remove(snapshotTable);
896 }
897
898 return h;
899 }
900
901
902
903
904
905
906
907
908 private void cleanupSentinels() {
909 cleanupSentinels(this.snapshotHandlers);
910 cleanupSentinels(this.restoreHandlers);
911 }
912
913
914
915
916
917
918 private synchronized void cleanupSentinels(final Map<TableName, SnapshotSentinel> sentinels) {
919 long currentTime = EnvironmentEdgeManager.currentTime();
920 Iterator<Map.Entry<TableName, SnapshotSentinel>> it =
921 sentinels.entrySet().iterator();
922 while (it.hasNext()) {
923 Map.Entry<TableName, SnapshotSentinel> entry = it.next();
924 SnapshotSentinel sentinel = entry.getValue();
925 if (sentinel.isFinished() &&
926 (currentTime - sentinel.getCompletionTimestamp()) > SNAPSHOT_SENTINELS_CLEANUP_TIMEOUT)
927 {
928 it.remove();
929 }
930 }
931 }
932
933
934
935
936
937 @Override
938 public void stop(String why) {
939
940 if (this.stopped) return;
941
942 this.stopped = true;
943
944 for (SnapshotSentinel snapshotHandler: this.snapshotHandlers.values()) {
945 snapshotHandler.cancel(why);
946 }
947
948
949 for (SnapshotSentinel restoreHandler: this.restoreHandlers.values()) {
950 restoreHandler.cancel(why);
951 }
952 try {
953 if (coordinator != null) {
954 coordinator.close();
955 }
956 } catch (IOException e) {
957 LOG.error("stop ProcedureCoordinator error", e);
958 }
959 }
960
961 @Override
962 public boolean isStopped() {
963 return this.stopped;
964 }
965
966
967
968
969
970
971 public void checkSnapshotSupport() throws UnsupportedOperationException {
972 if (!this.isSnapshotSupported) {
973 throw new UnsupportedOperationException(
974 "To use snapshots, You must add to the hbase-site.xml of the HBase Master: '" +
975 HBASE_SNAPSHOT_ENABLED + "' property with value 'true'.");
976 }
977 }
978
979
980
981
982
983
984
985
986
987
988
989 private void checkSnapshotSupport(final Configuration conf, final MasterFileSystem mfs)
990 throws IOException, UnsupportedOperationException {
991
992 String enabled = conf.get(HBASE_SNAPSHOT_ENABLED);
993 boolean snapshotEnabled = conf.getBoolean(HBASE_SNAPSHOT_ENABLED, false);
994 boolean userDisabled = (enabled != null && enabled.trim().length() > 0 && !snapshotEnabled);
995
996
997 Set<String> hfileCleaners = new HashSet<String>();
998 String[] cleaners = conf.getStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS);
999 if (cleaners != null) Collections.addAll(hfileCleaners, cleaners);
1000
1001 Set<String> logCleaners = new HashSet<String>();
1002 cleaners = conf.getStrings(HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS);
1003 if (cleaners != null) Collections.addAll(logCleaners, cleaners);
1004
1005
1006 Path oldSnapshotDir = new Path(mfs.getRootDir(), HConstants.OLD_SNAPSHOT_DIR_NAME);
1007 FileSystem fs = mfs.getFileSystem();
1008 List<SnapshotDescription> ss = getCompletedSnapshots(new Path(rootDir, oldSnapshotDir));
1009 if (ss != null && !ss.isEmpty()) {
1010 LOG.error("Snapshots from an earlier release were found under: " + oldSnapshotDir);
1011 LOG.error("Please rename the directory as " + HConstants.SNAPSHOT_DIR_NAME);
1012 }
1013
1014
1015
1016
1017 if (snapshotEnabled) {
1018
1019 hfileCleaners.add(SnapshotHFileCleaner.class.getName());
1020 hfileCleaners.add(HFileLinkCleaner.class.getName());
1021 logCleaners.add(SnapshotLogCleaner.class.getName());
1022
1023
1024 conf.setStrings(HFileCleaner.MASTER_HFILE_CLEANER_PLUGINS,
1025 hfileCleaners.toArray(new String[hfileCleaners.size()]));
1026 conf.setStrings(HConstants.HBASE_MASTER_LOGCLEANER_PLUGINS,
1027 logCleaners.toArray(new String[logCleaners.size()]));
1028 } else {
1029
1030 snapshotEnabled = logCleaners.contains(SnapshotLogCleaner.class.getName()) &&
1031 hfileCleaners.contains(SnapshotHFileCleaner.class.getName()) &&
1032 hfileCleaners.contains(HFileLinkCleaner.class.getName());
1033
1034
1035 if (snapshotEnabled) {
1036 LOG.warn("Snapshot log and hfile cleaners are present in the configuration, " +
1037 "but the '" + HBASE_SNAPSHOT_ENABLED + "' property " +
1038 (userDisabled ? "is set to 'false'." : "is not set."));
1039 }
1040 }
1041
1042
1043 this.isSnapshotSupported = snapshotEnabled && !userDisabled;
1044
1045
1046
1047 if (!snapshotEnabled) {
1048 LOG.info("Snapshot feature is not enabled, missing log and hfile cleaners.");
1049 Path snapshotDir = SnapshotDescriptionUtils.getSnapshotsDir(mfs.getRootDir());
1050 if (fs.exists(snapshotDir)) {
1051 FileStatus[] snapshots = FSUtils.listStatus(fs, snapshotDir,
1052 new SnapshotDescriptionUtils.CompletedSnaphotDirectoriesFilter(fs));
1053 if (snapshots != null) {
1054 LOG.error("Snapshots are present, but cleaners are not enabled.");
1055 checkSnapshotSupport();
1056 }
1057 }
1058 }
1059 }
1060
1061 @Override
1062 public void initialize(MasterServices master, MetricsMaster metricsMaster) throws KeeperException,
1063 IOException, UnsupportedOperationException {
1064 this.master = master;
1065
1066 this.rootDir = master.getMasterFileSystem().getRootDir();
1067 checkSnapshotSupport(master.getConfiguration(), master.getMasterFileSystem());
1068
1069
1070 Configuration conf = master.getConfiguration();
1071 long wakeFrequency = conf.getInt(SNAPSHOT_WAKE_MILLIS_KEY, SNAPSHOT_WAKE_MILLIS_DEFAULT);
1072 long timeoutMillis = Math.max(conf.getLong(SnapshotDescriptionUtils.SNAPSHOT_TIMEOUT_MILLIS_KEY,
1073 SnapshotDescriptionUtils.SNAPSHOT_TIMEOUT_MILLIS_DEFAULT),
1074 conf.getLong(SnapshotDescriptionUtils.MASTER_SNAPSHOT_TIMEOUT_MILLIS,
1075 SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME));
1076 int opThreads = conf.getInt(SNAPSHOT_POOL_THREADS_KEY, SNAPSHOT_POOL_THREADS_DEFAULT);
1077
1078
1079 String name = master.getServerName().toString();
1080 ThreadPoolExecutor tpool = ProcedureCoordinator.defaultPool(name, opThreads);
1081 ProcedureCoordinatorRpcs comms = new ZKProcedureCoordinatorRpcs(
1082 master.getZooKeeper(), SnapshotManager.ONLINE_SNAPSHOT_CONTROLLER_DESCRIPTION, name);
1083
1084 this.coordinator = new ProcedureCoordinator(comms, tpool, timeoutMillis, wakeFrequency);
1085 this.executorService = master.getExecutorService();
1086 resetTempDir();
1087 }
1088
1089 @Override
1090 public String getProcedureSignature() {
1091 return ONLINE_SNAPSHOT_CONTROLLER_DESCRIPTION;
1092 }
1093
1094 @Override
1095 public void execProcedure(ProcedureDescription desc) throws IOException {
1096 takeSnapshot(toSnapshotDescription(desc));
1097 }
1098
1099 @Override
1100 public boolean isProcedureDone(ProcedureDescription desc) throws IOException {
1101 return isSnapshotDone(toSnapshotDescription(desc));
1102 }
1103
1104 private SnapshotDescription toSnapshotDescription(ProcedureDescription desc)
1105 throws IOException {
1106 SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
1107 if (!desc.hasInstance()) {
1108 throw new IOException("Snapshot name is not defined: " + desc.toString());
1109 }
1110 String snapshotName = desc.getInstance();
1111 List<NameStringPair> props = desc.getConfigurationList();
1112 String table = null;
1113 for (NameStringPair prop : props) {
1114 if ("table".equalsIgnoreCase(prop.getName())) {
1115 table = prop.getValue();
1116 }
1117 }
1118 if (table == null) {
1119 throw new IOException("Snapshot table is not defined: " + desc.toString());
1120 }
1121 TableName tableName = TableName.valueOf(table);
1122 builder.setTable(tableName.getNameAsString());
1123 builder.setName(snapshotName);
1124 builder.setType(SnapshotDescription.Type.FLUSH);
1125 return builder.build();
1126 }
1127 }