Git Product home page Git Product logo

echovault / echovault Goto Github PK

View Code? Open in Web Editor NEW
137.0 137.0 3.0 7.1 MB

Embeddable Distributed in-memory data store with an emphasis on speed and reliability.

License: Apache License 2.0

Makefile 0.03% Go 99.97%
cache client-server cluster consistent database distributed distributed-database golang in-memory-database lfu-cache lru-cache memory networking pubsub redis store tcp tcp-server tls

echovault's Introduction

Go Go Report Card codecov
GitHub Release License
Go Reference


echovault_logo

What is EchoVault?

EchoVault is a highly configurable, distributed, in-memory data store and cache implemented in Go. It can be imported as a Go library or run as an independent service.

EchoVault aims to provide a rich set of data structures and functions for manipulating data in memory. These data structures include, but are not limited to: Lists, Sets, Sorted Sets, Hashes, and much more to come soon.

EchoVault provides a persistence layer for increased reliability. Both Append-Only files and snapshots can be used to persist data in the disk for recovery in case of unexpected shutdowns.

Replication is a core feature of EchoVault and is implemented using the RAFT algorithm, allowing you to create a fault-tolerant cluster of EchoVault nodes to improve reliability. If you do not need a replication cluster, you can always run EchoVault in standalone mode and have a fully capable single node.

EchoVault aims to not only be a server but to be importable to existing projects to enhance them with EchoVault features, this capability is always being worked on and improved.

Features

Some key features offered by EchoVault include:

  1. TLS and mTLS support for multiple server and client RootCAs.
  2. Replication cluster support using the RAFT algorithm.
  3. ACL Layer for user Authentication and Authorization.
  4. Distributed Pub/Sub functionality with consumer groups.
  5. Sets, Sorted Sets, Hashes, Lists and more.
  6. Persistence layer with Snapshots and Append-Only files.
  7. Key Eviction Policies.

We are working hard to add more features to EchoVault to make it much more powerful. Features in the roadmap include:

  1. Sharding
  2. Shared Object File Plugins
  3. Streams
  4. Transactions
  5. Bitmap
  6. HyperLogLog
  7. Lua Modules
  8. JSON
  9. Improved Observability

Usage (Embedded)

Install EchoVault with: go get github.com/echoVault/echoVault. Run go mod tidy to pull all of EchoVault's dependencies.

Here's an example of using EchoVault as an embedded library. You can access all of EchoVault's commands using an ergonomic API.

func main() {
	server, err := echovault.NewEchoVault(
		echovault.WithConfig(config.DefaultConfig()),
		echovault.WithCommands(commands.All()),
	)

	if err != nil {
		log.Fatal(err)
	}

	_, _ = server.SET("key", "Hello, world!", echovault.SETOptions{})

	v, _ := server.GET("key")

	fmt.Println(v) // Hello, world!

	wg := sync.WaitGroup{}

	// Subscribe to multiple EchoVault channels.
	readMessage := server.SUBSCRIBE("subscriber1", "channel_1", "channel_2", "channel_3")
	wg.Add(1)
	go func() {
		wg.Done()
		for {
			message := readMessage()
			fmt.Printf("EVENT: %s, CHANNEL: %s, MESSAGE: %s\n", message[0], message[1], message[2])
		}
	}()
	wg.Wait()

	wg.Add(1)
	go func() {
		for i := 1; i <= 3; i++ {
			// Simulating delay.
			<-time.After(1 * time.Second)
			// Publish message to each EchoVault channel.
			_, _ = server.PUBLISH(fmt.Sprintf("channel_%d", i), "Hello!")
		}
		wg.Done()
	}()
	wg.Wait()

	// (Optional): Listen for TCP connections on this EchoVault instance.
	server.Start()
}

An embedded EchoVault instance can still be part of a cluster, and the changes triggered from the API will be consistent across the cluster.

Usage (Client-Server)

Homebrew

To install via homebrew, run:

  1. brew tap echovault/echovault
  2. brew install echovault/echovault/echovault

Once installed, you can run the server with the following command: echovault --bind-addr=localhost --data-dir="path/to/persistence/directory"

Next, install the client via homebrew.

Binaries

You can download the binaries by clicking on a release tag and downloading the binary for your system.

Checkout the configuration section for the possible configuration flags.

Clients

EchoVault uses RESP, which makes it compatible with existing Redis clients.

Development Setup

Pre-requisites:

  1. Go
  2. Docker
  3. Docker Compose
  4. x86_64-linux-musl-gcc cross-compile toolchain as the development image is built for an Alpine container

