rediscloudflarekv

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2021 License: MIT Imports: 4 Imported by: 0

README

Redis caching layer for Cloudflare KV in Golang

Go Reference

Ask questions in the GitHub issues

Why redis-cloudflare-kv?

  • Cloudflare KV is a highly distributed, eventually consistent, key-value store that spans Cloudflare's global edge. It allows you to store billions of key-value pairs and read them with some average latency (2 to 3 seconds) anywhere in the world.
  • Cloudflare KV Read operation is very slow when compared to Redis which offers memory efficiency, fast operating speed, and high availability. So it's better to use Redis as a caching layer for Cloudflare KV to provide fast, economic scaling for your application.
  • redis-cloudflare-kv checks if there is a cached result for a query in Redis.
  • If not found in the cache, it will retrieve data from Cloudflare KV and on the successful result, cache it in Redis for future queries.

Latency Comparison between Cloudflare KV vs Redis

We're calculating latency for Read operation between Cloudflare KV vs Redis. Here Redis Read operation is 6.8X (124.216708ms) times faster than Cloudflare KV (1604.654375s).

package main

import (
	"fmt"
	"log"
	"os"
	"time"

	rediscloudflarekv "github.com/dineshsonachalam/redis-cloudflare-kv"
)

var kvClient = rediscloudflarekv.New(
	os.Getenv("REDIS_URL"),
	os.Getenv("CLOUDFLARE_ACCESS_KEY"),
	os.Getenv("CLOUDFLARE_EMAIL_ADDRESS"),
	os.Getenv("CLOUDFLARE_ACCOUNT_ID"),
)

func measureTime(funcName string) func() {
	start := time.Now()
	return func() {
		fmt.Printf("Time taken by %s function is %v \n", funcName, time.Since(start))
		fmt.Println("==============================================================")
	}
}

func RedisRead(key string) {
	defer measureTime("RedisRead")()
	value, err := kvClient.RedisRead(key)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v : %v \n", key, string(value))
}

func CloudflareKVRead(key string, namespaceID string) {
	defer measureTime("CloudflareKVRead")()
	value, err := kvClient.CloudflareKVRead(key, namespaceID)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v : %v \n", key, string(value))
}

func main() {
	key := "opensource.facebook.react-native"
	namespaceID := os.Getenv("TEST_NAMESPACE_ID")
	RedisRead(key)
	CloudflareKVRead(key, namespaceID)
}

// dineshsonachalam@macbook examples % go run latency_check.go
// opensource.facebook.react-native : A framework for building native apps with React.
// Time taken by RedisRead function is 124.216708ms
// ==============================================================
// opensource.facebook.react-native : A framework for building native apps with React.
// Time taken by CloudflareKVRead function is 1.604654375s
// ==============================================================
// dineshsonachalam@macbook examples %

Application Architecture

Installation

You need a working Go environment.

go get github.com/dineshsonachalam/redis-cloudflare-kv

Quickstart

package main

import (
	"fmt"
	"log"
	"os"

	rediscloudflarekv "github.com/dineshsonachalam/redis-cloudflare-kv"
)

func main() {
	// Construct a new KV Client object
	kvClient := rediscloudflarekv.New(
		// REDIS_URL -> TCP Connection (Without SSL):  redis://<user>:<password>@<host>:<port>/<db_number>
		//              TCP Connection (With SSL):     rediss://<user>:<password>@<host>:<port>/<db_number>
		//              UNIX Connection: unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
		os.Getenv("REDIS_URL"),
		os.Getenv("CLOUDFLARE_ACCESS_KEY"),
		os.Getenv("CLOUDFLARE_EMAIL_ADDRESS"),
		os.Getenv("CLOUDFLARE_ACCOUNT_ID"),
	)
	namespaceID := os.Getenv("TEST_NAMESPACE_ID")
	key1 := "opensource.facebook.react"
	value1 := "A declarative, efficient, and flexible JavaScript library for building user interfaces."
	key2 := "opensource.facebook.react-native"
	value2 := "A framework for building native apps with React."
	key3 := "opensource.facebook.flow"
	value3 := "Adds static typing to JavaScript to improve developer productivity and code quality."
	key4 := "opensource.facebook.docusaurus"
	value4 := "Easy to maintain open source documentation websites."

	// Write writes a value identified by a key in Redis and Cloudflare KV
	status, err := kvClient.Write(key1, []byte(value1), namespaceID)
	if !status && err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Write operation is successful for key: %v\n", key1)

	// Read returns the value associated with the given key
	// If the key is not available in the Redis server,
	// it searches for the key in Cloudflare KV and if the key is available, it writes the key/value in the Redis.
	// Then it returns the value associated with the given key.
	value, err := kvClient.Read(key1, namespaceID)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Read operation is successful. Key: %v, Value: %v\n", key1, string(value))

	// Delete deletes a key/value in Redis and Cloudflare KV
	status, err = kvClient.Delete(key1, namespaceID)
	if !status && err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Delete operation is successful for key: %v\n", key1)

	kvClient.Write(key2, []byte(value2), namespaceID)
	kvClient.Write(key3, []byte(value3), namespaceID)
	kvClient.Write(key4, []byte(value4), namespaceID)

	// ListKeysByPrefix returns keys that matches the prefix
	// If there are no key's that matches the prefix in the Redis
	// We search for the prefix pattern in Cloudflare KV, if there are keys
	// that matches the prefix, we return the keys.
	keys, err := kvClient.ListKeysByPrefix("opensource.facebook", namespaceID)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("ListKeysByPrefix operation is successful. Keys: %v\n", keys)
}

