diff --git a/go.mod b/go.mod index d2443287..a25c0b21 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 github.com/tidwall/pretty v1.2.1 - go.etcd.io/bbolt v1.4.2 + go.etcd.io/bbolt v1.4.3 golang.org/x/mod v0.27.0 golang.org/x/sys v0.35.0 google.golang.org/grpc v1.74.2 diff --git a/go.sum b/go.sum index 51ba4ba3..5c73951d 100644 --- a/go.sum +++ b/go.sum @@ -357,8 +357,8 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= -go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= diff --git a/vendor/go.etcd.io/bbolt/.go-version b/vendor/go.etcd.io/bbolt/.go-version index b6773170..7bdcec52 100644 --- a/vendor/go.etcd.io/bbolt/.go-version +++ b/vendor/go.etcd.io/bbolt/.go-version @@ -1 +1 @@ -1.23.10 +1.23.12 diff --git a/vendor/go.etcd.io/bbolt/tx.go b/vendor/go.etcd.io/bbolt/tx.go index 7123ded8..1669fb16 100644 --- a/vendor/go.etcd.io/bbolt/tx.go +++ b/vendor/go.etcd.io/bbolt/tx.go @@ -387,16 +387,43 @@ func (tx *Tx) Copy(w io.Writer) error { // 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 with WriteFlag - f, err := tx.db.openFile(tx.db.path, os.O_RDONLY|tx.WriteFlag, 0) - if err != nil { - return 0, err - } - defer func() { - if cerr := f.Close(); err == nil { - err = cerr + var f *os.File + // There is a risk that between the time a read-only transaction + // is created and the time the file is actually opened, the + // underlying db file at tx.db.path may have been replaced + // (e.g. via rename). In that case, opening the file again would + // unexpectedly point to a different file, rather than the one + // the transaction was based on. + // + // To overcome this, we reuse the already opened file handle when + // WritFlag not set. When the WriteFlag is set, we reopen the file + // but verify that it still refers to the same underlying file + // (by device and inode). If it does not, we fall back to + // reusing the existing already opened file handle. + if tx.WriteFlag != 0 { + // Attempt to open reader with WriteFlag + f, err = tx.db.openFile(tx.db.path, os.O_RDONLY|tx.WriteFlag, 0) + if err != nil { + return 0, err } - }() + + if ok, err := sameFile(tx.db.file, f); !ok { + lg := tx.db.Logger() + if cerr := f.Close(); cerr != nil { + lg.Errorf("failed to close the file (%s): %v", tx.db.path, cerr) + } + lg.Warningf("The underlying file has changed, so reuse the already opened file (%s): %v", tx.db.path, err) + f = tx.db.file + } else { + defer func() { + if cerr := f.Close(); err == nil { + err = cerr + } + }() + } + } else { + f = tx.db.file + } // Generate a meta page. We use the same page data for both meta pages. buf := make([]byte, tx.db.pageSize) @@ -423,13 +450,13 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { 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), io.SeekStart); err != nil { - return n, fmt.Errorf("seek: %s", err) - } + // Copy data pages using a SectionReader to avoid affecting f's offset. + dataOffset := int64(tx.db.pageSize * 2) + dataSize := tx.Size() - dataOffset + sr := io.NewSectionReader(f, dataOffset, dataSize) // Copy data pages. - wn, err := io.CopyN(w, f, tx.Size()-int64(tx.db.pageSize*2)) + wn, err := io.CopyN(w, sr, dataSize) n += wn if err != nil { return n, err @@ -438,6 +465,19 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { return n, nil } +func sameFile(f1, f2 *os.File) (bool, error) { + fi1, err := f1.Stat() + if err != nil { + return false, fmt.Errorf("failed to get fileInfo of the first file (%s): %w", f1.Name(), err) + } + fi2, err := f2.Stat() + if err != nil { + return false, fmt.Errorf("failed to get fileInfo of the second file (%s): %w", f2.Name(), err) + } + + return os.SameFile(fi1, fi2), nil +} + // 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. diff --git a/vendor/modules.txt b/vendor/modules.txt index 06e8d092..96afb59c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -468,7 +468,7 @@ github.com/vanng822/go-premailer/premailer # github.com/x448/float16 v0.8.4 ## explicit; go 1.11 github.com/x448/float16 -# go.etcd.io/bbolt v1.4.2 +# go.etcd.io/bbolt v1.4.3 ## explicit; go 1.23 go.etcd.io/bbolt go.etcd.io/bbolt/errors