Steps:

  1. Clone the repository.
  2. If you're on MacOS, you can run make build && docker-compose up --build to build the project and spin up the development docker container.
  3. If you're on another OS, you will have to use go build with the relevant flags for your system.

Table of Contents

  1. Configuration
  2. Eviction
  3. Contribution

Configuration

EchoVault is highly configurable. It provides the following configuration options to you:

Flag: --config
Type: string/path
Description: The file path for the server configuration. A JSON or YAML file can be used for server configuration. You can combine CLI flags and config files, but remember that config files override CLI flags. The config file will be prioritised if you have the same config option in the CLI flags and the config file.

Flag: --port
Type: integer
Description: The port on which to listen to client connections. The default is 7480.

Flag: --bind-addr
Type: string
Description: Specify the IP address to which the listener is bound.

Flag: --require-pass
Type: boolean
Description: Determines whether the server should require a password for the default user before allowing commands. The default is false. If this option is provided, it must be accompanied by the --password config.

Flag: --password
Type: string
Description: The password used to authorize the default user to run commands. This flag should be provided alongside the --require-pass flag.

Flag: --tls
Type: boolean
Description: A TLS connection with a client is required. The default is false.

Flag: mtls
Type: boolean
Description: Require mTLS connection with client. It is useful when the client and the server need to verify each other. If --tls and mtls are provided, --mtls will take higher priority. The default is false.

Flag: --cert-key-pair
Type: string
Description: The cert/key pair used by the server to authenticate itself to the client when using TLS or mTLS. This flag can be provided multiple times with multiple cert/key pairs. This is a comma-separated string in the following format: <path-to-cert>,<path-to-key>,

Flag: --client-ca
Type: string
Description: The path to the RootCA that is used to verify client certs when the --mtls flag is provided to enable verifying the client. This flag can be passed multiple times with paths to several client RootCAs.

Flag: --server-id
Type: string
Description: If this node is part of a raft replication cluster, then this flag provides the server ID to use within the cluster configuration. This ID must be unique to all the other nodes' IDs in the cluster.

Flag: --join-addr
Type: string
Description: When adding a node to a replication cluster, this is the address and port of any cluster member. The current node will use this to request permission to join the cluster. The format of this flag is <ip-address>:<memberlist-port>.

Flag: --raft-port
Type: integer
Description: If starting a node in a raft replication cluster, this port will be used for communication between nodes on the raft layer. The default is 7481.

Flag: --memberlist-port
Type: integer
Description. If starting a node in a replication cluster, this port is used for communication between nodes on the memberlist layer. The default is 7946.

Flag: --in-memory
Type: boolean
Description: When starting a node in a raft replication cluster, this directs the raft layer to store logs and snapshots in memory. It is only recommended in test mode. The default is false.

Flag: --data-dir
Type: string
Description: The directory for storing Append-Only Logs, Write Ahead Logs, and Snapshots. The default is /var/lib/echovault

Flag: --bootstrap-cluster
Type: boolean
Description: Whether to initialize a new replication cluster with this node as the leader. The default is false.

Flag: --acl-config
Type: string
Description: The file path for the ACL layer config file. The ACL configuration file can be a YAML or JSON file.

Flag: --snapshot-threshold
Type: integer
Description: The number of write commands required to trigger a snapshot. The default is 1,000

Flag: --snapshot-interval
Type: string
Description: The interval between snapshots. You can provide a parseable time format such as 30m45s or 1h45m. The default is 5 minutes.

Flag: --restore-snapshot
Type: boolean
Description: Determines whether to restore from a snapshot on startup. The default is false.

Flag: --restore-aof
Type: boolean
Description: This flag determines whether to restore from an aof file on startup. If both this flag and --restore-snapshot are provided, this flag will take higher priority.

Flag: --forward-commands
Type: boolean
Description: This flag allows you to send write commands to any node in the cluster. The node will forward the command to the cluster leader. When this is false, write commands can only be accepted by the leader. The default is false.

Flag: --max-memory
Type: string
Examples: "200mb", "8gb", "1tb"
Description: The maximum memory usage that EchoVault should observe. Once this limit is reached, the chosen key eviction strategy is triggered. The default is no limit.

