1// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mvcc
16
17import (
18	"os"
19	"reflect"
20	"testing"
21	"time"
22
23	"github.com/coreos/etcd/lease"
24	"github.com/coreos/etcd/mvcc/backend"
25)
26
27func TestScheduleCompaction(t *testing.T) {
28	revs := []revision{{1, 0}, {2, 0}, {3, 0}}
29
30	tests := []struct {
31		rev   int64
32		keep  map[revision]struct{}
33		wrevs []revision
34	}{
35		// compact at 1 and discard all history
36		{
37			1,
38			nil,
39			revs[1:],
40		},
41		// compact at 3 and discard all history
42		{
43			3,
44			nil,
45			nil,
46		},
47		// compact at 1 and keeps history one step earlier
48		{
49			1,
50			map[revision]struct{}{
51				{main: 1}: {},
52			},
53			revs,
54		},
55		// compact at 1 and keeps history two steps earlier
56		{
57			3,
58			map[revision]struct{}{
59				{main: 2}: {},
60				{main: 3}: {},
61			},
62			revs[1:],
63		},
64	}
65	for i, tt := range tests {
66		b, tmpPath := backend.NewDefaultTmpBackend()
67		s := NewStore(b, &lease.FakeLessor{}, nil)
68		tx := s.b.BatchTx()
69
70		tx.Lock()
71		ibytes := newRevBytes()
72		for _, rev := range revs {
73			revToBytes(rev, ibytes)
74			tx.UnsafePut(keyBucketName, ibytes, []byte("bar"))
75		}
76		tx.Unlock()
77
78		s.scheduleCompaction(tt.rev, tt.keep)
79
80		tx.Lock()
81		for _, rev := range tt.wrevs {
82			revToBytes(rev, ibytes)
83			keys, _ := tx.UnsafeRange(keyBucketName, ibytes, nil, 0)
84			if len(keys) != 1 {
85				t.Errorf("#%d: range on %v = %d, want 1", i, rev, len(keys))
86			}
87		}
88		_, vals := tx.UnsafeRange(metaBucketName, finishedCompactKeyName, nil, 0)
89		revToBytes(revision{main: tt.rev}, ibytes)
90		if w := [][]byte{ibytes}; !reflect.DeepEqual(vals, w) {
91			t.Errorf("#%d: vals on %v = %+v, want %+v", i, finishedCompactKeyName, vals, w)
92		}
93		tx.Unlock()
94
95		cleanup(s, b, tmpPath)
96	}
97}
98
99func TestCompactAllAndRestore(t *testing.T) {
100	b, tmpPath := backend.NewDefaultTmpBackend()
101	s0 := NewStore(b, &lease.FakeLessor{}, nil)
102	defer os.Remove(tmpPath)
103
104	s0.Put([]byte("foo"), []byte("bar"), lease.NoLease)
105	s0.Put([]byte("foo"), []byte("bar1"), lease.NoLease)
106	s0.Put([]byte("foo"), []byte("bar2"), lease.NoLease)
107	s0.DeleteRange([]byte("foo"), nil)
108
109	rev := s0.Rev()
110	// compact all keys
111	done, err := s0.Compact(rev)
112	if err != nil {
113		t.Fatal(err)
114	}
115
116	select {
117	case <-done:
118	case <-time.After(10 * time.Second):
119		t.Fatal("timeout waiting for compaction to finish")
120	}
121
122	err = s0.Close()
123	if err != nil {
124		t.Fatal(err)
125	}
126
127	s1 := NewStore(b, &lease.FakeLessor{}, nil)
128	if s1.Rev() != rev {
129		t.Errorf("rev = %v, want %v", s1.Rev(), rev)
130	}
131	_, err = s1.Range([]byte("foo"), nil, RangeOptions{})
132	if err != nil {
133		t.Errorf("unexpect range error %v", err)
134	}
135}
136