// TestRocksDBCompaction verifies that a garbage collector can be // installed on a RocksDB engine and will properly compact entries // response cache and transaction entries. func TestRocksDBCompaction(t *testing.T) { gob.Register(proto.Timestamp{}) loc := util.CreateTempDirectory() rocksdb := NewRocksDB(proto.Attributes{Attrs: []string{"ssd"}}, loc) rocksdb.SetGCTimeouts(func() (minTxnTS, minRCacheTS int64) { minTxnTS = 1 minRCacheTS = 2 return }) err := rocksdb.Start() if err != nil { t.Fatalf("could not create new rocksdb db instance at %s: %v", loc, err) } defer func(t *testing.T) { rocksdb.Close() if err := rocksdb.Destroy(); err != nil { t.Errorf("could not delete rocksdb db at %s: %v", loc, err) } }(t) rcPre := KeyLocalRangeResponseCachePrefix txnPre := KeyLocalTransactionPrefix // Write a two transaction values and two response cache values such // that exactly one of each should be GC'd based on our GC timeouts. batch := []interface{}{ // TODO(spencer): use Transaction and Response protobufs here. BatchPut{Key: MakeKey(rcPre, Key("a")), Value: encodePutResponse(makeTS(2, 0), t)}, BatchPut{Key: MakeKey(rcPre, Key("b")), Value: encodePutResponse(makeTS(3, 0), t)}, BatchPut{Key: MakeKey(txnPre, Key("a")), Value: encodeTransaction(makeTS(1, 0), t)}, BatchPut{Key: MakeKey(txnPre, Key("b")), Value: encodeTransaction(makeTS(2, 0), t)}, } if err := rocksdb.WriteBatch(batch); err != nil { t.Fatal(err) } // Compact range and scan remaining values to compare. rocksdb.CompactRange(nil, nil) keyvals, err := rocksdb.Scan(KeyMin, KeyMax, 0) if err != nil { t.Fatalf("could not run scan: %v", err) } var keys []Key for _, kv := range keyvals { keys = append(keys, kv.Key) } expKeys := []Key{ MakeKey(rcPre, Key("b")), MakeKey(txnPre, Key("b")), } if !reflect.DeepEqual(expKeys, keys) { t.Errorf("expected keys %s, got keys %s", expKeys, keys) } }
// TestResponseCacheGC verifies that response cache entries are // garbage collected periodically. func TestResponseCacheGC(t *testing.T) { loc := util.CreateTempDirectory() rocksdb := engine.NewRocksDB(proto.Attributes{Attrs: []string{"ssd"}}, loc) if err := rocksdb.Start(); err != nil { t.Fatalf("could not create new rocksdb db instance at %s: %v", loc, err) } defer func(t *testing.T) { rocksdb.Close() if err := rocksdb.Destroy(); err != nil { t.Errorf("could not destroy rocksdb db at %s: %v", loc, err) } }(t) rc := NewResponseCache(1, rocksdb) cmdID := makeCmdID(1, 1) // Add response for cmdID with timestamp at time=1ns. copyIncR := incR copyIncR.Timestamp.WallTime = 1 if err := rc.PutResponse(cmdID, ©IncR); err != nil { t.Fatalf("unexpected error putting responpse: %v", err) } rocksdb.SetGCTimeouts(func() (minTxnTS, minRCacheTS int64) { minRCacheTS = 0 // avoids GC return }) rocksdb.CompactRange(nil, nil) val := proto.IncrementResponse{} if ok, err := rc.GetResponse(cmdID, &val); !ok || err != nil || val.NewValue != 1 { t.Fatalf("unexpected response or error: %t, %v, %+v", ok, err, val) } // Now set minRCacheTS to 1, which will GC. rocksdb.SetGCTimeouts(func() (minTxnTS, minRCacheTS int64) { minRCacheTS = 1 return }) rocksdb.CompactRange(nil, nil) if ok, err := rc.GetResponse(cmdID, &val); ok || err != nil { t.Errorf("unexpected response or error: %t, %v", ok, err) } }
// TestRocksDBCompaction verifies that a garbage collector can be // installed on a RocksDB engine and will properly compact response // cache and transaction entries. func TestRocksDBCompaction(t *testing.T) { gob.Register(proto.Timestamp{}) loc := util.CreateTempDirectory() rocksdb := newMemRocksDB(proto.Attributes{Attrs: []string{"ssd"}}, testCacheSize) err := rocksdb.Start() if err != nil { t.Fatalf("could not create new rocksdb db instance at %s: %v", loc, err) } rocksdb.SetGCTimeouts(1, 2) defer func(t *testing.T) { rocksdb.Stop() if err := rocksdb.Destroy(); err != nil { t.Errorf("could not delete rocksdb db at %s: %v", loc, err) } }(t) cmdID := &proto.ClientCmdID{WallTime: 1, Random: 1} // Write two transaction values and two response cache values such // that exactly one of each should be GC'd based on our GC timeouts. kvs := []proto.KeyValue{ { Key: ResponseCacheKey(1, cmdID), Value: proto.Value{Bytes: encodePutResponse(makeTS(2, 0), t)}, }, { Key: ResponseCacheKey(2, cmdID), Value: proto.Value{Bytes: encodePutResponse(makeTS(3, 0), t)}, }, { Key: TransactionKey(proto.Key("a"), proto.Key(uuid.New())), Value: proto.Value{Bytes: encodeTransaction(makeTS(1, 0), t)}, }, { Key: TransactionKey(proto.Key("b"), proto.Key(uuid.New())), Value: proto.Value{Bytes: encodeTransaction(makeTS(2, 0), t)}, }, } for _, kv := range kvs { if err := MVCCPut(rocksdb, nil, kv.Key, proto.ZeroTimestamp, kv.Value, nil); err != nil { t.Fatal(err) } } // Compact range and scan remaining values to compare. rocksdb.CompactRange(nil, nil) actualKVs, err := MVCCScan(rocksdb, KeyMin, KeyMax, 0, proto.ZeroTimestamp, nil) if err != nil { t.Fatalf("could not run scan: %v", err) } var keys []proto.Key for _, kv := range actualKVs { keys = append(keys, kv.Key) } expKeys := []proto.Key{ kvs[1].Key, kvs[3].Key, } if !reflect.DeepEqual(expKeys, keys) { t.Errorf("expected keys %+v, got keys %+v", expKeys, keys) } }