arc_cache_test.go 5.65 KB
Newer Older
1 2 3
package blockstore

import (
4
	"context"
5 6
	"testing"

7
	blocks "github.com/ipfs/go-block-format"
8 9 10
	cid "github.com/ipfs/go-cid"
	ds "github.com/ipfs/go-datastore"
	syncds "github.com/ipfs/go-datastore/sync"
11 12
)

13 14
var exampleBlock = blocks.NewBlock([]byte("foo"))

15
func testArcCached(ctx context.Context, bs Blockstore) (*arccache, error) {
16 17 18 19 20 21
	if ctx == nil {
		ctx = context.TODO()
	}
	opts := DefaultCacheOpts()
	opts.HasBloomFilterSize = 0
	opts.HasBloomFilterHashes = 0
22
	bbs, err := CachedBlockstore(ctx, bs, opts)
23 24 25
	if err == nil {
		return bbs.(*arccache), nil
	}
26
	return nil, err
27 28
}

29
func createStores(t *testing.T) (*arccache, Blockstore, *callbackDatastore) {
30 31
	cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()}
	bs := NewBlockstore(syncds.MutexWrap(cd))
32
	arc, err := testArcCached(context.TODO(), bs)
33 34 35
	if err != nil {
		t.Fatal(err)
	}
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	return arc, bs, cd
}

func trap(message string, cd *callbackDatastore, t *testing.T) {
	cd.SetFunc(func() {
		t.Fatal(message)
	})
}
func untrap(cd *callbackDatastore) {
	cd.SetFunc(func() {})
}

func TestRemoveCacheEntryOnDelete(t *testing.T) {
	arc, _, cd := createStores(t)

	arc.Put(exampleBlock)
52 53 54 55 56 57 58 59 60

	cd.Lock()
	writeHitTheDatastore := false
	cd.Unlock()

	cd.SetFunc(func() {
		writeHitTheDatastore = true
	})

61
	arc.DeleteBlock(exampleBlock.Cid())
62
	arc.Put(exampleBlock)
63 64 65 66 67 68
	if !writeHitTheDatastore {
		t.Fail()
	}
}

func TestElideDuplicateWrite(t *testing.T) {
69 70 71 72 73 74 75 76 77 78
	arc, _, cd := createStores(t)

	arc.Put(exampleBlock)
	trap("write hit datastore", cd, t)
	arc.Put(exampleBlock)
}

func TestHasRequestTriggersCache(t *testing.T) {
	arc, _, cd := createStores(t)

79
	arc.Has(exampleBlock.Cid())
80
	trap("has hit datastore", cd, t)
81
	if has, err := arc.Has(exampleBlock.Cid()); has || err != nil {
82 83 84 85 86
		t.Fatal("has was true but there is no such block")
	}

	untrap(cd)
	err := arc.Put(exampleBlock)
87 88 89 90
	if err != nil {
		t.Fatal(err)
	}

91
	trap("has hit datastore", cd, t)
92

93
	if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil {
94 95 96 97 98 99 100
		t.Fatal("has returned invalid result")
	}
}

func TestGetFillsCache(t *testing.T) {
	arc, _, cd := createStores(t)

101
	if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err == nil {
102 103 104 105 106
		t.Fatal("block was found or there was no error")
	}

	trap("has hit datastore", cd, t)

107
	if has, err := arc.Has(exampleBlock.Cid()); has || err != nil {
108 109
		t.Fatal("has was true but there is no such block")
	}
110
	if _, err := arc.GetSize(exampleBlock.Cid()); err != ErrNotFound {
111 112
		t.Fatal("getsize was true but there is no such block")
	}
113 114 115 116 117 118 119 120 121

	untrap(cd)

	if err := arc.Put(exampleBlock); err != nil {
		t.Fatal(err)
	}

	trap("has hit datastore", cd, t)

122
	if has, err := arc.Has(exampleBlock.Cid()); !has || err != nil {
123 124
		t.Fatal("has returned invalid result")
	}
125
	if blockSize, err := arc.GetSize(exampleBlock.Cid()); blockSize == -1 || err != nil {
126
		t.Fatal("getsize returned invalid result", blockSize, err)
127
	}
128 129
}

