Alex Lambert

Alex Lambert

1601681760

Redis client for Deno

deno-redis

An experimental implementation of redis client for deno

Usage

needs --allow-net privilege

Stateless Commands

import { connect } from "https://denopkg.com/keroxp/deno-redis/mod.ts";
const redis = await connect({
  hostname: "127.0.0.1",
  port: 6379
});
const ok = await redis.set("hoge", "fuga");
const fuga = await redis.get("hoge");

PubSub

const sub = await redis.subscribe("channel");
(async function() {
  for await (const { channel, message } of sub.receive()) {
    // on message
  }
})();

Streams

await redis.xadd(
  "somestream",
  "*", // let redis assign message ID
  { yes: "please", no: "thankyou" },
  { elements: 10 },
);

const [stream] = await client.xread(
  [{ key: "somestream", xid: 0 }], // read from beginning
  { block: 5000 },
);

const msgFV = stream.messages[0].field_values;
const plz = msgFV.get("yes");
const thx = msgFV.get("no");

Cluster

await redis.meet("127.0.0.1", 6380);
await redis.nodes();
// ... 127.0.0.1:6379@16379 myself,master - 0 1593978765000 0 connected
// ... 127.0.0.1:6380@16380 master - 0 1593978766503 1 connected

Advanced Usage

Retriable connection

By default, a client’s connection will throw an error if the server dies or the network becomes unavailable. A connection can be made “retriable” by setting the value maxRetryCount when connecting a new client.

const redis = await connect({ ...options, maxRetryCount: 10 });

// The client will try to connect to the server 10 times if the server dies or the network becomes unavailable.

The property is set automatically to 10 when creating a subscriber client. After a reconnection succeeds, the client will subscribe again to all the channels and patterns.

const redis = await connect(options);
const subscriberClient = await redis.subscribe("channel");

// The client's connection will now be forced to try to connect to the server 10 times if the server dies or the network
//   becomes unavailable.

Execute raw commands

redis.executor is raw level redis protocol executor. You can send raw redis commands and receive replies.

await redis.executor.exec("SET", "redis", "nice"); // => ["status", "OK"]
await redis.executor.exec("GET", "redis"); // => ["bulk", "nice"]

Pipelining

https://redis.io/topics/pipelining

const redis = await connect({
  hostname: "127.0.0.1",
  port: 6379
});
const pl = redis.pipeline();
pl.ping();
pl.ping();
pl.set("set1", "value1");
pl.set("set2", "value2");
pl.mget("set1", "set2");
pl.del("set1");
pl.del("set2");
const replies = await pl.flush();

TxPipeline (pipeline with MULTI/EXEC)

We recommend to use tx() instead of multi()/exec() for transactional operation.
MULTI/EXEC are potentially stateful operation so that operation’s atomicity is guaranteed but redis’s state may change between MULTI and EXEC.

WATCH is designed for these problems. You can ignore it by using TxPipeline because pipelined MULTI/EXEC commands are strictly executed in order at the time and no changes will happen during execution.

See detail https://redis.io/topics/transactions

const tx = redis.tx();
tx.set("a", "aa");
tx.set("b", "bb");
tx.del("c");
await tx.flush();
// MULTI
// SET a aa
// SET b bb
// DEL c
// EXEC

Roadmap for v1

Download Details:

Author: denolib

Source Code: https://github.com/denolib/deno-redis

#deno #nodejs #node #javascript

What is GEEK

Buddha Community

Redis client for Deno
Loma  Baumbach

Loma Baumbach

1596679140

Redis Transactions & Long-Running Lua Scripts

Redis offers two mechanisms for handling transactions – MULTI/EXEC based transactions and Lua scripts evaluation. Redis Lua scripting is the recommended approach and is fairly popular in usage.

Our Redis™ customers who have Lua scripts deployed often report this error – “BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE”. In this post, we will explain the Redis transactional property of scripts, what this error is about, and why we must be extra careful about it on Sentinel-managed systems that can failover.

Redis Lua Scripts Diagram - ScaleGrid Blog

Transactional Nature of Redis Lua Scripts

