github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/connector.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package dosa
    22  
    23  import (
    24  	"context"
    25  
    26  	"github.com/pkg/errors"
    27  )
    28  
    29  //go:generate stringer -type=Operator
    30  
    31  // Operator defines an operator against some data for range scans
    32  type Operator int
    33  
    34  // order of appearance matter here
    35  const (
    36  	// Eq is the equals operator
    37  	Eq Operator = iota + 1
    38  
    39  	// Lt is the less than operator
    40  	Lt
    41  
    42  	// LtOrEq is the less than or equal operator
    43  	LtOrEq
    44  
    45  	// Gt is the greater than operator
    46  	Gt
    47  
    48  	// GtOrEq is the greater than or equal operator
    49  	GtOrEq
    50  
    51  	// InvalidVersion is version which is less than 1
    52  	InvalidVersion = -1
    53  )
    54  
    55  // FieldNameValuePair is a field name and value
    56  type FieldNameValuePair struct {
    57  	Name  string
    58  	Value FieldValue
    59  }
    60  
    61  // SchemaRef is a reference to the table and schema version of an object
    62  type SchemaRef struct {
    63  	Scope      string
    64  	NamePrefix string
    65  	EntityName string
    66  	IndexName  string
    67  	Version    int32
    68  }
    69  
    70  // EntityInfo is all the information about an entity, including the schema reference
    71  // as well as the entity definition
    72  type EntityInfo struct {
    73  	Ref *SchemaRef
    74  	Def *EntityDefinition
    75  }
    76  
    77  // FieldValue holds a field value. It's just a marker.
    78  type FieldValue interface{}
    79  
    80  // FieldValuesOrError either holds a slice of field values for a row, or an error
    81  type FieldValuesOrError struct {
    82  	Values map[string]FieldValue
    83  	Error  error
    84  }
    85  
    86  // SchemaStatus saves the version and application status of a schema
    87  type SchemaStatus struct {
    88  	// the version of the schema
    89  	Version int32
    90  	// the application status of the schema
    91  	Status string
    92  }
    93  
    94  // Connector is the interface that must be implemented for a backend service
    95  // It can also be implemented using an RPC such as thrift (dosa-idl)
    96  // When fields are returned from read/range/scan methods, it's legal for the connector
    97  // to return more fields than originally requested. The caller of the connector should never mutate
    98  // the returned columns either, in case they are from a cache
    99  type Connector interface {
   100  	// DML operations (CRUD + range + scan)
   101  	// CreateIfNotExists creates a row, but only if it does not exist.
   102  	CreateIfNotExists(ctx context.Context, ei *EntityInfo, values map[string]FieldValue) error
   103  	// Read fetches a row by primary key
   104  	// If minimumFields is empty or nil, all non-key fields would be fetched.
   105  	Read(ctx context.Context, ei *EntityInfo, keys map[string]FieldValue, minimumFields []string) (values map[string]FieldValue, err error)
   106  	// MultiRead fetches several rows by primary key
   107  	// If minimumFields is empty or nil, all non-key fields would be fetched.
   108  	MultiRead(ctx context.Context, ei *EntityInfo, keys []map[string]FieldValue, minimumFields []string) (results []*FieldValuesOrError, err error)
   109  	// Upsert updates some columns of a row, or creates a new one if it doesn't exist yet.
   110  	Upsert(ctx context.Context, ei *EntityInfo, values map[string]FieldValue) error
   111  	// MultiUpsert updates some columns of several rows, or creates a new ones if they doesn't exist yet
   112  	MultiUpsert(ctx context.Context, ei *EntityInfo, multiValues []map[string]FieldValue) (result []error, err error)
   113  	// Remove deletes a row
   114  	Remove(ctx context.Context, ei *EntityInfo, keys map[string]FieldValue) error
   115  	// RemoveRange removes all entities in a particular range
   116  	RemoveRange(ctx context.Context, ei *EntityInfo, columnConditions map[string][]*Condition) error
   117  	// MultiRemove removes multiple rows
   118  	MultiRemove(ctx context.Context, ei *EntityInfo, multiKeys []map[string]FieldValue) (result []error, err error)
   119  	// Range does a range scan using a set of conditions.
   120  	// If minimumFields is empty or nil, all fields (including key fields) would be fetched.
   121  	Range(ctx context.Context, ei *EntityInfo, columnConditions map[string][]*Condition, minimumFields []string, token string, limit int) ([]map[string]FieldValue, string, error)
   122  	// Scan reads the whole table, for doing a sequential search or dump/load use cases
   123  	// If minimumFields is empty or nil, all fields (including key fields) would be fetched.
   124  	Scan(ctx context.Context, ei *EntityInfo, minimumFields []string, token string, limit int) (multiValues []map[string]FieldValue, nextToken string, err error)
   125  
   126  	// DDL operations (schema)
   127  	// CheckSchema validates that the set of entities you have provided is valid and registered already
   128  	// It returns a list of SchemaRef objects for use with later DML operations.
   129  	CheckSchema(ctx context.Context, scope string, namePrefix string, ed []*EntityDefinition) (version int32, err error)
   130  	// UpsertSchema updates the schema to match what you provide as entities, if possible
   131  	UpsertSchema(ctx context.Context, scope string, namePrefix string, ed []*EntityDefinition) (status *SchemaStatus, err error)
   132  	// CheckSchemaStatus checks the status of the schema whether it is accepted or in progress of application.
   133  	CheckSchemaStatus(ctx context.Context, scope string, namePrefix string, version int32) (*SchemaStatus, error)
   134  
   135  	// Datastore management
   136  	// CreateScope creates a scope for storage of data, usually implemented by a keyspace for this data
   137  	// This is usually followed by UpsertSchema
   138  	CreateScope(ctx context.Context, scope string) error
   139  	// TruncateScope keeps the scope around, but removes all the data
   140  	TruncateScope(ctx context.Context, scope string) error
   141  	// DropScope removes the scope and all of the data
   142  	DropScope(ctx context.Context, scope string) error
   143  	// ScopeExists checks whether a scope exists or not
   144  	ScopeExists(ctx context.Context, scope string) (bool, error)
   145  
   146  	// Shutdown finishes the connector to do clean up work
   147  	Shutdown() error
   148  }
   149  
   150  // CreationArgs contains values for configuring different connectors
   151  type CreationArgs map[string]interface{}
   152  
   153  // CreationFuncType is the type of a creation function that creates an instance of a registered connector
   154  type CreationFuncType func(CreationArgs) (Connector, error)
   155  
   156  var registeredConnectors map[string]CreationFuncType
   157  
   158  func init() {
   159  	// Can't seem to do this inline and make lint happy
   160  	registeredConnectors = map[string]CreationFuncType{}
   161  }
   162  
   163  // RegisterConnector registers a connector given a name
   164  func RegisterConnector(name string, creationFunc func(CreationArgs) (Connector, error)) {
   165  	registeredConnectors[name] = creationFunc
   166  }
   167  
   168  // GetConnector gets a connector by name, along with some options
   169  func GetConnector(name string, args CreationArgs) (Connector, error) {
   170  	if creationFunc, ok := registeredConnectors[name]; ok {
   171  		return creationFunc(args)
   172  	}
   173  	return nil, errors.Errorf("No such connector %q", name)
   174  }