blob: 1dc80e7053c9f628c57bf80792fed0e1315adaa2 [file] [log] [blame]
package vsync
// Tests for the Veyron Sync ILog component.
import (
"os"
"reflect"
"testing"
"veyron2/storage"
)
// TestILogStore tests creating a backing file for ILog.
func TestILogStore(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new ILog file %s, err %v", logfile, err)
}
fsize := getFileSize(logfile)
if fsize < 0 {
t.Errorf("Log file %s not created", logfile)
}
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
}
oldfsize := fsize
fsize = getFileSize(logfile)
if fsize <= oldfsize {
t.Errorf("Log file %s not flushed", logfile)
}
if err := log.close(); err != nil {
t.Errorf("Cannot close ILog file %s, err %v", logfile)
}
oldfsize = getFileSize(logfile)
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open existing log file %s, err %v", logfile)
}
fsize = getFileSize(logfile)
if fsize != oldfsize {
t.Errorf("Log file %s size changed across re-open (%d %d)", logfile, fsize, oldfsize)
}
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile)
}
if err := log.close(); err != nil {
t.Errorf("Cannot close ILog file %s, err %v", logfile)
}
}
// TestInvalidLog tests log methods on an invalid (closed) log ptr.
func TestInvalidLog(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new ILog file %s, err %v", logfile, err)
}
if err := log.close(); err != nil {
t.Errorf("Cannot close ILog file %s, err %v", logfile, err)
}
err = log.close()
if err == nil || err != errInvalidLog {
t.Errorf("Close did not fail on a closed log: %v", err)
}
err = log.flush()
if err == nil || err != errInvalidLog {
t.Errorf("Flush did not fail on a closed log: %v", err)
}
err = log.compact()
if err == nil || err != errInvalidLog {
t.Errorf("Compact did not fail on a closed log: %v", err)
}
_, err = log.putLogRec(&LogRec{})
if err == nil || err != errInvalidLog {
t.Errorf("PutLogRec did not fail on a closed log: %v", err)
}
var devid DeviceID = "VeyronPhone"
var gnum GenID = 1
var lsn LSN
_, err = log.getLogRec(devid, gnum, lsn)
if err == nil || err != errInvalidLog {
t.Errorf("GetLogRec did not fail on a closed log: %v", err)
}
if log.hasLogRec(devid, gnum, lsn) {
if err == nil || err != errInvalidLog {
t.Errorf("HasLogRec did not fail on a closed log: %v", err)
}
}
err = log.delLogRec(devid, gnum, lsn)
if err == nil || err != errInvalidLog {
t.Errorf("DelLogRec did not fail on a closed log: %v", err)
}
_, err = log.getGenMetadata(devid, gnum)
if err == nil || err != errInvalidLog {
t.Errorf("GetGenMetadata did not fail on a closed log: %v", err)
}
err = log.delGenMetadata(devid, gnum)
if err == nil || err != errInvalidLog {
t.Errorf("DelGenMetadata did not fail on a closed log: %v", err)
}
err = log.createRemoteGeneration(devid, gnum, &genMetadata{})
if err == nil || err != errInvalidLog {
t.Errorf("CreateRemoteGeneration did not fail on a closed log: %v", err)
}
_, err = log.createLocalGeneration()
if err == nil || err != errInvalidLog {
t.Errorf("CreateLocalGeneration did not fail on a closed log: %v", err)
}
err = log.processWatchRecord(storage.NewID(), 2, []storage.Version{0, 1}, &LogValue{})
if err == nil || err != errInvalidLog {
t.Errorf("ProcessWatchRecord did not fail on a closed log: %v", err)
}
}
// TestPutGetLogHeader tests setting and getting log header across log open/close/reopen.
func TestPutGetLogHeader(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
// In memory head should be initialized.
if log.head.Curgen != 1 || log.head.Curlsn != 0 || log.head.Curorder != 0 {
t.Errorf("First time log create should reset header")
}
// No head should be there in db.
if err = log.getHead(); err == nil {
t.Errorf("getHead() found non-existent head in log file %s, err %v", logfile, err)
}
if log.hasHead() {
t.Errorf("hasHead() found non-existent head in log file %s", logfile)
}
log.head = &iLogHeader{
Curgen: 10,
Curlsn: 100,
Curorder: 1000,
}
if err := log.putHead(); err != nil {
t.Errorf("Cannot put head %v in log file %s, err %v", log.head, logfile, err)
}
// Reset values.
log.head.Curgen = 0
log.head.Curlsn = 0
log.head.Curorder = 0
for i := 0; i < 2; i++ {
if err := log.getHead(); err != nil {
t.Fatalf("getHead() can not find head (i=%d) in log file %s, err %v", i, logfile, err)
}
if !log.hasHead() {
t.Errorf("hasHead() can not find head (i=%d) in log file %s", i, logfile)
}
if log.head.Curgen != 10 && log.head.Curlsn != 100 && log.head.Curorder != 1000 {
t.Errorf("Data mismatch for head (i=%d) in log file %s: %v",
i, logfile, log.head)
}
if i == 0 {
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestPersistLogHeader tests that log header is automatically persisted across log open/close/reopen.
func TestPersistLogHeader(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
// In memory head should be initialized.
if log.head.Curgen != 1 || log.head.Curlsn != 0 || log.head.Curorder != 0 {
t.Errorf("First time log create should reset header")
}
log.head = &iLogHeader{
Curgen: 10,
Curlsn: 100,
Curorder: 1000,
}
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
// In memory head should be initialized from db.
if log.head.Curgen != 10 && log.head.Curlsn != 100 && log.head.Curorder != 1000 {
t.Errorf("Data mismatch for head in log file %s: %v", logfile, log.head)
}
log.head = &iLogHeader{
Curgen: 1000,
Curlsn: 10,
Curorder: 100,
}
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
}
// Reset values.
log.head.Curgen = 0
log.head.Curlsn = 0
log.head.Curorder = 0
if err := log.getHead(); err != nil {
t.Fatalf("getHead() can not find head in log file %s, err %v", logfile, err)
}
// In memory head should be initialized from db.
if log.head.Curgen != 1000 && log.head.Curlsn != 10 && log.head.Curorder != 100 {
t.Errorf("Data mismatch for head in log file %s: %v", logfile, log.head)
}
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestPutGetLogRec tests setting and getting a log record across log open/close/reopen.
func TestPutGetLogRec(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
var devid DeviceID = "VeyronTab"
var gnum GenID = 100
var lsn LSN = 1000
rec, err := log.getLogRec(devid, gnum, lsn)
if err == nil || rec != nil {
t.Errorf("GetLogRec() found non-existent object %s:%d:%d in log file %s: %v, err %v",
devid, gnum, lsn, logfile, rec, err)
}
if log.hasLogRec(devid, gnum, lsn) {
t.Errorf("HasLogRec() found non-existent object %s:%d:%d in log file %s",
devid, gnum, lsn, logfile)
}
objID := storage.NewID()
rec = &LogRec{
DevID: devid,
GNum: gnum,
LSN: lsn,
ObjID: objID,
CurVers: 2,
Parents: []storage.Version{0, 1},
Value: LogValue{},
}
if _, err := log.putLogRec(rec); err != nil {
t.Errorf("Cannot put object %s:%d:%d (%v) in log file %s, err %v", devid, gnum, lsn, rec, logfile, err)
}
for i := 0; i < 2; i++ {
curRec, err := log.getLogRec(devid, gnum, lsn)
if err != nil || curRec == nil {
t.Fatalf("GetLogRec() can not find object %s:%d:%d (i=%d) in log file %s, err %v",
devid, gnum, lsn, i, logfile, err)
}
if !log.hasLogRec(devid, gnum, lsn) {
t.Errorf("HasLogRec() can not find object %s:%d:%d (i=%d) in log file %s",
devid, gnum, lsn, i, logfile)
}
if !reflect.DeepEqual(rec, curRec) {
t.Errorf("Data mismatch for object %s:%d:%d (i=%d) in log file %s: %v instead of %v",
devid, gnum, lsn, i, logfile, curRec, rec)
}
if i == 0 {
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestDelLogRec tests deleting a log record across log open/close/reopen.
func TestDelLogRec(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
var devid DeviceID = "VeyronTab"
var gnum GenID = 100
var lsn LSN = 1000
objID := storage.NewID()
rec := &LogRec{
DevID: devid,
GNum: gnum,
LSN: lsn,
ObjID: objID,
CurVers: 2,
Parents: []storage.Version{0, 1},
Value: LogValue{},
}
if _, err := log.putLogRec(rec); err != nil {
t.Errorf("Cannot put object %s:%d:%d (%v) in log file %s, err %v", devid, gnum, lsn, rec, logfile, err)
}
curRec, err := log.getLogRec(devid, gnum, lsn)
if err != nil || curRec == nil {
t.Fatalf("GetLogRec() can not find object %s:%d:%d in log file %s, err %v",
devid, gnum, lsn, logfile, err)
}
if err := log.delLogRec(devid, gnum, lsn); err != nil {
t.Fatalf("DelLogRec() can not delete object %s:%d:%d in log file %s, err %v",
devid, gnum, lsn, logfile, err)
}
for i := 0; i < 2; i++ {
curRec, err = log.getLogRec(devid, gnum, lsn)
if err == nil || curRec != nil {
t.Fatalf("GetLogRec() finds deleted object %s:%d:%d (i=%d) in log file %s, err %v",
devid, gnum, lsn, i, logfile, err)
}
if log.hasLogRec(devid, gnum, lsn) {
t.Errorf("HasLogRec() finds deleted object %s:%d:%d (i=%d) in log file %s",
devid, gnum, lsn, i, logfile)
}
if i == 0 {
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestPutGetGenMetadata tests setting and getting generation metadata across log open/close/reopen.
func TestPutGetGenMetadata(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
var devid DeviceID = "VeyronTab"
var gnum GenID = 100
val, err := log.getGenMetadata(devid, gnum)
if err == nil || val != nil {
t.Errorf("GetGenMetadata() found non-existent object %s:%d in log file %s: %v, err %v",
devid, gnum, logfile, val, err)
}
if log.hasGenMetadata(devid, gnum) {
t.Errorf("hasGenMetadata() found non-existent object %s:%d in log file %s",
devid, gnum, logfile)
}
val = &genMetadata{Pos: 40, Count: 100, MaxLSN: 99}
if err := log.putGenMetadata(devid, gnum, val); err != nil {
t.Errorf("Cannot put object %s:%d in log file %s, err %v", devid, gnum, logfile, err)
}
for i := 0; i < 2; i++ {
curVal, err := log.getGenMetadata(devid, gnum)
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d (i=%d) in log file %s, err %v",
devid, gnum, i, logfile, err)
}
if !log.hasGenMetadata(devid, gnum) {
t.Errorf("hasGenMetadata() can not find object %s:%d (i=%d) in log file %s",
devid, gnum, i, logfile)
}
if !reflect.DeepEqual(val, curVal) {
t.Errorf("Data mismatch for object %s:%d (i=%d) in log file %s: %v instead of %v",
devid, gnum, i, logfile, curVal, val)
}
if i == 0 {
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestDelGenMetadata tests deleting generation metadata across log open/close/reopen.
func TestDelGenMetadata(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
var devid DeviceID = "VeyronTab"
var gnum GenID = 100
val := &genMetadata{Pos: 40, Count: 100, MaxLSN: 99}
if err := log.putGenMetadata(devid, gnum, val); err != nil {
t.Errorf("Cannot put object %s:%d in log file %s, err %v", devid, gnum, logfile, err)
}
curVal, err := log.getGenMetadata(devid, gnum)
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
devid, gnum, logfile, err)
}
if err := log.delGenMetadata(devid, gnum); err != nil {
t.Fatalf("DelGenMetadata() can not delete object %s:%d in log file %s, err %v",
devid, gnum, logfile, err)
}
for i := 0; i < 2; i++ {
curVal, err := log.getGenMetadata(devid, gnum)
if err == nil || curVal != nil {
t.Fatalf("GetGenMetadata() finds deleted object %s:%d (i=%d) in log file %s, err %v",
devid, gnum, i, logfile, err)
}
if log.hasGenMetadata(devid, gnum) {
t.Errorf("hasGenMetadata() finds deleted object %s:%d (i=%d) in log file %s",
devid, gnum, i, logfile)
}
if i == 0 {
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestPersistLogState tests that generation metadata and record state
// is persisted across log open/close/reopen.
func TestPersistLogState(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
log, err := openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
var devid DeviceID = "VeyronTab"
// Add several generations.
for i := uint32(0); i < 10; i++ {
val := &genMetadata{Pos: i}
if err := log.putGenMetadata(devid, GenID(i+10), val); err != nil {
t.Errorf("Cannot put object %s:%d in log file %s, err %v", devid, i, logfile, err)
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, nil)
if err != nil {
t.Fatalf("Cannot re-open log file %s, err %v", logfile, err)
}
for i := uint32(0); i < 10; i++ {
curVal, err := log.getGenMetadata(devid, GenID(i+10))
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
devid, i, logfile, err)
}
if curVal.Pos != i {
t.Errorf("Data mismatch for object %s:%d in log file %s: %v",
devid, i, logfile, curVal)
}
// Should safely overwrite the same keys.
curVal.Pos = i + 10
if err := log.putGenMetadata(devid, GenID(i+10), curVal); err != nil {
t.Errorf("Cannot put object %s:%d in log file %s, err %v", devid, i, logfile, err)
}
}
for i := uint32(0); i < 10; i++ {
curVal, err := log.getGenMetadata(devid, GenID(i+10))
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
devid, i, logfile, err)
}
if curVal.Pos != (i + 10) {
t.Errorf("Data mismatch for object %s:%d in log file %s: %v, err %v",
devid, i, logfile, curVal, err)
}
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// fillFakeLogRecords fills fake log records for testing purposes.
func (l *iLog) fillFakeLogRecords() {
const num = 10
var parvers []storage.Version
id := storage.NewID()
for i := int(0); i < num; i++ {
// Create a local log record.
curvers := storage.Version(i)
rec, err := l.createLocalLogRec(id, curvers, parvers, &LogValue{})
if err != nil {
return
}
// Insert the new log record into the log.
_, err = l.putLogRec(rec)
if err != nil {
return
}
parvers = []storage.Version{curvers}
}
}
// TestCreateGeneration tests that local log records and local
// generations are created uniquely and remote generations are
// correctly inserted in log order.
func TestCreateGeneration(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
s := &syncd{id: "VeyronTab"}
log, err := openILog(logfile, s)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
if g, err := log.createLocalGeneration(); err != errNoUpdates {
t.Errorf("Should not find local updates gen %d with error %v", g, err)
}
const num = 10
var parvers []storage.Version
id := storage.NewID()
for i := int(0); i < num; i++ {
// Create a local log record.
curvers := storage.Version(i)
rec, err := log.createLocalLogRec(id, curvers, parvers, &LogValue{})
if err != nil {
t.Fatalf("Cannot create local log rec ObjID: %v Current: %s Parents: %v Error: %v",
id, curvers, parvers, err)
}
temprec := &LogRec{
DevID: log.s.id,
GNum: GenID(1),
LSN: LSN(i),
ObjID: id,
CurVers: curvers,
Parents: parvers,
Value: LogValue{},
}
// Verify that the log record has the right values.
if !reflect.DeepEqual(rec, temprec) {
t.Errorf("Data mismtach in log record %v instead of %v", rec, temprec)
}
// Insert the new log record into the log.
_, err = log.putLogRec(rec)
if err != nil {
t.Errorf("Cannot put log record:: failed with err %v", err)
}
parvers = []storage.Version{curvers}
}
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
log, err = openILog(logfile, s)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
if log.head.Curgen != 1 || log.head.Curlsn != num {
t.Errorf("Data mismatch in log header %v", log.head)
}
g, err := log.createLocalGeneration()
if g != 1 || err != nil {
t.Errorf("Could not create local generation gen %d with error %v", g, err)
}
curVal, err := log.getGenMetadata(log.s.id, g)
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
log.s.id, g, logfile, err)
}
expVal := &genMetadata{Pos: 0, Count: 10, MaxLSN: 9}
if !reflect.DeepEqual(expVal, curVal) {
t.Errorf("Data mismatch for object %s:%d in log file %s: %v instead of %v",
log.s.id, g, logfile, curVal, expVal)
}
if log.head.Curgen != 2 || log.head.Curlsn != 0 || log.head.Curorder != 1 {
t.Errorf("Data mismatch in log header %v", log.head)
}
if g, err := log.createLocalGeneration(); err != errNoUpdates {
t.Errorf("Should not find local updates gen %d with error %v", g, err)
}
// Populate one more generation.
log.fillFakeLogRecords()
g, err = log.createLocalGeneration()
if g != 2 || err != nil {
t.Errorf("Could not create local generation gen %d with error %v", g, err)
}
// Create a remote generation.
expGen := &genMetadata{Count: 1, MaxLSN: 50}
if err = log.createRemoteGeneration("VeyronPhone", 1, expGen); err == nil {
t.Errorf("Remote generation create should have failed", g, err)
}
expGen.MaxLSN = 0
if err = log.createRemoteGeneration("VeyronPhone", 1, expGen); err != nil {
t.Errorf("createRemoteGeneration failed with err %v", err)
}
if expGen.Pos != 2 {
t.Errorf("createRemoteGeneration created incorrect log order %d", expGen.Pos)
}
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
// Reopen the log and ensure that all log records for generations exist.
// Also ensure that generation metadata is accurate.
log, err = openILog(logfile, s)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
for g := GenID(1); g < 3; g++ {
// Check all log records.
for i := LSN(0); i < 10; i++ {
curRec, err := log.getLogRec(log.s.id, g, i)
if err != nil || curRec == nil {
t.Fatalf("GetLogRec() can not find object %s:%d:%d in log file %s, err %v",
log.s.id, g, i, logfile, err)
}
}
// Check generation metadata.
curVal, err := log.getGenMetadata(log.s.id, g)
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
log.s.id, g, logfile, err)
}
expVal.Pos = uint32(g - 1)
if !reflect.DeepEqual(expVal, curVal) {
t.Errorf("Data mismatch for object %s:%d in log file %s: %v instead of %v",
log.s.id, g, logfile, curVal, expVal)
}
}
// Check remote generation metadata.
curVal, err = log.getGenMetadata("VeyronPhone", 1)
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object in log file %s, err %v",
logfile, err)
}
if !reflect.DeepEqual(expGen, curVal) {
t.Errorf("Data mismatch for object in log file %s: %v instead of %v",
logfile, curVal, expGen)
}
if log.head.Curgen != 3 || log.head.Curlsn != 0 || log.head.Curorder != 3 {
t.Errorf("Data mismatch in log header %v", log.head)
}
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestProcessWatchRecord tests that local updates are correctly handled.
// Commands are in file testdata/local-init-00.sync.
func TestProcessWatchRecord(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
dagfile := getFileName()
defer os.Remove(dagfile)
var err error
s := &syncd{id: "VeyronTab"}
if s.dag, err = openDAG(dagfile); err != nil {
t.Fatalf("Cannot open new dag file %s, err %v", logfile, err)
}
log, err := openILog(logfile, s)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
if _, err = logReplayCommands(log, "local-init-00.sync"); err != nil {
t.Error(err)
}
if log.head.Curgen != 1 || log.head.Curlsn != 3 || log.head.Curorder != 0 {
t.Errorf("Data mismatch in log header %v", log.head)
}
objid, err := strToObjID("12345")
if err != nil {
t.Errorf("Could not create objid %v", err)
}
// Check all log records.
for i := LSN(0); i < 3; i++ {
curRec, err := log.getLogRec(log.s.id, GenID(1), i)
if err != nil || curRec == nil {
t.Fatalf("GetLogRec() can not find object %s:%d:%d in log file %s, err %v",
log.s.id, 0, i, logfile, err)
}
if curRec.ObjID != objid {
t.Errorf("Data mismatch in log record %v", curRec)
}
}
// Verify DAG state.
if head, err := log.s.dag.getHead(objid); err != nil || head != 2 {
t.Errorf("Invalid object %d head in DAG %s, err %v", objid, head, err)
}
s.dag.flush()
s.dag.close()
if err = log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}
// TestILogCompact tests compacting of ilog's kvdb file.
func TestILogCompact(t *testing.T) {
logfile := getFileName()
defer os.Remove(logfile)
s := &syncd{id: "VeyronTab"}
log, err := openILog(logfile, s)
if err != nil {
t.Fatalf("Cannot open new log file %s, err %v", logfile, err)
}
// Put some data in "records" table.
log.fillFakeLogRecords()
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
}
// Put some data in "gens" table.
for i := uint32(0); i < 10; i++ {
val := &genMetadata{Pos: i, Count: uint64(i + 100)}
if err := log.putGenMetadata(s.id, GenID(i+10), val); err != nil {
t.Errorf("Cannot put object %s:%d in log file %s, err %v", s.id, i, logfile, err)
}
// Flush immediately to let the kvdb file grow.
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
}
}
// Put some data in "header" table.
log.head = &iLogHeader{
Curgen: 1000,
Curlsn: 10,
Curorder: 100,
}
if err := log.flush(); err != nil {
t.Errorf("Cannot flush ILog file %s, err %v", logfile, err)
}
// Get size before compaction.
oldSize := getFileSize(logfile)
if oldSize < 0 {
t.Fatalf("Log file %s not created", logfile)
}
if err := log.compact(); err != nil {
t.Errorf("Cannot compact ILog file %s, err %v", logfile, err)
}
// Verify size of kvdb file is reduced.
size := getFileSize(logfile)
if size < 0 {
t.Fatalf("ILog file %s not created", logfile)
}
if size > oldSize {
t.Fatalf("ILog file %s not compacted", logfile)
}
// Check data exists after compaction.
for i := LSN(0); i < 10; i++ {
curRec, err := log.getLogRec(log.s.id, GenID(1), i)
if err != nil || curRec == nil {
t.Fatalf("GetLogRec() can not find object %s:1:%d in log file %s, err %v",
log.s.id, i, logfile, err)
}
if curRec.CurVers != storage.Version(i) {
t.Errorf("Data mismatch for logrec %s:1:%d in log file %s: %v",
log.s.id, i, logfile, curRec)
}
}
for i := uint32(0); i < 10; i++ {
curVal, err := log.getGenMetadata(log.s.id, GenID(i+10))
if err != nil || curVal == nil {
t.Fatalf("GetGenMetadata() can not find object %s:%d in log file %s, err %v",
log.s.id, i, logfile, err)
}
if curVal.Pos != i || curVal.Count != uint64(i+100) {
t.Errorf("Data mismatch for object %s:%d in log file %s: %v",
log.s.id, i, logfile, curVal)
}
}
log.head.Curgen = 0
log.head.Curlsn = 0
log.head.Curorder = 0
if err := log.getHead(); err != nil {
t.Fatalf("getHead() can not find head in log file %s, err %v", logfile, err)
}
if log.head.Curgen != 1000 && log.head.Curlsn != 10 && log.head.Curorder != 100 {
t.Errorf("Data mismatch for head in log file %s: %v", logfile, log.head)
}
if err := log.close(); err != nil {
t.Errorf("Cannot close log file %s, err %v", logfile, err)
}
}