// Output:
// dineshsonachalam@macbook examples % go run main.go
// Write operation is successful for key: opensource.facebook.react
// Read operation is successful. Key: opensource.facebook.react, Value: A declarative, efficient, and flexible JavaScript library for building user interfaces.
// Delete operation is successful for key: opensource.facebook.react
// ListKeysByPrefix operation is successful. Keys: [opensource.facebook.docusaurus opensource.facebook.react-native opensource.facebook.flow]

License

MIT © dineshsonachalam

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewCloudflareClient

func NewCloudflareClient(apiKey string, email string, accountID string) *cloudflare.API

NewCloudflareClient returns a new Cloudflare v4 API client

func NewRedisClient

func NewRedisClient(redisURL string) *redis.Client

NewRedisClient returns a client to the Redis Server

Types

type Client added in v1.0.2

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

Client represent Cloudflare and Redis client information

func New

func New(redisURL string, cloudflareApiKey string, cloudflareEmail string, cloudflareAccountID string) *Client

New returns a client for Redis and CloudFlare KV

func (*Client) CloudflareKVDelete added in v1.0.2

func (opt *Client) CloudflareKVDelete(key string, namespaceID string) (bool, error)

CloudflareKVDelete deletes a key and value for a provided storage namespace

func (*Client) CloudflareKVListKeysByPrefix added in v1.0.2

func (opt *Client) CloudflareKVListKeysByPrefix(prefix string, namespaceID string) ([]string, error)

CloudflareKVListKeysByPrefix returns keys that matches the prefix

func (*Client) CloudflareKVRead added in v1.0.2

func (opt *Client) CloudflareKVRead(key string, namespaceID string) ([]byte, error)

CloudflareKVRead returns the value associated with the given key in the given namespace

func (*Client) CloudflareKVWrite added in v1.0.2

func (opt *Client) CloudflareKVWrite(key string, value []byte, namespaceID string) (bool, error)

CloudflareKVWrite writes a value identified by a key.

func (*Client) Delete added in v1.0.2

func (opt *Client) Delete(key string, namespaceID string) (bool, error)

Delete deletes a key and value

func (*Client) ListKeysByPrefix added in v1.0.2

func (opt *Client) ListKeysByPrefix(prefix string, namespaceID string) ([]string, error)

ListKeysByPrefix returns keys that matches the prefix

func (*Client) Read added in v1.0.2

func (opt *Client) Read(key string, namespaceID string) ([]byte, error)

Read returns the value associated with the given key in the given redis server or Cloudflare KV namespace

func (*Client) RedisDelete added in v1.0.2

func (opt *Client) RedisDelete(key string) (bool, error)

RedisDelete deletes a key and value

func (*Client) RedisListKeysByPrefix added in v1.0.2

func (opt *Client) RedisListKeysByPrefix(prefix string) ([]string, error)

RedisListKeysByPrefix returns keys that matches the prefix

func (*Client) RedisRead added in v1.0.2

func (opt *Client) RedisRead(key string) ([]byte, error)

RedisRead returns value for that key

func (*Client) RedisWrite added in v1.0.2

func (opt *Client) RedisWrite(key string, value []byte) (bool, error)

RedisWrite writes a value identified by a key.

func (*Client) Write added in v1.0.2

func (opt *Client) Write(key string, value []byte, namespaceID string) (bool, error)

Write writes a value identified by a key.

type KV

type KV interface {
	Read(key string, namespaceID string) ([]byte, error)
	Write(key string, value []byte, namespaceID string) (bool, error)
	ListKeysByPrefix(prefix string, namespaceID string) ([]string, error)
	Delete(key string, namespaceID string) (bool, error)
	RedisRead(key string) ([]byte, error)
	RedisWrite(key string, value []byte) (bool, error)
	RedisListKeysByPrefix(prefix string) ([]string, error)
	RedisDelete(key string) (bool, error)
	CloudflareKVRead(key string, namespaceID string) ([]byte, error)
	CloudflareKVWrite(key string, value []byte, namespaceID string) (bool, error)
	CloudflareKVListKeysByPrefix(prefix string, namespaceID string) ([]string, error)
	CloudflareKVDelete(key string, namespaceID string) (bool, error)
}

KV is a type that implements a Redis and Cloudflare key-value store.

Jump to

Keyboard shortcuts

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