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] }