Flag: --eviction-policy
Type: string
Description: This flag allows you to choose the key eviction strategy when the maximum memory is reached. The flag accepts the following options:

  1. noeviction - Do not evict any keys even when max-memory is exceeded. All new write operations will be rejected. This is the default eviction strategy.
  2. allkeys-lfu - Evict the least frequently used keys when max-memory is exceeded.
  3. allkeys-lru - Evict the least recently used keys when max-memory is exceeded.
  4. volatile-lfu - Evict the least frequently used keys with an expiration when max-memory is exceeded.
  5. volatile-lru - Evict the least recently used keys with an expiration when max-memory is exceeded.
  6. allkeys-random - Evict random keys until we get under the max-memory limit when max-memory is exceeded.
  7. volatile-random - Evict random keys with an expiration when max-memory is exceeded.

Flag: --eviction-sample
Type: integer
Description: An integer specifying the number of keys to sample when checking for expired keys. By default, EchoVault will sample 20 keys. The sampling is repeated if the number of expired keys found exceeds 20%.

Flag: --eviction-interval
Type: string
Example: "10s", "5m30s", "100ms"
Description: The interval between each sampling of keys to evict. By default, this happens every 100 milliseconds.

Eviction

Memory Limit

The memory limit can be set using the --max-memory config flag. This flag accepts a parsable memory value (e.g 100mb, 16gb). If the limit set is 0, then no memory limit is imposed. The default value is 0.

Passive eviction

In passive eviction, the expired key is not deleted immediately after the expiry time. The key will remain in the store until the next time it is accessed. When attempting to access an expired key, that is when the key is deleted.

Active eviction

Echovault will run a background goroutine that samples a set of volatile keys at a given interval. Any keys that are found to be expired will be deleted. If 20% or more of the sampled keys are deleted, then the process will immediately begin again. Otherwise, wait for the given interval until the next round of sampling/eviction. The default number of keys sampled is 20, and the default interval for sampling is 100 milliseconds. These can be configured using the --eviction-sample and --eviction-interval flags.

Eviction Policies

Eviction policy can be set using the --eviction-policy flag. The following options are available.

noeviction:
This policy does not evict any keys. When max memory is reached, all new write commands will be rejected until keys are manually deleted by the user.

allkeys-lfu:
With this policy, all keys are considered for eviction when the max memory is reached. When max memory is reached, the least frequently accessed keys will be evicted until the memory usage is under the memory limit.

allkeys-lru:
This policy will consider all keys for eviction when max memory is reached. The least recently accessed keys will be deleted one by one until we are below the memory limit.

allkeys-random:
Evict random keys until we're below the max memory limit.

volatile-lfu:
With this policy, only keys with an associated expiry time will be evicted to adhere to the memory limit. When the memory limit is exceeded, volatile keys will be evicted starting from the least frequently used until we are below the memory limit or are out of volatile keys to evict.

volatile-lru:
With this policy, only keys with an associated expiry time will be evicted to adhere to the memory limit. When the memory limit is exceeded, volatile keys will be evicted starting from the list recently used until we are below the memory limit or are out of volatile keys to evict.

volatile-random:
Evict random volatile keys until we're below the memory limit, or we're out of volatile keys to evict.

Contribution

Contributions are welcome! If you're interested in contributing, feel free to clone the repository and submit a Pull Request.

Join the discord server if you'd like to discuss your contribution and/or be a part of the community.

echovault's People

Contributors

kelvinmwinuka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

echovault's Issues

Compatibility with Redis Clients

Improve compatibility with Redis clients to allow EchoVault to be a potential drop-in replacement for Redis. The following considerations need to be made:

  1. Handshake with Redis client.
  2. Make sure each command returns a valid RESP response.
  3. Make Pub/Sub module compatible with Redis client.

Refactor command modules to use dependency injection of required parameters for command handlers

At the moment, HandlerFunc accepts a parameter that implements the EchoVault interafce. This causes the command modules to depend on the the echovault package, forcing the command modules to be placed in the pkg folder which is not idea.

The fix is to use Inversion of control to pass in all the functions, context, command and connection needed by the command handler, thus removing the dependence on the echovault package in any of the command modules.

This fix should also allow us to move the modules folder from /pkg to /internal. It should also make it easier to implement shared object file extension of echovault commands.

Create eviction policies

In order to keep memory usage in control, it's essential to have eviction policies that allow us to purge keys. As part of this issue, we would like to have the following eviction policies implemented:

  1. noeviction - Do not evict any keys regardless of memory usage.
  2. allkeys-lru - Use an LRU algorithm to remove the least recently used keys.
  3. allkeys-lfy - Use an LFU algorithm to remove the least frequently used keys.
  4. volatile-lru - Use an LRU algorithm to remove the least recently used keys with the expire property set to true.
  5. volatile-lfu - Use an LFU algorithm to remove the least frequently used keys with the expire property set to true.
  6. allkeys-random - Randomly remove any of the keys.
  7. volatile-random - Randomly remove any of the keys that have the expire property set to true.
  8. volatile-ttl - Remove keys with the shortest remaining ttl.

