github.com/cayleygraph/cayley@v0.7.7/graph/kv/indexing.go (about) 1 // Copyright 2016 The Cayley Authors. All rights reserved. 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 15 package kv 16 17 import ( 18 "context" 19 "encoding/binary" 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 "sort" 25 "time" 26 27 "github.com/cayleygraph/cayley/clog" 28 "github.com/cayleygraph/cayley/graph" 29 "github.com/cayleygraph/cayley/graph/log" 30 "github.com/cayleygraph/cayley/graph/proto" 31 "github.com/cayleygraph/quad" 32 "github.com/cayleygraph/quad/pquads" 33 34 "github.com/hidal-go/hidalgo/kv" 35 "github.com/prometheus/client_golang/prometheus" 36 boom "github.com/tylertreat/BoomFilters" 37 ) 38 39 var ( 40 metaBucket = kv.Key{[]byte("meta")} 41 logIndex = kv.Key{[]byte("log")} 42 43 keyMetaIndexes = metaBucket.AppendBytes([]byte("indexes")) 44 45 // List of all buckets in the current version of the database. 46 buckets = []kv.Key{ 47 metaBucket, 48 logIndex, 49 } 50 51 // legacyQuadIndexes is a set of indexes used in Cayley < 0.7.6 52 legacyQuadIndexes = []QuadIndex{ 53 {Dirs: []quad.Direction{quad.Subject}}, 54 {Dirs: []quad.Direction{quad.Object}}, 55 } 56 57 DefaultQuadIndexes = []QuadIndex{ 58 // First index optimizes forward traversals. Getting all relations for a node should 59 // also be reasonably fast (prefix scan). 60 {Dirs: []quad.Direction{quad.Subject, quad.Predicate}}, 61 62 // Second index helps with reverse traversals as well as full quad lookups. 63 // It also prevents issues with super-nodes, since most of those are values 64 // with a high in-degree. 65 {Dirs: []quad.Direction{quad.Object, quad.Predicate, quad.Subject}}, 66 } 67 ) 68 69 var quadKeyEnc = binary.BigEndian 70 71 type QuadIndex struct { 72 Dirs []quad.Direction `json:"dirs"` 73 Unique bool `json:"unique"` 74 } 75 76 func (ind QuadIndex) Key(vals []uint64) kv.Key { 77 key := make([]byte, 8*len(vals)) 78 n := 0 79 for i := range vals { 80 quadKeyEnc.PutUint64(key[n:], vals[i]) 81 n += 8 82 } 83 // TODO(dennwc): split into parts? 84 return ind.bucket().AppendBytes(key) 85 } 86 func (ind QuadIndex) KeyFor(p *proto.Primitive) kv.Key { 87 key := make([]byte, 8*len(ind.Dirs)) 88 n := 0 89 for _, d := range ind.Dirs { 90 quadKeyEnc.PutUint64(key[n:], p.GetDirection(d)) 91 n += 8 92 } 93 // TODO(dennwc): split into parts? 94 return ind.bucket().AppendBytes(key) 95 } 96 func (ind QuadIndex) bucket() kv.Key { 97 buf := make([]byte, len(ind.Dirs)) 98 for i, d := range ind.Dirs { 99 buf[i] = d.Prefix() 100 } 101 key := make(kv.Key, 1, 2) 102 key[0] = buf 103 return key 104 } 105 106 func bucketForVal(i, j byte) kv.Key { 107 return kv.Key{[]byte{'v', i, j}} 108 } 109 110 func bucketForValRefs(i, j byte) kv.Key { 111 return kv.Key{[]byte{'n', i, j}} 112 } 113 114 func (qs *QuadStore) createBuckets(ctx context.Context, upfront bool) error { 115 err := kv.Update(ctx, qs.db, func(tx kv.Tx) error { 116 for _, index := range buckets { 117 _ = kv.CreateBucket(ctx, tx, index) 118 } 119 for _, ind := range qs.indexes.all { 120 _ = kv.CreateBucket(ctx, tx, ind.bucket()) 121 } 122 return nil 123 }) 124 if err != nil { 125 return err 126 } 127 if !upfront { 128 return nil 129 } 130 for i := 0; i < 256; i++ { 131 err := kv.Update(ctx, qs.db, func(tx kv.Tx) error { 132 for j := 0; j < 256; j++ { 133 _ = kv.CreateBucket(ctx, tx, bucketForVal(byte(i), byte(j))) 134 _ = kv.CreateBucket(ctx, tx, bucketForValRefs(byte(i), byte(j))) 135 } 136 return nil 137 }) 138 if err != nil { 139 return err 140 } 141 } 142 return nil 143 } 144 145 func (qs *QuadStore) incSize(ctx context.Context, tx kv.Tx, size int64) error { 146 _, err := qs.incMetaInt(ctx, tx, "size", size) 147 return err 148 } 149 150 // writeIndexesMeta writes metadata about current indexes to the KV database, 151 // so we can read this information back later. 152 func (qs *QuadStore) writeIndexesMeta(ctx context.Context) error { 153 // TODO(dennwc): change to protobuf later? 154 data, err := json.Marshal(qs.indexes.all) 155 if err != nil { 156 return err 157 } 158 return kv.Update(ctx, qs.db, func(tx kv.Tx) error { 159 return tx.Put(keyMetaIndexes, data) 160 }) 161 } 162 163 // readIndexesMeta read metadata about current indexes from the KV database. 164 // If no indexes are set, it returns a list of legacy indexes to preserve backward compatibility. 165 func (qs *QuadStore) readIndexesMeta(ctx context.Context) ([]QuadIndex, error) { 166 tx, err := qs.db.Tx(false) 167 if err != nil { 168 return nil, err 169 } 170 defer tx.Close() 171 tx = wrapTx(tx) 172 val, err := tx.Get(ctx, keyMetaIndexes) 173 if err == kv.ErrNotFound { 174 return legacyQuadIndexes, nil 175 } else if err != nil { 176 return nil, err 177 } 178 var out []QuadIndex 179 if err := json.Unmarshal(val, &out); err != nil { 180 return nil, fmt.Errorf("cannot decode indexes: %v", err) 181 } else if len(out) == 0 { 182 return legacyQuadIndexes, nil 183 } 184 return out, nil 185 } 186 187 func (qs *QuadStore) resolveValDeltas(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate, fnc func(i int, id uint64)) error { 188 inds := make([]int, 0, len(deltas)) 189 keys := make([]kv.Key, 0, len(deltas)) 190 for i, d := range deltas { 191 if iri, ok := d.Val.(quad.IRI); ok { 192 if x, ok := qs.valueLRU.Get(string(iri)); ok { 193 fnc(i, x.(uint64)) 194 continue 195 } 196 } else if d.Val == nil { 197 fnc(i, 0) 198 continue 199 } 200 if qs.mapNodes != nil && !qs.mapNodes.Test(d.Hash[:]) { 201 fnc(i, 0) 202 continue 203 } 204 inds = append(inds, i) 205 keys = append(keys, bucketKeyForHash(d.Hash)) 206 } 207 if len(keys) == 0 { 208 return nil 209 } 210 resp, err := tx.GetBatch(ctx, keys) 211 if err != nil { 212 return err 213 } 214 keys = nil 215 for i, b := range resp { 216 if len(b) == 0 { 217 fnc(inds[i], 0) 218 continue 219 } 220 ind := inds[i] 221 id, _ := binary.Uvarint(b) 222 d := &deltas[ind] 223 if iri, ok := d.Val.(quad.IRI); ok && id != 0 { 224 qs.valueLRU.Put(string(iri), uint64(id)) 225 } 226 fnc(ind, uint64(id)) 227 } 228 return nil 229 } 230 231 func (qs *QuadStore) getMetaIntTx(ctx context.Context, tx kv.Tx, key string) (int64, error) { 232 val, err := tx.Get(ctx, metaBucket.AppendBytes([]byte(key))) 233 if err == kv.ErrNotFound { 234 return 0, err 235 } else if err != nil { 236 return 0, fmt.Errorf("cannot get horizon value: %v", err) 237 } 238 return int64(binary.LittleEndian.Uint64(val)), nil 239 } 240 241 func (qs *QuadStore) incMetaInt(ctx context.Context, tx kv.Tx, key string, n int64) (int64, error) { 242 if n == 0 { 243 return 0, nil 244 } 245 v, err := qs.getMetaIntTx(ctx, tx, key) 246 if err != nil && err != kv.ErrNotFound { 247 return 0, fmt.Errorf("cannot get %s: %v", key, err) 248 } 249 start := v 250 v += n 251 252 buf := make([]byte, 8) // bolt needs all slices available on Commit 253 binary.LittleEndian.PutUint64(buf, uint64(v)) 254 255 err = tx.Put(metaBucket.AppendBytes([]byte(key)), buf) 256 if err != nil { 257 return 0, fmt.Errorf("cannot inc %s: %v", key, err) 258 } 259 return start, nil 260 } 261 262 func (qs *QuadStore) genIDs(ctx context.Context, tx kv.Tx, n int) (uint64, error) { 263 if n == 0 { 264 return 0, nil 265 } 266 start, err := qs.incMetaInt(ctx, tx, "horizon", int64(n)) 267 if err != nil { 268 return 0, err 269 } 270 return uint64(start + 1), nil 271 } 272 273 type nodeUpdate struct { 274 Ind int 275 ID uint64 276 graphlog.NodeUpdate 277 } 278 279 func (qs *QuadStore) incNodesCnt(ctx context.Context, tx kv.Tx, deltas, newDeltas []nodeUpdate) ([]int, error) { 280 var buf [binary.MaxVarintLen64]byte 281 // increment nodes 282 keys := make([]kv.Key, 0, len(deltas)) 283 for _, d := range deltas { 284 keys = append(keys, bucketKeyForHashRefs(d.Hash)) 285 } 286 sizes, err := tx.GetBatch(ctx, keys) 287 if err != nil { 288 return nil, err 289 } 290 var del []int 291 for i, d := range deltas { 292 k := keys[i] 293 var sz int64 294 if sizes[i] != nil { 295 szu, _ := binary.Uvarint(sizes[i]) 296 sz = int64(szu) 297 sizes[i] = nil // cannot reuse buffer since it belongs to kv 298 } 299 sz += int64(d.RefInc) 300 if sz <= 0 { 301 if err := tx.Del(k); err != nil { 302 return del, err 303 } 304 mNodesDel.Inc() 305 del = append(del, i) 306 continue 307 } 308 n := binary.PutUvarint(buf[:], uint64(sz)) 309 val := append([]byte{}, buf[:n]...) 310 if err := tx.Put(k, val); err != nil { 311 return del, err 312 } 313 mNodesUpd.Inc() 314 } 315 // create new nodes 316 for _, d := range newDeltas { 317 n := binary.PutUvarint(buf[:], uint64(d.RefInc)) 318 val := append([]byte{}, buf[:n]...) 319 if err := tx.Put(bucketKeyForHashRefs(d.Hash), val); err != nil { 320 return nil, err 321 } 322 mNodesNew.Inc() 323 } 324 return del, nil 325 } 326 327 type resolvedNode struct { 328 ID uint64 329 New bool 330 } 331 332 func (qs *QuadStore) incNodes(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate) (map[graph.ValueHash]resolvedNode, error) { 333 var ( 334 ins []nodeUpdate 335 upd = make([]nodeUpdate, 0, len(deltas)) 336 ids = make(map[graph.ValueHash]resolvedNode, len(deltas)) 337 ) 338 err := qs.resolveValDeltas(ctx, tx, deltas, func(i int, id uint64) { 339 if id == 0 { 340 // not exists, should create 341 ins = append(ins, nodeUpdate{Ind: i, NodeUpdate: deltas[i]}) 342 } else { 343 // exists, should update 344 upd = append(upd, nodeUpdate{Ind: i, ID: id, NodeUpdate: deltas[i]}) 345 ids[deltas[i].Hash] = resolvedNode{ID: id} 346 } 347 }) 348 if err != nil { 349 return ids, err 350 } 351 if len(ins) != 0 { 352 // preallocate IDs 353 start, err := qs.genIDs(ctx, tx, len(ins)) 354 if err != nil { 355 return ids, err 356 } 357 // create and index new nodes 358 for i, iv := range ins { 359 id := start + uint64(i) 360 node, err := createNodePrimitive(iv.Val) 361 if err != nil { 362 return ids, err 363 } 364 365 node.ID = id 366 ids[iv.Hash] = resolvedNode{ID: id, New: true} 367 if err := qs.indexNode(tx, node, iv.Val); err != nil { 368 return ids, err 369 } 370 ins[i].ID = id 371 } 372 } 373 _, err = qs.incNodesCnt(ctx, tx, upd, ins) 374 return ids, err 375 } 376 func (qs *QuadStore) decNodes(ctx context.Context, tx kv.Tx, deltas []graphlog.NodeUpdate, nodes map[graph.ValueHash]uint64) error { 377 upds := make([]nodeUpdate, 0, len(deltas)) 378 for i, d := range deltas { 379 id := nodes[d.Hash] 380 if id == 0 || d.RefInc == 0 { 381 continue 382 } 383 upds = append(upds, nodeUpdate{Ind: i, ID: id, NodeUpdate: d}) 384 } 385 del, err := qs.incNodesCnt(ctx, tx, upds, nil) 386 if err != nil { 387 return err 388 } 389 for _, i := range del { 390 d := upds[i] 391 key := bucketForVal(d.Hash[0], d.Hash[1]).AppendBytes(d.Hash[:]) 392 if err = tx.Del(key); err != nil { 393 return err 394 } 395 if iri, ok := d.Val.(quad.IRI); ok { 396 qs.valueLRU.Del(string(iri)) 397 } 398 if err := qs.delLog(tx, d.ID); err != nil { 399 return err 400 } 401 } 402 return nil 403 } 404 405 func (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) { 406 return &quadWriter{qs: qs}, nil 407 } 408 409 type quadWriter struct { 410 qs *QuadStore 411 tx kv.Tx 412 err error 413 n int 414 } 415 416 func (w *quadWriter) WriteQuad(q quad.Quad) error { 417 _, err := w.WriteQuads([]quad.Quad{q}) 418 return err 419 } 420 421 func (w *quadWriter) flush() error { 422 w.n = 0 423 ctx := context.TODO() 424 if err := w.qs.flushMapBucket(ctx, w.tx); err != nil { 425 w.err = err 426 return err 427 } 428 if err := w.tx.Commit(ctx); err != nil { 429 w.qs.writer.Unlock() 430 w.tx = nil 431 w.err = err 432 return err 433 } 434 tx, err := w.qs.db.Tx(true) 435 if err != nil { 436 w.qs.writer.Unlock() 437 w.err = err 438 return err 439 } 440 w.tx = wrapTx(tx) 441 return nil 442 } 443 444 func (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) { 445 mApplyBatch.Observe(float64(len(buf))) 446 defer prometheus.NewTimer(mApplySeconds).ObserveDuration() 447 448 if w.tx == nil { 449 w.qs.writer.Lock() 450 tx, err := w.qs.db.Tx(true) 451 if err != nil { 452 w.qs.writer.Unlock() 453 w.err = err 454 return 0, err 455 } 456 w.tx = wrapTx(tx) 457 } 458 deltas := graphlog.InsertQuads(buf) 459 if _, err := w.qs.applyAddDeltas(w.tx, nil, deltas, graph.IgnoreOpts{IgnoreDup: true}); err != nil { 460 w.err = err 461 return 0, err 462 } 463 w.n += len(buf) 464 if w.n >= quad.DefaultBatch*20 { 465 if err := w.flush(); err != nil { 466 return 0, err 467 } 468 } 469 return len(buf), nil 470 } 471 472 func (w *quadWriter) Close() error { 473 if w.tx == nil { 474 return w.err 475 } 476 defer w.qs.writer.Unlock() 477 478 if w.err != nil { 479 _ = w.tx.Close() 480 w.tx = nil 481 return w.err 482 } 483 484 ctx := context.TODO() 485 // flush quad indexes and commit 486 err := w.qs.flushMapBucket(ctx, w.tx) 487 if err != nil { 488 _ = w.tx.Close() 489 w.tx = nil 490 return err 491 } 492 err = w.tx.Commit(ctx) 493 w.tx = nil 494 return err 495 } 496 497 func (qs *QuadStore) applyAddDeltas(tx kv.Tx, in []graph.Delta, deltas *graphlog.Deltas, ignoreOpts graph.IgnoreOpts) (map[graph.ValueHash]resolvedNode, error) { 498 ctx := context.TODO() 499 500 // first add all new nodes 501 nodes, err := qs.incNodes(ctx, tx, deltas.IncNode) 502 if err != nil { 503 return nil, err 504 } 505 deltas.IncNode = nil 506 // resolve and insert all new quads 507 links := make([]proto.Primitive, 0, len(deltas.QuadAdd)) 508 qadd := make(map[[4]uint64]struct{}, len(deltas.QuadAdd)) 509 for _, q := range deltas.QuadAdd { 510 var link proto.Primitive 511 mustBeNew := false 512 var qkey [4]uint64 513 for i, dir := range quad.Directions { 514 n, ok := nodes[q.Quad.Get(dir)] 515 if !ok { 516 continue 517 } 518 mustBeNew = mustBeNew || n.New 519 link.SetDirection(dir, n.ID) 520 qkey[i] = n.ID 521 } 522 if _, ok := qadd[qkey]; ok { 523 continue 524 } 525 qadd[qkey] = struct{}{} 526 if !mustBeNew { 527 p, err := qs.hasPrimitive(ctx, tx, &link, false) 528 if err != nil { 529 return nil, err 530 } 531 if p != nil { 532 if ignoreOpts.IgnoreDup { 533 continue // already exists, no need to insert 534 } 535 err = graph.ErrQuadExists 536 if len(in) != 0 { 537 return nil, &graph.DeltaError{Delta: in[q.Ind], Err: err} 538 } 539 return nil, err 540 } 541 } 542 links = append(links, link) 543 } 544 qadd = nil 545 deltas.QuadAdd = nil 546 547 qstart, err := qs.genIDs(ctx, tx, len(links)) 548 if err != nil { 549 return nil, err 550 } 551 for i := range links { 552 links[i].ID = qstart + uint64(i) 553 links[i].Timestamp = time.Now().UnixNano() 554 } 555 if err := qs.indexLinks(ctx, tx, links); err != nil { 556 return nil, err 557 } 558 return nodes, nil 559 } 560 561 func (qs *QuadStore) ApplyDeltas(in []graph.Delta, ignoreOpts graph.IgnoreOpts) error { 562 mApplyBatch.Observe(float64(len(in))) 563 defer prometheus.NewTimer(mApplySeconds).ObserveDuration() 564 565 ctx := context.TODO() 566 qs.writer.Lock() 567 defer qs.writer.Unlock() 568 tx, err := qs.db.Tx(true) 569 if err != nil { 570 return err 571 } 572 defer tx.Close() 573 tx = wrapTx(tx) 574 575 deltas := graphlog.SplitDeltas(in) 576 if len(deltas.QuadDel) != 0 || len(deltas.DecNode) != 0 { 577 qs.mapNodes = nil 578 } 579 580 nodes, err := qs.applyAddDeltas(tx, in, deltas, ignoreOpts) 581 if err != nil { 582 return err 583 } 584 585 if len(deltas.QuadDel) != 0 || len(deltas.DecNode) != 0 { 586 links := make([]proto.Primitive, 0, len(deltas.QuadDel)) 587 // resolve all nodes that will be removed 588 dnodes := make(map[graph.ValueHash]uint64, len(deltas.DecNode)) 589 if err := qs.resolveValDeltas(ctx, tx, deltas.DecNode, func(i int, id uint64) { 590 dnodes[deltas.DecNode[i].Hash] = id 591 }); err != nil { 592 return err 593 } 594 595 // check for existence and delete quads 596 fixNodes := make(map[graph.ValueHash]int) 597 for _, q := range deltas.QuadDel { 598 var link proto.Primitive 599 exists := true 600 // resolve values of all quad directions 601 // if any of the direction does not exists, the quad does not exists as well 602 for _, dir := range quad.Directions { 603 h := q.Quad.Get(dir) 604 n, ok := nodes[h] 605 if !ok { 606 var id uint64 607 id, ok = dnodes[h] 608 n.ID = id 609 } 610 if !ok { 611 exists = exists && !h.Valid() 612 continue 613 } 614 link.SetDirection(dir, n.ID) 615 } 616 if exists { 617 p, err := qs.hasPrimitive(ctx, tx, &link, true) 618 if err != nil { 619 return err 620 } else if p == nil || p.Deleted { 621 exists = false 622 } else { 623 link = *p 624 } 625 } 626 if !exists { 627 if !ignoreOpts.IgnoreMissing { 628 return &graph.DeltaError{Delta: in[q.Ind], Err: graph.ErrQuadNotExist} 629 } 630 // revert counters for all directions of this quad 631 for _, dir := range quad.Directions { 632 if h := q.Quad.Get(dir); h.Valid() { 633 fixNodes[h]++ 634 } 635 } 636 continue 637 } 638 links = append(links, link) 639 } 640 deltas.QuadDel = nil 641 if err := qs.markLinksDead(ctx, tx, links); err != nil { 642 return err 643 } 644 links = nil 645 nodes = nil 646 647 // we decremented some nodes that has non-existent quads - let's fix this 648 if len(fixNodes) != 0 { 649 for i, n := range deltas.DecNode { 650 if dn := fixNodes[n.Hash]; dn != 0 { 651 deltas.DecNode[i].RefInc += dn 652 } 653 } 654 } 655 656 // finally decrement and remove nodes 657 if err := qs.decNodes(ctx, tx, deltas.DecNode, dnodes); err != nil { 658 return err 659 } 660 deltas = nil 661 dnodes = nil 662 } 663 // flush quad indexes and commit 664 err = qs.flushMapBucket(ctx, tx) 665 if err != nil { 666 return err 667 } 668 return tx.Commit(ctx) 669 } 670 671 func (qs *QuadStore) indexNode(tx kv.Tx, p *proto.Primitive, val quad.Value) error { 672 var err error 673 if val == nil { 674 val, err = pquads.UnmarshalValue(p.Value) 675 if err != nil { 676 return err 677 } 678 } 679 hash := quad.HashOf(val) 680 err = tx.Put(bucketForVal(hash[0], hash[1]).AppendBytes(hash), uint64toBytes(p.ID)) 681 if err != nil { 682 return err 683 } 684 if iri, ok := val.(quad.IRI); ok { 685 qs.valueLRU.Put(string(iri), p.ID) 686 } 687 if qs.mapNodes != nil { 688 qs.mapNodes.Add(hash[:]) 689 } 690 return qs.addToLog(tx, p) 691 } 692 693 func (qs *QuadStore) indexLinks(ctx context.Context, tx kv.Tx, links []proto.Primitive) error { 694 for _, p := range links { 695 if err := qs.indexLink(tx, &p); err != nil { 696 return err 697 } 698 } 699 return qs.incSize(ctx, tx, int64(len(links))) 700 } 701 func (qs *QuadStore) indexLink(tx kv.Tx, p *proto.Primitive) error { 702 var err error 703 qs.indexes.RLock() 704 all := qs.indexes.all 705 qs.indexes.RUnlock() 706 for _, ind := range all { 707 err = qs.addToMapBucket(tx, ind.KeyFor(p), p.ID) 708 if err != nil { 709 return err 710 } 711 } 712 qs.bloomAdd(p) 713 err = qs.indexSchema(tx, p) 714 if err != nil { 715 return err 716 } 717 return qs.addToLog(tx, p) 718 } 719 720 func (qs *QuadStore) markAsDead(tx kv.Tx, p *proto.Primitive) error { 721 p.Deleted = true 722 //TODO(barakmich): Add tombstone? 723 qs.bloomRemove(p) 724 return qs.addToLog(tx, p) 725 } 726 727 func (qs *QuadStore) delLog(tx kv.Tx, id uint64) error { 728 return tx.Del(logIndex.Append(uint64KeyBytes(id))) 729 } 730 731 func (qs *QuadStore) markLinksDead(ctx context.Context, tx kv.Tx, links []proto.Primitive) error { 732 for _, p := range links { 733 if err := qs.markAsDead(tx, &p); err != nil { 734 return err 735 } 736 } 737 return qs.incSize(ctx, tx, -int64(len(links))) 738 } 739 740 func (qs *QuadStore) getBucketIndexes(ctx context.Context, tx kv.Tx, keys []kv.Key) ([][]uint64, error) { 741 vals, err := tx.GetBatch(ctx, keys) 742 if err != nil { 743 return nil, err 744 } 745 out := make([][]uint64, len(keys)) 746 for i, v := range vals { 747 if len(v) == 0 { 748 continue 749 } 750 ind, err := decodeIndex(v) 751 if err != nil { 752 return out, err 753 } 754 out[i] = ind 755 } 756 return out, nil 757 } 758 759 func countIndex(b []byte) (int64, error) { 760 var cnt int64 761 for len(b) > 0 { 762 _, n := binary.Uvarint(b) 763 if n == 0 { 764 return 0, io.ErrUnexpectedEOF 765 } else if n < 0 { 766 return 0, errors.New("varint: overflow") 767 } 768 cnt++ 769 b = b[n:] 770 } 771 return cnt, nil 772 } 773 774 func decodeIndex(b []byte) ([]uint64, error) { 775 var out []uint64 776 for len(b) > 0 { 777 v, n := binary.Uvarint(b) 778 if n == 0 { 779 return out, io.ErrUnexpectedEOF 780 } else if n < 0 { 781 return out, errors.New("varint: overflow") 782 } 783 out = append(out, v) 784 b = b[n:] 785 } 786 return out, nil 787 } 788 789 func appendIndex(bytelist []byte, l []uint64) []byte { 790 b := make([]byte, len(bytelist)+(binary.MaxVarintLen64*len(l))) 791 copy(b[:len(bytelist)], bytelist) 792 off := len(bytelist) 793 for _, x := range l { 794 n := binary.PutUvarint(b[off:], x) 795 off += n 796 } 797 return b[:off] 798 } 799 800 func (qs *QuadStore) bestUnique() ([]QuadIndex, error) { 801 qs.indexes.RLock() 802 ind := qs.indexes.exists 803 qs.indexes.RUnlock() 804 if len(ind) != 0 { 805 return ind, nil 806 } 807 qs.indexes.Lock() 808 defer qs.indexes.Unlock() 809 if len(qs.indexes.exists) != 0 { 810 return qs.indexes.exists, nil 811 } 812 for _, in := range qs.indexes.all { 813 if in.Unique { 814 if clog.V(2) { 815 clog.Infof("using unique index: %v", in.Dirs) 816 } 817 qs.indexes.exists = []QuadIndex{in} 818 return qs.indexes.exists, nil 819 } 820 } 821 // TODO: find best combination of indexes 822 inds := qs.indexes.all 823 if len(inds) == 0 { 824 return nil, fmt.Errorf("no indexes defined") 825 } 826 if clog.V(2) { 827 clog.Infof("using index intersection: %v", inds) 828 } 829 qs.indexes.exists = inds 830 return qs.indexes.exists, nil 831 } 832 833 func hasDir(dirs []quad.Direction, d quad.Direction) bool { 834 for _, d2 := range dirs { 835 if d == d2 { 836 return true 837 } 838 } 839 return false 840 } 841 842 func (qs *QuadStore) bestIndexes(dirs []quad.Direction) []QuadIndex { 843 qs.indexes.RLock() 844 all := qs.indexes.all 845 qs.indexes.RUnlock() 846 var ( 847 max int // more specific index is better 848 best QuadIndex 849 ) 850 for _, ind := range all { 851 if len(ind.Dirs) < len(dirs) { 852 continue // TODO(dennwc): allow intersecting indexes 853 } 854 match := 0 855 for i, d := range ind.Dirs { 856 if i >= len(dirs) || !hasDir(dirs, d) { 857 break 858 } 859 match++ 860 } 861 if match == len(dirs) { 862 // exact index match 863 return []QuadIndex{ind} 864 } 865 if match > 0 && match > max { 866 best = ind 867 max = match 868 } 869 } 870 if max == 0 { 871 return nil 872 } 873 // TODO(dennwc): intersect with some other index 874 return []QuadIndex{best} 875 } 876 877 func (qs *QuadStore) hasPrimitive(ctx context.Context, tx kv.Tx, p *proto.Primitive, get bool) (*proto.Primitive, error) { 878 if !qs.testBloom(p) { 879 mQuadsBloomHit.Inc() 880 return nil, nil 881 } 882 mQuadsBloomMiss.Inc() 883 inds, err := qs.bestUnique() 884 if err != nil { 885 return nil, err 886 } 887 unique := len(inds) != 0 && inds[0].Unique 888 keys := make([]kv.Key, len(inds)) 889 for i, in := range inds { 890 keys[i] = in.KeyFor(p) 891 } 892 lists, err := qs.getBucketIndexes(ctx, tx, keys) 893 if err != nil { 894 return nil, err 895 } 896 var options []uint64 897 for len(lists) > 0 { 898 if len(lists) == 1 { 899 options = lists[0] 900 break 901 } 902 a, b := lists[0], lists[1] 903 lists = lists[1:] 904 a = intersectSortedUint64(a, b) 905 lists[0] = a 906 } 907 if !get && unique { 908 return p, nil 909 } 910 for i := len(options) - 1; i >= 0; i-- { 911 // TODO: batch 912 prim, err := qs.getPrimitiveFromLog(ctx, tx, options[i]) 913 if err != nil { 914 return nil, err 915 } 916 if prim.Deleted { 917 continue 918 } 919 if prim.IsSameLink(p) { 920 return prim, nil 921 } 922 } 923 return nil, nil 924 } 925 926 func intersectSortedUint64(a, b []uint64) []uint64 { 927 var c []uint64 928 boff := 0 929 outer: 930 for _, x := range a { 931 for { 932 if boff >= len(b) { 933 break outer 934 } 935 if x > b[boff] { 936 boff++ 937 continue 938 } 939 if x < b[boff] { 940 break 941 } 942 if x == b[boff] { 943 c = append(c, x) 944 boff++ 945 break 946 } 947 } 948 } 949 return c 950 } 951 952 func (qs *QuadStore) addToMapBucket(tx kv.Tx, key kv.Key, value uint64) error { 953 if len(key) != 2 { 954 return fmt.Errorf("trying to add to map bucket with invalid key: %v", key) 955 } 956 b, k := key[0], key[1] 957 if len(k) == 0 { 958 return fmt.Errorf("trying to add to map bucket %s with key 0", b) 959 } 960 if qs.mapBucket == nil { 961 qs.mapBucket = make(map[string]map[string][]uint64) 962 } 963 bucket := string(b) 964 m, ok := qs.mapBucket[bucket] 965 if !ok { 966 m = make(map[string][]uint64) 967 qs.mapBucket[bucket] = m 968 } 969 m[string(k)] = append(m[string(k)], value) 970 mIndexWriteBufferEntries.WithLabelValues(bucket).Inc() 971 return nil 972 } 973 974 func (qs *QuadStore) flushMapBucket(ctx context.Context, tx kv.Tx) error { 975 bs := make([]string, 0, len(qs.mapBucket)) 976 for k := range qs.mapBucket { 977 bs = append(bs, k) 978 } 979 sort.Strings(bs) 980 for _, bucket := range bs { 981 m := qs.mapBucket[bucket] 982 if len(m) == 0 { 983 continue 984 } 985 bloom := qs.mapBloom[bucket] 986 mIndexWriteBufferFlushBatch.WithLabelValues(bucket).Observe(float64(len(m))) 987 entryBytes := mIndexEntrySizeBytes.WithLabelValues(bucket) 988 b := kv.Key{[]byte(bucket)} 989 var ( 990 keys []kv.Key 991 keysPut []kv.Key 992 ) 993 if qs.mapBloom == nil { 994 keys = make([]kv.Key, 0, len(m)) 995 } 996 for k := range m { 997 bk := []byte(k) 998 if qs.mapBloom != nil && (bloom == nil || !bloom.Test(bk)) { 999 keysPut = append(keysPut, b.AppendBytes(bk)) 1000 } else { 1001 keys = append(keys, b.AppendBytes(bk)) 1002 } 1003 } 1004 sort.Sort(kv.ByKey(keysPut)) 1005 sort.Sort(kv.ByKey(keys)) 1006 vals, err := tx.GetBatch(ctx, keys) 1007 if err != nil { 1008 return err 1009 } 1010 if qs.mapBloom != nil && bloom == nil { 1011 bloom = boom.NewBloomFilter(100*1000*1000, 0.05) 1012 qs.mapBloom[bucket] = bloom 1013 } 1014 for _, k := range keysPut { 1015 l := m[string(k[1])] 1016 err = tx.Put(k, appendIndex(nil, l)) 1017 if err != nil { 1018 return err 1019 } 1020 if bloom != nil { 1021 bloom.Add(k[1]) 1022 } 1023 } 1024 for i, k := range keys { 1025 l := m[string(k[1])] 1026 buf := appendIndex(vals[i], l) 1027 entryBytes.Observe(float64(len(buf))) 1028 err = tx.Put(k, buf) 1029 if err != nil { 1030 return err 1031 } 1032 if bloom != nil { 1033 bloom.Add(k[1]) 1034 } 1035 } 1036 mIndexWriteBufferEntries.WithLabelValues(bucket).Set(0) 1037 } 1038 qs.mapBucket = nil 1039 return nil 1040 } 1041 1042 func (qs *QuadStore) indexSchema(tx kv.Tx, p *proto.Primitive) error { 1043 return nil 1044 } 1045 1046 func (qs *QuadStore) addToLog(tx kv.Tx, p *proto.Primitive) error { 1047 buf, err := p.Marshal() 1048 if err != nil { 1049 return err 1050 } 1051 if err := tx.Put(logIndex.Append(uint64KeyBytes(p.ID)), buf); err != nil { 1052 return err 1053 } 1054 mPrimitiveAppend.Inc() 1055 return nil 1056 } 1057 1058 func createNodePrimitive(v quad.Value) (*proto.Primitive, error) { 1059 p := &proto.Primitive{} 1060 b, err := pquads.MarshalValue(v) 1061 if err != nil { 1062 return p, err 1063 } 1064 p.Value = b 1065 p.Timestamp = time.Now().UnixNano() 1066 return p, nil 1067 } 1068 1069 func (qs *QuadStore) resolveQuadValue(ctx context.Context, tx kv.Tx, v quad.Value) (uint64, error) { 1070 out, err := qs.resolveQuadValues(ctx, tx, []quad.Value{v}) 1071 if err != nil { 1072 return 0, err 1073 } 1074 return out[0], nil 1075 } 1076 1077 func bucketKeyForVal(v quad.Value) kv.Key { 1078 hash := graph.HashOf(v) 1079 return bucketKeyForHash(hash) 1080 } 1081 1082 func bucketKeyForHash(h graph.ValueHash) kv.Key { 1083 return bucketForVal(h[0], h[1]).AppendBytes(h[:]) 1084 } 1085 1086 func bucketKeyForHashRefs(h graph.ValueHash) kv.Key { 1087 return bucketForValRefs(h[0], h[1]).AppendBytes(h[:]) 1088 } 1089 1090 func (qs *QuadStore) resolveQuadValues(ctx context.Context, tx kv.Tx, vals []quad.Value) ([]uint64, error) { 1091 out := make([]uint64, len(vals)) 1092 inds := make([]int, 0, len(vals)) 1093 keys := make([]kv.Key, 0, len(vals)) 1094 for i, v := range vals { 1095 if iri, ok := v.(quad.IRI); ok { 1096 if x, ok := qs.valueLRU.Get(string(iri)); ok { 1097 out[i] = x.(uint64) 1098 continue 1099 } 1100 } else if v == nil { 1101 continue 1102 } 1103 inds = append(inds, i) 1104 keys = append(keys, bucketKeyForVal(v)) 1105 } 1106 if len(keys) == 0 { 1107 return out, nil 1108 } 1109 resp, err := tx.GetBatch(ctx, keys) 1110 if err != nil { 1111 return out, err 1112 } 1113 for i, b := range resp { 1114 if len(b) == 0 { 1115 continue 1116 } 1117 ind := inds[i] 1118 out[ind], _ = binary.Uvarint(b) 1119 if iri, ok := vals[ind].(quad.IRI); ok && out[ind] != 0 { 1120 qs.valueLRU.Put(string(iri), uint64(out[ind])) 1121 } 1122 } 1123 return out, nil 1124 } 1125 1126 func uint64toBytes(x uint64) []byte { 1127 b := make([]byte, binary.MaxVarintLen64) 1128 return uint64toBytesAt(x, b) 1129 } 1130 1131 func uint64toBytesAt(x uint64, bytes []byte) []byte { 1132 n := binary.PutUvarint(bytes, x) 1133 return bytes[:n] 1134 } 1135 1136 func uint64KeyBytes(x uint64) kv.Key { 1137 k := make([]byte, 8) 1138 quadKeyEnc.PutUint64(k, x) 1139 return kv.Key{k} 1140 } 1141 1142 func (qs *QuadStore) getPrimitivesFromLog(ctx context.Context, tx kv.Tx, keys []uint64) ([]*proto.Primitive, error) { 1143 bkeys := make([]kv.Key, len(keys)) 1144 for i, k := range keys { 1145 bkeys[i] = logIndex.Append(uint64KeyBytes(k)) 1146 } 1147 vals, err := tx.GetBatch(ctx, bkeys) 1148 if err != nil { 1149 return nil, err 1150 } 1151 mPrimitiveFetch.Add(float64(len(vals))) 1152 out := make([]*proto.Primitive, len(keys)) 1153 var last error 1154 for i, v := range vals { 1155 if v == nil { 1156 mPrimitiveFetchMiss.Inc() 1157 continue 1158 } 1159 var p proto.Primitive 1160 if err = p.Unmarshal(v); err != nil { 1161 last = err 1162 } else { 1163 out[i] = &p 1164 } 1165 } 1166 return out, last 1167 } 1168 1169 func (qs *QuadStore) getPrimitiveFromLog(ctx context.Context, tx kv.Tx, k uint64) (*proto.Primitive, error) { 1170 out, err := qs.getPrimitivesFromLog(ctx, tx, []uint64{k}) 1171 if err != nil { 1172 return nil, err 1173 } else if out[0] == nil { 1174 return nil, kv.ErrNotFound 1175 } 1176 return out[0], nil 1177 } 1178 1179 func (qs *QuadStore) initBloomFilter(ctx context.Context) error { 1180 if qs.exists.disabled { 1181 return nil 1182 } 1183 qs.exists.buf = make([]byte, 3*8) 1184 qs.exists.DeletableBloomFilter = boom.NewDeletableBloomFilter(100*1000*1000, 120, 0.05) 1185 return kv.View(qs.db, func(tx kv.Tx) error { 1186 p := proto.Primitive{} 1187 it := tx.Scan(logIndex) 1188 defer it.Close() 1189 for it.Next(ctx) { 1190 v := it.Val() 1191 p = proto.Primitive{} 1192 err := p.Unmarshal(v) 1193 if err != nil { 1194 return err 1195 } 1196 if p.IsNode() { 1197 continue 1198 } else if p.Deleted { 1199 continue 1200 } 1201 writePrimToBuf(&p, qs.exists.buf) 1202 qs.exists.Add(qs.exists.buf) 1203 } 1204 return it.Err() 1205 }) 1206 } 1207 1208 func (qs *QuadStore) testBloom(p *proto.Primitive) bool { 1209 if qs.exists.disabled { 1210 return true // false positives are expected 1211 } 1212 qs.exists.Lock() 1213 defer qs.exists.Unlock() 1214 writePrimToBuf(p, qs.exists.buf) 1215 return qs.exists.Test(qs.exists.buf) 1216 } 1217 1218 func (qs *QuadStore) bloomRemove(p *proto.Primitive) { 1219 if qs.exists.disabled { 1220 return 1221 } 1222 qs.exists.Lock() 1223 defer qs.exists.Unlock() 1224 writePrimToBuf(p, qs.exists.buf) 1225 qs.exists.TestAndRemove(qs.exists.buf) 1226 } 1227 1228 func (qs *QuadStore) bloomAdd(p *proto.Primitive) { 1229 if qs.exists.disabled { 1230 return 1231 } 1232 qs.exists.Lock() 1233 defer qs.exists.Unlock() 1234 writePrimToBuf(p, qs.exists.buf) 1235 qs.exists.Add(qs.exists.buf) 1236 } 1237 1238 func writePrimToBuf(p *proto.Primitive, buf []byte) { 1239 quadKeyEnc.PutUint64(buf[0:8], p.Subject) 1240 quadKeyEnc.PutUint64(buf[8:16], p.Predicate) 1241 quadKeyEnc.PutUint64(buf[16:24], p.Object) 1242 } 1243 1244 type Int64Set []uint64 1245 1246 func (a Int64Set) Len() int { return len(a) } 1247 func (a Int64Set) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 1248 func (a Int64Set) Less(i, j int) bool { return a[i] < a[j] }