From 2f4a6a755cd34e929faf55daacb8f69fdde6be43 Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Tue, 17 Mar 2009 02:00:09 +0000
Subject: Added capability to perform hot backups.

---
 .../carbonado/repo/sleepycat/BDBRepository.java    | 98 ++++++++++++++++++++++
 .../repo/sleepycat/CheckpointCapability.java       |  7 +-
 .../repo/sleepycat/HotBackupCapability.java        | 56 +++++++++++++
 3 files changed, 157 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/com/amazon/carbonado/repo/sleepycat/HotBackupCapability.java

(limited to 'src/main/java/com/amazon/carbonado')

diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
index 9f3d9ac..01e3668 100644
--- a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBRepository.java
@@ -83,6 +83,7 @@ abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
     implements Repository,
                RepositoryAccess,
                IndexInfoCapability,
+               HotBackupCapability,
                CheckpointCapability,
                EnvironmentCapability,
                ShutdownCapability,
@@ -116,6 +117,9 @@ abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
     final String mSingleFileName;
     final Map<String, String> mFileNameMap;
 
+    final Object mBackupLock = new Object();
+    int mBackupCount = 0;
+
     private LayoutFactory mLayoutFactory;
 
     private LobEngine mLobEngine;
@@ -221,6 +225,63 @@ abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
         return StorableIntrospector.examine(type).getAllProperties().get(name) != null;
     }
 
+    @Override
+    public Backup startBackup() throws RepositoryException {
+        synchronized (mBackupLock) {
+            int count = mBackupCount;
+            if (count == 0) {
+                try {
+                    enterBackupMode();
+                } catch (Exception e) {
+                    throw mExTransformer.toRepositoryException(e);
+                }
+            }
+            mBackupCount = count + 1;
+
+            return new Backup() {
+                private boolean mDone;
+
+                @Override
+                public void endBackup() throws RepositoryException {
+                    synchronized (mBackupLock) {
+                        if (mDone) {
+                            return;
+                        }
+                        mDone = true;
+
+                        int count = mBackupCount - 1;
+                        try {
+                            if (count == 0) {
+                                try {
+                                    exitBackupMode();
+                                } catch (Exception e) {
+                                    throw mExTransformer.toRepositoryException(e);
+                                }
+                            }
+                        } finally {
+                            mBackupCount = count;
+                        }
+                    }
+                }
+
+                @Override
+                public File[] getFiles() throws RepositoryException {
+                    synchronized (mBackupLock) {
+                        if (mDone) {
+                            throw new IllegalStateException("Backup has ended");
+                        }
+
+                        try {
+                            return backupFiles();
+                        } catch (Exception e) {
+                            throw mExTransformer.toRepositoryException(e);
+                        }
+                    }
+                }
+            };
+        }
+    }
+
     /**
      * Suspend the checkpointer until the suspension time has expired or until
      * manually resumed. If a checkpoint is in progress, this method will block
@@ -365,6 +426,28 @@ abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
         return mIsMaster;
     }
 
+    String[] getAllDatabaseNames() throws RepositoryException {
+        Repository metaRepo = getRootRepository();
+
+        Cursor<StoredDatabaseInfo> cursor =
+            metaRepo.storageFor(StoredDatabaseInfo.class)
+            .query().orderBy("databaseName").fetch();
+
+        ArrayList<String> names = new ArrayList<String>();
+        // This one needs to manually added since it is the metadata db itself.
+        names.add(StoredDatabaseInfo.class.getName());
+
+        try {
+            while (cursor.hasNext()) {
+                names.add(cursor.next().getDatabaseName());
+            }
+        } finally {
+            cursor.close();
+        }
+
+        return names.toArray(new String[names.size()]);
+    }
+
     String getDatabaseFileName(final String dbName) {
         String singleFileName = mSingleFileName;
         if (singleFileName == null && mFileNameMap != null) {
@@ -527,6 +610,21 @@ abstract class BDBRepository<Txn> extends AbstractRepository<Txn>
     abstract <S extends Storable> BDBStorage<Txn, S> createBDBStorage(Class<S> type)
         throws Exception;
 
+    /**
+     * Called only the first time a backup is started.
+     */
+    abstract void enterBackupMode() throws Exception;
+
+    /**
+     * Called only after the last backup ends.
+     */
+    abstract void exitBackupMode() throws Exception;
+
+    /**
+     * Called only if in backup mode.
+     */
+    abstract File[] backupFiles() throws Exception;
+
     FetchException toFetchException(Throwable e) {
         return mExTransformer.toFetchException(e);
     }
diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/CheckpointCapability.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/CheckpointCapability.java
index dc4b3d7..c261d8a 100644
--- a/src/main/java/com/amazon/carbonado/repo/sleepycat/CheckpointCapability.java
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/CheckpointCapability.java
@@ -22,9 +22,10 @@ import com.amazon.carbonado.PersistException;
 import com.amazon.carbonado.capability.Capability;
 
 /**
- * Capability to control BDB checkpointing. Useful when performing hot backups.
+ * Capability to control BDB checkpointing.
  *
  * @author Brian S O'Neill
+ * @see HotBackupCapability
  */
 public interface CheckpointCapability extends Capability {
     /**
@@ -33,9 +34,7 @@ public interface CheckpointCapability extends Capability {
      * until it is finished. If checkpointing is disabled, calling this method
      * has no effect.
      *
-     * <p>Calling this method repeatedly resets the suspension time. This
-     * technique should be used by hot backup processes to ensure that its
-     * failure does not leave the checkpointer permanently suspended. Each
+     * <p>Calling this method repeatedly resets the suspension time. Each
      * invocation of suspendCheckpointer is like a lease renewal or heartbeat.
      *
      * @param suspensionTime minimum length of suspension, in milliseconds,
diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/HotBackupCapability.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/HotBackupCapability.java
new file mode 100644
index 0000000..8bce86d
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/HotBackupCapability.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2009 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.sleepycat;
+
+import java.io.File;
+
+import com.amazon.carbonado.RepositoryException;
+
+import com.amazon.carbonado.capability.Capability;
+
+/**
+ * Capability for performing a backup of an active BDB environment.
+ *
+ * @author Brian S O'Neill
+ * @since 1.2.1
+ */
+public interface HotBackupCapability extends Capability {
+    /**
+     * Starts the backup by disabling log file deletion. Be sure to call
+     * endBackup when done to resume log file cleanup. Concurrent backups is
+     * supported.
+     */
+    Backup startBackup() throws RepositoryException;
+
+    public static interface Backup {
+        /**
+         * End the backup and resume log file cleanup.
+         */
+        void endBackup() throws RepositoryException;
+
+        /**
+         * Returns all the files to be copied, in the exact order in which they
+         * must be copied.
+         *
+         * @return ordered array of absolute files
+         * @throws IllegalStateException if backup has ended
+         */
+        File[] getFiles() throws RepositoryException;
+    }
+}
-- 
cgit v1.2.3