github.com/clly/consul@v1.4.5/agent/consul/state/graveyard_test.go (about)

     1  package state
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  func TestGraveyard_Lifecycle(t *testing.T) {
    10  	g := NewGraveyard(nil)
    11  
    12  	// Make a donor state store to steal its database, all prepared for
    13  	// tombstones.
    14  	s := testStateStore(t)
    15  
    16  	// Create some tombstones.
    17  	func() {
    18  		tx := s.db.Txn(true)
    19  		defer tx.Abort()
    20  
    21  		if err := g.InsertTxn(tx, "foo/in/the/house", 2); err != nil {
    22  			t.Fatalf("err: %s", err)
    23  		}
    24  		if err := g.InsertTxn(tx, "foo/bar/baz", 5); err != nil {
    25  			t.Fatalf("err: %s", err)
    26  		}
    27  		if err := g.InsertTxn(tx, "foo/bar/zoo", 8); err != nil {
    28  			t.Fatalf("err: %s", err)
    29  		}
    30  		if err := g.InsertTxn(tx, "some/other/path", 9); err != nil {
    31  			t.Fatalf("err: %s", err)
    32  		}
    33  		tx.Commit()
    34  	}()
    35  
    36  	// Check some prefixes.
    37  	func() {
    38  		tx := s.db.Txn(false)
    39  		defer tx.Abort()
    40  
    41  		if idx, err := g.GetMaxIndexTxn(tx, "foo"); idx != 8 || err != nil {
    42  			t.Fatalf("bad: %d (%s)", idx, err)
    43  		}
    44  		if idx, err := g.GetMaxIndexTxn(tx, "foo/in/the/house"); idx != 2 || err != nil {
    45  			t.Fatalf("bad: %d (%s)", idx, err)
    46  		}
    47  		if idx, err := g.GetMaxIndexTxn(tx, "foo/bar/baz"); idx != 5 || err != nil {
    48  			t.Fatalf("bad: %d (%s)", idx, err)
    49  		}
    50  		if idx, err := g.GetMaxIndexTxn(tx, "foo/bar/zoo"); idx != 8 || err != nil {
    51  			t.Fatalf("bad: %d (%s)", idx, err)
    52  		}
    53  		if idx, err := g.GetMaxIndexTxn(tx, "some/other/path"); idx != 9 || err != nil {
    54  			t.Fatalf("bad: %d (%s)", idx, err)
    55  		}
    56  		if idx, err := g.GetMaxIndexTxn(tx, ""); idx != 9 || err != nil {
    57  			t.Fatalf("bad: %d (%s)", idx, err)
    58  		}
    59  		if idx, err := g.GetMaxIndexTxn(tx, "nope"); idx != 0 || err != nil {
    60  			t.Fatalf("bad: %d (%s)", idx, err)
    61  		}
    62  	}()
    63  
    64  	// Reap some tombstones.
    65  	func() {
    66  		tx := s.db.Txn(true)
    67  		defer tx.Abort()
    68  
    69  		if err := g.ReapTxn(tx, 6); err != nil {
    70  			t.Fatalf("err: %s", err)
    71  		}
    72  		tx.Commit()
    73  	}()
    74  
    75  	// Check prefixes to see that the reap took effect at the right index.
    76  	func() {
    77  		tx := s.db.Txn(false)
    78  		defer tx.Abort()
    79  
    80  		if idx, err := g.GetMaxIndexTxn(tx, "foo"); idx != 8 || err != nil {
    81  			t.Fatalf("bad: %d (%s)", idx, err)
    82  		}
    83  		if idx, err := g.GetMaxIndexTxn(tx, "foo/in/the/house"); idx != 0 || err != nil {
    84  			t.Fatalf("bad: %d (%s)", idx, err)
    85  		}
    86  		if idx, err := g.GetMaxIndexTxn(tx, "foo/bar/baz"); idx != 0 || err != nil {
    87  			t.Fatalf("bad: %d (%s)", idx, err)
    88  		}
    89  		if idx, err := g.GetMaxIndexTxn(tx, "foo/bar/zoo"); idx != 8 || err != nil {
    90  			t.Fatalf("bad: %d (%s)", idx, err)
    91  		}
    92  		if idx, err := g.GetMaxIndexTxn(tx, "some/other/path"); idx != 9 || err != nil {
    93  			t.Fatalf("bad: %d (%s)", idx, err)
    94  		}
    95  		if idx, err := g.GetMaxIndexTxn(tx, ""); idx != 9 || err != nil {
    96  			t.Fatalf("bad: %d (%s)", idx, err)
    97  		}
    98  		if idx, err := g.GetMaxIndexTxn(tx, "nope"); idx != 0 || err != nil {
    99  			t.Fatalf("bad: %d (%s)", idx, err)
   100  		}
   101  	}()
   102  }
   103  
   104  func TestGraveyard_GC_Trigger(t *testing.T) {
   105  	// Set up a fast-expiring GC.
   106  	ttl, granularity := 100*time.Millisecond, 20*time.Millisecond
   107  	gc, err := NewTombstoneGC(ttl, granularity)
   108  	if err != nil {
   109  		t.Fatalf("err: %s", err)
   110  	}
   111  
   112  	// Make a new graveyard and assign the GC.
   113  	g := NewGraveyard(gc)
   114  	gc.SetEnabled(true)
   115  
   116  	// Make sure there's nothing already expiring.
   117  	if gc.PendingExpiration() {
   118  		t.Fatalf("should not have any expiring items")
   119  	}
   120  
   121  	// Create a tombstone but abort the transaction, this should not trigger
   122  	// GC.
   123  	s := testStateStore(t)
   124  	func() {
   125  		tx := s.db.Txn(true)
   126  		defer tx.Abort()
   127  
   128  		if err := g.InsertTxn(tx, "foo/in/the/house", 2); err != nil {
   129  			t.Fatalf("err: %s", err)
   130  		}
   131  	}()
   132  
   133  	// Make sure there's nothing already expiring.
   134  	if gc.PendingExpiration() {
   135  		t.Fatalf("should not have any expiring items")
   136  	}
   137  
   138  	// Now commit.
   139  	func() {
   140  		tx := s.db.Txn(true)
   141  		defer tx.Abort()
   142  
   143  		if err := g.InsertTxn(tx, "foo/in/the/house", 2); err != nil {
   144  			t.Fatalf("err: %s", err)
   145  		}
   146  		tx.Commit()
   147  	}()
   148  
   149  	// Make sure the GC got hinted.
   150  	if !gc.PendingExpiration() {
   151  		t.Fatalf("should have a pending expiration")
   152  	}
   153  
   154  	// Make sure the index looks good.
   155  	select {
   156  	case idx := <-gc.ExpireCh():
   157  		if idx != 2 {
   158  			t.Fatalf("bad index: %d", idx)
   159  		}
   160  	case <-time.After(2 * ttl):
   161  		t.Fatalf("should have gotten an expire notice")
   162  	}
   163  }
   164  
   165  func TestGraveyard_Snapshot_Restore(t *testing.T) {
   166  	g := NewGraveyard(nil)
   167  
   168  	// Make a donor state store to steal its database, all prepared for
   169  	// tombstones.
   170  	s := testStateStore(t)
   171  
   172  	// Create some tombstones.
   173  	func() {
   174  		tx := s.db.Txn(true)
   175  		defer tx.Abort()
   176  
   177  		if err := g.InsertTxn(tx, "foo/in/the/house", 2); err != nil {
   178  			t.Fatalf("err: %s", err)
   179  		}
   180  		if err := g.InsertTxn(tx, "foo/bar/baz", 5); err != nil {
   181  			t.Fatalf("err: %s", err)
   182  		}
   183  		if err := g.InsertTxn(tx, "foo/bar/zoo", 8); err != nil {
   184  			t.Fatalf("err: %s", err)
   185  		}
   186  		if err := g.InsertTxn(tx, "some/other/path", 9); err != nil {
   187  			t.Fatalf("err: %s", err)
   188  		}
   189  		tx.Commit()
   190  	}()
   191  
   192  	// Verify the index was set correctly.
   193  	if idx := s.maxIndex("tombstones"); idx != 9 {
   194  		t.Fatalf("bad index: %d", idx)
   195  	}
   196  
   197  	// Dump them as if we are doing a snapshot.
   198  	dump := func() []*Tombstone {
   199  		tx := s.db.Txn(false)
   200  		defer tx.Abort()
   201  
   202  		iter, err := g.DumpTxn(tx)
   203  		if err != nil {
   204  			t.Fatalf("err: %s", err)
   205  		}
   206  		var dump []*Tombstone
   207  		for ti := iter.Next(); ti != nil; ti = iter.Next() {
   208  			dump = append(dump, ti.(*Tombstone))
   209  		}
   210  		return dump
   211  	}()
   212  
   213  	// Verify the dump, which should be ordered by key.
   214  	expected := []*Tombstone{
   215  		&Tombstone{Key: "foo/bar/baz", Index: 5},
   216  		&Tombstone{Key: "foo/bar/zoo", Index: 8},
   217  		&Tombstone{Key: "foo/in/the/house", Index: 2},
   218  		&Tombstone{Key: "some/other/path", Index: 9},
   219  	}
   220  	if !reflect.DeepEqual(dump, expected) {
   221  		t.Fatalf("bad: %v", dump)
   222  	}
   223  
   224  	// Make another state store and restore from the dump.
   225  	func() {
   226  		s := testStateStore(t)
   227  		func() {
   228  			tx := s.db.Txn(true)
   229  			defer tx.Abort()
   230  
   231  			for _, stone := range dump {
   232  				if err := g.RestoreTxn(tx, stone); err != nil {
   233  					t.Fatalf("err: %s", err)
   234  				}
   235  			}
   236  			tx.Commit()
   237  		}()
   238  
   239  		// Verify that the restore works.
   240  		if idx := s.maxIndex("tombstones"); idx != 9 {
   241  			t.Fatalf("bad index: %d", idx)
   242  		}
   243  
   244  		dump := func() []*Tombstone {
   245  			tx := s.db.Txn(false)
   246  			defer tx.Abort()
   247  
   248  			iter, err := g.DumpTxn(tx)
   249  			if err != nil {
   250  				t.Fatalf("err: %s", err)
   251  			}
   252  			var dump []*Tombstone
   253  			for ti := iter.Next(); ti != nil; ti = iter.Next() {
   254  				dump = append(dump, ti.(*Tombstone))
   255  			}
   256  			return dump
   257  		}()
   258  		if !reflect.DeepEqual(dump, expected) {
   259  			t.Fatalf("bad: %v", dump)
   260  		}
   261  	}()
   262  }