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  }