github.com/cayleygraph/cayley@v0.7.7/graph/quadwriter.go (about)

     1  // Copyright 2014 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 graph
    16  
    17  // Defines the interface for consistent replication of a graph instance.
    18  //
    19  // Separate from the backend, this dictates how individual quads get
    20  // identified and replicated consistently across (potentially) multiple
    21  // instances. The simplest case is to keep an append-only log of quad
    22  // changes.
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"io"
    28  
    29  	"github.com/cayleygraph/quad"
    30  )
    31  
    32  type Procedure int8
    33  
    34  func (p Procedure) String() string {
    35  	switch p {
    36  	case +1:
    37  		return "add"
    38  	case -1:
    39  		return "delete"
    40  	default:
    41  		return "invalid"
    42  	}
    43  }
    44  
    45  // The different types of actions a transaction can do.
    46  const (
    47  	Add    Procedure = +1
    48  	Delete Procedure = -1
    49  )
    50  
    51  type Delta struct {
    52  	Quad   quad.Quad
    53  	Action Procedure
    54  }
    55  
    56  // Unwrap returns an original QuadStore value if it was wrapped by Handle.
    57  // This prevents shadowing of optional interface implementations.
    58  func Unwrap(qs QuadStore) QuadStore {
    59  	if h, ok := qs.(*Handle); ok {
    60  		return h.QuadStore
    61  	}
    62  	return qs
    63  }
    64  
    65  type Handle struct {
    66  	QuadStore
    67  	QuadWriter
    68  }
    69  
    70  type IgnoreOpts struct {
    71  	IgnoreDup, IgnoreMissing bool
    72  }
    73  
    74  func (h *Handle) Close() error {
    75  	err := h.QuadWriter.Close()
    76  	h.QuadStore.Close()
    77  	return err
    78  }
    79  
    80  var (
    81  	ErrQuadExists    = errors.New("quad exists")
    82  	ErrQuadNotExist  = errors.New("quad does not exist")
    83  	ErrInvalidAction = errors.New("invalid action")
    84  	ErrNodeNotExists = errors.New("node does not exist")
    85  )
    86  
    87  // DeltaError records an error and the delta that caused it.
    88  type DeltaError struct {
    89  	Delta Delta
    90  	Err   error
    91  }
    92  
    93  func (e *DeltaError) Error() string {
    94  	if !e.Delta.Quad.IsValid() {
    95  		return e.Err.Error()
    96  	}
    97  	return e.Delta.Action.String() + " " + e.Delta.Quad.String() + ": " + e.Err.Error()
    98  }
    99  
   100  // IsQuadExist returns whether an error is a DeltaError
   101  // with the Err field equal to ErrQuadExists.
   102  func IsQuadExist(err error) bool {
   103  	if err == ErrQuadExists {
   104  		return true
   105  	}
   106  	de, ok := err.(*DeltaError)
   107  	return ok && de.Err == ErrQuadExists
   108  }
   109  
   110  // IsQuadNotExist returns whether an error is a DeltaError
   111  // with the Err field equal to ErrQuadNotExist.
   112  func IsQuadNotExist(err error) bool {
   113  	if err == ErrQuadNotExist {
   114  		return true
   115  	}
   116  	de, ok := err.(*DeltaError)
   117  	return ok && de.Err == ErrQuadNotExist
   118  }
   119  
   120  // IsInvalidAction returns whether an error is a DeltaError
   121  // with the Err field equal to ErrInvalidAction.
   122  func IsInvalidAction(err error) bool {
   123  	if err == ErrInvalidAction {
   124  		return true
   125  	}
   126  	de, ok := err.(*DeltaError)
   127  	return ok && de.Err == ErrInvalidAction
   128  }
   129  
   130  var (
   131  	// IgnoreDuplicates specifies whether duplicate quads
   132  	// cause an error during loading or are ignored.
   133  	IgnoreDuplicates = true
   134  
   135  	// IgnoreMissing specifies whether missing quads
   136  	// cause an error during deletion or are ignored.
   137  	IgnoreMissing = false
   138  )
   139  
   140  type QuadWriter interface {
   141  	// AddQuad adds a quad to the store.
   142  	AddQuad(quad.Quad) error
   143  
   144  	// TODO(barakmich): Deprecate in favor of transaction.
   145  	// AddQuadSet adds a set of quads to the store, atomically if possible.
   146  	AddQuadSet([]quad.Quad) error
   147  
   148  	// RemoveQuad removes a quad matching the given one  from the database,
   149  	// if it exists. Does nothing otherwise.
   150  	RemoveQuad(quad.Quad) error
   151  
   152  	// ApplyTransaction applies a set of quad changes.
   153  	ApplyTransaction(*Transaction) error
   154  
   155  	// RemoveNode removes all quads which have the given node as subject, predicate, object, or label.
   156  	//
   157  	// It returns ErrNodeNotExists if node is missing.
   158  	RemoveNode(quad.Value) error
   159  
   160  	// Close cleans up replication and closes the writing aspect of the database.
   161  	Close() error
   162  }
   163  
   164  type NewQuadWriterFunc func(QuadStore, Options) (QuadWriter, error)
   165  
   166  var writerRegistry = make(map[string]NewQuadWriterFunc)
   167  
   168  func RegisterWriter(name string, newFunc NewQuadWriterFunc) {
   169  	if _, found := writerRegistry[name]; found {
   170  		panic("already registered QuadWriter " + name)
   171  	}
   172  	writerRegistry[name] = newFunc
   173  }
   174  
   175  func NewQuadWriter(name string, qs QuadStore, opts Options) (QuadWriter, error) {
   176  	newFunc, hasNew := writerRegistry[name]
   177  	if !hasNew {
   178  		return nil, errors.New("replication: name '" + name + "' is not registered")
   179  	}
   180  	return newFunc(qs, opts)
   181  }
   182  
   183  func WriterMethods() []string {
   184  	t := make([]string, 0, len(writerRegistry))
   185  	for n := range writerRegistry {
   186  		t = append(t, n)
   187  	}
   188  	return t
   189  }
   190  
   191  type BatchWriter interface {
   192  	quad.WriteCloser
   193  	Flush() error
   194  }
   195  
   196  // NewWriter creates a quad writer for a given QuadStore.
   197  //
   198  // Caller must call Flush or Close to flush an internal buffer.
   199  func NewWriter(qs QuadWriter) BatchWriter {
   200  	return &batchWriter{qs: qs}
   201  }
   202  
   203  type batchWriter struct {
   204  	qs  QuadWriter
   205  	buf []quad.Quad
   206  }
   207  
   208  func (w *batchWriter) flushBuffer(force bool) error {
   209  	if !force && len(w.buf) < quad.DefaultBatch {
   210  		return nil
   211  	}
   212  	_, err := w.WriteQuads(w.buf)
   213  	w.buf = w.buf[:0]
   214  	return err
   215  }
   216  
   217  func (w *batchWriter) WriteQuad(q quad.Quad) error {
   218  	if err := w.flushBuffer(false); err != nil {
   219  		return err
   220  	}
   221  	w.buf = append(w.buf, q)
   222  	return nil
   223  }
   224  func (w *batchWriter) WriteQuads(quads []quad.Quad) (int, error) {
   225  	if err := w.qs.AddQuadSet(quads); err != nil {
   226  		return 0, err
   227  	}
   228  	return len(quads), nil
   229  }
   230  func (w *batchWriter) Flush() error {
   231  	return w.flushBuffer(true)
   232  }
   233  func (w *batchWriter) Close() error {
   234  	return w.Flush()
   235  }
   236  
   237  // NewTxWriter creates a writer that applies a given procedures for all quads in stream.
   238  // If procedure is zero, Add operation will be used.
   239  func NewTxWriter(tx *Transaction, p Procedure) quad.Writer {
   240  	if p == 0 {
   241  		p = Add
   242  	}
   243  	return &txWriter{tx: tx, p: p}
   244  }
   245  
   246  type txWriter struct {
   247  	tx *Transaction
   248  	p  Procedure
   249  }
   250  
   251  func (w *txWriter) WriteQuad(q quad.Quad) error {
   252  	switch w.p {
   253  	case Add:
   254  		w.tx.AddQuad(q)
   255  	case Delete:
   256  		w.tx.RemoveQuad(q)
   257  	default:
   258  		return ErrInvalidAction
   259  	}
   260  	return nil
   261  }
   262  
   263  func (w *txWriter) WriteQuads(buf []quad.Quad) (int, error) {
   264  	for i, q := range buf {
   265  		if err := w.WriteQuad(q); err != nil {
   266  			return i, err
   267  		}
   268  	}
   269  	return len(buf), nil
   270  }
   271  
   272  // NewRemover creates a quad writer for a given QuadStore which removes quads instead of adding them.
   273  func NewRemover(qs QuadWriter) BatchWriter {
   274  	return &removeWriter{qs: qs}
   275  }
   276  
   277  type removeWriter struct {
   278  	qs QuadWriter
   279  }
   280  
   281  func (w *removeWriter) WriteQuad(q quad.Quad) error {
   282  	return w.qs.RemoveQuad(q)
   283  }
   284  func (w *removeWriter) WriteQuads(quads []quad.Quad) (int, error) {
   285  	tx := NewTransaction()
   286  	for _, q := range quads {
   287  		tx.RemoveQuad(q)
   288  	}
   289  	if err := w.qs.ApplyTransaction(tx); err != nil {
   290  		return 0, err
   291  	}
   292  	return len(quads), nil
   293  }
   294  func (w *removeWriter) Flush() error {
   295  	return nil // TODO: batch deletes automatically
   296  }
   297  func (w *removeWriter) Close() error { return nil }
   298  
   299  // NewResultReader creates a quad reader for a given QuadStore.
   300  func NewQuadStoreReader(qs QuadStore) quad.ReadSkipCloser {
   301  	return NewResultReader(qs, nil)
   302  }
   303  
   304  // NewResultReader creates a quad reader for a given QuadStore and iterator.
   305  // If iterator is nil QuadsAllIterator will be used.
   306  //
   307  // Only quads returned by iterator's Result will be used.
   308  //
   309  // Iterator will be closed with the reader.
   310  func NewResultReader(qs QuadStore, it Iterator) quad.ReadSkipCloser {
   311  	if it == nil {
   312  		it = qs.QuadsAllIterator()
   313  	}
   314  	return &quadReader{qs: qs, it: it}
   315  }
   316  
   317  type quadReader struct {
   318  	qs QuadStore
   319  	it Iterator
   320  }
   321  
   322  func (r *quadReader) ReadQuad() (quad.Quad, error) {
   323  	if r.it.Next(context.TODO()) {
   324  		return r.qs.Quad(r.it.Result()), nil
   325  	}
   326  	err := r.it.Err()
   327  	if err == nil {
   328  		err = io.EOF
   329  	}
   330  	return quad.Quad{}, err
   331  }
   332  func (r *quadReader) SkipQuad() error {
   333  	if r.it.Next(context.TODO()) {
   334  		return nil
   335  	}
   336  	if err := r.it.Err(); err != nil {
   337  		return err
   338  	}
   339  	return io.EOF
   340  }
   341  func (r *quadReader) Close() error { return r.it.Close() }