Redis “transactions” aren’t really transactions as understood conventionally – in case of errors, there is no rollback of writes made by the script.

Atomicity” of Redis scripts is guaranteed in the following manner:

  • Once a script begins executing, all other commands/scripts are blocked until the script completes. So, other clients either see the changes made by the script or they don’t. This is because they can only execute either before the script or after the script.
  • However, Redis doesn’t do rollbacks, so on an error within a script, any changes already made by the script will be retained and future commands/scripts will see those partial changes.
  • Since all other clients are blocked while the script executes, it is critical that the script is well-behaved and finishes in time.

The ‘lua-time-limit’ Value

It is highly recommended that the script complete within a time limit. Redis enforces this in a weak manner with the ‘lua-time-limit’ value. This is the maximum allowed time (in ms) that the script is allowed to run. The default value is 5 seconds. This is a really long time for CPU-bound activity (scripts have limited access and can’t run commands that access the disk).

However, the script is not killed when it executes beyond this time. Redis starts accepting client commands again, but responds to them with a BUSY error.

If you must kill the script at this point, there are two options available:

  • SCRIPT KILL command can be used to stop a script that hasn’t yet done any writes.
  • If the script has already performed writes to the server and must still be killed, use the SHUTDOWN NOSAVE to shutdown the server completely.

It is usually better to just wait for the script to complete its operation. The complete information on methods to kill the script execution and related behavior are available in the documentation.

#cloud #database #developer #high availability #howto #redis #scalegrid #lua-time-limit #redis diagram #redis master #redis scripts #redis sentinel #redis servers #redis transactions #sentinel-managed #server failures

Go-redis/redis: Type-safe Redis Client for Golang

Redis client for Go 

go-redis is brought to you by :star: uptrace/uptrace. Uptrace is an open source and blazingly fast distributed tracing tool powered by OpenTelemetry and ClickHouse. Give it a star as well!

Features

Installation

go-redis supports 2 last Go versions and requires a Go version with modules support. So make sure to initialize a Go module:

go mod init github.com/my/repo

If you are using Redis 6, install go-redis/v8:

go get github.com/go-redis/redis/v8

If you are using Redis 7, install go-redis/v9:

go get github.com/go-redis/redis/v9

Quickstart

import (
    "context"
    "github.com/go-redis/redis/v8"
    "fmt"
)

var ctx = context.Background()

func ExampleClient() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    err := rdb.Set(ctx, "key", "value", 0).Err()
    if err != nil {
        panic(err)
    }

    val, err := rdb.Get(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("key", val)

    val2, err := rdb.Get(ctx, "key2").Result()
    if err == redis.Nil {
        fmt.Println("key2 does not exist")
    } else if err != nil {
        panic(err)
    } else {
        fmt.Println("key2", val2)
    }
    // Output: key value
    // key2 does not exist
}

Look and feel

Some corner cases:

// SET key value EX 10 NX
set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result()

// SET key value keepttl NX
set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result()

// SORT list LIMIT 0 2 ASC
vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()

// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{
    Min: "-inf",
    Max: "+inf",
    Offset: 0,
    Count: 2,
}).Result()

// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{
    Keys: []string{"zset1", "zset2"},
    Weights: []int64{2, 3}
}).Result()

// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()

// custom command
res, err := rdb.Do(ctx, "set", "key", "value").Result()

Run the test

go-redis will start a redis-server and run the test cases.

The paths of redis-server bin file and redis config file are defined in main_test.go:

var (
    redisServerBin, _  = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
    redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf"))
)

For local testing, you can change the variables to refer to your local files, or create a soft link to the corresponding folder for redis-server and copy the config file to testdata/redis/:

ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src
cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/

Lastly, run:

go test

See also

Resources

Ecosystem

This client also works with kvrocks, a distributed key value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol.

Download Details:

Author: Go-redis
Source Code: https://github.com/go-redis/redis 
License: BSD-2-Clause license

#go #golang #redis #client 

Alex Lambert

Alex Lambert

1607446620

A simple Redis Client in Tune with Functional Programming principles in JavaScript for Deno

Functional Redis

A simple Redis client in tune with Functional Programming principles in JavaScript for Deno.

Redis Request

