{"action":"created","comment":{"url":"https://api.github.com/repos/basho/leveldb/pulls/comments/77459055","id":77459055,"diff_hunk":"@@ -0,0 +1,600 @@\n+// -------------------------------------------------------------------\n+//\n+// hot_backup_test.cc\n+//\n+// Copyright (c) 2016 Basho Technologies, Inc. All Rights Reserved.\n+//\n+// This file is provided to you under the Apache License,\n+// Version 2.0 (the \"License\"); you may not use this file\n+// except in compliance with the License.  You may obtain\n+// a copy of the License at\n+//\n+//   http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing,\n+// software distributed under the License is distributed on an\n+// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+// KIND, either express or implied.  See the License for the\n+// specific language governing permissions and limitations\n+// under the License.\n+//\n+// -------------------------------------------------------------------\n+\n+#include <fcntl.h>\n+#include <libgen.h>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+\n+#include \"leveldb/db.h\"\n+#include \"util/testharness.h\"\n+#include \"util/testutil.h\"\n+\n+#include \"db/dbformat.h\"\n+#include \"db/db_impl.h\"\n+#include \"db/filename.h\"\n+#include \"util/hot_backup.h\"\n+\n+\n+/**\n+ * Execution routine\n+ */\n+int main(int argc, char** argv)\n+{\n+    int ret_val;\n+\n+    ret_val=leveldb::test::RunAllTests();\n+\n+    return(ret_val);\n+}   // main\n+\n+\n+namespace leveldb {\n+\n+\n+/**\n+ * Wrapper class for tests.  Holds working variables\n+ * and helper functions.\n+ */\n+class HotBackupTester : public HotBackup\n+{\n+public:\n+    std::string m_DBName;\n+    std::string m_Trigger;\n+\n+    HotBackupTester()\n+    {\n+        m_DBName = test::TmpDir() + \"/hot_backup\";\n+        m_Trigger = test::TmpDir() + \"/trigger\";\n+        gHotBackup=this;\n+    };\n+\n+    virtual ~HotBackupTester()\n+    {\n+        ResetHotBackupObject();\n+    };\n+\n+    virtual const char * GetTriggerPath() {return(m_Trigger.c_str());};\n+\n+    void ClearAllBackups(\n+        Options & InOptions)\n+    {\n+        int loop;\n+        Options options;\n+\n+        // clear old if exists\n+        for (loop=0; loop<=config::kNumBackups; ++loop)\n+        {\n+            options=InOptions;\n+\n+            SetBackupPaths(options, loop);\n+            DestroyDB(\"\", options);\n+        }   // if\n+\n+        options.tiered_slow_level=4;\n+        options.tiered_fast_prefix=m_DBName + \"/fast\";\n+        options.tiered_slow_prefix=m_DBName + \"/slow\";\n+        DestroyDB(\"\", options);\n+    }   // ClearAllBackups\n+\n+};  // class HotBackupTester\n+\n+\n+/**\n+ * Initial code used the existance of /etc/riak/hot_backup file\n+ *  as flag to start a backup.\n+ */\n+TEST(HotBackupTester, FileTriggerTest)\n+{\n+    char * dup_path, *path;\n+    int ret_val;\n+    bool ret_flag;\n+    uint64_t perf_before, perf_after;\n+\n+    perf_before=gPerfCounters->Value(ePerfSyslogWrite);\n+\n+    // cleanup anything existing, likely fails\n+    ///  hmm, should there be a way to move this trigger\n+    ///  to a \"safe\" area like /tmp?\n+    unlink(GetTriggerPath());\n+\n+    // does parent path exist?\n+    //  bypass test if it does not ... Travis CI and\n+    //  other users might not be able to access /etc/riak\n+    dup_path=strdup(GetTriggerPath());\n+    path=dirname(dup_path);\n+    ret_val=access(path, R_OK | W_OK);\n+    ASSERT_TRUE(-1!=ret_val);\n+\n+    // is a trigger seen (hope not)\n+    ret_flag=HotBackup::IsTriggerSet();\n+    ASSERT_TRUE(!ret_flag);\n+\n+    // make a trigger\n+    ret_val=open(GetTriggerPath(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);\n+    ASSERT_TRUE(-1!=ret_val);\n+    close(ret_val);\n+\n+    // test the trigger\n+    ret_flag=HotBackup::IsTriggerSet();\n+    ASSERT_TRUE(ret_flag);\n+\n+    // pretend to be back process\n+    /// schedule twice\n+    HotBackup::HotBackupScheduled();\n+    HotBackup::HotBackupScheduled();\n+\n+    // trigger still there?\n+    ret_flag=HotBackup::IsTriggerSet();\n+    ASSERT_TRUE(ret_flag);\n+\n+    // release one, trigger goes away\n+    HotBackup::HotBackupFinished();\n+    ret_flag=HotBackup::IsTriggerSet();\n+    ASSERT_TRUE(!ret_flag);\n+\n+    // did our simulation create a syslog entry?\n+    //  (bonus if you manually check /var/log/syslog for actual entries)\n+    perf_after=gPerfCounters->Value(ePerfSyslogWrite) - perf_before;\n+    ASSERT_TRUE( 1==perf_after );\n+\n+    // clean up second count.\n+    HotBackupFinished();\n+\n+    // clean up\n+    free(dup_path);\n+    dup_path=NULL;\n+\n+}   // FileTriggerTest\n+\n+/**\n+ * Verify that backup directories rotate\n+ */\n+TEST(HotBackupTester, DirectoryRotationTest)\n+{\n+    int ret_val, loop, inner_loop, offset, file_num;\n+    Options options, backup_options, inner_options;\n+    bool ret_flag, should_find, did_find;\n+    std::string table_file, backup_name;\n+\n+    options.tiered_slow_level=4;\n+    options.tiered_fast_prefix=m_DBName + \"/fast\";\n+    options.tiered_slow_prefix=m_DBName + \"/slow\";\n+    ClearAllBackups(options);\n+\n+    // manually create database directories\n+    ret_val=mkdir(m_DBName.c_str(), 0777);\n+    ret_val=mkdir(options.tiered_fast_prefix.c_str(), 0777);\n+    ASSERT_TRUE(0==ret_val);\n+    ret_val=mkdir(options.tiered_slow_prefix.c_str(), 0777);\n+    ASSERT_TRUE(0==ret_val);\n+    MakeLevelDirectories(Env::Default(), options);\n+\n+    backup_options=options;\n+    SetBackupPaths(backup_options, 0);\n+\n+    // this loop goes one higher than permitted retention\n+    //  to validate deletion of oldest\n+    for (loop=0; loop<=config::kNumBackups; ++loop)\n+    {\n+        // rotate directories\n+        ret_flag=PrepareDirectories(options);\n+        ASSERT_TRUE(ret_flag);\n+\n+        // these files are to \"mark\" the backups, not\n+        //  pretending this is a true file link\n+        // make a file in fast tier ... 10 + backup iteration\n+        table_file=TableFileName(backup_options, 10+loop, 1);\n+        ret_val=open(table_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);\n+        ASSERT_TRUE(-1!=ret_val);\n+        close(ret_val);\n+\n+        // make a file in slow tier ... 20 + backup iteration\n+        table_file=TableFileName(backup_options, 20+loop, 5);\n+        ret_val=open(table_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);\n+        ASSERT_TRUE(-1!=ret_val);\n+        close(ret_val);\n+\n+        // test files\n+        for (inner_loop=config::kNumBackups+1; 0<=inner_loop; --inner_loop)\n+        {\n+            offset=(loop<config::kNumBackups) ? 0 : loop-config::kNumBackups +1;\n+            should_find=(inner_loop<=loop) && (offset<=inner_loop);\n+\n+            inner_options=options;\n+            SetBackupPaths(inner_options, inner_loop-offset);\n+\n+            file_num=loop - inner_loop + offset;\n+\n+            table_file=TableFileName(inner_options, 10+file_num, 1);\n+            ret_val=access(table_file.c_str(), F_OK);\n+            ASSERT_TRUE( (0==ret_val) == should_find);\n+\n+            table_file=TableFileName(inner_options, 20+file_num, 5);\n+            ret_val=access(table_file.c_str(), F_OK);\n+            ASSERT_TRUE( (0==ret_val) == should_find);\n+        }   // for\n+    }   // for\n+\n+    ClearAllBackups(options);\n+\n+    // clean up ... other unit test programs leave stuff, this fails\n+    ret_val=rmdir(m_DBName.c_str());\n+    //ASSERT_TRUE(0==ret_val);\n+\n+}   // DirectoryRotationTest\n+\n+\n+/**\n+ * Test that code has access permissions to copy\n+ * live LOG file.\n+ */\n+TEST(HotBackupTester, LOGCopyTest)\n+{\n+    Options options;\n+    Status s;\n+    DB * db;\n+    WriteOptions write_ops;\n+    std::string backup_path, backup_LOG;\n+    long log_position;\n+    bool ret_flag;\n+    struct stat file_stat;\n+    int ret_val;\n+\n+    backup_path=BackupPath(m_DBName, 0);\n+    backup_LOG=InfoLogFileName(backup_path);\n+\n+    // preclean\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(m_DBName, options);\n+    rmdir(m_DBName.c_str());\n+\n+    // need a live database for this test.\n+    options.create_if_missing=true;\n+    options.error_if_exists=false;\n+    s=DB::Open(options, m_DBName, &db);\n+    ASSERT_OK(s);\n+\n+    // where is log now\n+    log_position=db->GetLogger()->LogSize();\n+\n+    // add to the log\n+    Log(db->GetLogger(), \"LOGCopyTest line 1\");\n+    Log(db->GetLogger(), \"LOGCopyTest line 2\");\n+\n+    // create destination path manually\n+    s=Env::Default()->CreateDir(backup_path.c_str());\n+    ASSERT_OK(s);\n+\n+    // Test the routine\n+    ret_flag=((DBImpl*)db)->CopyLOGSegment(log_position);\n+    ASSERT_TRUE(ret_flag);\n+\n+    // Test size of destination file\n+    ret_val=lstat(backup_LOG.c_str(), &file_stat);\n+    ASSERT_TRUE(0==ret_val);\n+    ASSERT_TRUE(log_position==file_stat.st_size);\n+\n+    // clean up\n+    delete db;\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(m_DBName, options);\n+    rmdir(m_DBName.c_str());\n+\n+}   // LOGCopyTest\n+\n+\n+/**\n+ * Test contents of each backup database\n+ */\n+TEST(HotBackupTester, ContentReviewTest)\n+{\n+    Options options;\n+    Status s;\n+    DB * db;\n+    WriteOptions write_ops;\n+    ReadOptions read_ops;\n+    std::string backup_path, out_buffer;\n+\n+    // preclean\n+    backup_path=BackupPath(m_DBName, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+\n+    backup_path=BackupPath(m_DBName, 1);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+\n+    DestroyDB(m_DBName, options);\n+    rmdir(m_DBName.c_str());\n+\n+    // need a live database for this test.\n+    options.create_if_missing=true;\n+    options.error_if_exists=false;\n+    s=DB::Open(options, m_DBName, &db);\n+    ASSERT_OK(s);\n+\n+    // little bit of data\n+    s=db->Put(write_ops, \"10\", \"apple\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"12\", \"banana\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"14\", \"canalope\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"16\", \"date\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"18\", \"egg plant\");\n+    ASSERT_OK(s);\n+\n+    // first backup\n+    //  pretend it came via CheckHotBackupTrigger\n+    HotBackupScheduled();\n+    HotBackupTask task((DBImpl*)db);\n+\n+    task();\n+\n+    // now add 5 more and try again\n+    s=db->Put(write_ops, \"21\", \"ardvark\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"23\", \"bunny\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"25\", \"cat\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"27\", \"dog\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"29\", \"eagle\");\n+    ASSERT_OK(s);\n+\n+    // second backup\n+    //  pretend it came via CheckHotBackupTrigger\n+    HotBackupScheduled();\n+\n+    task();\n+\n+    // now add 5 more that will not backup\n+    s=db->Put(write_ops, \"70.3\", \"half iron\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"140.6\", \"full iron\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"26.2\", \"marathon\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"13.1\", \"half marathon\");\n+    ASSERT_OK(s);\n+    s=db->Put(write_ops, \"0\", \"did not finish\");\n+    ASSERT_OK(s);\n+\n+    // sample the three sets\n+    s=db->Get(read_ops, \"13.1\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"27\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"16\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"14\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"25\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"26.2\", &out_buffer);\n+    ASSERT_OK(s);\n+    // test fail case\n+    s=db->Get(read_ops, \"26.1\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+\n+    // close parent\n+    delete db;\n+    db=NULL;\n+\n+    // open most recent backup\n+    backup_path=BackupPath(m_DBName, 0);\n+    s=DB::Open(options, backup_path, &db);\n+    ASSERT_OK(s);\n+\n+    // should have data from first two sets, but not third\n+    s=db->Get(read_ops, \"21\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"10\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"70.3\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+\n+    // close backup\n+    delete db;\n+    db=NULL;\n+\n+    // first backup\n+    backup_path=BackupPath(m_DBName, 1);\n+    s=DB::Open(options, backup_path, &db);\n+    ASSERT_OK(s);\n+\n+    // should have data from first set only\n+    s=db->Get(read_ops, \"18\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"29\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+    s=db->Get(read_ops, \"26.2\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+\n+    // close backup\n+    delete db;\n+    db=NULL;\n+\n+    // test links ... get rid of parent, retry backup\n+    DestroyDB(m_DBName, options);\n+\n+    backup_path=BackupPath(m_DBName, 1);\n+    s=DB::Open(options, backup_path, &db);\n+    ASSERT_OK(s);\n+\n+    // should have data from first set only\n+    s=db->Get(read_ops, \"18\", &out_buffer);\n+    ASSERT_OK(s);\n+    s=db->Get(read_ops, \"29\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+    s=db->Get(read_ops, \"26.2\", &out_buffer);\n+    ASSERT_TRUE(!s.ok());\n+\n+    // close backup\n+    delete db;\n+    db=NULL;\n+\n+    // clean up all\n+    backup_path=BackupPath(m_DBName, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+\n+    backup_path=BackupPath(m_DBName, 1);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+\n+    DestroyDB(m_DBName, options);\n+    rmdir(m_DBName.c_str());\n+\n+}   // ContentReviewTest\n+\n+\n+/**\n+ * Verify two DBs will backup when\n+ *  request issued via trigger\n+ */\n+TEST(HotBackupTester, DoubleDB)\n+{\n+    Options options;\n+    Status s;\n+    DB * db1, * db2;\n+    WriteOptions write_ops;\n+    ReadOptions read_ops;\n+    std::string db_path, backup_path, out_buffer, sst_path;\n+    int ret_val, count;\n+\n+    // preclean\n+    unlink(GetTriggerPath());\n+\n+    db_path=m_DBName + \"/db1\";\n+    backup_path=BackupPath(db_path, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(db_path, options);\n+    rmdir(db_path.c_str());\n+\n+    db_path=m_DBName + \"/db2\";\n+    backup_path=BackupPath(db_path, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(db_path, options);\n+    rmdir(db_path.c_str());\n+\n+    // need live databases for this test.\n+    options.create_if_missing=true;\n+    options.error_if_exists=false;\n+\n+    mkdir(m_DBName.c_str(), 0777);\n+    db_path=m_DBName + \"/db1\";\n+    s=DB::Open(options, db_path, &db1);\n+    ASSERT_OK(s);\n+\n+    db_path=m_DBName + \"/db2\";\n+    s=DB::Open(options, db_path, &db2);\n+    ASSERT_OK(s);\n+\n+    // 5 to first db\n+    s=db1->Put(write_ops, \"10\", \"apple\");\n+    ASSERT_OK(s);\n+    s=db1->Put(write_ops, \"12\", \"banana\");\n+    ASSERT_OK(s);\n+    s=db1->Put(write_ops, \"14\", \"canalope\");\n+    ASSERT_OK(s);\n+    s=db1->Put(write_ops, \"16\", \"date\");\n+    ASSERT_OK(s);\n+    s=db1->Put(write_ops, \"18\", \"egg plant\");\n+    ASSERT_OK(s);\n+\n+    // 5 to second db\n+    s=db2->Put(write_ops, \"21\", \"ardvark\");\n+    ASSERT_OK(s);\n+    s=db2->Put(write_ops, \"23\", \"bunny\");\n+    ASSERT_OK(s);\n+    s=db2->Put(write_ops, \"25\", \"cat\");\n+    ASSERT_OK(s);\n+    s=db2->Put(write_ops, \"27\", \"dog\");\n+    ASSERT_OK(s);\n+    s=db2->Put(write_ops, \"29\", \"eagle\");\n+    ASSERT_OK(s);\n+\n+    // set trigger\n+    ret_val=open(GetTriggerPath(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);\n+    ASSERT_TRUE(-1!=ret_val);\n+    close(ret_val);\n+\n+    // cheat, play role of throttle loop and test for trigger\n+    CheckHotBackupTrigger();\n+\n+    // wait for backup to process, but only 10 seconds\n+    count=0;\n+    do\n+    {\n+        sleep(1);\n+        ++count;\n+    } while(0==access(GetTriggerPath(), F_OK) && count<10);\n+    ASSERT_TRUE(count<10);\n+\n+    // expected .sst file exist?\n+    db_path=m_DBName + \"/db1\";\n+    backup_path=BackupPath(db_path,0);\n+    options.tiered_slow_prefix=backup_path;\n+    sst_path=TableFileName(options, 5, 3);\n+    ret_val=access(sst_path.c_str(), F_OK);\n+    ASSERT_TRUE(0==ret_val);\n+\n+    db_path=m_DBName + \"/db2\";\n+    backup_path=BackupPath(db_path,0);\n+    options.tiered_slow_prefix=backup_path;\n+    sst_path=TableFileName(options, 5, 3);\n+    ret_val=access(sst_path.c_str(), F_OK);\n+    ASSERT_TRUE(0==ret_val);\n+\n+    // cleanup\n+    delete db1;\n+    delete db2;\n+\n+    db_path=m_DBName + \"/db1\";\n+    backup_path=BackupPath(db_path, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(db_path, options);\n+    rmdir(db_path.c_str());\n+\n+    db_path=m_DBName + \"/db2\";\n+    backup_path=BackupPath(db_path, 0);\n+    DestroyDB(backup_path, options);\n+    rmdir(backup_path.c_str());\n+    DestroyDB(db_path, options);\n+    rmdir(db_path.c_str());\n+\n+\n+}   // DoubleDBTest\n+\n+}   // namespace leveldb\n+","path":"util/hot_backup_test.cc","position":600,"original_position":600,"commit_id":"9b58e117f4249df583516a6ca3539826525b4b93","original_commit_id":"9b58e117f4249df583516a6ca3539826525b4b93","user":{"login":"paulplace","id":13229428,"avatar_url":"https://avatars.githubusercontent.com/u/13229428?v=3","gravatar_id":"","url":"https://api.github.com/users/paulplace","html_url":"https://github.com/paulplace","followers_url":"https://api.github.com/users/paulplace/followers","following_url":"https://api.github.com/users/paulplace/following{/other_user}","gists_url":"https://api.github.com/users/paulplace/gists{/gist_id}","starred_url":"https://api.github.com/users/paulplace/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paulplace/subscriptions","organizations_url":"https://api.github.com/users/paulplace/orgs","repos_url":"https://api.github.com/users/paulplace/repos","events_url":"https://api.github.com/users/paulplace/events{/privacy}","received_events_url":"https://api.github.com/users/paulplace/received_events","type":"User","site_admin":false},"body":"Overall, this is a very good set of unit tests.\r\n\r\nAnother test that would be good to have is one where we test running a bunch of hot backup jobs while another thread is adding a bunch of records and starting compactions.","created_at":"2016-09-04T21:31:38Z","updated_at":"2016-09-04T21:31:38Z","html_url":"https://github.com/basho/leveldb/pull/202#discussion_r77459055","pull_request_url":"https://api.github.com/repos/basho/leveldb/pulls/202","_links":{"self":{"href":"https://api.github.com/repos/basho/leveldb/pulls/comments/77459055"},"html":{"href":"https://github.com/basho/leveldb/pull/202#discussion_r77459055"},"pull_request":{"href":"https://api.github.com/repos/basho/leveldb/pulls/202"}}},"pull_request":{"url":"https://api.github.com/repos/basho/leveldb/pulls/202","id":79965069,"html_url":"https://github.com/basho/leveldb/pull/202","diff_url":"https://github.com/basho/leveldb/pull/202.diff","patch_url":"https://github.com/basho/leveldb/pull/202.patch","issue_url":"https://api.github.com/repos/basho/leveldb/issues/202","number":202,"state":"open","locked":false,"title":"Mv hot backup [JIRA: RIAK-2784]","user":{"login":"matthewvon","id":225681,"avatar_url":"https://avatars.githubusercontent.com/u/225681?v=3","gravatar_id":"","url":"https://api.github.com/users/matthewvon","html_url":"https://github.com/matthewvon","followers_url":"https://api.github.com/users/matthewvon/followers","following_url":"https://api.github.com/users/matthewvon/following{/other_user}","gists_url":"https://api.github.com/users/matthewvon/gists{/gist_id}","starred_url":"https://api.github.com/users/matthewvon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matthewvon/subscriptions","organizations_url":"https://api.github.com/users/matthewvon/orgs","repos_url":"https://api.github.com/users/matthewvon/repos","events_url":"https://api.github.com/users/matthewvon/events{/privacy}","received_events_url":"https://api.github.com/users/matthewvon/received_events","type":"User","site_admin":false},"body":"Hot Backup allows an external trigger file, /etc/riak/hot_backup, to cause all open leveldb databases on a server to create a backup image, live.  The leveldb process does not the user to stop or pause data operations.  Hot backup will maintain up to five backup snapshots in a rolling fashion if the user does not wish to copy and erase after each backup.\r\n\r\nEach backup snapshot is \"ready to run\".  Previous Basho and 3rd party solutions required a database \"repair\" or similar lengthy process to guarantee state before using their backup.\r\n\r\nUsage and code details are here:  https://github.com/basho/leveldb/wiki/mv-hot-backup","created_at":"2016-08-03T19:34:35Z","updated_at":"2016-09-04T21:31:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2d2b031fca3737164b4d5e908174ab7fdc442fce","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/basho/leveldb/pulls/202/commits","review_comments_url":"https://api.github.com/repos/basho/leveldb/pulls/202/comments","review_comment_url":"https://api.github.com/repos/basho/leveldb/pulls/comments{/number}","comments_url":"https://api.github.com/repos/basho/leveldb/issues/202/comments","statuses_url":"https://api.github.com/repos/basho/leveldb/statuses/9b58e117f4249df583516a6ca3539826525b4b93","head":{"label":"basho:mv-hot-backup","ref":"mv-hot-backup","sha":"9b58e117f4249df583516a6ca3539826525b4b93","user":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"repo":{"id":3280270,"name":"leveldb","full_name":"basho/leveldb","owner":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/basho/leveldb","description":"Clone of http://code.google.com/p/leveldb/","fork":false,"url":"https://api.github.com/repos/basho/leveldb","forks_url":"https://api.github.com/repos/basho/leveldb/forks","keys_url":"https://api.github.com/repos/basho/leveldb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/basho/leveldb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/basho/leveldb/teams","hooks_url":"https://api.github.com/repos/basho/leveldb/hooks","issue_events_url":"https://api.github.com/repos/basho/leveldb/issues/events{/number}","events_url":"https://api.github.com/repos/basho/leveldb/events","assignees_url":"https://api.github.com/repos/basho/leveldb/assignees{/user}","branches_url":"https://api.github.com/repos/basho/leveldb/branches{/branch}","tags_url":"https://api.github.com/repos/basho/leveldb/tags","blobs_url":"https://api.github.com/repos/basho/leveldb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/basho/leveldb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/basho/leveldb/git/refs{/sha}","trees_url":"https://api.github.com/repos/basho/leveldb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/basho/leveldb/statuses/{sha}","languages_url":"https://api.github.com/repos/basho/leveldb/languages","stargazers_url":"https://api.github.com/repos/basho/leveldb/stargazers","contributors_url":"https://api.github.com/repos/basho/leveldb/contributors","subscribers_url":"https://api.github.com/repos/basho/leveldb/subscribers","subscription_url":"https://api.github.com/repos/basho/leveldb/subscription","commits_url":"https://api.github.com/repos/basho/leveldb/commits{/sha}","git_commits_url":"https://api.github.com/repos/basho/leveldb/git/commits{/sha}","comments_url":"https://api.github.com/repos/basho/leveldb/comments{/number}","issue_comment_url":"https://api.github.com/repos/basho/leveldb/issues/comments{/number}","contents_url":"https://api.github.com/repos/basho/leveldb/contents/{+path}","compare_url":"https://api.github.com/repos/basho/leveldb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/basho/leveldb/merges","archive_url":"https://api.github.com/repos/basho/leveldb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/basho/leveldb/downloads","issues_url":"https://api.github.com/repos/basho/leveldb/issues{/number}","pulls_url":"https://api.github.com/repos/basho/leveldb/pulls{/number}","milestones_url":"https://api.github.com/repos/basho/leveldb/milestones{/number}","notifications_url":"https://api.github.com/repos/basho/leveldb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/basho/leveldb/labels{/name}","releases_url":"https://api.github.com/repos/basho/leveldb/releases{/id}","deployments_url":"https://api.github.com/repos/basho/leveldb/deployments","created_at":"2012-01-27T04:49:26Z","updated_at":"2016-09-01T11:00:52Z","pushed_at":"2016-08-23T01:04:18Z","git_url":"git://github.com/basho/leveldb.git","ssh_url":"git@github.com:basho/leveldb.git","clone_url":"https://github.com/basho/leveldb.git","svn_url":"https://github.com/basho/leveldb","homepage":"","size":5851,"stargazers_count":391,"watchers_count":391,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":156,"mirror_url":null,"open_issues_count":22,"forks":156,"open_issues":22,"watchers":391,"default_branch":"develop"}},"base":{"label":"basho:develop","ref":"develop","sha":"04a60dbaba2d25bcf94e45f6c5f24f43d8ef733a","user":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"repo":{"id":3280270,"name":"leveldb","full_name":"basho/leveldb","owner":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/basho/leveldb","description":"Clone of http://code.google.com/p/leveldb/","fork":false,"url":"https://api.github.com/repos/basho/leveldb","forks_url":"https://api.github.com/repos/basho/leveldb/forks","keys_url":"https://api.github.com/repos/basho/leveldb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/basho/leveldb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/basho/leveldb/teams","hooks_url":"https://api.github.com/repos/basho/leveldb/hooks","issue_events_url":"https://api.github.com/repos/basho/leveldb/issues/events{/number}","events_url":"https://api.github.com/repos/basho/leveldb/events","assignees_url":"https://api.github.com/repos/basho/leveldb/assignees{/user}","branches_url":"https://api.github.com/repos/basho/leveldb/branches{/branch}","tags_url":"https://api.github.com/repos/basho/leveldb/tags","blobs_url":"https://api.github.com/repos/basho/leveldb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/basho/leveldb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/basho/leveldb/git/refs{/sha}","trees_url":"https://api.github.com/repos/basho/leveldb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/basho/leveldb/statuses/{sha}","languages_url":"https://api.github.com/repos/basho/leveldb/languages","stargazers_url":"https://api.github.com/repos/basho/leveldb/stargazers","contributors_url":"https://api.github.com/repos/basho/leveldb/contributors","subscribers_url":"https://api.github.com/repos/basho/leveldb/subscribers","subscription_url":"https://api.github.com/repos/basho/leveldb/subscription","commits_url":"https://api.github.com/repos/basho/leveldb/commits{/sha}","git_commits_url":"https://api.github.com/repos/basho/leveldb/git/commits{/sha}","comments_url":"https://api.github.com/repos/basho/leveldb/comments{/number}","issue_comment_url":"https://api.github.com/repos/basho/leveldb/issues/comments{/number}","contents_url":"https://api.github.com/repos/basho/leveldb/contents/{+path}","compare_url":"https://api.github.com/repos/basho/leveldb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/basho/leveldb/merges","archive_url":"https://api.github.com/repos/basho/leveldb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/basho/leveldb/downloads","issues_url":"https://api.github.com/repos/basho/leveldb/issues{/number}","pulls_url":"https://api.github.com/repos/basho/leveldb/pulls{/number}","milestones_url":"https://api.github.com/repos/basho/leveldb/milestones{/number}","notifications_url":"https://api.github.com/repos/basho/leveldb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/basho/leveldb/labels{/name}","releases_url":"https://api.github.com/repos/basho/leveldb/releases{/id}","deployments_url":"https://api.github.com/repos/basho/leveldb/deployments","created_at":"2012-01-27T04:49:26Z","updated_at":"2016-09-01T11:00:52Z","pushed_at":"2016-08-23T01:04:18Z","git_url":"git://github.com/basho/leveldb.git","ssh_url":"git@github.com:basho/leveldb.git","clone_url":"https://github.com/basho/leveldb.git","svn_url":"https://github.com/basho/leveldb","homepage":"","size":5851,"stargazers_count":391,"watchers_count":391,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":156,"mirror_url":null,"open_issues_count":22,"forks":156,"open_issues":22,"watchers":391,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/basho/leveldb/pulls/202"},"html":{"href":"https://github.com/basho/leveldb/pull/202"},"issue":{"href":"https://api.github.com/repos/basho/leveldb/issues/202"},"comments":{"href":"https://api.github.com/repos/basho/leveldb/issues/202/comments"},"review_comments":{"href":"https://api.github.com/repos/basho/leveldb/pulls/202/comments"},"review_comment":{"href":"https://api.github.com/repos/basho/leveldb/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/basho/leveldb/pulls/202/commits"},"statuses":{"href":"https://api.github.com/repos/basho/leveldb/statuses/9b58e117f4249df583516a6ca3539826525b4b93"}}}}
{"actor":{"display_login":"paulplace"}}