@@ -11,6 +11,7 @@ import (
11
11
"os"
12
12
"path/filepath"
13
13
"reflect"
14
+ "runtime"
14
15
"strings"
15
16
"sync"
16
17
"testing"
@@ -760,6 +761,114 @@ func TestDB_Concurrent_WriteTo_and_ConsistentRead(t *testing.T) {
760
761
}
761
762
}
762
763
764
+ // TestDB_WriteTo_and_Overwrite verifies that `(tx *Tx) WriteTo` can still
765
+ // work even the underlying file is overwritten between the time a read-only
766
+ // transaction is created and the time the file is actually opened
767
+ func TestDB_WriteTo_and_Overwrite (t * testing.T ) {
768
+ testCases := []struct {
769
+ name string
770
+ writeFlag int
771
+ }{
772
+ {
773
+ name : "writeFlag not set" ,
774
+ writeFlag : 0 ,
775
+ },
776
+ /* syscall.O_DIRECT not supported on some platforms, i.e. Windows and MacOS
777
+ {
778
+ name: "writeFlag set",
779
+ writeFlag: syscall.O_DIRECT,
780
+ },*/
781
+ }
782
+
783
+ fRead := func (db * bolt.DB , bucketName []byte ) map [string ]string {
784
+ data := make (map [string ]string )
785
+ _ = db .View (func (tx * bolt.Tx ) error {
786
+ b := tx .Bucket (bucketName )
787
+ berr := b .ForEach (func (k , v []byte ) error {
788
+ data [string (k )] = string (v )
789
+ return nil
790
+ })
791
+ require .NoError (t , berr )
792
+ return nil
793
+ })
794
+ return data
795
+ }
796
+
797
+ for _ , tc := range testCases {
798
+ t .Run (tc .name , func (t * testing.T ) {
799
+ db := btesting .MustCreateDBWithOption (t , & bolt.Options {
800
+ PageSize : 4096 ,
801
+ })
802
+ filePathOfDb := db .Path ()
803
+
804
+ var (
805
+ bucketName = []byte ("data" )
806
+ dataExpected map [string ]string
807
+ dataActual map [string ]string
808
+ )
809
+
810
+ t .Log ("Populate some data" )
811
+ err := db .Update (func (tx * bolt.Tx ) error {
812
+ b , berr := tx .CreateBucket (bucketName )
813
+ if berr != nil {
814
+ return berr
815
+ }
816
+ for k := 0 ; k < 10 ; k ++ {
817
+ key , value := fmt .Sprintf ("key_%d" , rand .Intn (10 )), fmt .Sprintf ("value_%d" , rand .Intn (100 ))
818
+ if perr := b .Put ([]byte (key ), []byte (value )); perr != nil {
819
+ return perr
820
+ }
821
+ }
822
+ return nil
823
+ })
824
+ require .NoError (t , err )
825
+
826
+ t .Log ("Read all the data before calling WriteTo" )
827
+ dataExpected = fRead (db .DB , bucketName )
828
+
829
+ t .Log ("Create a readonly transaction for WriteTo" )
830
+ rtx , rerr := db .Begin (false )
831
+ require .NoError (t , rerr )
832
+
833
+ // Some platforms (i.e. Windows) don't support renaming a file
834
+ // when the target file already exist and is opened.
835
+ if runtime .GOOS == "linux" {
836
+ t .Log ("Create another empty db file" )
837
+ db2 := btesting .MustCreateDBWithOption (t , & bolt.Options {
838
+ PageSize : 4096 ,
839
+ })
840
+ db2 .MustClose ()
841
+ filePathOfDb2 := db2 .Path ()
842
+
843
+ t .Logf ("Renaming the new empty db file (%s) to the original db path (%s)" , filePathOfDb2 , filePathOfDb )
844
+ err = os .Rename (filePathOfDb2 , filePathOfDb )
845
+ require .NoError (t , err )
846
+ } else {
847
+ t .Log ("Ignore renaming step on non-Linux platform" )
848
+ }
849
+
850
+ t .Logf ("Call WriteTo to copy the data of the original db file" )
851
+ f := filepath .Join (t .TempDir (), "-backup-db" )
852
+ err = rtx .CopyFile (f , 0600 )
853
+ require .NoError (t , err )
854
+ require .NoError (t , rtx .Rollback ())
855
+
856
+ t .Logf ("Read all the data from the backup db after calling WriteTo" )
857
+ newDB , err := bolt .Open (f , 0600 , & bolt.Options {
858
+ ReadOnly : true ,
859
+ })
860
+ require .NoError (t , err )
861
+ dataActual = fRead (newDB , bucketName )
862
+ err = newDB .Close ()
863
+ require .NoError (t , err )
864
+
865
+ t .Log ("Compare the dataExpected and dataActual" )
866
+ same := reflect .DeepEqual (dataExpected , dataActual )
867
+ require .True (t , same , fmt .Sprintf ("found inconsistent data, dataExpected: %v, ddataActual : %v" , dataExpected , dataActual ))
868
+ })
869
+ }
870
+ }
871
+
763
872
// Ensure that opening a transaction while the DB is closed returns an error.
764
873
func TestDB_BeginRW_Closed (t * testing.T ) {
765
874
var db bolt.DB
0 commit comments