github.com/cayleygraph/cayley@v0.7.7/graph/kv/quadstore_test.go (about) 1 package kv_test 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/binary" 7 henc "encoding/hex" 8 "fmt" 9 "sort" 10 "sync" 11 "testing" 12 13 "github.com/cayleygraph/cayley/graph" 14 "github.com/cayleygraph/cayley/graph/kv" 15 "github.com/cayleygraph/cayley/graph/kv/btree" 16 "github.com/cayleygraph/cayley/writer" 17 "github.com/cayleygraph/quad" 18 hkv "github.com/hidal-go/hidalgo/kv" 19 "github.com/stretchr/testify/require" 20 ) 21 22 func hex(s string) []byte { 23 b, err := henc.DecodeString(s) 24 if err != nil { 25 panic(err) 26 } 27 return b 28 } 29 30 func irih(s string) []byte { 31 h := graph.HashOf(quad.IRI(s)) 32 return h[:] 33 } 34 35 func irib(s string) string { 36 h := graph.HashOf(quad.IRI(s)) 37 return string([]byte{'v', h[0], h[1]}) 38 } 39 40 func iric(s string) string { 41 h := graph.HashOf(quad.IRI(s)) 42 return string([]byte{'n', h[0], h[1]}) 43 } 44 45 func key(b string, k []byte) hkv.Key { 46 return hkv.Key{[]byte(b), k} 47 } 48 49 func be(v ...uint64) []byte { 50 b := make([]byte, 8*len(v)) 51 for i, vi := range v { 52 binary.BigEndian.PutUint64(b[i*8:], vi) 53 } 54 return b[:] 55 } 56 func le(v uint64) []byte { 57 var b [8]byte 58 binary.LittleEndian.PutUint64(b[:], uint64(v)) 59 return b[:] 60 } 61 62 const ( 63 bMeta = "meta" 64 bLog = "log" 65 ) 66 67 var ( 68 kVers = []byte("version") 69 vVers = le(2) 70 71 vAuto = []byte("auto") 72 73 kIndexes = []byte("indexes") 74 ) 75 76 type Ops []kvOp 77 78 func (s Ops) Len() int { 79 return len(s) 80 } 81 82 func (s Ops) Less(i, j int) bool { 83 a, b := s[i], s[j] 84 return a.key.Compare(b.key) < 0 85 } 86 87 func (s Ops) Swap(i, j int) { 88 s[i], s[j] = s[j], s[i] 89 } 90 91 func (s Ops) String() string { 92 buf := bytes.NewBuffer(nil) 93 for _, op := range s { 94 se := "" 95 if op.err != nil { 96 se = " (" + op.err.Error() + ")" 97 } 98 fmt.Fprintf(buf, "%v: %q = %x%s\n", op.typ, op.key, op.val, se) 99 } 100 return buf.String() 101 } 102 103 func TestApplyDeltas(t *testing.T) { 104 kdb := btree.New() 105 106 hook := &kvHook{db: kdb} 107 expect := func(exp Ops) { 108 got := hook.log() 109 if len(exp) == len(got) { 110 if false { 111 sortByOp(exp, got) 112 } 113 // TODO: make node insert predictable 114 for i, d := range exp { 115 if bytes.Equal(d.key[0], vAuto) { 116 exp[i].key = got[i].key 117 } 118 if bytes.Equal(d.val, vAuto) { 119 exp[i].val = got[i].val 120 } 121 } 122 } 123 require.Equal(t, exp, got, "%d\n%v\nvs\n\n%d\n%v", len(exp), exp, len(got), got) 124 } 125 126 err := kv.Init(hook, nil) 127 require.NoError(t, err) 128 129 expect(Ops{ 130 {opGet, key(bMeta, kVers), nil, hkv.ErrNotFound}, 131 {opPut, key(bMeta, []byte{}), nil, nil}, 132 {opPut, key(bLog, []byte{}), nil, nil}, 133 {opPut, key("sp", []byte{}), nil, nil}, 134 {opPut, key("ops", []byte{}), nil, nil}, 135 {opPut, key(bMeta, kVers), vVers, nil}, 136 {opPut, key(bMeta, kIndexes), []byte(`[{"dirs":"AQI=","unique":false},{"dirs":"AwIB","unique":false}]`), nil}, 137 }) 138 139 qs, err := kv.New(hook, nil) 140 require.NoError(t, err) 141 defer qs.Close() 142 143 expect(Ops{ 144 {opGet, key(bMeta, kVers), vVers, nil}, 145 {opGet, key(bMeta, kIndexes), []byte(`[{"dirs":"AQI=","unique":false},{"dirs":"AwIB","unique":false}]`), nil}, 146 {opGet, key(bMeta, []byte("size")), nil, hkv.ErrNotFound}, 147 }) 148 149 qw, err := writer.NewSingle(qs, graph.IgnoreOpts{}) 150 require.NoError(t, err) 151 152 err = qw.AddQuad(quad.MakeIRI("a", "b", "c", "")) 153 require.NoError(t, err) 154 155 expect(Ops{ 156 {opGet, key(bMeta, []byte("horizon")), nil, hkv.ErrNotFound}, 157 {opPut, key(bMeta, []byte("horizon")), le(3), nil}, 158 159 {opPut, key(irib("a"), irih("a")), vAuto, nil}, 160 {opPut, key(bLog, be(1)), vAuto, nil}, 161 {opPut, key(irib("b"), irih("b")), vAuto, nil}, 162 {opPut, key(bLog, be(2)), vAuto, nil}, 163 {opPut, key(irib("c"), irih("c")), vAuto, nil}, 164 {opPut, key(bLog, be(3)), vAuto, nil}, 165 166 {opPut, key(iric("a"), irih("a")), hex("01"), nil}, 167 {opPut, key(iric("b"), irih("b")), hex("01"), nil}, 168 {opPut, key(iric("c"), irih("c")), hex("01"), nil}, 169 {opGet, key(bMeta, []byte("horizon")), le(3), nil}, 170 {opPut, key(bMeta, []byte("horizon")), le(4), nil}, 171 {opPut, key(bLog, be(4)), vAuto, nil}, 172 {opGet, key(bMeta, []byte("size")), nil, hkv.ErrNotFound}, 173 {opPut, key(bMeta, []byte("size")), le(1), nil}, 174 {opPut, key("ops", be(3, 2, 1)), hex("04"), nil}, 175 {opPut, key("sp", be(1, 2)), hex("04"), nil}, 176 }) 177 178 err = qw.AddQuad(quad.MakeIRI("a", "b", "e", "")) 179 require.NoError(t, err) 180 181 expect(Ops{ 182 // served from IRI cache 183 //{opGet, irib("a"), irih("a"), vAuto, nil}, 184 //{opGet, irib("b"), irih("b"), vAuto, nil}, 185 {opGet, key(bMeta, []byte("horizon")), le(4), nil}, 186 {opPut, key(bMeta, []byte("horizon")), le(5), nil}, 187 188 {opPut, key(irib("e"), irih("e")), vAuto, nil}, 189 {opPut, key(bLog, be(5)), vAuto, nil}, 190 191 {opGet, key(iric("a"), irih("a")), hex("01"), nil}, 192 {opGet, key(iric("b"), irih("b")), hex("01"), nil}, 193 {opPut, key(iric("a"), irih("a")), hex("02"), nil}, 194 {opPut, key(iric("b"), irih("b")), hex("02"), nil}, 195 {opPut, key(iric("e"), irih("e")), hex("01"), nil}, 196 {opGet, key(bMeta, []byte("horizon")), le(5), nil}, 197 {opPut, key(bMeta, []byte("horizon")), le(6), nil}, 198 {opPut, key(bLog, be(6)), vAuto, nil}, 199 {opGet, key(bMeta, []byte("size")), le(1), nil}, 200 {opPut, key(bMeta, []byte("size")), le(2), nil}, 201 {opPut, key("ops", be(5, 2, 1)), hex("06"), nil}, 202 {opGet, key("sp", be(1, 2)), hex("04"), nil}, 203 {opPut, key("sp", be(1, 2)), hex("0406"), nil}, 204 }) 205 206 err = qw.RemoveQuad(quad.MakeIRI("a", "b", "c", "")) 207 expect(Ops{ 208 {opGet, key("sp", be(1, 2)), hex("0406"), nil}, 209 {opGet, key("ops", be(3, 2, 1)), hex("04"), nil}, 210 {opGet, key(bLog, be(4)), vAuto, nil}, 211 {opPut, key(bLog, be(4)), vAuto, nil}, 212 {opGet, key(bMeta, []byte("size")), le(2), nil}, 213 {opPut, key(bMeta, []byte("size")), le(1), nil}, 214 {opGet, key(iric("a"), irih("a")), hex("02"), nil}, 215 {opGet, key(iric("b"), irih("b")), hex("02"), nil}, 216 {opGet, key(iric("c"), irih("c")), hex("01"), nil}, 217 {opPut, key(iric("a"), irih("a")), hex("01"), nil}, 218 {opPut, key(iric("b"), irih("b")), hex("01"), nil}, 219 {opDel, key(iric("c"), irih("c")), nil, nil}, 220 {opDel, key(irib("c"), irih("c")), nil, nil}, 221 {opDel, key(bLog, be(3)), nil, nil}, 222 }) 223 require.NoError(t, err) 224 } 225 226 func clone(b []byte) []byte { 227 if b == nil { 228 return nil 229 } 230 return append([]byte{}, b...) 231 } 232 233 func sortByOp(exp, got Ops) { 234 // sort ops of one type 235 li := -1 236 typ, b := -1, "" 237 check := func(i int) { 238 if li < 0 || i-li <= 0 { 239 return 240 } 241 sort.Sort(exp[li:i]) 242 sort.Sort(got[li:i]) 243 //sort.Sort(bothOps{a: exp[li:i], b: got[li:i]}) 244 li, typ, b = -1, -1, "" 245 } 246 for i, op := range exp { 247 if op.typ != typ { 248 check(i) 249 } 250 if li < 0 { 251 li, typ, b = i, op.typ, string(op.key[0]) 252 } 253 } 254 _ = b 255 check(len(exp)) 256 } 257 258 const ( 259 opGet = iota 260 opPut 261 opDel 262 ) 263 264 type kvOp struct { 265 typ int 266 key hkv.Key 267 val hkv.Value 268 err error 269 } 270 271 var _ hkv.KV = (*kvHook)(nil) 272 273 type kvHook struct { 274 db hkv.KV 275 276 mu sync.Mutex 277 ops Ops 278 } 279 280 func (h *kvHook) log() Ops { 281 h.mu.Lock() 282 ops := h.ops 283 h.ops = nil 284 h.mu.Unlock() 285 return ops 286 } 287 288 func (h *kvHook) addOp(op kvOp) { 289 h.mu.Lock() 290 h.ops = append(h.ops, op) 291 h.mu.Unlock() 292 } 293 294 func (h *kvHook) Close() error { 295 return h.db.Close() 296 } 297 298 func (h *kvHook) Tx(rw bool) (hkv.Tx, error) { 299 tx, err := h.db.Tx(rw) 300 if err != nil { 301 return nil, err 302 } 303 return txHook{h: h, tx: tx}, nil 304 } 305 306 type txHook struct { 307 h *kvHook 308 tx hkv.Tx 309 } 310 311 func (h txHook) Commit(ctx context.Context) error { 312 return h.tx.Commit(ctx) 313 } 314 315 func (h txHook) Close() error { 316 return h.tx.Close() 317 } 318 319 func (h txHook) GetBatch(ctx context.Context, keys []hkv.Key) ([]hkv.Value, error) { 320 vals, err := h.tx.GetBatch(ctx, keys) 321 if err != nil { 322 return nil, err 323 } 324 for i, k := range keys { 325 h.h.addOp(kvOp{ 326 key: k.Clone(), 327 val: vals[i].Clone(), 328 }) 329 } 330 return vals, nil 331 } 332 333 func (h txHook) Get(ctx context.Context, k hkv.Key) (hkv.Value, error) { 334 v, err := h.tx.Get(ctx, k) 335 h.h.addOp(kvOp{ 336 key: k.Clone(), 337 val: v.Clone(), 338 err: err, 339 }) 340 return v, err 341 } 342 343 func (h txHook) Put(k hkv.Key, v hkv.Value) error { 344 err := h.tx.Put(k, v) 345 h.h.addOp(kvOp{ 346 typ: opPut, 347 key: k.Clone(), 348 val: v.Clone(), 349 err: err, 350 }) 351 return err 352 } 353 354 func (h txHook) Del(k hkv.Key) error { 355 err := h.tx.Del(k) 356 h.h.addOp(kvOp{ 357 typ: opDel, 358 key: k.Clone(), 359 err: err, 360 }) 361 return err 362 } 363 364 func (h txHook) Scan(pref hkv.Key) hkv.Iterator { 365 return h.tx.Scan(pref) 366 }