From 522043366c6e60fc92776c09658a20b7e22ac120 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Fri, 19 Feb 2016 09:18:07 -0700 Subject: [PATCH] use tx.meta during Tx.WriteTo() This commit changes `Tx.WriteTo()` to use the transaction's in-memory meta page instead of copying from the disk. This is needed because the transaction uses the size from its meta page but writes the current meta page on disk which may have allocated additional pages since the transaction started. Fixes #513 --- tx.go | 32 +++++++++++++++++++++++++++----- tx_test.go | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/tx.go b/tx.go index e74d2ca..299c073 100644 --- a/tx.go +++ b/tx.go @@ -297,12 +297,34 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { } defer func() { _ = f.Close() }() - // Copy the meta pages. - tx.db.metalock.Lock() - n, err = io.CopyN(w, f, int64(tx.db.pageSize*2)) - tx.db.metalock.Unlock() + // Generate a meta page. We use the same page data for both meta pages. + buf := make([]byte, tx.db.pageSize) + page := (*page)(unsafe.Pointer(&buf[0])) + page.flags = metaPageFlag + *page.meta() = *tx.meta + + // Write meta 0. + page.id = 0 + page.meta().checksum = page.meta().sum64() + nn, err := w.Write(buf) + n += int64(nn) if err != nil { - return n, fmt.Errorf("meta copy: %s", err) + return n, fmt.Errorf("meta 0 copy: %s", err) + } + + // Write meta 1 with a lower transaction id. + page.id = 1 + page.meta().txid -= 1 + page.meta().checksum = page.meta().sum64() + nn, err = w.Write(buf) + n += int64(nn) + if err != nil { + return n, fmt.Errorf("meta 1 copy: %s", err) + } + + // Move past the meta pages in the file. + if _, err := f.Seek(int64(tx.db.pageSize*2), os.SEEK_SET); err != nil { + return n, fmt.Errorf("seek: %s", err) } // Copy data pages. diff --git a/tx_test.go b/tx_test.go index 18ff166..2201e79 100644 --- a/tx_test.go +++ b/tx_test.go @@ -570,7 +570,7 @@ func TestTx_CopyFile_Error_Meta(t *testing.T) { if err := db.View(func(tx *bolt.Tx) error { return tx.Copy(&failWriter{}) - }); err == nil || err.Error() != "meta copy: error injected for tests" { + }); err == nil || err.Error() != "meta 0 copy: error injected for tests" { t.Fatalf("unexpected error: %v", err) } }