The RedisRequest represents a Redis request. It has three attributes: the first is the Redis command, the second is a typed array named “raw”, the last is an array of arguments. The RedisRequest type is mostly interoperable with RedisResponse, Resource, File, (HTTP) Request
and (HTTP) Response.

The RedisRequest type implements the following algebras:

  • [x] Group
  • [x] Comonad
  • [x] Monad

Example

import RedisRequest from "https://deno.land/x/functional-redis@v0.1.1/library/RedisRequest.js";

const redisRequest = RedisRequest("GET", new Uint8Array([]), [ "hoge" ]);

assert(RedisRequest.is(redisRequest));

A Symbol named rawPlaceholder may be used as a placeholder for the buffer. In the following example, the request will resolve to: SET hoge piyo.

import { encodeText } from "https://deno.land/x/functional@v1.2.1/library/utilities.js";
import RedisRequest from "https://deno.land/x/functional-redis@v0.1.1/library/RedisRequest.js";
import { $$rawPlaceholder } from "https://deno.land/x/functional-redis@v0.1.0/library/Symbol.js";

const redisRequest = RedisRequest("SET", encodeText("piyo"), [ "hoge", $$rawPlaceholder ]);

assert(RedisRequest.is(redisRequest));

The placeholder can be used multiple times if the buffer has multiple values separated by CLRF (\r\n).

import { encodeText } from "https://deno.land/x/functional@v1.2.1/library/utilities.js";
import RedisRequest from "https://deno.land/x/functional-redis@v0.1.1/library/RedisRequest.js";
import { $$rawPlaceholder } from "https://deno.land/x/functional-redis@v0.1.0/library/Symbol.js";

const redisRequest = RedisRequest(
  "MSET",
  encodeText("piyo\r\nfuga"),
  [ "hoge", $$rawPlaceholder, "hogefuga", $$rawPlaceholder ]
);

assert(RedisRequest.is(redisRequest));

Utilities

The RedisRequest namespace comes with methods for convenience to create an instance of RedisRequest with various commands. The methods are curried.

String commands

RedisRequest.append 📕

const redisRequest = RedisRequest.append("hoge", "piyo");

RedisRequest.bitcount 📕

const redisRequest = RedisRequest.bitcount("hoge", [ 0, 1 ]);

RedisRequest.bitfield 📕

const redisRequest = RedisRequest.bitfield("hoge", [ "GET", "i8", 100 ]);

RedisRequest.bitop 📕

const redisRequest = RedisRequest.bitop("AND", "hoge", [ "piyo", "fuga" ]);

RedisRequest.bitpos 📕

const redisRequest = RedisRequest.bitpos("hoge", [ 0, 1 ]);

RedisRequest.decr 📕

const redisRequest = RedisRequest.decr("hoge");

RedisRequest.decrby 📕

const redisRequest = RedisRequest.decrby("hoge", 3);

RedisRequest.get 📕

const redisRequest = RedisRequest.get("hoge");

RedisRequest.getbit 📕

const redisRequest = RedisRequest.getbit("hoge", 3);

RedisRequest.getrange 📕

const redisRequest = RedisRequest.getrange("hoge", [ 0, 1 ]);

RedisRequest.getset 📕

const redisRequestA = RedisRequest.getset("hoge", "piyo");
const redisRequestB = RedisRequest.getset("hoge", encodeText("piyo"));

RedisRequest.incr 📕

const redisRequest = RedisRequest.incr("hoge");

RedisRequest.incrby 📕

const redisRequest = RedisRequest.incrby("hoge", 3);

RedisRequest.incrbyfloat 📕

const redisRequest = RedisRequest.incrbyfloat("hoge", 0.1);

RedisRequest.mget 📕

const redisRequest = RedisRequest.mget("hoge", "piyo");

RedisRequest.mset 📕

const redisRequestA = RedisRequest.mset("hoge", "piyo", "hogefuga", "fuga");
const redisRequestB = RedisRequest.mset(
  [ "hoge", $$rawPlaceholder, "hogefuga", $$rawPlaceholder ],
  encodeText("piyo\r\nfuga\r\n")
);

RedisRequest.msetnx 📕

