mirror of https://github.com/etcd-io/bbolt.git
Merge pull request #330 from benbjohnson/tx-writer-to
Implement io.WriterTo interface on Tx.pull/34/head
commit
a9651995e1
|
@ -370,7 +370,7 @@ func (*Bucket) DeleteBucket(key []byte) error
|
||||||
|
|
||||||
### Database backups
|
### Database backups
|
||||||
|
|
||||||
Bolt is a single file so it's easy to backup. You can use the `Tx.Copy()`
|
Bolt is a single file so it's easy to backup. You can use the `Tx.WriteTo()`
|
||||||
function to write a consistent view of the database to a writer. If you call
|
function to write a consistent view of the database to a writer. If you call
|
||||||
this from a read-only transaction, it will perform a hot backup and not block
|
this from a read-only transaction, it will perform a hot backup and not block
|
||||||
your other database reads and writes. It will also use `O_DIRECT` when available
|
your other database reads and writes. It will also use `O_DIRECT` when available
|
||||||
|
@ -385,7 +385,8 @@ func BackupHandleFunc(w http.ResponseWriter, req *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
|
w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
|
||||||
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
|
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
|
||||||
return tx.Copy(w)
|
_, err := tx.WriteTo(w)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
|
27
tx.go
27
tx.go
|
@ -252,37 +252,42 @@ func (tx *Tx) close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy writes the entire database to a writer.
|
// Copy writes the entire database to a writer.
|
||||||
// A reader transaction is maintained during the copy so it is safe to continue
|
// This function exists for backwards compatibility. Use WriteTo() in
|
||||||
// using the database while a copy is in progress.
|
|
||||||
// Copy will write exactly tx.Size() bytes into the writer.
|
|
||||||
func (tx *Tx) Copy(w io.Writer) error {
|
func (tx *Tx) Copy(w io.Writer) error {
|
||||||
var f *os.File
|
_, err := tx.WriteTo(w)
|
||||||
var err error
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes the entire database to a writer.
|
||||||
|
// If err == nil then exactly tx.Size() bytes will be written into the writer.
|
||||||
|
func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
// Attempt to open reader directly.
|
// Attempt to open reader directly.
|
||||||
|
var f *os.File
|
||||||
if f, err = os.OpenFile(tx.db.path, os.O_RDONLY|odirect, 0); err != nil {
|
if f, err = os.OpenFile(tx.db.path, os.O_RDONLY|odirect, 0); err != nil {
|
||||||
// Fallback to a regular open if that doesn't work.
|
// Fallback to a regular open if that doesn't work.
|
||||||
if f, err = os.OpenFile(tx.db.path, os.O_RDONLY, 0); err != nil {
|
if f, err = os.OpenFile(tx.db.path, os.O_RDONLY, 0); err != nil {
|
||||||
return err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the meta pages.
|
// Copy the meta pages.
|
||||||
tx.db.metalock.Lock()
|
tx.db.metalock.Lock()
|
||||||
_, err = io.CopyN(w, f, int64(tx.db.pageSize*2))
|
n, err = io.CopyN(w, f, int64(tx.db.pageSize*2))
|
||||||
tx.db.metalock.Unlock()
|
tx.db.metalock.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
return fmt.Errorf("meta copy: %s", err)
|
return n, fmt.Errorf("meta copy: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy data pages.
|
// Copy data pages.
|
||||||
if _, err := io.CopyN(w, f, tx.Size()-int64(tx.db.pageSize*2)); err != nil {
|
wn, err := io.CopyN(w, f, tx.Size()-int64(tx.db.pageSize*2))
|
||||||
|
n += wn
|
||||||
|
if err != nil {
|
||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
return err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.Close()
|
return n, f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyFile copies the entire database to file at the given path.
|
// CopyFile copies the entire database to file at the given path.
|
||||||
|
|
Loading…
Reference in New Issue