blob: 5eee85750645b883876eaeadfa009451fa81b1fc [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package transactions
import (
"container/list"
"testing"
"v.io/x/ref/services/syncbase/store"
)
// package syncbase/store/memstore imports syncbase/transactions, so we can't
// use memstore as the underlying BatchStore. Instead, we use a mockBatchStore
// with all methods stubbed out.
type mockBatchStore struct{}
func (mbs mockBatchStore) Get(key, valbuf []byte) ([]byte, error) {
return nil, nil
}
func (mbs mockBatchStore) Scan(start, limit []byte) store.Stream {
return nil
}
func (mbs mockBatchStore) WriteBatch(batch ...WriteOp) error {
return nil
}
func (mbs mockBatchStore) Close() error {
return nil
}
func (mbs mockBatchStore) NewSnapshot() store.Snapshot {
return mockSnapshot{}
}
type mockSnapshot struct {
*store.SnapshotSpecImpl
}
func (ms mockSnapshot) Get(key, valbuf []byte) ([]byte, error) {
return nil, nil
}
func (ms mockSnapshot) Scan(start, limit []byte) store.Stream {
return nil
}
func (ms mockSnapshot) Abort() error {
return nil
}
// TestGcTransactions tests that calling gcTransactions() garbage collects all
// committed transactions.
func TestGcTransactions(t *testing.T) {
mg := &manager{
BatchStore: mockBatchStore{},
events: list.New(),
txTable: newTrie(),
}
// Make a Put to the DB. Since there are no open transactions this should
// not add to the events queue.
if err := mg.Put([]byte("foo"), []byte("foo")); err != nil {
t.Fatal(err)
}
if want, got := 0, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Start a new transaction and leave it open. This will add a single event
// to the queue.
tx1 := mg.NewTransaction()
if want, got := 1, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Make two more Puts to the DB, which will add a two events to the queue.
if err := mg.Put([]byte("bar"), []byte("bar")); err != nil {
t.Fatal(err)
}
if err := mg.Put([]byte("baz"), []byte("baz")); err != nil {
t.Fatal(err)
}
if want, got := 3, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Garbage collection should not remove anything, since tx1 is still open.
mg.gcTransactions()
if want, got := 3, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Start a second transaction and leave it open. This will add a single event
// to the queue.
tx2 := mg.NewTransaction()
if want, got := 4, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Do a Put and Commit on tx1. tx1 will be removed from the queue, but a
// commit event added to the end, so the length is not changed.
if err := tx1.Put([]byte("qux"), []byte("qux")); err != nil {
t.Fatal(err)
}
if err := tx1.Commit(); err != nil {
t.Fatal(err)
}
if want, got := 4, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Garbage collection should remove the two recent Puts.
mg.gcTransactions()
if want, got := 2, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// Abort tx2, which will remove it from the queue.
if err := tx2.Abort(); err != nil {
t.Fatal(err)
}
if want, got := 1, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
// A final garbage collection will remove the commit event for tx1.
mg.gcTransactions()
if want, got := 0, mg.events.Len(); want != got {
t.Errorf("wanted mg.events.Len() to be %v but got %v", want, got)
}
}