aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kobetic <mkobetic@gmail.com>2014-05-21 15:08:37 +0000
committerMartin Kobetic <mkobetic@gmail.com>2014-05-21 15:08:37 +0000
commit519d65228ef2bd99336a251f02c278a94b2df4d4 (patch)
tree53f28c761973abb3cf7cc3071a3eaa3db1a114f2
parentREADME (diff)
downloaddedo-519d65228ef2bd99336a251f02c278a94b2df4d4.tar.gz
dedo-519d65228ef2bd99336a251f02c278a94b2df4d4.tar.xz
move Copy and CopyFile from DB to Tx
-rw-r--r--db.go60
-rw-r--r--db_test.go61
-rw-r--r--tx.go51
-rw-r--r--tx_test.go58
4 files changed, 111 insertions, 119 deletions
diff --git a/db.go b/db.go
index c768ce3..1a1f997 100644
--- a/db.go
+++ b/db.go
@@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"hash/fnv"
- "io"
"os"
"strings"
"sync"
@@ -483,65 +482,6 @@ func (db *DB) View(fn func(*Tx) error) error {
return nil
}
-// Copy writes the entire database to a writer.
-// A reader transaction is maintained during the copy so it is safe to continue
-// using the database while a copy is in progress.
-func (db *DB) Copy(w io.Writer) error {
- // Maintain a reader transaction so pages don't get reclaimed.
- t, err := db.Begin(false)
- if err != nil {
- return err
- }
-
- // Open reader on the database.
- f, err := os.Open(db.path)
- if err != nil {
- _ = t.Rollback()
- return err
- }
-
- // Copy the meta pages.
- db.metalock.Lock()
- _, err = io.CopyN(w, f, int64(db.pageSize*2))
- db.metalock.Unlock()
- if err != nil {
- _ = t.Rollback()
- _ = f.Close()
- return fmt.Errorf("meta copy: %s", err)
- }
-
- // Copy data pages.
- if _, err := io.Copy(w, f); err != nil {
- _ = t.Rollback()
- _ = f.Close()
- return err
- }
-
- // Close read transaction and exit.
- if err := t.Rollback(); err != nil {
- _ = f.Close()
- return err
- }
- return f.Close()
-}
-
-// CopyFile copies the entire database to file at the given path.
-// A reader transaction is maintained during the copy so it is safe to continue
-// using the database while a copy is in progress.
-func (db *DB) CopyFile(path string, mode os.FileMode) error {
- f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
- if err != nil {
- return err
- }
-
- err = db.Copy(f)
- if err != nil {
- _ = f.Close()
- return err
- }
- return f.Close()
-}
-
// Stats retrieves ongoing performance stats for the database.
// This is only updated when a transaction closes.
func (db *DB) Stats() Stats {
diff --git a/db_test.go b/db_test.go
index 903f65e..7b1f554 100644
--- a/db_test.go
+++ b/db_test.go
@@ -238,30 +238,6 @@ func TestDB_View_Error(t *testing.T) {
})
}
-// Ensure that the database can be copied to a file path.
-func TestDB_CopyFile(t *testing.T) {
- withOpenDB(func(db *DB, path string) {
- var dest = tempfile()
- db.Update(func(tx *Tx) error {
- tx.CreateBucket([]byte("widgets"))
- tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
- tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat"))
- return nil
- })
- assert.NoError(t, db.CopyFile(dest, 0600))
-
- db2, err := Open(dest, 0600)
- assert.NoError(t, err)
- defer db2.Close()
-
- db2.View(func(tx *Tx) error {
- assert.Equal(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo")))
- assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz")))
- return nil
- })
- })
-}
-
// Ensure that an error is returned when a database write fails.
func TestDB_Commit_WriteFail(t *testing.T) {
t.Skip("pending") // TODO(benbjohnson)
@@ -450,39 +426,6 @@ func ExampleDB_Begin_ReadOnly() {
// zephyr likes purple
}
-func ExampleDB_CopyFile() {
- // Open the database.
- db, _ := Open(tempfile(), 0666)
- defer os.Remove(db.Path())
- defer db.Close()
-
- // Create a bucket and a key.
- db.Update(func(tx *Tx) error {
- tx.CreateBucket([]byte("widgets"))
- tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
- return nil
- })
-
- // Copy the database to another file.
- toFile := tempfile()
- db.CopyFile(toFile, 0666)
- defer os.Remove(toFile)
-
- // Open the cloned database.
- db2, _ := Open(toFile, 0666)
- defer db2.Close()
-
- // Ensure that the key exists in the copy.
- db2.View(func(tx *Tx) error {
- value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
- fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
- return nil
- })
-
- // Output:
- // The value for 'foo' in the clone is: bar
-}
-
// tempfile returns a temporary file path.
func tempfile() string {
f, _ := ioutil.TempFile("", "bolt-")
@@ -523,7 +466,7 @@ func mustCheck(db *DB) {
if err := db.Check(); err != nil {
// Copy db off first.
var path = tempfile()
- db.CopyFile(path, 0600)
+ db.View(func(tx *Tx) error { return tx.CopyFile(path, 0600) })
if errors, ok := err.(ErrorList); ok {
for _, err := range errors {
@@ -564,7 +507,7 @@ func truncDuration(d time.Duration) string {
// copyAndFailNow copies a database to a new location and then fails then test.
func copyAndFailNow(t *testing.T, db *DB) {
path := tempfile()
- db.CopyFile(path, 0600)
+ db.View(func(tx *Tx) error { return tx.CopyFile(path, 0600) })
fmt.Println("db copied to: ", path)
t.FailNow()
}
diff --git a/tx.go b/tx.go
index fd456eb..35d47f8 100644
--- a/tx.go
+++ b/tx.go
@@ -3,6 +3,8 @@ package bolt
import (
"errors"
"fmt"
+ "io"
+ "os"
"sort"
"time"
"unsafe"
@@ -227,6 +229,55 @@ func (tx *Tx) close() {
tx.db = nil
}
+// Copy writes the entire database to a writer.
+// A reader transaction is maintained during the copy so it is safe to continue
+// using the database while a copy is in progress.
+func (tx *Tx) Copy(w io.Writer) error {
+
+ // Open reader on the database.
+ f, err := os.Open(tx.db.path)
+ if err != nil {
+ _ = tx.Rollback()
+ return err
+ }
+
+ // Copy the meta pages.
+ tx.db.metalock.Lock()
+ _, err = io.CopyN(w, f, int64(tx.db.pageSize*2))
+ tx.db.metalock.Unlock()
+ if err != nil {
+ _ = tx.Rollback()
+ _ = f.Close()
+ return fmt.Errorf("meta copy: %s", err)
+ }
+
+ // Copy data pages.
+ if _, err := io.Copy(w, f); err != nil {
+ _ = tx.Rollback()
+ _ = f.Close()
+ return err
+ }
+
+ return f.Close()
+}
+
+// CopyFile copies the entire database to file at the given path.
+// A reader transaction is maintained during the copy so it is safe to continue
+// using the database while a copy is in progress.
+func (tx *Tx) CopyFile(path string, mode os.FileMode) error {
+ f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
+ if err != nil {
+ return err
+ }
+
+ err = tx.Copy(f)
+ if err != nil {
+ _ = f.Close()
+ return err
+ }
+ return f.Close()
+}
+
// Check performs several consistency checks on the database for this transaction.
// An error is returned if any inconsistency is found or if executed on a read-only transaction.
func (tx *Tx) Check() error {
diff --git a/tx_test.go b/tx_test.go
index 7bf369b..05e7466 100644
--- a/tx_test.go
+++ b/tx_test.go
@@ -313,6 +313,31 @@ func TestTx_Check_Corrupt(t *testing.T) {
assert.Equal(t, "check fail: 1 errors occurred: page 3: already freed", msg)
}
+// Ensure that the database can be copied to a file path.
+func TestTx_CopyFile(t *testing.T) {
+ withOpenDB(func(db *DB, path string) {
+ var dest = tempfile()
+ db.Update(func(tx *Tx) error {
+ tx.CreateBucket([]byte("widgets"))
+ tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
+ tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat"))
+ return nil
+ })
+
+ assert.NoError(t, db.View(func(tx *Tx) error { return tx.CopyFile(dest, 0600) }))
+
+ db2, err := Open(dest, 0600)
+ assert.NoError(t, err)
+ defer db2.Close()
+
+ db2.View(func(tx *Tx) error {
+ assert.Equal(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo")))
+ assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz")))
+ return nil
+ })
+ })
+}
+
func ExampleTx_Rollback() {
// Open the database.
db, _ := Open(tempfile(), 0666)
@@ -346,3 +371,36 @@ func ExampleTx_Rollback() {
// Output:
// The value for 'foo' is still: bar
}
+
+func ExampleTx_CopyFile() {
+ // Open the database.
+ db, _ := Open(tempfile(), 0666)
+ defer os.Remove(db.Path())
+ defer db.Close()
+
+ // Create a bucket and a key.
+ db.Update(func(tx *Tx) error {
+ tx.CreateBucket([]byte("widgets"))
+ tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
+ return nil
+ })
+
+ // Copy the database to another file.
+ toFile := tempfile()
+ db.View(func(tx *Tx) error { return tx.CopyFile(toFile, 0666) })
+ defer os.Remove(toFile)
+
+ // Open the cloned database.
+ db2, _ := Open(toFile, 0666)
+ defer db2.Close()
+
+ // Ensure that the key exists in the copy.
+ db2.View(func(tx *Tx) error {
+ value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
+ fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
+ return nil
+ })
+
+ // Output:
+ // The value for 'foo' in the clone is: bar
+}