const redisRequestA = RedisRequest.msetnx("hoge", "piyo", "hogefuga", "fuga");
const redisRequestB = RedisRequest.msetnx(
  [ "hoge", $$rawPlaceholder, "hogefuga", $$rawPlaceholder ],
  encodeText("piyo\r\nfuga\r\n")
);

RedisRequest.psetex 📕

const redisRequestA = RedisRequest.psetex(1000, "hoge", "piyo");
const redisRequestB = RedisRequest.psetex(1000, "hoge", encodeText("piyo"));

RedisRequest.set 📕

const redisRequestA = RedisRequest.set({}, "hoge", "piyo");
const redisRequestB = RedisRequest.set({}, "hoge", encodeText("piyo"));
const redisRequestC = RedisRequest.set({ EX: 2000 }, "hoge", encodeText("piyo"));
const redisRequestD = RedisRequest.set({ KEEPTTL: true }, "hoge", encodeText("piyo"));

RedisRequest.setbit 📕

const redisRequest = RedisRequest.setbit("hoge", 7, 1);

RedisRequest.setex 📕

const redisRequestA = RedisRequest.setex(10, "hoge", "piyo");
const redisRequestB = RedisRequest.setex(10, "hoge", encodeText("piyo"));

RedisRequest.setnx 📕

const redisRequestA = RedisRequest.setnx("hoge", "piyo");
const redisRequestB = RedisRequest.setnx("hoge", encodeText("piyo"));

RedisRequest.setrange 📕

const redisRequest = RedisRequest.setrange("hoge", 2, "FU");

RedisRequest.stralgo 📕

