...

Source file src/go.etcd.io/bbolt/tx_test.go

Documentation: go.etcd.io/bbolt

     1  package bbolt_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"os"
     9  	"runtime"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	bolt "go.etcd.io/bbolt"
    17  	"go.etcd.io/bbolt/internal/btesting"
    18  )
    19  
    20  // TestTx_Check_ReadOnly tests consistency checking on a ReadOnly database.
    21  func TestTx_Check_ReadOnly(t *testing.T) {
    22  	db := btesting.MustCreateDB(t)
    23  	if err := db.Update(func(tx *bolt.Tx) error {
    24  		b, err := tx.CreateBucket([]byte("widgets"))
    25  		if err != nil {
    26  			t.Fatal(err)
    27  		}
    28  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
    29  			t.Fatal(err)
    30  		}
    31  		return nil
    32  	}); err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if err := db.Close(); err != nil {
    36  		t.Fatal(err)
    37  	}
    38  
    39  	readOnlyDB, err := bolt.Open(db.Path(), 0666, &bolt.Options{ReadOnly: true})
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	defer readOnlyDB.Close()
    44  
    45  	tx, err := readOnlyDB.Begin(false)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	// ReadOnly DB will load freelist on Check call.
    50  	numChecks := 2
    51  	errc := make(chan error, numChecks)
    52  	check := func() {
    53  		errc <- <-tx.Check()
    54  	}
    55  	// Ensure the freelist is not reloaded and does not race.
    56  	for i := 0; i < numChecks; i++ {
    57  		go check()
    58  	}
    59  	for i := 0; i < numChecks; i++ {
    60  		if err := <-errc; err != nil {
    61  			t.Fatal(err)
    62  		}
    63  	}
    64  	// Close the view transaction
    65  	err = tx.Rollback()
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  }
    70  
    71  // Ensure that committing a closed transaction returns an error.
    72  func TestTx_Commit_ErrTxClosed(t *testing.T) {
    73  	db := btesting.MustCreateDB(t)
    74  	tx, err := db.Begin(true)
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  
    79  	if _, err := tx.CreateBucket([]byte("foo")); err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	if err := tx.Commit(); err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	if err := tx.Commit(); err != bolt.ErrTxClosed {
    88  		t.Fatalf("unexpected error: %s", err)
    89  	}
    90  }
    91  
    92  // Ensure that rolling back a closed transaction returns an error.
    93  func TestTx_Rollback_ErrTxClosed(t *testing.T) {
    94  	db := btesting.MustCreateDB(t)
    95  
    96  	tx, err := db.Begin(true)
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  
   101  	if err := tx.Rollback(); err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	if err := tx.Rollback(); err != bolt.ErrTxClosed {
   105  		t.Fatalf("unexpected error: %s", err)
   106  	}
   107  }
   108  
   109  // Ensure that committing a read-only transaction returns an error.
   110  func TestTx_Commit_ErrTxNotWritable(t *testing.T) {
   111  	db := btesting.MustCreateDB(t)
   112  	tx, err := db.Begin(false)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	if err := tx.Commit(); err != bolt.ErrTxNotWritable {
   117  		t.Fatal(err)
   118  	}
   119  	// Close the view transaction
   120  	err = tx.Rollback()
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  }
   125  
   126  // Ensure that a transaction can retrieve a cursor on the root bucket.
   127  func TestTx_Cursor(t *testing.T) {
   128  	db := btesting.MustCreateDB(t)
   129  	if err := db.Update(func(tx *bolt.Tx) error {
   130  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   131  			t.Fatal(err)
   132  		}
   133  
   134  		if _, err := tx.CreateBucket([]byte("woojits")); err != nil {
   135  			t.Fatal(err)
   136  		}
   137  
   138  		c := tx.Cursor()
   139  		if k, v := c.First(); !bytes.Equal(k, []byte("widgets")) {
   140  			t.Fatalf("unexpected key: %v", k)
   141  		} else if v != nil {
   142  			t.Fatalf("unexpected value: %v", v)
   143  		}
   144  
   145  		if k, v := c.Next(); !bytes.Equal(k, []byte("woojits")) {
   146  			t.Fatalf("unexpected key: %v", k)
   147  		} else if v != nil {
   148  			t.Fatalf("unexpected value: %v", v)
   149  		}
   150  
   151  		if k, v := c.Next(); k != nil {
   152  			t.Fatalf("unexpected key: %v", k)
   153  		} else if v != nil {
   154  			t.Fatalf("unexpected value: %v", k)
   155  		}
   156  
   157  		return nil
   158  	}); err != nil {
   159  		t.Fatal(err)
   160  	}
   161  }
   162  
   163  // Ensure that creating a bucket with a read-only transaction returns an error.
   164  func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) {
   165  	db := btesting.MustCreateDB(t)
   166  	if err := db.View(func(tx *bolt.Tx) error {
   167  		_, err := tx.CreateBucket([]byte("foo"))
   168  		if err != bolt.ErrTxNotWritable {
   169  			t.Fatalf("unexpected error: %s", err)
   170  		}
   171  		return nil
   172  	}); err != nil {
   173  		t.Fatal(err)
   174  	}
   175  }
   176  
   177  // Ensure that creating a bucket on a closed transaction returns an error.
   178  func TestTx_CreateBucket_ErrTxClosed(t *testing.T) {
   179  	db := btesting.MustCreateDB(t)
   180  	tx, err := db.Begin(true)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	if err := tx.Commit(); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	if _, err := tx.CreateBucket([]byte("foo")); err != bolt.ErrTxClosed {
   189  		t.Fatalf("unexpected error: %s", err)
   190  	}
   191  }
   192  
   193  // Ensure that a Tx can retrieve a bucket.
   194  func TestTx_Bucket(t *testing.T) {
   195  	db := btesting.MustCreateDB(t)
   196  	if err := db.Update(func(tx *bolt.Tx) error {
   197  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   198  			t.Fatal(err)
   199  		}
   200  		if tx.Bucket([]byte("widgets")) == nil {
   201  			t.Fatal("expected bucket")
   202  		}
   203  		return nil
   204  	}); err != nil {
   205  		t.Fatal(err)
   206  	}
   207  }
   208  
   209  // Ensure that a Tx retrieving a non-existent key returns nil.
   210  func TestTx_Get_NotFound(t *testing.T) {
   211  	db := btesting.MustCreateDB(t)
   212  	if err := db.Update(func(tx *bolt.Tx) error {
   213  		b, err := tx.CreateBucket([]byte("widgets"))
   214  		if err != nil {
   215  			t.Fatal(err)
   216  		}
   217  
   218  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   219  			t.Fatal(err)
   220  		}
   221  		if b.Get([]byte("no_such_key")) != nil {
   222  			t.Fatal("expected nil value")
   223  		}
   224  		return nil
   225  	}); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  }
   229  
   230  // Ensure that a bucket can be created and retrieved.
   231  func TestTx_CreateBucket(t *testing.T) {
   232  	db := btesting.MustCreateDB(t)
   233  
   234  	// Create a bucket.
   235  	if err := db.Update(func(tx *bolt.Tx) error {
   236  		b, err := tx.CreateBucket([]byte("widgets"))
   237  		if err != nil {
   238  			t.Fatal(err)
   239  		} else if b == nil {
   240  			t.Fatal("expected bucket")
   241  		}
   242  		return nil
   243  	}); err != nil {
   244  		t.Fatal(err)
   245  	}
   246  
   247  	// Read the bucket through a separate transaction.
   248  	if err := db.View(func(tx *bolt.Tx) error {
   249  		if tx.Bucket([]byte("widgets")) == nil {
   250  			t.Fatal("expected bucket")
   251  		}
   252  		return nil
   253  	}); err != nil {
   254  		t.Fatal(err)
   255  	}
   256  }
   257  
   258  // Ensure that a bucket can be created if it doesn't already exist.
   259  func TestTx_CreateBucketIfNotExists(t *testing.T) {
   260  	db := btesting.MustCreateDB(t)
   261  	if err := db.Update(func(tx *bolt.Tx) error {
   262  		// Create bucket.
   263  		if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
   264  			t.Fatal(err)
   265  		} else if b == nil {
   266  			t.Fatal("expected bucket")
   267  		}
   268  
   269  		// Create bucket again.
   270  		if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
   271  			t.Fatal(err)
   272  		} else if b == nil {
   273  			t.Fatal("expected bucket")
   274  		}
   275  
   276  		return nil
   277  	}); err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	// Read the bucket through a separate transaction.
   282  	if err := db.View(func(tx *bolt.Tx) error {
   283  		if tx.Bucket([]byte("widgets")) == nil {
   284  			t.Fatal("expected bucket")
   285  		}
   286  		return nil
   287  	}); err != nil {
   288  		t.Fatal(err)
   289  	}
   290  }
   291  
   292  // Ensure transaction returns an error if creating an unnamed bucket.
   293  func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) {
   294  	db := btesting.MustCreateDB(t)
   295  	if err := db.Update(func(tx *bolt.Tx) error {
   296  		if _, err := tx.CreateBucketIfNotExists([]byte{}); err != bolt.ErrBucketNameRequired {
   297  			t.Fatalf("unexpected error: %s", err)
   298  		}
   299  
   300  		if _, err := tx.CreateBucketIfNotExists(nil); err != bolt.ErrBucketNameRequired {
   301  			t.Fatalf("unexpected error: %s", err)
   302  		}
   303  
   304  		return nil
   305  	}); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  }
   309  
   310  // Ensure that a bucket cannot be created twice.
   311  func TestTx_CreateBucket_ErrBucketExists(t *testing.T) {
   312  	db := btesting.MustCreateDB(t)
   313  
   314  	// Create a bucket.
   315  	if err := db.Update(func(tx *bolt.Tx) error {
   316  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   317  			t.Fatal(err)
   318  		}
   319  		return nil
   320  	}); err != nil {
   321  		t.Fatal(err)
   322  	}
   323  
   324  	// Create the same bucket again.
   325  	if err := db.Update(func(tx *bolt.Tx) error {
   326  		if _, err := tx.CreateBucket([]byte("widgets")); err != bolt.ErrBucketExists {
   327  			t.Fatalf("unexpected error: %s", err)
   328  		}
   329  		return nil
   330  	}); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  }
   334  
   335  // Ensure that a bucket is created with a non-blank name.
   336  func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) {
   337  	db := btesting.MustCreateDB(t)
   338  	if err := db.Update(func(tx *bolt.Tx) error {
   339  		if _, err := tx.CreateBucket(nil); err != bolt.ErrBucketNameRequired {
   340  			t.Fatalf("unexpected error: %s", err)
   341  		}
   342  		return nil
   343  	}); err != nil {
   344  		t.Fatal(err)
   345  	}
   346  }
   347  
   348  // Ensure that a bucket can be deleted.
   349  func TestTx_DeleteBucket(t *testing.T) {
   350  	db := btesting.MustCreateDB(t)
   351  
   352  	// Create a bucket and add a value.
   353  	if err := db.Update(func(tx *bolt.Tx) error {
   354  		b, err := tx.CreateBucket([]byte("widgets"))
   355  		if err != nil {
   356  			t.Fatal(err)
   357  		}
   358  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   359  			t.Fatal(err)
   360  		}
   361  		return nil
   362  	}); err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	// Delete the bucket and make sure we can't get the value.
   367  	if err := db.Update(func(tx *bolt.Tx) error {
   368  		if err := tx.DeleteBucket([]byte("widgets")); err != nil {
   369  			t.Fatal(err)
   370  		}
   371  		if tx.Bucket([]byte("widgets")) != nil {
   372  			t.Fatal("unexpected bucket")
   373  		}
   374  		return nil
   375  	}); err != nil {
   376  		t.Fatal(err)
   377  	}
   378  
   379  	if err := db.Update(func(tx *bolt.Tx) error {
   380  		// Create the bucket again and make sure there's not a phantom value.
   381  		b, err := tx.CreateBucket([]byte("widgets"))
   382  		if err != nil {
   383  			t.Fatal(err)
   384  		}
   385  		if v := b.Get([]byte("foo")); v != nil {
   386  			t.Fatalf("unexpected phantom value: %v", v)
   387  		}
   388  		return nil
   389  	}); err != nil {
   390  		t.Fatal(err)
   391  	}
   392  }
   393  
   394  // Ensure that deleting a bucket on a closed transaction returns an error.
   395  func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) {
   396  	db := btesting.MustCreateDB(t)
   397  	tx, err := db.Begin(true)
   398  	if err != nil {
   399  		t.Fatal(err)
   400  	}
   401  	if err := tx.Commit(); err != nil {
   402  		t.Fatal(err)
   403  	}
   404  	if err := tx.DeleteBucket([]byte("foo")); err != bolt.ErrTxClosed {
   405  		t.Fatalf("unexpected error: %s", err)
   406  	}
   407  }
   408  
   409  // Ensure that deleting a bucket with a read-only transaction returns an error.
   410  func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
   411  	db := btesting.MustCreateDB(t)
   412  	if err := db.View(func(tx *bolt.Tx) error {
   413  		if err := tx.DeleteBucket([]byte("foo")); err != bolt.ErrTxNotWritable {
   414  			t.Fatalf("unexpected error: %s", err)
   415  		}
   416  		return nil
   417  	}); err != nil {
   418  		t.Fatal(err)
   419  	}
   420  }
   421  
   422  // Ensure that nothing happens when deleting a bucket that doesn't exist.
   423  func TestTx_DeleteBucket_NotFound(t *testing.T) {
   424  	db := btesting.MustCreateDB(t)
   425  	if err := db.Update(func(tx *bolt.Tx) error {
   426  		if err := tx.DeleteBucket([]byte("widgets")); err != bolt.ErrBucketNotFound {
   427  			t.Fatalf("unexpected error: %s", err)
   428  		}
   429  		return nil
   430  	}); err != nil {
   431  		t.Fatal(err)
   432  	}
   433  }
   434  
   435  // Ensure that no error is returned when a tx.ForEach function does not return
   436  // an error.
   437  func TestTx_ForEach_NoError(t *testing.T) {
   438  	db := btesting.MustCreateDB(t)
   439  	if err := db.Update(func(tx *bolt.Tx) error {
   440  		b, err := tx.CreateBucket([]byte("widgets"))
   441  		if err != nil {
   442  			t.Fatal(err)
   443  		}
   444  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   445  			t.Fatal(err)
   446  		}
   447  
   448  		if err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
   449  			return nil
   450  		}); err != nil {
   451  			t.Fatal(err)
   452  		}
   453  		return nil
   454  	}); err != nil {
   455  		t.Fatal(err)
   456  	}
   457  }
   458  
   459  // Ensure that an error is returned when a tx.ForEach function returns an error.
   460  func TestTx_ForEach_WithError(t *testing.T) {
   461  	db := btesting.MustCreateDB(t)
   462  	if err := db.Update(func(tx *bolt.Tx) error {
   463  		b, err := tx.CreateBucket([]byte("widgets"))
   464  		if err != nil {
   465  			t.Fatal(err)
   466  		}
   467  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   468  			t.Fatal(err)
   469  		}
   470  
   471  		marker := errors.New("marker")
   472  		if err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
   473  			return marker
   474  		}); err != marker {
   475  			t.Fatalf("unexpected error: %s", err)
   476  		}
   477  		return nil
   478  	}); err != nil {
   479  		t.Fatal(err)
   480  	}
   481  }
   482  
   483  // Ensure that Tx commit handlers are called after a transaction successfully commits.
   484  func TestTx_OnCommit(t *testing.T) {
   485  	db := btesting.MustCreateDB(t)
   486  
   487  	var x int
   488  	if err := db.Update(func(tx *bolt.Tx) error {
   489  		tx.OnCommit(func() { x += 1 })
   490  		tx.OnCommit(func() { x += 2 })
   491  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   492  			t.Fatal(err)
   493  		}
   494  		return nil
   495  	}); err != nil {
   496  		t.Fatal(err)
   497  	} else if x != 3 {
   498  		t.Fatalf("unexpected x: %d", x)
   499  	}
   500  }
   501  
   502  // Ensure that Tx commit handlers are NOT called after a transaction rolls back.
   503  func TestTx_OnCommit_Rollback(t *testing.T) {
   504  	db := btesting.MustCreateDB(t)
   505  
   506  	var x int
   507  	if err := db.Update(func(tx *bolt.Tx) error {
   508  		tx.OnCommit(func() { x += 1 })
   509  		tx.OnCommit(func() { x += 2 })
   510  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   511  			t.Fatal(err)
   512  		}
   513  		return errors.New("rollback this commit")
   514  	}); err == nil || err.Error() != "rollback this commit" {
   515  		t.Fatalf("unexpected error: %s", err)
   516  	} else if x != 0 {
   517  		t.Fatalf("unexpected x: %d", x)
   518  	}
   519  }
   520  
   521  // Ensure that the database can be copied to a file path.
   522  func TestTx_CopyFile(t *testing.T) {
   523  	db := btesting.MustCreateDB(t)
   524  
   525  	path := tempfile()
   526  	if err := db.Update(func(tx *bolt.Tx) error {
   527  		b, err := tx.CreateBucket([]byte("widgets"))
   528  		if err != nil {
   529  			t.Fatal(err)
   530  		}
   531  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   532  			t.Fatal(err)
   533  		}
   534  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   535  			t.Fatal(err)
   536  		}
   537  		return nil
   538  	}); err != nil {
   539  		t.Fatal(err)
   540  	}
   541  
   542  	if err := db.View(func(tx *bolt.Tx) error {
   543  		return tx.CopyFile(path, 0600)
   544  	}); err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	db2, err := bolt.Open(path, 0600, nil)
   549  	if err != nil {
   550  		t.Fatal(err)
   551  	}
   552  
   553  	if err := db2.View(func(tx *bolt.Tx) error {
   554  		if v := tx.Bucket([]byte("widgets")).Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) {
   555  			t.Fatalf("unexpected value: %v", v)
   556  		}
   557  		if v := tx.Bucket([]byte("widgets")).Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) {
   558  			t.Fatalf("unexpected value: %v", v)
   559  		}
   560  		return nil
   561  	}); err != nil {
   562  		t.Fatal(err)
   563  	}
   564  
   565  	if err := db2.Close(); err != nil {
   566  		t.Fatal(err)
   567  	}
   568  }
   569  
   570  type failWriterError struct{}
   571  
   572  func (failWriterError) Error() string {
   573  	return "error injected for tests"
   574  }
   575  
   576  type failWriter struct {
   577  	// fail after this many bytes
   578  	After int
   579  }
   580  
   581  func (f *failWriter) Write(p []byte) (n int, err error) {
   582  	n = len(p)
   583  	if n > f.After {
   584  		n = f.After
   585  		err = failWriterError{}
   586  	}
   587  	f.After -= n
   588  	return n, err
   589  }
   590  
   591  // Ensure that Copy handles write errors right.
   592  func TestTx_CopyFile_Error_Meta(t *testing.T) {
   593  	db := btesting.MustCreateDB(t)
   594  	if err := db.Update(func(tx *bolt.Tx) error {
   595  		b, err := tx.CreateBucket([]byte("widgets"))
   596  		if err != nil {
   597  			t.Fatal(err)
   598  		}
   599  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   600  			t.Fatal(err)
   601  		}
   602  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   603  			t.Fatal(err)
   604  		}
   605  		return nil
   606  	}); err != nil {
   607  		t.Fatal(err)
   608  	}
   609  
   610  	if err := db.View(func(tx *bolt.Tx) error {
   611  		return tx.Copy(&failWriter{})
   612  	}); err == nil || err.Error() != "meta 0 copy: error injected for tests" {
   613  		t.Fatalf("unexpected error: %v", err)
   614  	}
   615  }
   616  
   617  // Ensure that Copy handles write errors right.
   618  func TestTx_CopyFile_Error_Normal(t *testing.T) {
   619  	db := btesting.MustCreateDB(t)
   620  	if err := db.Update(func(tx *bolt.Tx) error {
   621  		b, err := tx.CreateBucket([]byte("widgets"))
   622  		if err != nil {
   623  			t.Fatal(err)
   624  		}
   625  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   626  			t.Fatal(err)
   627  		}
   628  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   629  			t.Fatal(err)
   630  		}
   631  		return nil
   632  	}); err != nil {
   633  		t.Fatal(err)
   634  	}
   635  
   636  	if err := db.View(func(tx *bolt.Tx) error {
   637  		return tx.Copy(&failWriter{3 * db.Info().PageSize})
   638  	}); err == nil || err.Error() != "error injected for tests" {
   639  		t.Fatalf("unexpected error: %v", err)
   640  	}
   641  }
   642  
   643  // TestTx_Rollback ensures there is no error when tx rollback whether we sync freelist or not.
   644  func TestTx_Rollback(t *testing.T) {
   645  	for _, isSyncFreelist := range []bool{false, true} {
   646  		// Open the database.
   647  		db, err := bolt.Open(tempfile(), 0666, nil)
   648  		if err != nil {
   649  			log.Fatal(err)
   650  		}
   651  		defer os.Remove(db.Path())
   652  		db.NoFreelistSync = isSyncFreelist
   653  
   654  		tx, err := db.Begin(true)
   655  		if err != nil {
   656  			t.Fatalf("Error starting tx: %v", err)
   657  		}
   658  		bucket := []byte("mybucket")
   659  		if _, err := tx.CreateBucket(bucket); err != nil {
   660  			t.Fatalf("Error creating bucket: %v", err)
   661  		}
   662  		if err := tx.Commit(); err != nil {
   663  			t.Fatalf("Error on commit: %v", err)
   664  		}
   665  
   666  		tx, err = db.Begin(true)
   667  		if err != nil {
   668  			t.Fatalf("Error starting tx: %v", err)
   669  		}
   670  		b := tx.Bucket(bucket)
   671  		if err := b.Put([]byte("k"), []byte("v")); err != nil {
   672  			t.Fatalf("Error on put: %v", err)
   673  		}
   674  		// Imagine there is an error and tx needs to be rolled-back
   675  		if err := tx.Rollback(); err != nil {
   676  			t.Fatalf("Error on rollback: %v", err)
   677  		}
   678  
   679  		tx, err = db.Begin(false)
   680  		if err != nil {
   681  			t.Fatalf("Error starting tx: %v", err)
   682  		}
   683  		b = tx.Bucket(bucket)
   684  		if v := b.Get([]byte("k")); v != nil {
   685  			t.Fatalf("Value for k should not have been stored")
   686  		}
   687  		if err := tx.Rollback(); err != nil {
   688  			t.Fatalf("Error on rollback: %v", err)
   689  		}
   690  
   691  	}
   692  }
   693  
   694  // TestTx_releaseRange ensures db.freePages handles page releases
   695  // correctly when there are transaction that are no longer reachable
   696  // via any read/write transactions and are "between" ongoing read
   697  // transactions, which requires they must be freed by
   698  // freelist.releaseRange.
   699  func TestTx_releaseRange(t *testing.T) {
   700  	// Set initial mmap size well beyond the limit we will hit in this
   701  	// test, since we are testing with long running read transactions
   702  	// and will deadlock if db.grow is triggered.
   703  	db := btesting.MustCreateDBWithOption(t, &bolt.Options{InitialMmapSize: os.Getpagesize() * 100})
   704  
   705  	bucket := "bucket"
   706  
   707  	put := func(key, value string) {
   708  		if err := db.Update(func(tx *bolt.Tx) error {
   709  			b, err := tx.CreateBucketIfNotExists([]byte(bucket))
   710  			if err != nil {
   711  				t.Fatal(err)
   712  			}
   713  			return b.Put([]byte(key), []byte(value))
   714  		}); err != nil {
   715  			t.Fatal(err)
   716  		}
   717  	}
   718  
   719  	del := func(key string) {
   720  		if err := db.Update(func(tx *bolt.Tx) error {
   721  			b, err := tx.CreateBucketIfNotExists([]byte(bucket))
   722  			if err != nil {
   723  				t.Fatal(err)
   724  			}
   725  			return b.Delete([]byte(key))
   726  		}); err != nil {
   727  			t.Fatal(err)
   728  		}
   729  	}
   730  
   731  	getWithTxn := func(txn *bolt.Tx, key string) []byte {
   732  		return txn.Bucket([]byte(bucket)).Get([]byte(key))
   733  	}
   734  
   735  	openReadTxn := func() *bolt.Tx {
   736  		readTx, err := db.Begin(false)
   737  		if err != nil {
   738  			t.Fatal(err)
   739  		}
   740  		return readTx
   741  	}
   742  
   743  	checkWithReadTxn := func(txn *bolt.Tx, key string, wantValue []byte) {
   744  		value := getWithTxn(txn, key)
   745  		if !bytes.Equal(value, wantValue) {
   746  			t.Errorf("Wanted value to be %s for key %s, but got %s", wantValue, key, string(value))
   747  		}
   748  	}
   749  
   750  	rollback := func(txn *bolt.Tx) {
   751  		if err := txn.Rollback(); err != nil {
   752  			t.Fatal(err)
   753  		}
   754  	}
   755  
   756  	put("k1", "v1")
   757  	rtx1 := openReadTxn()
   758  	put("k2", "v2")
   759  	hold1 := openReadTxn()
   760  	put("k3", "v3")
   761  	hold2 := openReadTxn()
   762  	del("k3")
   763  	rtx2 := openReadTxn()
   764  	del("k1")
   765  	hold3 := openReadTxn()
   766  	del("k2")
   767  	hold4 := openReadTxn()
   768  	put("k4", "v4")
   769  	hold5 := openReadTxn()
   770  
   771  	// Close the read transactions we established to hold a portion of the pages in pending state.
   772  	rollback(hold1)
   773  	rollback(hold2)
   774  	rollback(hold3)
   775  	rollback(hold4)
   776  	rollback(hold5)
   777  
   778  	// Execute a write transaction to trigger a releaseRange operation in the db
   779  	// that will free multiple ranges between the remaining open read transactions, now that the
   780  	// holds have been rolled back.
   781  	put("k4", "v4")
   782  
   783  	// Check that all long running reads still read correct values.
   784  	checkWithReadTxn(rtx1, "k1", []byte("v1"))
   785  	checkWithReadTxn(rtx2, "k2", []byte("v2"))
   786  	rollback(rtx1)
   787  	rollback(rtx2)
   788  
   789  	// Check that the final state is correct.
   790  	rtx7 := openReadTxn()
   791  	checkWithReadTxn(rtx7, "k1", nil)
   792  	checkWithReadTxn(rtx7, "k2", nil)
   793  	checkWithReadTxn(rtx7, "k3", nil)
   794  	checkWithReadTxn(rtx7, "k4", []byte("v4"))
   795  	rollback(rtx7)
   796  }
   797  
   798  func ExampleTx_Rollback() {
   799  	// Open the database.
   800  	db, err := bolt.Open(tempfile(), 0666, nil)
   801  	if err != nil {
   802  		log.Fatal(err)
   803  	}
   804  	defer os.Remove(db.Path())
   805  
   806  	// Create a bucket.
   807  	if err := db.Update(func(tx *bolt.Tx) error {
   808  		_, err := tx.CreateBucket([]byte("widgets"))
   809  		return err
   810  	}); err != nil {
   811  		log.Fatal(err)
   812  	}
   813  
   814  	// Set a value for a key.
   815  	if err := db.Update(func(tx *bolt.Tx) error {
   816  		return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
   817  	}); err != nil {
   818  		log.Fatal(err)
   819  	}
   820  
   821  	// Update the key but rollback the transaction so it never saves.
   822  	tx, err := db.Begin(true)
   823  	if err != nil {
   824  		log.Fatal(err)
   825  	}
   826  	b := tx.Bucket([]byte("widgets"))
   827  	if err := b.Put([]byte("foo"), []byte("baz")); err != nil {
   828  		log.Fatal(err)
   829  	}
   830  	if err := tx.Rollback(); err != nil {
   831  		log.Fatal(err)
   832  	}
   833  
   834  	// Ensure that our original value is still set.
   835  	if err := db.View(func(tx *bolt.Tx) error {
   836  		value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
   837  		fmt.Printf("The value for 'foo' is still: %s\n", value)
   838  		return nil
   839  	}); err != nil {
   840  		log.Fatal(err)
   841  	}
   842  
   843  	// Close database to release file lock.
   844  	if err := db.Close(); err != nil {
   845  		log.Fatal(err)
   846  	}
   847  
   848  	// Output:
   849  	// The value for 'foo' is still: bar
   850  }
   851  
   852  func ExampleTx_CopyFile() {
   853  	// Open the database.
   854  	db, err := bolt.Open(tempfile(), 0666, nil)
   855  	if err != nil {
   856  		log.Fatal(err)
   857  	}
   858  	defer os.Remove(db.Path())
   859  
   860  	// Create a bucket and a key.
   861  	if err := db.Update(func(tx *bolt.Tx) error {
   862  		b, err := tx.CreateBucket([]byte("widgets"))
   863  		if err != nil {
   864  			return err
   865  		}
   866  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   867  			return err
   868  		}
   869  		return nil
   870  	}); err != nil {
   871  		log.Fatal(err)
   872  	}
   873  
   874  	// Copy the database to another file.
   875  	toFile := tempfile()
   876  	if err := db.View(func(tx *bolt.Tx) error {
   877  		return tx.CopyFile(toFile, 0666)
   878  	}); err != nil {
   879  		log.Fatal(err)
   880  	}
   881  	defer os.Remove(toFile)
   882  
   883  	// Open the cloned database.
   884  	db2, err := bolt.Open(toFile, 0666, nil)
   885  	if err != nil {
   886  		log.Fatal(err)
   887  	}
   888  
   889  	// Ensure that the key exists in the copy.
   890  	if err := db2.View(func(tx *bolt.Tx) error {
   891  		value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
   892  		fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
   893  		return nil
   894  	}); err != nil {
   895  		log.Fatal(err)
   896  	}
   897  
   898  	// Close database to release file lock.
   899  	if err := db.Close(); err != nil {
   900  		log.Fatal(err)
   901  	}
   902  
   903  	if err := db2.Close(); err != nil {
   904  		log.Fatal(err)
   905  	}
   906  
   907  	// Output:
   908  	// The value for 'foo' in the clone is: bar
   909  }
   910  
   911  func TestTxStats_GetAndIncAtomically(t *testing.T) {
   912  	var stats bolt.TxStats
   913  
   914  	stats.IncPageCount(1)
   915  	assert.Equal(t, int64(1), stats.GetPageCount())
   916  
   917  	stats.IncPageAlloc(2)
   918  	assert.Equal(t, int64(2), stats.GetPageAlloc())
   919  
   920  	stats.IncCursorCount(3)
   921  	assert.Equal(t, int64(3), stats.GetCursorCount())
   922  
   923  	stats.IncNodeCount(100)
   924  	assert.Equal(t, int64(100), stats.GetNodeCount())
   925  
   926  	stats.IncNodeDeref(101)
   927  	assert.Equal(t, int64(101), stats.GetNodeDeref())
   928  
   929  	stats.IncRebalance(1000)
   930  	assert.Equal(t, int64(1000), stats.GetRebalance())
   931  
   932  	stats.IncRebalanceTime(1001 * time.Second)
   933  	assert.Equal(t, 1001*time.Second, stats.GetRebalanceTime())
   934  
   935  	stats.IncSplit(10000)
   936  	assert.Equal(t, int64(10000), stats.GetSplit())
   937  
   938  	stats.IncSpill(10001)
   939  	assert.Equal(t, int64(10001), stats.GetSpill())
   940  
   941  	stats.IncSpillTime(10001 * time.Second)
   942  	assert.Equal(t, 10001*time.Second, stats.GetSpillTime())
   943  
   944  	stats.IncWrite(100000)
   945  	assert.Equal(t, int64(100000), stats.GetWrite())
   946  
   947  	stats.IncWriteTime(100001 * time.Second)
   948  	assert.Equal(t, 100001*time.Second, stats.GetWriteTime())
   949  
   950  	assert.Equal(t,
   951  		bolt.TxStats{
   952  			PageCount:     1,
   953  			PageAlloc:     2,
   954  			CursorCount:   3,
   955  			NodeCount:     100,
   956  			NodeDeref:     101,
   957  			Rebalance:     1000,
   958  			RebalanceTime: 1001 * time.Second,
   959  			Split:         10000,
   960  			Spill:         10001,
   961  			SpillTime:     10001 * time.Second,
   962  			Write:         100000,
   963  			WriteTime:     100001 * time.Second,
   964  		},
   965  		stats,
   966  	)
   967  }
   968  
   969  func TestTxStats_Sub(t *testing.T) {
   970  	statsA := bolt.TxStats{
   971  		PageCount:     1,
   972  		PageAlloc:     2,
   973  		CursorCount:   3,
   974  		NodeCount:     100,
   975  		NodeDeref:     101,
   976  		Rebalance:     1000,
   977  		RebalanceTime: 1001 * time.Second,
   978  		Split:         10000,
   979  		Spill:         10001,
   980  		SpillTime:     10001 * time.Second,
   981  		Write:         100000,
   982  		WriteTime:     100001 * time.Second,
   983  	}
   984  
   985  	statsB := bolt.TxStats{
   986  		PageCount:     2,
   987  		PageAlloc:     3,
   988  		CursorCount:   4,
   989  		NodeCount:     101,
   990  		NodeDeref:     102,
   991  		Rebalance:     1001,
   992  		RebalanceTime: 1002 * time.Second,
   993  		Split:         11001,
   994  		Spill:         11002,
   995  		SpillTime:     11002 * time.Second,
   996  		Write:         110001,
   997  		WriteTime:     110010 * time.Second,
   998  	}
   999  
  1000  	diff := statsB.Sub(&statsA)
  1001  	assert.Equal(t, int64(1), diff.GetPageCount())
  1002  	assert.Equal(t, int64(1), diff.GetPageAlloc())
  1003  	assert.Equal(t, int64(1), diff.GetCursorCount())
  1004  	assert.Equal(t, int64(1), diff.GetNodeCount())
  1005  	assert.Equal(t, int64(1), diff.GetNodeDeref())
  1006  	assert.Equal(t, int64(1), diff.GetRebalance())
  1007  	assert.Equal(t, time.Second, diff.GetRebalanceTime())
  1008  	assert.Equal(t, int64(1001), diff.GetSplit())
  1009  	assert.Equal(t, int64(1001), diff.GetSpill())
  1010  	assert.Equal(t, 1001*time.Second, diff.GetSpillTime())
  1011  	assert.Equal(t, int64(10001), diff.GetWrite())
  1012  	assert.Equal(t, 10009*time.Second, diff.GetWriteTime())
  1013  }
  1014  
  1015  // TestTx_TruncateBeforeWrite ensures the file is truncated ahead whether we sync freelist or not.
  1016  func TestTx_TruncateBeforeWrite(t *testing.T) {
  1017  	if runtime.GOOS == "windows" {
  1018  		return
  1019  	}
  1020  	for _, isSyncFreelist := range []bool{false, true} {
  1021  		t.Run(fmt.Sprintf("isSyncFreelist:%v", isSyncFreelist), func(t *testing.T) {
  1022  			// Open the database.
  1023  			db := btesting.MustCreateDBWithOption(t, &bolt.Options{
  1024  				NoFreelistSync: isSyncFreelist,
  1025  			})
  1026  
  1027  			bigvalue := make([]byte, db.AllocSize/100)
  1028  			count := 0
  1029  			for {
  1030  				count++
  1031  				tx, err := db.Begin(true)
  1032  				require.NoError(t, err)
  1033  				b, err := tx.CreateBucketIfNotExists([]byte("bucket"))
  1034  				require.NoError(t, err)
  1035  				err = b.Put([]byte{byte(count)}, bigvalue)
  1036  				require.NoError(t, err)
  1037  				err = tx.Commit()
  1038  				require.NoError(t, err)
  1039  
  1040  				size := fileSize(db.Path())
  1041  
  1042  				if size > int64(db.AllocSize) && size < int64(db.AllocSize)*2 {
  1043  					// db.grow expands the file aggresively, that double the size while smaller than db.AllocSize,
  1044  					// or increase with a step of db.AllocSize if larger, by which we can test if db.grow has run.
  1045  					t.Fatalf("db.grow doesn't run when file size changes. file size: %d", size)
  1046  				}
  1047  				if size > int64(db.AllocSize) {
  1048  					break
  1049  				}
  1050  			}
  1051  			db.MustClose()
  1052  			db.MustDeleteFile()
  1053  		})
  1054  	}
  1055  }
  1056  

View as plain text