130
func TestGetAndDeleteFalseShortCircuit(t *testing.T) {
131 132
	arc, _, cd := createStores(t)

133
	arc.Has(exampleBlock.Cid())
134
	arc.GetSize(exampleBlock.Cid())
135 136 137

	trap("get hit datastore", cd, t)

138
	if bl, err := arc.Get(exampleBlock.Cid()); bl != nil || err != ErrNotFound {
139 140 141
		t.Fatal("get returned invalid result")
	}

Steven Allen's avatar
Steven Allen committed
142 143
	if arc.DeleteBlock(exampleBlock.Cid()) != nil {
		t.Fatal("expected deletes to be idempotent")
144 145 146 147
	}
}

func TestArcCreationFailure(t *testing.T) {
148
	if arc, err := newARCCachedBS(context.TODO(), nil, -1); arc != nil || err == nil {
149 150 151 152 153 154 155
		t.Fatal("expected error and no cache")
	}
}

func TestInvalidKey(t *testing.T) {
	arc, _, _ := createStores(t)

156
	bl, err := arc.Get(cid.Cid{})
157 158 159 160 161 162 163 164 165 166 167 168 169 170

	if bl != nil {
		t.Fatal("blocks should be nil")
	}
	if err == nil {
		t.Fatal("expected error")
	}
}

func TestHasAfterSucessfulGetIsCached(t *testing.T) {
	arc, bs, cd := createStores(t)

	bs.Put(exampleBlock)

171
	arc.Get(exampleBlock.Cid())
172 173

	trap("has hit datastore", cd, t)
174 175 176
	arc.Has(exampleBlock.Cid())
}

177 178 179 180 181 182 183 184 185 186 187
func TestGetSizeAfterSucessfulGetIsCached(t *testing.T) {
	arc, bs, cd := createStores(t)

	bs.Put(exampleBlock)

	arc.Get(exampleBlock.Cid())

	trap("has hit datastore", cd, t)
	arc.GetSize(exampleBlock.Cid())
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
func TestGetSizeAfterSucessfulHas(t *testing.T) {
	arc, bs, _ := createStores(t)

	bs.Put(exampleBlock)
	has, err := arc.Has(exampleBlock.Cid())
	if err != nil {
		t.Fatal(err)
	}
	if !has {
		t.Fatal("expected to have block")
	}

	if size, err := arc.GetSize(exampleBlock.Cid()); err != nil {
		t.Fatal(err)
	} else if size != len(exampleBlock.RawData()) {
		t.Fatalf("expected size %d, got %d", len(exampleBlock.RawData()), size)
	}
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
func TestGetSizeMissingZeroSizeBlock(t *testing.T) {
	arc, bs, cd := createStores(t)
	emptyBlock := blocks.NewBlock([]byte{})
	missingBlock := blocks.NewBlock([]byte("missingBlock"))

	bs.Put(emptyBlock)

	arc.Get(emptyBlock.Cid())

	trap("has hit datastore", cd, t)
	if blockSize, err := arc.GetSize(emptyBlock.Cid()); blockSize != 0 || err != nil {
		t.Fatal("getsize returned invalid result")
	}
	untrap(cd)

	arc.Get(missingBlock.Cid())

	trap("has hit datastore", cd, t)
225
	if _, err := arc.GetSize(missingBlock.Cid()); err != ErrNotFound {
226 227 228 229
		t.Fatal("getsize returned invalid result")
	}
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
func TestDifferentKeyObjectsWork(t *testing.T) {
	arc, bs, cd := createStores(t)

	bs.Put(exampleBlock)

	arc.Get(exampleBlock.Cid())

	trap("has hit datastore", cd, t)
	cidstr := exampleBlock.Cid().String()

	ncid, err := cid.Decode(cidstr)
	if err != nil {
		t.Fatal(err)
	}

	arc.Has(ncid)
246 247 248 249 250 251 252
}

func TestPutManyCaches(t *testing.T) {
	arc, _, cd := createStores(t)
	arc.PutMany([]blocks.Block{exampleBlock})

	trap("has hit datastore", cd, t)
253
	arc.Has(exampleBlock.Cid())
254
	arc.GetSize(exampleBlock.Cid())
255
	untrap(cd)
256
	arc.DeleteBlock(exampleBlock.Cid())
257 258 259 260

	arc.Put(exampleBlock)
	trap("PunMany has hit datastore", cd, t)
	arc.PutMany([]blocks.Block{exampleBlock})
261
}