1601681760
An experimental implementation of redis client for deno
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
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.
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"]
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();
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
Author: denolib
Source Code: https://github.com/denolib/deno-redis
#deno #nodejs #node #javascript
1596679140
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 “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:
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:
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
1660303800
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!
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
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
}
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()
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
This client also works with kvrocks, a distributed key value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol.
Author: Go-redis
Source Code: https://github.com/go-redis/redis
License: BSD-2-Clause license
1607446620
A simple Redis client in tune with Functional Programming principles in JavaScript for Deno.
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:
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));
The RedisRequest
namespace comes with methods for convenience to create an instance of RedisRequest
with various commands. The methods are curried.
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");
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:
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));
Author: sebastienfilion
Source Code: https://github.com/sebastienfilion/functional-redis
#deno #node #nodejs #javascript #redis
1660307760
redis.go is a client for the redis key-value store.
Some features include:
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
Most of the examples connect to a redis database running in the default port -- 6379.
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))
}
var client redis.Client
client.Set("a", []byte("hello"))
val, _ := client.Get("a")
println(string(val))
client.Del("a")
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")
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.
Author: Hoisie
Source Code: https://github.com/hoisie/redis
License: MIT license
1672269960
A Redis client for the Crystal programming language.
Add it to your shard.yml
:
dependencies:
redis:
github: stefanwille/crystal-redis
and then install the library into your project:
$ shards install
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:
$ brew install openssl
PKG_CONFIG_PATH
:$ export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig
Note: Please write me if you know a better way!
This library needs Crystal version >= 0.34.0
I haven't tested older Crystal versions.
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")
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.
To get started, see the examples:
Redis
.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.
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.
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
Running the spec will delete database number 0!
If you have questions or need help, please open a ticket in the GitHub issue tracker. This way others can benefit from the discussion.
Author: Stefanwille
Source Code: https://github.com/stefanwille/crystal-redis
License: MIT license