Make EchoVault Embeddable

Make EchoVault embeddable so any application can enhance its feature set with EchoVault features by simply importing EchoVault.

Bonus: Allow projects to import some modules from EchoVault. For example, import only the Append-Only engine or the Snapshot engine.

Skip ACL Authorisation for Embedded API Calls

The handleCommand method checks if the caller is authorized to execute the provided command. This makes sense for clients connected over TCP. However, this check must be skipped for embedded clients as there's no need to authorize embedded API call commands.

Differentiate Read and Write Keys when Authorising commands in ACL Layer

The ACL layer uses a command's KeyExtractionFunc to extract all the keys the command accesses.

KeyExtractionFunc returns a slice containing all the keys (read and write). However, the ACL layer cannot differentiate which keys are read and which ones are write.

This forces the ACL layer to check all returned keys against all allowed/excluded read and write keys.

TODO:

KeyExtractionFunc should return a struct containing a slice of read keys and a slice of write keys to allow the ACL layer to conduct a more granular check.

build does not work

How can I run this project and make it reproducible?

error output

cd /root; cd octavia/diskimage-create/; ./diskimage-create.sh -a amd64 -d jammy -i ubuntu-minimal -o amphora-x64-haproxy-jammy.qcow2 -s 3

# go version
go version go1.22.2 linux/amd64

# git log -p -2
commit 2528082d4158680c7b59d7e526aec2b62885d8c4 (HEAD -> main, origin/main, origin/HEAD)
Author: Kelvin Mwinuka <[email protected]>
Date:   Thu Apr 11 11:01:45 2024 +0800

    Update README.md

diff --git a/README.md b/README.md
index 33bd7bd..e581f7a 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,6 @@
 <br/>
 [![Go Reference](https://pkg.go.dev/badge/github.com/echovault/echovault.svg)](https://pkg.go.dev/github.com/echovault/echovault)
 <br/>
-[![Discord](https://img.shields.io/discord/1211815152291414037?style=flat&label=Discord&color=%235865F2)
-](https://discord.gg/aasBFwDm)
-<br/>
 <hr/>

 <img alt="echovault_logo" src="./images/EchoVault GitHub Cover.png" width="5062" />

commit c6b246efa455caaea53a6ba7a83115db826d36e1
Merge: 9b7d93f 64bbda6
Author: Kelvin Mwinuka <[email protected]>
Date:   Sat Apr 6 02:51:27 2024 +0800

    Merge pull request #21 from EchoVault/chore/fix-race-condition

    Fixed race conditions in acl module tests
#

# make build
env CC=x86_64-linux-musl-gcc GOOS=linux GOARCH=amd64 DEST=bin/linux/x86_64 make build-server
make[1]: вход в каталог «/root/EchoVault»
CC=x86_64-linux-musl-gcc GOOS=linux GOARCH=amd64 go build -o bin/linux/x86_64/server ./cmd/main.go
# runtime/cgo
cgo: C compiler "x86_64-linux-musl-gcc" not found: exec: "x86_64-linux-musl-gcc": executable file not found in $PATH
make[1]: *** [Makefile:2: build-server] Ошибка 1
make[1]: выход из каталога «/root/EchoVault»
make: *** [Makefile:5: build] Ошибка 2

Allow configuration of consistency policy

EchoVault is currently strongly consistent. This change will aim to make this policy configurable.

At the moment, echovault has a memberlist layer and a raft layer. The memberlist layer is mainly used to establish a cluster skeleton, essentially the "chassis" of the cluster. Memberlist nodes will communicate with other nodes to ask permission to join the raft cluster using the gossip protocol. Once the gossiped message reaches the raft cluster leader, the leader will add the node to the raft cluster.

We must allow a user to configure this functionality using the config flags. The option will enable the user to forgo the raft layer and use the memberlist layer for gossip-based state propagation to allow for eventual consistency. Otherwise, the user can opt for the strong consistency of the raft layer.

Allow shared object (.so) plugins to be loaded into commands list

As a user, I should be able to pass a list of .so files to be loaded on startup in order to extend EchoVault functionality with custom commands.

The plugins should be available in both client-server and embedded mode. For embedded mode, provide a generic function that accepts a custom []string to trigger custom plugin commands. It should return an error or a []byte representing the RESP response from the command handler.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.