github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bdb/tx_test.go (about)

     1  // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bdb_test
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"log"
    22  	"os"
    23  	"testing"
    24  
    25  	"github.com/zuoyebang/bitalosdb/bitree/bdb"
    26  	"github.com/zuoyebang/bitalosdb/internal/options"
    27  )
    28  
    29  func TestTx_Check_ReadOnly(t *testing.T) {
    30  	db := MustOpenDB()
    31  	defer db.Close()
    32  	if err := db.Update(func(tx *bdb.Tx) error {
    33  		b, err := tx.CreateBucket([]byte("widgets"))
    34  		if err != nil {
    35  			t.Fatal(err)
    36  		}
    37  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
    38  			t.Fatal(err)
    39  		}
    40  		return nil
    41  	}); err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	if err := db.DB.Close(); err != nil {
    45  		t.Fatal(err)
    46  	}
    47  
    48  	opts := options.DefaultBdbOptions
    49  	opts.ReadOnly = true
    50  	readOnlyDB, err := bdb.Open(db.f, opts)
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  	defer readOnlyDB.Close()
    55  
    56  	tx, err := readOnlyDB.Begin(false)
    57  	if err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	numChecks := 2
    61  	errc := make(chan error, numChecks)
    62  	check := func() {
    63  		errc <- <-tx.Check()
    64  	}
    65  
    66  	for i := 0; i < numChecks; i++ {
    67  		go check()
    68  	}
    69  	for i := 0; i < numChecks; i++ {
    70  		if err := <-errc; err != nil {
    71  			t.Fatal(err)
    72  		}
    73  	}
    74  
    75  	tx.Rollback()
    76  }
    77  
    78  func TestTx_Commit_ErrTxClosed(t *testing.T) {
    79  	db := MustOpenDB()
    80  	defer db.MustClose()
    81  	tx, err := db.Begin(true)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	if _, err := tx.CreateBucket([]byte("foo")); err != nil {
    87  		t.Fatal(err)
    88  	}
    89  
    90  	if err := tx.Commit(); err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	if err := tx.Commit(); err != bdb.ErrTxClosed {
    95  		t.Fatalf("unexpected error: %s", err)
    96  	}
    97  }
    98  
    99  func TestTx_Rollback_ErrTxClosed(t *testing.T) {
   100  	db := MustOpenDB()
   101  	defer db.MustClose()
   102  
   103  	tx, err := db.Begin(true)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	if err := tx.Rollback(); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  	if err := tx.Rollback(); err != bdb.ErrTxClosed {
   112  		t.Fatalf("unexpected error: %s", err)
   113  	}
   114  }
   115  
   116  func TestTx_Commit_ErrTxNotWritable(t *testing.T) {
   117  	db := MustOpenDB()
   118  	defer db.MustClose()
   119  	tx, err := db.Begin(false)
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	if err := tx.Commit(); err != bdb.ErrTxNotWritable {
   124  		t.Fatal(err)
   125  	}
   126  	tx.Rollback()
   127  }
   128  
   129  // Ensure that a transaction can retrieve a cursor on the root bucket.
   130  func TestTx_Cursor(t *testing.T) {
   131  	db := MustOpenDB()
   132  	defer db.MustClose()
   133  	if err := db.Update(func(tx *bdb.Tx) error {
   134  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   135  			t.Fatal(err)
   136  		}
   137  
   138  		if _, err := tx.CreateBucket([]byte("woojits")); err != nil {
   139  			t.Fatal(err)
   140  		}
   141  
   142  		c := tx.Cursor()
   143  		if k, v := c.First(); !bytes.Equal(k, []byte("widgets")) {
   144  			t.Fatalf("unexpected key: %v", k)
   145  		} else if v != nil {
   146  			t.Fatalf("unexpected value: %v", v)
   147  		}
   148  
   149  		if k, v := c.Next(); !bytes.Equal(k, []byte("woojits")) {
   150  			t.Fatalf("unexpected key: %v", k)
   151  		} else if v != nil {
   152  			t.Fatalf("unexpected value: %v", v)
   153  		}
   154  
   155  		if k, v := c.Next(); k != nil {
   156  			t.Fatalf("unexpected key: %v", k)
   157  		} else if v != nil {
   158  			t.Fatalf("unexpected value: %v", k)
   159  		}
   160  
   161  		return nil
   162  	}); err != nil {
   163  		t.Fatal(err)
   164  	}
   165  }
   166  
   167  // Ensure that creating a bucket with a read-only transaction returns an error.
   168  func TestTx_CreateBucket_ErrTxNotWritable(t *testing.T) {
   169  	db := MustOpenDB()
   170  	defer db.MustClose()
   171  	if err := db.View(func(tx *bdb.Tx) error {
   172  		_, err := tx.CreateBucket([]byte("foo"))
   173  		if err != bdb.ErrTxNotWritable {
   174  			t.Fatalf("unexpected error: %s", err)
   175  		}
   176  		return nil
   177  	}); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  }
   181  
   182  func TestTx_CreateBucket_ErrTxClosed(t *testing.T) {
   183  	db := MustOpenDB()
   184  	defer db.MustClose()
   185  	tx, err := db.Begin(true)
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	if err := tx.Commit(); err != nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  	if _, err := tx.CreateBucket([]byte("foo")); err != bdb.ErrTxClosed {
   194  		t.Fatalf("unexpected error: %s", err)
   195  	}
   196  }
   197  
   198  func TestTx_Bucket(t *testing.T) {
   199  	db := MustOpenDB()
   200  	defer db.MustClose()
   201  	if err := db.Update(func(tx *bdb.Tx) error {
   202  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   203  			t.Fatal(err)
   204  		}
   205  		if tx.Bucket([]byte("widgets")) == nil {
   206  			t.Fatal("expected bucket")
   207  		}
   208  		return nil
   209  	}); err != nil {
   210  		t.Fatal(err)
   211  	}
   212  }
   213  
   214  func TestTx_Get_NotFound(t *testing.T) {
   215  	db := MustOpenDB()
   216  	defer db.MustClose()
   217  	if err := db.Update(func(tx *bdb.Tx) error {
   218  		b, err := tx.CreateBucket([]byte("widgets"))
   219  		if err != nil {
   220  			t.Fatal(err)
   221  		}
   222  
   223  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   224  			t.Fatal(err)
   225  		}
   226  		if b.Get([]byte("no_such_key")) != nil {
   227  			t.Fatal("expected nil value")
   228  		}
   229  		return nil
   230  	}); err != nil {
   231  		t.Fatal(err)
   232  	}
   233  }
   234  
   235  func TestTx_CreateBucket(t *testing.T) {
   236  	db := MustOpenDB()
   237  	defer db.MustClose()
   238  
   239  	if err := db.Update(func(tx *bdb.Tx) error {
   240  		b, err := tx.CreateBucket([]byte("widgets"))
   241  		if err != nil {
   242  			t.Fatal(err)
   243  		} else if b == nil {
   244  			t.Fatal("expected bucket")
   245  		}
   246  		return nil
   247  	}); err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	if err := db.View(func(tx *bdb.Tx) error {
   252  		if tx.Bucket([]byte("widgets")) == nil {
   253  			t.Fatal("expected bucket")
   254  		}
   255  		return nil
   256  	}); err != nil {
   257  		t.Fatal(err)
   258  	}
   259  }
   260  
   261  func TestTx_CreateBucketIfNotExists(t *testing.T) {
   262  	db := MustOpenDB()
   263  	defer db.MustClose()
   264  	if err := db.Update(func(tx *bdb.Tx) error {
   265  		if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
   266  			t.Fatal(err)
   267  		} else if b == nil {
   268  			t.Fatal("expected bucket")
   269  		}
   270  
   271  		if b, err := tx.CreateBucketIfNotExists([]byte("widgets")); err != nil {
   272  			t.Fatal(err)
   273  		} else if b == nil {
   274  			t.Fatal("expected bucket")
   275  		}
   276  
   277  		return nil
   278  	}); err != nil {
   279  		t.Fatal(err)
   280  	}
   281  
   282  	if err := db.View(func(tx *bdb.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  func TestTx_CreateBucketIfNotExists_ErrBucketNameRequired(t *testing.T) {
   293  	db := MustOpenDB()
   294  	defer db.MustClose()
   295  	if err := db.Update(func(tx *bdb.Tx) error {
   296  		if _, err := tx.CreateBucketIfNotExists([]byte{}); err != bdb.ErrBucketNameRequired {
   297  			t.Fatalf("unexpected error: %s", err)
   298  		}
   299  
   300  		if _, err := tx.CreateBucketIfNotExists(nil); err != bdb.ErrBucketNameRequired {
   301  			t.Fatalf("unexpected error: %s", err)
   302  		}
   303  
   304  		return nil
   305  	}); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  }
   309  
   310  func TestTx_CreateBucket_ErrBucketExists(t *testing.T) {
   311  	db := MustOpenDB()
   312  	defer db.MustClose()
   313  
   314  	if err := db.Update(func(tx *bdb.Tx) error {
   315  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   316  			t.Fatal(err)
   317  		}
   318  		return nil
   319  	}); err != nil {
   320  		t.Fatal(err)
   321  	}
   322  
   323  	if err := db.Update(func(tx *bdb.Tx) error {
   324  		if _, err := tx.CreateBucket([]byte("widgets")); err != bdb.ErrBucketExists {
   325  			t.Fatalf("unexpected error: %s", err)
   326  		}
   327  		return nil
   328  	}); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  }
   332  
   333  func TestTx_CreateBucket_ErrBucketNameRequired(t *testing.T) {
   334  	db := MustOpenDB()
   335  	defer db.MustClose()
   336  	if err := db.Update(func(tx *bdb.Tx) error {
   337  		if _, err := tx.CreateBucket(nil); err != bdb.ErrBucketNameRequired {
   338  			t.Fatalf("unexpected error: %s", err)
   339  		}
   340  		return nil
   341  	}); err != nil {
   342  		t.Fatal(err)
   343  	}
   344  }
   345  
   346  func TestTx_DeleteBucket(t *testing.T) {
   347  	db := MustOpenDB()
   348  	defer db.MustClose()
   349  
   350  	if err := db.Update(func(tx *bdb.Tx) error {
   351  		b, err := tx.CreateBucket([]byte("widgets"))
   352  		if err != nil {
   353  			t.Fatal(err)
   354  		}
   355  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   356  			t.Fatal(err)
   357  		}
   358  		return nil
   359  	}); err != nil {
   360  		t.Fatal(err)
   361  	}
   362  
   363  	if err := db.Update(func(tx *bdb.Tx) error {
   364  		if err := tx.DeleteBucket([]byte("widgets")); err != nil {
   365  			t.Fatal(err)
   366  		}
   367  		if tx.Bucket([]byte("widgets")) != nil {
   368  			t.Fatal("unexpected bucket")
   369  		}
   370  		return nil
   371  	}); err != nil {
   372  		t.Fatal(err)
   373  	}
   374  
   375  	if err := db.Update(func(tx *bdb.Tx) error {
   376  		b, err := tx.CreateBucket([]byte("widgets"))
   377  		if err != nil {
   378  			t.Fatal(err)
   379  		}
   380  		if v := b.Get([]byte("foo")); v != nil {
   381  			t.Fatalf("unexpected phantom value: %v", v)
   382  		}
   383  		return nil
   384  	}); err != nil {
   385  		t.Fatal(err)
   386  	}
   387  }
   388  
   389  func TestTx_DeleteBucket_ErrTxClosed(t *testing.T) {
   390  	db := MustOpenDB()
   391  	defer db.MustClose()
   392  	tx, err := db.Begin(true)
   393  	if err != nil {
   394  		t.Fatal(err)
   395  	}
   396  	if err := tx.Commit(); err != nil {
   397  		t.Fatal(err)
   398  	}
   399  	if err := tx.DeleteBucket([]byte("foo")); err != bdb.ErrTxClosed {
   400  		t.Fatalf("unexpected error: %s", err)
   401  	}
   402  }
   403  
   404  func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
   405  	db := MustOpenDB()
   406  	defer db.MustClose()
   407  	if err := db.View(func(tx *bdb.Tx) error {
   408  		if err := tx.DeleteBucket([]byte("foo")); err != bdb.ErrTxNotWritable {
   409  			t.Fatalf("unexpected error: %s", err)
   410  		}
   411  		return nil
   412  	}); err != nil {
   413  		t.Fatal(err)
   414  	}
   415  }
   416  
   417  func TestTx_DeleteBucket_NotFound(t *testing.T) {
   418  	db := MustOpenDB()
   419  	defer db.MustClose()
   420  	if err := db.Update(func(tx *bdb.Tx) error {
   421  		if err := tx.DeleteBucket([]byte("widgets")); err != bdb.ErrBucketNotFound {
   422  			t.Fatalf("unexpected error: %s", err)
   423  		}
   424  		return nil
   425  	}); err != nil {
   426  		t.Fatal(err)
   427  	}
   428  }
   429  
   430  func TestTx_ForEach_NoError(t *testing.T) {
   431  	db := MustOpenDB()
   432  	defer db.MustClose()
   433  	if err := db.Update(func(tx *bdb.Tx) error {
   434  		b, err := tx.CreateBucket([]byte("widgets"))
   435  		if err != nil {
   436  			t.Fatal(err)
   437  		}
   438  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   439  			t.Fatal(err)
   440  		}
   441  
   442  		if err := tx.ForEach(func(name []byte, b *bdb.Bucket) error {
   443  			return nil
   444  		}); err != nil {
   445  			t.Fatal(err)
   446  		}
   447  		return nil
   448  	}); err != nil {
   449  		t.Fatal(err)
   450  	}
   451  }
   452  
   453  // Ensure that an error is returned when a tx.ForEach function returns an error.
   454  func TestTx_ForEach_WithError(t *testing.T) {
   455  	db := MustOpenDB()
   456  	defer db.MustClose()
   457  	if err := db.Update(func(tx *bdb.Tx) error {
   458  		b, err := tx.CreateBucket([]byte("widgets"))
   459  		if err != nil {
   460  			t.Fatal(err)
   461  		}
   462  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   463  			t.Fatal(err)
   464  		}
   465  
   466  		marker := errors.New("marker")
   467  		if err := tx.ForEach(func(name []byte, b *bdb.Bucket) error {
   468  			return marker
   469  		}); err != marker {
   470  			t.Fatalf("unexpected error: %s", err)
   471  		}
   472  		return nil
   473  	}); err != nil {
   474  		t.Fatal(err)
   475  	}
   476  }
   477  
   478  func TestTx_OnCommit(t *testing.T) {
   479  	db := MustOpenDB()
   480  	defer db.MustClose()
   481  
   482  	var x int
   483  	if err := db.Update(func(tx *bdb.Tx) error {
   484  		tx.OnCommit(func() { x += 1 })
   485  		tx.OnCommit(func() { x += 2 })
   486  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   487  			t.Fatal(err)
   488  		}
   489  		return nil
   490  	}); err != nil {
   491  		t.Fatal(err)
   492  	} else if x != 3 {
   493  		t.Fatalf("unexpected x: %d", x)
   494  	}
   495  }
   496  
   497  func TestTx_OnCommit_Rollback(t *testing.T) {
   498  	db := MustOpenDB()
   499  	defer db.MustClose()
   500  
   501  	var x int
   502  	if err := db.Update(func(tx *bdb.Tx) error {
   503  		tx.OnCommit(func() { x += 1 })
   504  		tx.OnCommit(func() { x += 2 })
   505  		if _, err := tx.CreateBucket([]byte("widgets")); err != nil {
   506  			t.Fatal(err)
   507  		}
   508  		return errors.New("rollback this commit")
   509  	}); err == nil || err.Error() != "rollback this commit" {
   510  		t.Fatalf("unexpected error: %s", err)
   511  	} else if x != 0 {
   512  		t.Fatalf("unexpected x: %d", x)
   513  	}
   514  }
   515  
   516  func TestTx_CopyFile(t *testing.T) {
   517  	db := MustOpenDB()
   518  	defer db.MustClose()
   519  
   520  	path := tempfile()
   521  	if err := db.Update(func(tx *bdb.Tx) error {
   522  		b, err := tx.CreateBucket([]byte("widgets"))
   523  		if err != nil {
   524  			t.Fatal(err)
   525  		}
   526  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   527  			t.Fatal(err)
   528  		}
   529  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   530  			t.Fatal(err)
   531  		}
   532  		return nil
   533  	}); err != nil {
   534  		t.Fatal(err)
   535  	}
   536  
   537  	if err := db.View(func(tx *bdb.Tx) error {
   538  		return tx.CopyFile(path, 0600)
   539  	}); err != nil {
   540  		t.Fatal(err)
   541  	}
   542  
   543  	db2, err := bdb.Open(path, nil)
   544  	if err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	if err := db2.View(func(tx *bdb.Tx) error {
   549  		if v := tx.Bucket([]byte("widgets")).Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) {
   550  			t.Fatalf("unexpected value: %v", v)
   551  		}
   552  		if v := tx.Bucket([]byte("widgets")).Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) {
   553  			t.Fatalf("unexpected value: %v", v)
   554  		}
   555  		return nil
   556  	}); err != nil {
   557  		t.Fatal(err)
   558  	}
   559  
   560  	if err := db2.Close(); err != nil {
   561  		t.Fatal(err)
   562  	}
   563  }
   564  
   565  type failWriterError struct{}
   566  
   567  func (failWriterError) Error() string {
   568  	return "error injected for tests"
   569  }
   570  
   571  type failWriter struct {
   572  	After int
   573  }
   574  
   575  func (f *failWriter) Write(p []byte) (n int, err error) {
   576  	n = len(p)
   577  	if n > f.After {
   578  		n = f.After
   579  		err = failWriterError{}
   580  	}
   581  	f.After -= n
   582  	return n, err
   583  }
   584  
   585  func TestTx_CopyFile_Error_Meta(t *testing.T) {
   586  	db := MustOpenDB()
   587  	defer db.MustClose()
   588  	if err := db.Update(func(tx *bdb.Tx) error {
   589  		b, err := tx.CreateBucket([]byte("widgets"))
   590  		if err != nil {
   591  			t.Fatal(err)
   592  		}
   593  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   594  			t.Fatal(err)
   595  		}
   596  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   597  			t.Fatal(err)
   598  		}
   599  		return nil
   600  	}); err != nil {
   601  		t.Fatal(err)
   602  	}
   603  
   604  	if err := db.View(func(tx *bdb.Tx) error {
   605  		return tx.Copy(&failWriter{})
   606  	}); err == nil || err.Error() != "meta 0 copy: error injected for tests" {
   607  		t.Fatalf("unexpected error: %v", err)
   608  	}
   609  }
   610  
   611  func TestTx_CopyFile_Error_Normal(t *testing.T) {
   612  	db := MustOpenDB()
   613  	defer db.MustClose()
   614  	if err := db.Update(func(tx *bdb.Tx) error {
   615  		b, err := tx.CreateBucket([]byte("widgets"))
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   620  			t.Fatal(err)
   621  		}
   622  		if err := b.Put([]byte("baz"), []byte("bat")); err != nil {
   623  			t.Fatal(err)
   624  		}
   625  		return nil
   626  	}); err != nil {
   627  		t.Fatal(err)
   628  	}
   629  
   630  	if err := db.View(func(tx *bdb.Tx) error {
   631  		return tx.Copy(&failWriter{3 * db.Info().PageSize})
   632  	}); err == nil || err.Error() != "error injected for tests" {
   633  		t.Fatalf("unexpected error: %v", err)
   634  	}
   635  }
   636  
   637  func TestTx_Rollback(t *testing.T) {
   638  	for _, isSyncFreelist := range []bool{false, true} {
   639  		db, err := bdb.Open(tempfile(), nil)
   640  		if err != nil {
   641  			log.Fatal(err)
   642  		}
   643  		defer os.Remove(db.Path())
   644  		db.NoFreelistSync = isSyncFreelist
   645  
   646  		tx, err := db.Begin(true)
   647  		if err != nil {
   648  			t.Fatalf("Error starting tx: %v", err)
   649  		}
   650  		bucket := []byte("mybucket")
   651  		if _, err := tx.CreateBucket(bucket); err != nil {
   652  			t.Fatalf("Error creating bucket: %v", err)
   653  		}
   654  		if err := tx.Commit(); err != nil {
   655  			t.Fatalf("Error on commit: %v", err)
   656  		}
   657  
   658  		tx, err = db.Begin(true)
   659  		if err != nil {
   660  			t.Fatalf("Error starting tx: %v", err)
   661  		}
   662  		b := tx.Bucket(bucket)
   663  		if err := b.Put([]byte("k"), []byte("v")); err != nil {
   664  			t.Fatalf("Error on put: %v", err)
   665  		}
   666  
   667  		if err := tx.Rollback(); err != nil {
   668  			t.Fatalf("Error on rollback: %v", err)
   669  		}
   670  
   671  		tx, err = db.Begin(false)
   672  		if err != nil {
   673  			t.Fatalf("Error starting tx: %v", err)
   674  		}
   675  		b = tx.Bucket(bucket)
   676  		if v := b.Get([]byte("k")); v != nil {
   677  			t.Fatalf("Value for k should not have been stored")
   678  		}
   679  		if err := tx.Rollback(); err != nil {
   680  			t.Fatalf("Error on rollback: %v", err)
   681  		}
   682  
   683  	}
   684  }
   685  
   686  func TestTx_releaseRange(t *testing.T) {
   687  	opts := options.DefaultBdbOptions
   688  	opts.InitialMmapSize = os.Getpagesize() * 100
   689  	db := MustOpenWithOption(opts)
   690  	defer db.MustClose()
   691  
   692  	bucket := "bucket"
   693  
   694  	put := func(key, value string) {
   695  		if err := db.Update(func(tx *bdb.Tx) error {
   696  			b, err := tx.CreateBucketIfNotExists([]byte(bucket))
   697  			if err != nil {
   698  				t.Fatal(err)
   699  			}
   700  			return b.Put([]byte(key), []byte(value))
   701  		}); err != nil {
   702  			t.Fatal(err)
   703  		}
   704  	}
   705  
   706  	del := func(key string) {
   707  		if err := db.Update(func(tx *bdb.Tx) error {
   708  			b, err := tx.CreateBucketIfNotExists([]byte(bucket))
   709  			if err != nil {
   710  				t.Fatal(err)
   711  			}
   712  			return b.Delete([]byte(key))
   713  		}); err != nil {
   714  			t.Fatal(err)
   715  		}
   716  	}
   717  
   718  	getWithTxn := func(txn *bdb.Tx, key string) []byte {
   719  		return txn.Bucket([]byte(bucket)).Get([]byte(key))
   720  	}
   721  
   722  	openReadTxn := func() *bdb.Tx {
   723  		readTx, err := db.Begin(false)
   724  		if err != nil {
   725  			t.Fatal(err)
   726  		}
   727  		return readTx
   728  	}
   729  
   730  	checkWithReadTxn := func(txn *bdb.Tx, key string, wantValue []byte) {
   731  		value := getWithTxn(txn, key)
   732  		if !bytes.Equal(value, wantValue) {
   733  			t.Errorf("Wanted value to be %s for key %s, but got %s", wantValue, key, string(value))
   734  		}
   735  	}
   736  
   737  	rollback := func(txn *bdb.Tx) {
   738  		if err := txn.Rollback(); err != nil {
   739  			t.Fatal(err)
   740  		}
   741  	}
   742  
   743  	put("k1", "v1")
   744  	rtx1 := openReadTxn()
   745  	put("k2", "v2")
   746  	hold1 := openReadTxn()
   747  	put("k3", "v3")
   748  	hold2 := openReadTxn()
   749  	del("k3")
   750  	rtx2 := openReadTxn()
   751  	del("k1")
   752  	hold3 := openReadTxn()
   753  	del("k2")
   754  	hold4 := openReadTxn()
   755  	put("k4", "v4")
   756  	hold5 := openReadTxn()
   757  
   758  	rollback(hold1)
   759  	rollback(hold2)
   760  	rollback(hold3)
   761  	rollback(hold4)
   762  	rollback(hold5)
   763  
   764  	put("k4", "v4")
   765  
   766  	checkWithReadTxn(rtx1, "k1", []byte("v1"))
   767  	checkWithReadTxn(rtx2, "k2", []byte("v2"))
   768  	rollback(rtx1)
   769  	rollback(rtx2)
   770  
   771  	rtx7 := openReadTxn()
   772  	checkWithReadTxn(rtx7, "k1", nil)
   773  	checkWithReadTxn(rtx7, "k2", nil)
   774  	checkWithReadTxn(rtx7, "k3", nil)
   775  	checkWithReadTxn(rtx7, "k4", []byte("v4"))
   776  	rollback(rtx7)
   777  }
   778  
   779  func ExampleTx_Rollback() {
   780  	db, err := bdb.Open(tempfile(), nil)
   781  	if err != nil {
   782  		log.Fatal(err)
   783  	}
   784  	defer os.Remove(db.Path())
   785  
   786  	if err := db.Update(func(tx *bdb.Tx) error {
   787  		_, err := tx.CreateBucket([]byte("widgets"))
   788  		return err
   789  	}); err != nil {
   790  		log.Fatal(err)
   791  	}
   792  
   793  	if err := db.Update(func(tx *bdb.Tx) error {
   794  		return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
   795  	}); err != nil {
   796  		log.Fatal(err)
   797  	}
   798  
   799  	tx, err := db.Begin(true)
   800  	if err != nil {
   801  		log.Fatal(err)
   802  	}
   803  	b := tx.Bucket([]byte("widgets"))
   804  	if err := b.Put([]byte("foo"), []byte("baz")); err != nil {
   805  		log.Fatal(err)
   806  	}
   807  	if err := tx.Rollback(); err != nil {
   808  		log.Fatal(err)
   809  	}
   810  
   811  	if err := db.View(func(tx *bdb.Tx) error {
   812  		value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
   813  		fmt.Printf("The value for 'foo' is still: %s\n", value)
   814  		return nil
   815  	}); err != nil {
   816  		log.Fatal(err)
   817  	}
   818  
   819  	if err := db.Close(); err != nil {
   820  		log.Fatal(err)
   821  	}
   822  }
   823  
   824  func ExampleTx_CopyFile() {
   825  	db, err := bdb.Open(tempfile(), nil)
   826  	if err != nil {
   827  		log.Fatal(err)
   828  	}
   829  	defer os.Remove(db.Path())
   830  
   831  	if err := db.Update(func(tx *bdb.Tx) error {
   832  		b, err := tx.CreateBucket([]byte("widgets"))
   833  		if err != nil {
   834  			return err
   835  		}
   836  		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
   837  			return err
   838  		}
   839  		return nil
   840  	}); err != nil {
   841  		log.Fatal(err)
   842  	}
   843  
   844  	toFile := tempfile()
   845  	if err := db.View(func(tx *bdb.Tx) error {
   846  		return tx.CopyFile(toFile, 0666)
   847  	}); err != nil {
   848  		log.Fatal(err)
   849  	}
   850  	defer os.Remove(toFile)
   851  
   852  	db2, err := bdb.Open(toFile, nil)
   853  	if err != nil {
   854  		log.Fatal(err)
   855  	}
   856  
   857  	if err := db2.View(func(tx *bdb.Tx) error {
   858  		value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
   859  		fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
   860  		return nil
   861  	}); err != nil {
   862  		log.Fatal(err)
   863  	}
   864  
   865  	if err := db.Close(); err != nil {
   866  		log.Fatal(err)
   867  	}
   868  
   869  	if err := db2.Close(); err != nil {
   870  		log.Fatal(err)
   871  	}
   872  }