github.com/bhojpur/cache@v0.0.4/pkg/memory/tx_test.go (about)

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