https://github.com/ammario/redjet Skip to content Toggle navigation Sign up * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code Explore + All features + Documentation + GitHub Skills + Blog * Solutions For + Enterprise + Teams + Startups + Education By Solution + CI/CD & Automation + DevOps + DevSecOps Case Studies + Customer Stories + Resources * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles Repositories + Topics + Trending + Collections * Pricing [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. {{ message }} ammario / redjet Public * Notifications * Fork 0 * Star 14 High-performance Redis library for Go License CC0-1.0 license 14 stars 0 forks Star Notifications * Code * Issues 0 * Pull requests 0 * Actions * Projects 0 * Security * Insights More * Code * Issues * Pull requests * Actions * Projects * Security * Insights ammario/redjet This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. main Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags Name already in use A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? Cancel Create 3 branches 6 tags Code * Local * Codespaces * Clone HTTPS GitHub CLI [https://github.com/a] Use Git or checkout with SVN using the web URL. [gh repo clone ammari] Work fast with our official CLI. Learn more about the CLI. * Open with GitHub Desktop * Download ZIP Sign In Required Please sign in to use Codespaces. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching Xcode If nothing happens, download Xcode and try again. Launching Visual Studio Code Your codespace will open once ready. There was a problem preparing your codespace, please try again. Latest commit @ammario ammario Add Go report card ... b1cfb34 Jul 5, 2023 Add Go report card b1cfb34 Git stats * 56 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time .github/workflows Add CI July 4, 2023 17:02 bench Add fast-path for reading small strings July 5, 2023 13:08 .gitignore Add CI July 4, 2023 17:02 .golangci.yaml Add linting July 4, 2023 15:42 LICENSE Add LICENSE July 4, 2023 15:35 Makefile Optimize readBulkString July 5, 2023 12:49 README.md Add Go report card July 5, 2023 14:09 client.go Optimize readBulkString July 5, 2023 12:49 client_test.go Add fast-path for reading small strings July 5, 2023 13:08 conn.go Optimize readBulkString July 5, 2023 12:49 go.mod Add benchmark and leak detector July 3, 2023 12:42 go.sum Add benchmark and leak detector July 3, 2023 12:42 pubsub.go Add PubSub July 4, 2023 14:33 result.go Add fast-path for reading small strings July 5, 2023 13:08 View code [ ] redjet Basic Usage Streaming Pipelining PubSub Connection Pooling Benchmarks Limitations README.md redjet Go Reference ci Coverage Status Go Report Card redjet is a high-performance Go library for Redis. Its hallmark feature is a low-allocation, streaming API. See the benchmarks section for more details. Unlike redigo and go-redis, redjet does not provide a function for every Redis command. Instead, it offers a generic interface that supports all commands and options. While this approach has less type-safety, it provides forward compatibility with new Redis features. In the aim of both performance and ease-of-use, redjet attempts to provide an API that closely resembles the protocol. For example, the Command method is really a Pipeline of size 1. Table of Contents * redjet + Basic Usage + Streaming + Pipelining + PubSub + Connection Pooling + Benchmarks + Limitations Basic Usage Install: go get github.com/ammario/redjet@latest For the most part, you can interact with Redis using a familiar interface: package main import ( "context" "fmt" "log" "github.com/ammario/redjet" ) func main() { client := redjet.New("localhost:6379") ctx := context.Background() err := client.Command(ctx, "SET", "foo", "bar").Ok() // check error got, err := client.Command(ctx, "GET", "foo").Bytes() // check error // got == []byte("bar") } Streaming To minimize allocations, call (*Result).WriteTo instead of (*Result).Bytes. WriteTo streams the response directly to an io.Writer such as a file or HTTP response. For example: _, err := client.Command(ctx, "GET", "big-object").WriteTo(os.Stdout) // check error Similarly, you can pass in a value that implements redjet.LenReader to Command to stream larger values into Redis. Unfortunately, the API cannot accept a regular io.Reader because bulk string messages in the Redis protocol are length-prefixed. Here's an example of streaming a large file into Redis: bigFile, err := os.Open("bigfile.txt") // check error defer bigFile.Close() stat, err := bigFile.Stat() // check error err = client.Command( ctx, "SET", "bigfile", redjet.NewLenReader(bigFile, stat.Size()), ).Ok() // check error If you have no way of knowing the size of your blob in advance and still want to avoid large allocations, you may chunk a stream into Redis using repeated APPEND commands. Pipelining redjet supports pipelining via the Pipeline method. This method accepts a Result, potentially that of a previous, open command. // Set foo0, foo1, ..., foo99 to "bar", and confirm that each succeeded. // // This entire example only takes one round-trip to Redis! var r *Result for i := 0; i < 100; i++ { r = client.Pipeline(r, "SET", fmt.Sprintf("foo%d", i), "bar") } for r.Next() { if err := r.Ok(); err != nil { log.Fatal(err) } } Fun fact: authentication happens over a pipeline, so it doesn't incur a round-trip. PubSub redjet suports PubSub via the NextSubMessage method. For example: // Subscribe to a channel sub := client.Command(ctx, "SUBSCRIBE", "my-channel") sub.NextSubMessage() // ignore the first message, which is a confirmation of the subscription // Publish a message to the channel n, err := client.Command(ctx, "PUBLISH", "my-channel", "hello world").Int() // check error // n == 1, since there is one subscriber // Receive the message sub.NextSubMessage() // sub.Payload == "hello world" // sub.Channel == "my-channel" // sub.Type == "message" Note that NextSubMessage will block until a message is received. To interrupt the subscription, cancel the context passed to Command. Once a connection enters subscribe mode, the internal pool does not re-use it. It is possible to subscribe to a channel in a performant, low-allocation way via the public API. NextSubMessage is just a convenience method. Connection Pooling Redjet provides automatic connection pooling. Configuration knobs exist within the Client struct that may be changed before any Commands are issued. If you want synchronous command execution over the same connection, use the Pipeline method and consume the Result after each call to Pipeline. Storing a long-lived Result offers the same functionality as storing a long-lived connection. Benchmarks On a pure throughput basis, redjet will perform similarly to redigo and go-redis. But, since redjet doesn't allocate memory for the entire response object, it consumes far less resources when handling large responses. Here are some benchmarks (reproducible via make gen-bench) to illustrate: goos: darwin goarch: arm64 pkg: github.com/ammario/redjet/bench | redjet | redigo | go-redis | | sec/op | sec/op vs base | sec/op vs base | 1.296m +- 10% 1.401m +- 10% ~ (p=0.075 n=10) 1.519m +- 14% +17.21% (p=0.000 n=10) | redjet | redigo | go-redis | | B/s | B/s vs base | B/s vs base | 771.8Mi +- 9% 715.9Mi +- 10% ~ (p=0.075 n=10) 658.6Mi +- 12% -14.67% (p=0.000 n=10) | redjet | redigo | go-redis | | B/op | B/op vs base | B/op vs base | 49.50 +- 9% 1047456.50 +- 0% +2115973.74% (p=0.000 n=10) 1056983.50 +- 0% +2135220.20% (p=0.000 n=10) | redjet | redigo | go-redis | | allocs/op | allocs/op vs base | allocs/op vs base | 3.000 +- 0% 3.000 +- 0% ~ (p=1.000 n=10) 6.000 +- 0% +100.00% (p=0.000 n=10) Note that these results are a bit contrived in that they GET a 1MB value. The performance of all libraries converge as response size decreases. Limitations * redjet does not have convenient support for client side caching. But, the redjet API is flexible enough that a client could implement it themselves by following the instructions here. * RESP3 is not supported. Practically, this means that connections aren't multiplexed, and other Redis libraries may perform better in high-concurrency scenarios. * Certain features have not been tested but may still work: + Redis Streams + Monitor About High-performance Redis library for Go Resources Readme License CC0-1.0 license Stars 14 stars Watchers 1 watching Forks 0 forks Report repository Releases 6 tags Packages 0 No packages published Used by 0 * @ammario @ammario / redjet Languages * Go 97.1% * Makefile 2.9% Footer (c) 2023 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time.