strato

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2019 License: MIT Imports: 17 Imported by: 0

README

Strato

Actions Status GoDoc

An all-in-one data service with support for:

  • Key/value operations
  • Counters and sets
  • Caching with TTL

Strato is meant to abstract away complex database interfaces (Redis, DynamoDB, Mongo, memory, disk, etc.) in favor of a unified set of dead-simple operations (see the full list of operations below).

You can run Strato as a gRPC server or an HTTP server (both expose the same interfaces). There's currently a gRPC client for Go only but in principle gRPC clients could be added for other languages. There are also three backends available: memory, disk, and Redis.

Since any server type can work with any backend, the following server/backend combinations are currently supported:

Server Backend
gRPC Memory
gRPC Disk
gRPC Redis
HTTP Memory
HTTP Disk
HTTP Redis

The project

Goals

Microservices or FaaS functions that rely on stateful data operations can use Strato instead of needing to interact with multiple databases. This greatly simplifies the service/function development process by sharply reducing the hassle of dealing with databases (i.e. no need to install/learn/use 5 different database clients).

Does your service need something that isn't provided by Strato? File an issue or submit a PR and I'll add it!

Current status

Strato is in its very early stages. The data interfaces it provides are almost comically simple. Please do not use Strato as a production data service just yet (though I'd like to get there). Instead, use it for prototyping and experimenting.

Also be aware that Strato runs as a single instance and has no clustering built in (and thus isn't highly available). If you use the Redis backend, however, you can run multiple instances of Strato that connect to a single Redis cluster.

Future directions

In the future, I imagine Strato acting as an abstraction layer over lots of different data systems, exposing a powerful interface that covers the overwhelming majority of data use cases without exposing the system internals of any of those systems. This would entail:

  • Making the current data interfaces more sophisticated and capable of covering a wider range of use cases
  • Adding new interfaces, such as a timeseries interface, a simple graph interface, etc.
  • Providing a relational interface that supports a subset of SQL (SQLite would likely suffice for this)
  • Providing optional pluggable backends behind Strato (e.g. using Redis for caching, Elasticsearch for search, etc.)
  • Providing a message queue/pub-sub interface, eliminating the need for a Kafka/Pulsar/RabbitMQ/etc. client
Want to contribute?

See the contributors guide for details.

Operations

The table below lists the available client operations for the Go client:

Operation Service Semantics
CacheGet(key string) Cache Fetches the value of a key from the cache or returns a not found error if the key doesn't exist or has expired.
CacheSet(key, value string, ttl int32) Cache Sets the value associated with a key and assigns a TTL (the default is 5 seconds). Overwrites the value and TTL if the key already exists.
CounterIncrement(key string, amount int64) Counter Increments a counter by the designated amount. Returns the new value of the counter or an error.
CounterGet(key string) Counter Fetches the current value of a counter. Returns zero if the counter isn't found.
SetGet(set string) Set Fetch the items currently in the specified set. Returns an empty string set ([]string) if the set isn't found.
SetAdd(set, item string) Set Adds an item to the specified set and returns the resulting set.
SetRemove(set, item string) Set Removes an item from the specified set and returns the resulting set. Returns an empty set isn't found or is already empty.
KVGet(key string) KV Gets the value associated with a key or returns a not found error. The value is currently just a byte array payload but could be made more complex later (e.g. a payload plus a content type, metadata, etc.).
KVPut(key string, value *Value) KV Sets the value associated with a key, overwriting any existing value.
KVDelete(key string) KV Deletes the value associated with a key or returns a not found error.

Backends

There are three backends available for Strato:

Backend Explanation
Disk Data is stored persistently on disk using the Badger library. Each service (cache, KV, etc.) is stored in its own separate on-disk DB, which guarantees key isolation.
Memory Data is stored in native Go data structures (maps, slices, etc.). This backend is blazing fast but all data is lost when the service restarts.
Redis The Strato server stores all data in a persistent Redis database. Each service uses a different Redis database, which provides key isolation.

Try it out

To try out Strato locally, you can run the Strato gRPC server in one shell session and some example client operations in another session:

git clone https://github.com/lucperkins/strato && cd strato

# Start the gRPC server...
go run examples/grpc-server/main.go

# And then in a different session...
go run examples/grpc-client/main.go

Installation

gRPC server

To install the Strato gRPC server:

# Executable
go get github.com/lucperkins/strato/cmd/strato-grpc

# Docker image
docker pull lucperkins/strato-grpc:latest

Then you can run it:

# Executable
strato-grpc

# Docker image
docker run --rm -it -p 8080:8080 lucperkins/strato-grpc:latest

You should see log output like this:

2019/07/27 14:37:09 Starting up the server on port 8080
HTTP server

To install the Strato HTTP server:

# Executable
go get github.com/lucperkins/strato/cmd/strato-http

# Docker image
docker pull lucperkins/strato-http:latest

Then you can run it:

# Executable
strato-http

# Docker image
docker run --rm -it -p 8081:8081 lucperkins/strato-http:latest
gRPC Go client

To use the Go client in your service or FaaS function:

go get github.com/lucperkins/strato

To instantiate a client:

import "github.com/lucperkins/strato"

// Supply the address of the Strato gRPC server
client, err := strato.NewGrpcClient("localhost:8080")
if err != nil { 
    // Handle error
}

// Now you can run the various data operations, for example:
if err := client.CacheSet("player1-session", "a1b2c3d4e5f6", 120); err != nil {
    // Handle error
}
HTTP Go client
import "github.com/lucperkins/strato"

client, err := strato.NewHttpClient("http://localhost:8081")
if err != nil {
    // Handle error
}

count, err := client.CounterIncrement("player1-points", 100)

Deployment

Kubernetes

There are two configuration files in the deploy directory that enable you to run the Strato gRPC and HTTP servers, respectively, on Kubernetes. Both use the default namespace.

gRPC
kubectl apply -f deploy/strato-grpc-k8s.yaml
HTTP
kubectl apply -f deploy/strato-http-k8s.yaml
Accessing the service

Once you've deployed Strato on Kubernetes, you can access it in your local environment using port forwarding:

# gRPC
kubectl port-forward svc/strato 8080:8080

# HTTP
kubectl port-forward svc/strato 8081:8081

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoKey                = errors.New("no resource key provided")
	ErrNoValue              = errors.New("no value provided")
	ErrHttpUnavailable      = errors.New("could not connect to Strato HTTP server")
	ErrNoAddress            = errors.New("no server address provided")
	ErrNoPort               = errors.New("no server port provided")
	ErrPortOutOfRange       = errors.New("port must be between 1024 and 49151")
	ErrBackendNotRecognized = errors.New("backend key not recognized")
	ErrNoBackend            = errors.New("no backend specified")
)

Functions

func IsNotFound

func IsNotFound(err error) bool

Types

type ClientConfig

type ClientConfig struct {
	Address string
}

func (*ClientConfig) Validate

func (c *ClientConfig) Validate() error

type GrpcClient added in v0.1.2

type GrpcClient struct {
	// contains filtered or unexported fields
}

func NewGrpcClient added in v0.1.2

func NewGrpcClient(cfg *ClientConfig) (*GrpcClient, error)

func (*GrpcClient) CacheGet added in v0.1.2

func (c *GrpcClient) CacheGet(key string) (string, error)

Cache

func (*GrpcClient) CacheSet added in v0.1.2

func (c *GrpcClient) CacheSet(key, value string, ttl int32) error

func (*GrpcClient) CounterGet added in v0.1.2

func (c *GrpcClient) CounterGet(key string) (int64, error)

Counter

func (*GrpcClient) CounterIncrement added in v0.1.2

func (c *GrpcClient) CounterIncrement(key string, amount int64) error

func (*GrpcClient) KVDelete added in v0.1.2

func (c *GrpcClient) KVDelete(key string) error

func (*GrpcClient) KVGet added in v0.1.2

func (c *GrpcClient) KVGet(key string) (*kv.Value, error)

KV

func (*GrpcClient) KVPut added in v0.1.2

func (c *GrpcClient) KVPut(key string, value *kv.Value) error

func (*GrpcClient) SetAdd added in v0.1.2

func (c *GrpcClient) SetAdd(set, item string) ([]string, error)

func (*GrpcClient) SetGet added in v0.1.2

func (c *GrpcClient) SetGet(set string) ([]string, error)

Set

func (*GrpcClient) SetRemove added in v0.1.2

func (c *GrpcClient) SetRemove(set, item string) ([]string, error)

type HttpClient added in v0.1.2

type HttpClient struct {
	// contains filtered or unexported fields
}

func NewHttpClient added in v0.1.2

func NewHttpClient(cfg *ClientConfig) (*HttpClient, error)

func (*HttpClient) CacheGet added in v0.1.2

func (c *HttpClient) CacheGet(key string) (string, error)

Cache operations

func (*HttpClient) CacheSet added in v0.1.2

func (c *HttpClient) CacheSet(key, value string, ttl int32) error

func (*HttpClient) CounterGet added in v0.1.2

func (c *HttpClient) CounterGet(key string) (int64, error)

Counter operations

func (*HttpClient) CounterIncrement added in v0.1.2

func (c *HttpClient) CounterIncrement(key string, increment int64) error

func (*HttpClient) FlagGet added in v0.1.3

func (c *HttpClient) FlagGet(key string) (bool, error)

func (*HttpClient) FlagSet added in v0.1.3

func (c *HttpClient) FlagSet(key string, value bool) error

func (*HttpClient) KVDelete added in v0.1.2

func (c *HttpClient) KVDelete(key string) error

func (*HttpClient) KVGet added in v0.1.2

func (c *HttpClient) KVGet(key string) (*kv.Value, error)

KV operations

func (*HttpClient) KVPut added in v0.1.2

func (c *HttpClient) KVPut(key string, value *kv.Value) error

func (*HttpClient) SetAdd added in v0.1.2

func (c *HttpClient) SetAdd(key, item string) ([]string, error)

func (*HttpClient) SetGet added in v0.1.2

func (c *HttpClient) SetGet(key string) ([]string, error)

Set

func (*HttpClient) SetRemove added in v0.1.2

func (c *HttpClient) SetRemove(key, item string) ([]string, error)

type NotFoundError

type NotFoundError struct {
	// contains filtered or unexported fields
}

func NotFound

func NotFound(key string) NotFoundError

func (NotFoundError) AsProtoStatus

func (e NotFoundError) AsProtoStatus() error

func (NotFoundError) Error

func (e NotFoundError) Error() string

type ServerConfig

type ServerConfig struct {
	Port     int
	Debug    bool
	Backend  string
	RedisUrl string
}

func (*ServerConfig) Validate

func (c *ServerConfig) Validate() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL