File: log_cache_test.go

package info (click to toggle)
golang-github-hashicorp-raft 1.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 920 kB
  • sloc: makefile: 41; sh: 9
file content (151 lines) | stat: -rw-r--r-- 2,835 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package raft

import (
	"errors"
	"strings"
	"sync"
	"testing"
)

func TestLogCache(t *testing.T) {
	store := NewInmemStore()
	c, _ := NewLogCache(16, store)

	// Insert into the in-mem store
	for i := 0; i < 32; i++ {
		log := &Log{Index: uint64(i) + 1}
		store.StoreLog(log)
	}

	// Check the indexes
	if idx, _ := c.FirstIndex(); idx != 1 {
		t.Fatalf("bad: %d", idx)
	}
	if idx, _ := c.LastIndex(); idx != 32 {
		t.Fatalf("bad: %d", idx)
	}

	// Try get log with a miss
	var out Log
	err := c.GetLog(1, &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if out.Index != 1 {
		t.Fatalf("bad: %#v", out)
	}

	// Store logs
	l1 := &Log{Index: 33}
	l2 := &Log{Index: 34}
	err = c.StoreLogs([]*Log{l1, l2})
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if idx, _ := c.LastIndex(); idx != 34 {
		t.Fatalf("bad: %d", idx)
	}

	// Check that it wrote-through
	err = store.GetLog(33, &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = store.GetLog(34, &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Delete in the backend
	err = store.DeleteRange(33, 34)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should be in the ring buffer
	err = c.GetLog(33, &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = c.GetLog(34, &out)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Purge the ring buffer
	err = c.DeleteRange(33, 34)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Should not be in the ring buffer
	err = c.GetLog(33, &out)
	if err != ErrLogNotFound {
		t.Fatalf("err: %v", err)
	}
	err = c.GetLog(34, &out)
	if err != ErrLogNotFound {
		t.Fatalf("err: %v", err)
	}
}

type errorStore struct {
	LogStore
	mu      sync.Mutex
	fail    bool
	failed  int
	failMax int
}

func (e *errorStore) StoreLogs(logs []*Log) error {
	e.mu.Lock()
	defer e.mu.Unlock()
	if e.fail {
		e.failed++
		if e.failed <= e.failMax {
			return errors.New("some error")
		}
		e.fail = false
	}
	return e.LogStore.StoreLogs(logs)
}

func (e *errorStore) failNext(count int) {
	e.mu.Lock()
	e.fail = true
	e.failMax = count
	e.mu.Unlock()
}

func TestLogCacheWithBackendStoreError(t *testing.T) {
	var err error
	store := NewInmemStore()
	errStore := &errorStore{LogStore: store}
	c, _ := NewLogCache(16, errStore)

	for i := 0; i < 4; i++ {
		log := &Log{Index: uint64(i) + 1}
		store.StoreLog(log)
	}
	errStore.failNext(1)
	log := &Log{Index: 5}
	err = c.StoreLog(log)
	if !strings.Contains(err.Error(), "some error") {
		t.Fatalf("wanted: some error,  got err=%v", err)
	}

	var out Log
	for i := 1; i < 5; i++ {
		if err := c.GetLog(uint64(i), &out); err != nil {
			t.Fatalf("err: %v", err)
		}
	}
	out = Log{}
	if err = c.GetLog(5, &out); err != ErrLogNotFound {
		t.Fatalf("Should have returned not found, got err=%v out=%+v", err, out)
	}
}