const redisRequest = RedisRequest.strlen("LCS", "KEYS, "hoge", "piyo");

RedisRequest.strlen 📕

const redisRequest = RedisRequest.strlen("hoge");

Redis Response

The RedisResponse represents a Redis response. It has only one argument, a typed array named “raw”. The RedisResponse type is mostly interoperable with RedisRequest, Resource, File, (HTTP) Request and (HTTP) Response.

The RedisResponse type implements the following algebras:

  • [x] Alternative
  • [x] Group
  • [x] Comonad
  • [x] Monad

Example

import RedisResponse from "https://deno.land/x/functional-redis@v0.1.1/library/RedisResponse.js";

const redisResponse = RedisResponse.Success(new Uint8Array([]));

assert(RedisResponse.is(redisResponse));

Download Details:

Author: sebastienfilion

Source Code: https://github.com/sebastienfilion/functional-redis

#deno #node #nodejs #javascript #redis

Hoisie/redis: A Simple, Powerful Redis Client for Go

redis.go

redis.go is a client for the redis key-value store.

Some features include:

  • Designed for Redis 2.6.x.
  • Support for all redis types - strings, lists, sets, sorted sets, and hashes
  • Very simple usage
  • Connection pooling ( with configurable size )
  • Support for concurrent access
  • Manages connections to the redis server, including dropped and timed out connections
  • Marshaling/Unmarshaling go types to hashes

This library is stable and is used in production environments. However, some commands have not been tested as thoroughly as others. If you find any bugs please file an issue!

Installation

Just run go get github.com/hoisie/redis

Examples

Most of the examples connect to a redis database running in the default port -- 6379.

Hello World example

package main

import "github.com/hoisie/redis"

func main() {
    var client redis.Client
    var key = "hello"
    client.Set(key, []byte("world"))
    val, _ := client.Get("hello")
    println(key, string(val))
}

Strings

var client redis.Client
client.Set("a", []byte("hello"))
val, _ := client.Get("a")
println(string(val))
client.Del("a")

Lists

var client redis.Client
vals := []string{"a", "b", "c", "d", "e"}
for _, v := range vals {
    client.Rpush("l", []byte(v))
}
dbvals,_ := client.Lrange("l", 0, 4)
for i, v := range dbvals {
    println(i,":",string(v))
}
client.Del("l")

Publish/Subscribe

sub := make(chan string, 1)
sub <- "foo"
messages := make(chan Message, 0)
go client.Subscribe(sub, nil, nil, nil, messages)

time.Sleep(10 * 1000 * 1000)
client.Publish("foo", []byte("bar"))

msg := <-messages
println("received from:", msg.Channel, " message:", string(msg.Message))

close(sub)
close(messages)

More examples coming soon. See redis_test.go for more usage examples.

Commands not supported yet

  • MULTI/EXEC/DISCARD/WATCH/UNWATCH
  • SORT
  • ZUNIONSTORE / ZINTERSTORE

Download Details:

Author: Hoisie
Source Code: https://github.com/hoisie/redis 
License: MIT license

#go #golang #redis #client 

Lawson  Wehner

Lawson Wehner

1672269960

Crystal-redis: Full Featured Redis Client for Crystal

Redis Client for Crystal

A Redis client for the Crystal programming language.

Features

  • Performance (> 680,000 commands per second using pipeline on a MacBook Air with a single client thread)
  • Pipelining
  • Transactions
  • LUA Scripting
  • All string commands
  • All hash commands
  • All list commands
  • All set commands
  • All hyperloglog commands
  • All commands for bit operations
  • All sorted set commands
  • All geo commands
  • Publish/subscribe

Installation

Add it to your shard.yml:

dependencies:
  redis:
    github: stefanwille/crystal-redis

and then install the library into your project:

$ shards install

Installation on MacOS X

On MacOS X you may get this error:

ld: library not found for -lssl (this usually means you need to install the development package for libssl)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
...

Or this warning:

Package libssl was not found in the pkg-config search path.
Perhaps you should add the directory containing `libssl.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libssl' found
Package libcrypto was not found in the pkg-config search path.
Perhaps you should add the directory containing `libcrypto.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libcrypto' found

The problem is that Crystal can't find openssl, because it is not installed by default on MacOS X.

The fix:

  1. Install openssl via Homebrew:
$ brew install openssl
  1. Set the environment variable PKG_CONFIG_PATH:
$ export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig

Note: Please write me if you know a better way!

Required Crystal Version

This library needs Crystal version >= 0.34.0

I haven't tested older Crystal versions.

Usage

Require the package:

  require "redis"

then

  redis = Redis.new

Then you can call Redis commands on the redis object:

  redis.set("foo", "bar")
  redis.get("foo")

Connection Pooling

Since version 2.0.0, a connection pool is built in. It is used implicitly through Redis::PooledClient:

redis = Redis::PooledClient.new
10.times do |i|
  spawn do
    redis.set("foo#{i}", "bar")
    redis.get("foo#{i}") # => "bar"
  end
end

This redis instance can be shared across fibers, and accepts the same Redis commands as the Redis class. It automatically allocates and frees connections from/to the pool, per command.

:warning: If you are using Redis in a web context (e. g. with a framework like Kemal), you need to use connection pooling.

Examples

To get started, see the examples:

Documentation

Performance

I have benchmarked Crystal-Redis against several other client libraries in various programming languages in this blog article.

Here are some results:

Crystal: With this library I get > 680,000 commands per second using pipeline on a MacBook Air with a single client thread.

C: The equivalent program written in C with Hiredis gets me 340,000 commands per second.

Ruby: Ruby 2.2.1 with the redis-rb and Hiredis driver handles 150,000 commands per second.

Read more results for Go, Java, Node.js.

Status

I have exercised every API method in the spec and built some example programs. Some people report production usage.

I took great care to make this library very usable with respect to API, reliability and documentation.

Development

This project requires a locally running redis server running on port 6379 and with a Unix socket located at /tmp/redis.sock. In Homebrew's default redis.config the Unix domain socket option is disabled. To enable, edit /usr/local/etc/redis.conf or whatever your redis.conf is and uncomment this line:

# unixsocket /tmp/redis.sock

so that it reads

unixsocket /tmp/redis.sock

Then you can run the specs via

$ crystal spec

See more information.

WARNING

Running the spec will delete database number 0!

Questions, Bugs & Support

If you have questions or need help, please open a ticket in the GitHub issue tracker. This way others can benefit from the discussion.

Download Details:

Author: Stefanwille
Source Code: https://github.com/stefanwille/crystal-redis 
License: MIT license

#redis #crystal #client