As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Commit 3fe9cea0 authored by Jacob Vosmaer's avatar Jacob Vosmaer
Browse files

Vendor redis_rb from Rubygems, not GitHub

Also just include the entire gem. Space used is negligible and this
way we don't have to think about what to leave in/out.

Create a vendor-gem script in Ruby instead of Make.
parent a3712cc1
No related branches found
No related tags found
No related merge requests found
Showing
with 1407 additions and 12 deletions
Loading
Loading
@@ -10,3 +10,4 @@ tags
.bundle/
custom_hooks
hooks/*.d
/*.gem
REDIS_RB_VERSION=v3.3.0
REDIS_RB_VENDOR_DIR=lib/vendor/redis
PWD=`pwd`
all:
update-redis:
rm -rf $(REDIS_RB_VENDOR_DIR)
git clone -b $(REDIS_RB_VERSION) https://github.com/redis/redis-rb.git $(REDIS_RB_VENDOR_DIR)
rm -rf $(REDIS_RB_VENDOR_DIR)/.git
.PHONY=update-redis
*.rdb
*.swp
Gemfile.lock
*.gem
/tmp/
/.idea
/.yardoc
/coverage/*
/doc/
/examples/sentinel/sentinel.conf
/nohup.out
/pkg/*
/rdsrv
/redis/*
/test/db
/test/test.conf
language: ruby
rvm:
- 1.8.7
- 1.9.3
- 2.0
- 2.1
- 2.2
- 2.3.0
- jruby-18mode
- jruby-19mode
- jruby-9.0.5.0
- rbx-2
gemfile: ".travis/Gemfile"
sudo: false
env:
global:
- VERBOSE=true
- TIMEOUT=1
matrix:
- conn=ruby REDIS_BRANCH=2.8
- conn=hiredis REDIS_BRANCH=2.8
- conn=synchrony REDIS_BRANCH=2.8
- conn=ruby REDIS_BRANCH=unstable
branches:
only:
- master
matrix:
exclude:
# hiredis
- rvm: jruby-18mode
gemfile: .travis/Gemfile
env: conn=hiredis REDIS_BRANCH=2.8
- rvm: jruby-19mode
gemfile: .travis/Gemfile
env: conn=hiredis REDIS_BRANCH=2.8
- rvm: jruby-9.0.5.0
gemfile: .travis/Gemfile
env: conn=hiredis REDIS_BRANCH=2.8
# synchrony
- rvm: 1.8.7
gemfile: .travis/Gemfile
env: conn=synchrony REDIS_BRANCH=2.8
- rvm: jruby-18mode
gemfile: .travis/Gemfile
env: conn=synchrony REDIS_BRANCH=2.8
- rvm: jruby-19mode
gemfile: .travis/Gemfile
env: conn=synchrony REDIS_BRANCH=2.8
- rvm: jruby-9.0.5.0
gemfile: .travis/Gemfile
env: conn=synchrony REDIS_BRANCH=2.8
allow_failures:
- rvm: rbx-2
notifications:
irc:
- irc.freenode.net#redis-rb
email: false
source "https://rubygems.org"
gemspec :path => "../"
case ENV["conn"]
when "hiredis"
gem "hiredis"
when "synchrony"
gem "hiredis"
gem "em-synchrony"
end
--exclude redis/connection
--exclude redis/compat
--markup markdown
# 4.x (unreleased)
## Planned breaking changes:
* `Redis#client` will no longer expose the underlying `Redis::Client`;
it has not yet been determined how 4.0 will expose the underlying
functionality, but we will make every attempt to provide a final minor
release of 3.x that provides the new interfaces in order to facilitate
a smooth transition.
* Ruby 1.8.7 (and the 1.8 modes of JRuby and Rubinius) will no longer be
supported; 1.8.x entered end-of-life in June of 2012 and stopped receiving
security updates in June of 2013; continuing to support it would prevent
the use of newer features of Ruby.
# 3.3.0
* Added support for SSL/TLS. Redis doesn't support SSL natively, so you still
need to run a terminating proxy on Redis' side. See #496.
* Added `read_timeout` and `write_timeout` options. See #437, #482.
* Added support for pub/sub with timeouts. See #329.
* Added `Redis#call`, `Redis#queue` and `Redis#commit` as a more minimal API to
the client.
* Deprecated `Redis#disconnect!` in favor of `Redis#close`.
# 3.2.2
* Added support for `ZADD` options `NX`, `XX`, `CH`, `INCR`. See #547.
* Added support for sentinel commands. See #556.
* New `:id` option allows you to identify the client against Redis. See #510.
* `Redis::Distributed` will raise when adding two nodes with the same ID.
See #354.
# 3.2.1
* Added support for `PUBSUB` command.
* More low-level socket errors are now raised as `CannotConnectError`.
* Added `:connect_timeout` option.
* Added support for `:limit` option for `ZREVRANGEBYLEX`.
* Fixed an issue where connections become inconsistent when using Ruby's
Timeout module outside of the client (see #501, #502).
* Added `Redis#disconnect!` as a public-API way of disconnecting the client
(without needing to use `QUIT`). See #506.
* Fixed Sentinel support with Hiredis.
* Fixed Sentinel support when using authentication and databases.
* Improved resilience when trying to contact sentinels.
# 3.2.0
* Redis Sentinel support.
# 3.1.0
* Added debug log sanitization (#428).
* Added support for HyperLogLog commands (Redis 2.8.9, #432).
* Added support for `BITPOS` command (Redis 2.9.11, #412).
* The client will now automatically reconnect after a fork (#414).
* If you want to disable the fork-safety check and prefer to share the
connection across child processes, you can now pass the `inherit_socket`
option (#409).
* If you want the client to attempt to reconnect more than once, you can now
pass the `reconnect_attempts` option (#347)
# 3.0.7
* Added method `Redis#dup` to duplicate a Redis connection.
* IPv6 support.
# 3.0.6
* Added support for `SCAN` and variants.
# 3.0.5
* Fix calling #select from a pipeline (#309).
* Added method `Redis#connected?`.
* Added support for `MIGRATE` (Redis 2.6).
* Support extended SET command (#343, thanks to @benubois).
# 3.0.4
* Ensure #watch without a block returns "OK" (#332).
* Make futures identifiable (#330).
* Fix an issue preventing STORE in a SORT with multiple GETs (#328).
# 3.0.3
* Blocking list commands (`BLPOP`, `BRPOP`, `BRPOPLPUSH`) use a socket
timeout equal to the sum of the command's timeout and the Redis
client's timeout, instead of disabling socket timeout altogether.
* Ruby 2.0 compatibility.
* Added support for `DUMP` and `RESTORE` (Redis 2.6).
* Added support for `BITCOUNT` and `BITOP` (Redis 2.6).
* Call `#to_s` on value argument for `SET`, `SETEX`, `PSETEX`, `GETSET`,
`SETNX`, and `SETRANGE`.
# 3.0.2
* Unescape CGI escaped password in URL.
* Fix test to check availability of `UNIXSocket`.
* Fix handling of score = +/- infinity for sorted set commands.
* Replace array splats with concatenation where possible.
* Raise if `EXEC` returns an error.
* Passing a nil value in options hash no longer overwrites the default.
* Allow string keys in options hash passed to `Redis.new` or
`Redis.connect`.
* Fix uncaught error triggering unrelated error (synchrony driver).
See f7ffd5f1a628029691084de69e5b46699bb8b96d and #248.
# 3.0.1
* Fix reconnect logic not kicking in on a write error.
See 427dbd52928af452f35aa0a57b621bee56cdcb18 and #238.
# 3.0.0
### Upgrading from 2.x to 3.0
The following items are the most important changes to review when
upgrading from redis-rb 2.x. A full list of changes can be found below.
* The methods for the following commands have changed the arguments they
take, their return value, or both.
* `BLPOP`, `BRPOP`, `BRPOPLPUSH`
* `SORT`
* `MSETNX`
* `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZREVRANGEBYSCORE`
* `ZINCRBY`, `ZSCORE`
* The return value from `#pipelined` and `#multi` no longer contains
unprocessed replies, but the same replies that would be returned if
the command had not been executed in these blocks.
* The client raises custom errors on connection errors, instead of
`RuntimeError` and errors in the `Errno` family.
### Changes
* Added support for scripting commands (Redis 2.6).
Scripts can be executed using `#eval` and `#evalsha`. Both can
commands can either take two arrays to specify `KEYS` and `ARGV`, or
take a hash containing `:keys` and `:argv` to specify `KEYS` and
`ARGV`.
```ruby
redis.eval("return ARGV[1] * ARGV[2]", :argv => [2, 3])
# => 6
```
Subcommands of the `SCRIPT` command can be executed via the
`#script` method.
For example:
```ruby
redis.script(:load, "return ARGV[1] * ARGV[2]")
# => "58db5d365a1922f32e7aa717722141ea9c2b0cf3"
redis.script(:exists, "58db5d365a1922f32e7aa717722141ea9c2b0cf3")
# => true
redis.script(:flush)
# => "OK"
```
* The repository now lives at [https://github.com/redis/redis-rb](https://github.com/redis/redis-rb).
Thanks, Ezra!
* Added support for `PEXPIRE`, `PEXPIREAT`, `PTTL`, `PSETEX`,
`INCRYBYFLOAT`, `HINCRYBYFLOAT` and `TIME` (Redis 2.6).
* `Redis.current` is now thread unsafe, because the client itself is thread safe.
In the future you'll be able to do something like:
```ruby
Redis.current = Redis::Pool.connect
```
This makes `Redis.current` actually usable in multi-threaded environments,
while not affecting those running a single thread.
* Change API for `BLPOP`, `BRPOP` and `BRPOPLPUSH`.
Both `BLPOP` and `BRPOP` now take a single argument equal to a
string key, or an array with string keys, followed by an optional
hash with a `:timeout` key. When not specified, the timeout defaults
to `0` to not time out.
```ruby
redis.blpop(["list1", "list2"], :timeout => 1.0)
```
`BRPOPLPUSH` also takes an optional hash with a `:timeout` key as
last argument for consistency. When not specified, the timeout
defaults to `0` to not time out.
```ruby
redis.brpoplpush("some_list", "another_list", :timeout => 1.0)
```
* When `SORT` is passed multiple key patterns to get via the `:get`
option, it now returns an array per result element, holding all `GET`
substitutions.
* The `MSETNX` command now returns a boolean.
* The `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE` and `ZREVRANGEBYSCORE` commands
now return an array containing `[String, Float]` pairs when
`:with_scores => true` is passed.
For example:
```ruby
redis.zrange("zset", 0, -1, :with_scores => true)
# => [["foo", 1.0], ["bar", 2.0]]
```
* The `ZINCRBY` and `ZSCORE` commands now return a `Float` score instead
of a string holding a representation of the score.
* The client now raises custom exceptions where it makes sense.
If by any chance you were rescuing low-level exceptions (`Errno::*`),
you should now rescue as follows:
Errno::ECONNRESET -> Redis::ConnectionError
Errno::EPIPE -> Redis::ConnectionError
Errno::ECONNABORTED -> Redis::ConnectionError
Errno::EBADF -> Redis::ConnectionError
Errno::EINVAL -> Redis::ConnectionError
Errno::EAGAIN -> Redis::TimeoutError
Errno::ECONNREFUSED -> Redis::CannotConnectError
* Always raise exceptions originating from erroneous command invocation
inside pipelines and MULTI/EXEC blocks.
The old behavior (swallowing exceptions) could cause application bugs
to go unnoticed.
* Implement futures for assigning values inside pipelines and MULTI/EXEC
blocks. Futures are assigned their value after the pipeline or
MULTI/EXEC block has executed.
```ruby
$redis.pipelined do
@future = $redis.get "key"
end
puts @future.value
```
* Ruby 1.8.6 is officially not supported.
* Support `ZCOUNT` in `Redis::Distributed` (Michael Dungan).
* Pipelined commands now return the same replies as when called outside
a pipeline.
In the past, pipelined replies were returned without post-processing.
* Support `SLOWLOG` command (Michael Bernstein).
* Calling `SHUTDOWN` effectively disconnects the client (Stefan Kaes).
* Basic support for mapping commands so that they can be renamed on the
server.
* Connecting using a URL now checks that a host is given.
It's just a small sanity check, cf. #126
* Support variadic commands introduced in Redis 2.4.
# 2.2.2
* Added method `Redis::Distributed#hsetnx`.
# 2.2.1
* Internal API: Client#call and family are now called with a single array
argument, since splatting a large number of arguments (100K+) results in a
stack overflow on 1.9.2.
* The `INFO` command can optionally take a subcommand. When the subcommand is
`COMMANDSTATS`, the client will properly format the returned statistics per
command. Subcommands for `INFO` are available since Redis v2.3.0 (unstable).
* Change `IO#syswrite` back to the buffered `IO#write` since some Rubies do
short writes for large (1MB+) buffers and some don't (see issue #108).
# 2.2.0
* Added method `Redis#without_reconnect` that ensures the client will not try
to reconnect when running the code inside the specified block.
* Thread-safe by default. Thread safety can be explicitly disabled by passing
`:thread_safe => false` as argument.
* Commands called inside a MULTI/EXEC no longer raise error replies, since a
successful EXEC means the commands inside the block were executed.
* MULTI/EXEC blocks are pipelined.
* Don't disconnect on error replies.
* Use `IO#syswrite` instead of `IO#write` because write buffering is not
necessary.
* Connect to a unix socket by passing the `:path` option as argument.
* The timeout value is coerced into a float, allowing sub-second timeouts.
* Accept both `:with_scores` _and_ `:withscores` as argument to sorted set
commands.
* Use [hiredis](https://github.com/pietern/hiredis-rb) (v0.3 or higher) by
requiring "redis/connection/hiredis".
* Use [em-synchrony](https://github.com/igrigorik/em-synchrony) by requiring
"redis/connection/synchrony".
# 2.1.1
See commit log.
# encoding: utf-8
source 'https://rubygems.org'
gemspec
Copyright (c) 2009 Ezra Zygmuntowicz
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
[travis-image]: https://secure.travis-ci.org/redis/redis-rb.png?branch=master
[travis-link]: http://travis-ci.org/redis/redis-rb
[travis-home]: http://travis-ci.org/
[inchpages-image]: http://inch-ci.org/github/redis/redis-rb.png
[inchpages-link]: http://inch-ci.org/github/redis/redis-rb
A Ruby client library for [Redis][redis-home].
[redis-home]: http://redis.io
A Ruby client that tries to match Redis' API one-to-one, while still
providing an idiomatic interface. It features thread-safety, client-side
sharding, pipelining, and an obsession for performance.
## Upgrading from 2.x to 3.0
Please refer to the [CHANGELOG][changelog-3.0.0] for a summary of the
most important changes, as well as a full list of changes.
[changelog-3.0.0]: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300
## Getting started
To install **redis-rb**, run the following command:
```
gem install redis
```
Or if you are using **bundler**, add
```
gem 'redis', '~>3.2'
```
to your `Gemfile`, and run `bundle install`
As of version 2.0 this client only targets Redis version 2.0 and higher.
You can use an older version of this client if you need to interface
with a Redis instance older than 2.0, but this is no longer supported.
You can connect to Redis by instantiating the `Redis` class:
```ruby
require "redis"
redis = Redis.new
```
This assumes Redis was started with a default configuration, and is
listening on `localhost`, port 6379. If you need to connect to a remote
server or a different port, try:
```ruby
redis = Redis.new(:host => "10.0.1.1", :port => 6380, :db => 15)
```
You can also specify connection options as a [`redis://` URL][redis-url]:
```ruby
redis = Redis.new(:url => "redis://:p4ssw0rd@10.0.1.1:6380/15")
```
[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
By default, the client will try to read the `REDIS_URL` environment variable
and use that as URL to connect to. The above statement is therefore equivalent
to setting this environment variable and calling `Redis.new` without arguments.
To connect to Redis listening on a Unix socket, try:
```ruby
redis = Redis.new(:path => "/tmp/redis.sock")
```
To connect to a password protected Redis instance, use:
```ruby
redis = Redis.new(:password => "mysecret")
```
The Redis class exports methods that are named identical to the commands
they execute. The arguments these methods accept are often identical to
the arguments specified on the [Redis website][redis-commands]. For
instance, the `SET` and `GET` commands can be called like this:
[redis-commands]: http://redis.io/commands
```ruby
redis.set("mykey", "hello world")
# => "OK"
redis.get("mykey")
# => "hello world"
```
All commands, their arguments and return values are documented, and
available on [rdoc.info][rdoc].
[rdoc]: http://rdoc.info/github/redis/redis-rb/
## Sentinel support
The client is able to perform automatic failovers by using [Redis
Sentinel](http://redis.io/topics/sentinel). Make sure to run Redis 2.8+
if you want to use this feature.
To connect using Sentinel, use:
```ruby
SENTINELS = [{:host => "127.0.0.1", :port => 26380},
{:host => "127.0.0.1", :port => 26381}]
redis = Redis.new(:url => "redis://mymaster", :sentinels => SENTINELS, :role => :master)
```
* The master name identifies a group of Redis instances composed of a master
and one or more slaves (`mymaster` in the example).
* It is possible to optionally provide a role. The allowed roles are `master`
and `slave`. When the role is `slave`, the client will try to connect to a
random slave of the specified master. If a role is not specified, the client
will connect to the master.
* When using the Sentinel support you need to specify a list of sentinels to
connect to. The list does not need to enumerate all your Sentinel instances,
but a few so that if one is down the client will try the next one. The client
is able to remember the last Sentinel that was able to reply correctly and will
use it for the next requests.
## Storing objects
Redis only stores strings as values. If you want to store an object, you
can use a serialization mechanism such as JSON:
```ruby
require "json"
redis.set "foo", [1, 2, 3].to_json
# => OK
JSON.parse(redis.get("foo"))
# => [1, 2, 3]
```
## Pipelining
When multiple commands are executed sequentially, but are not dependent,
the calls can be *pipelined*. This means that the client doesn't wait
for reply of the first command before sending the next command. The
advantage is that multiple commands are sent at once, resulting in
faster overall execution.
The client can be instructed to pipeline commands by using the
`#pipelined` method. After the block is executed, the client sends all
commands to Redis and gathers their replies. These replies are returned
by the `#pipelined` method.
```ruby
redis.pipelined do
redis.set "foo", "bar"
redis.incr "baz"
end
# => ["OK", 1]
```
### Executing commands atomically
You can use `MULTI/EXEC` to run a number of commands in an atomic
fashion. This is similar to executing a pipeline, but the commands are
preceded by a call to `MULTI`, and followed by a call to `EXEC`. Like
the regular pipeline, the replies to the commands are returned by the
`#multi` method.
```ruby
redis.multi do
redis.set "foo", "bar"
redis.incr "baz"
end
# => ["OK", 1]
```
### Futures
Replies to commands in a pipeline can be accessed via the *futures* they
emit (since redis-rb 3.0). All calls inside a pipeline block return a
`Future` object, which responds to the `#value` method. When the
pipeline has successfully executed, all futures are assigned their
respective replies and can be used.
```ruby
redis.pipelined do
@set = redis.set "foo", "bar"
@incr = redis.incr "baz"
end
@set.value
# => "OK"
@incr.value
# => 1
```
## Error Handling
In general, if something goes wrong you'll get an exception. For example, if
it can't connect to the server a `Redis::CannotConnectError` error will be raised.
```ruby
begin
redis.ping
rescue Exception => e
e.inspect
# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
e.message
# => Timed out connecting to Redis on 10.0.1.1:6380
end
```
See lib/redis/errors.rb for information about what exceptions are possible.
## Timeouts
The client allows you to configure connect, read, and write timeouts.
Passing a single `timeout` option will set all three values:
```ruby
Redis.new(:timeout => 1)
```
But you can use specific values for each of them:
```ruby
Redis.new(
:connect_timeout => 0.2,
:read_timeout => 1.0,
:write_timeout => 0.5
)
```
All timeout values are specified in seconds.
When using pub/sub, you can subscribe to a channel using a timeout as well:
```ruby
redis.subscribe_with_timeout(5, "news") do |on|
on.message do |channel, message|
# ...
end
end
```
If no message is received after 5 seconds, the client will unsubscribe.
## SSL/TLS Support
This library supports natively terminating client side SSL/TLS connections
when talking to Redis via a server-side proxy such as [stunnel], [hitch],
or [ghostunnel].
To enable SSL support, pass the `:ssl => :true` option when configuring the
Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
You will also need to pass in an `:ssl_params => { ... }` hash used to
configure the `OpenSSL::SSL::SSLContext` object used for the connection:
```ruby
redis = Redis.new(
:url => "rediss://:p4ssw0rd@10.0.1.1:6381/15",
:ssl_params => {
:ca_file => "/path/to/ca.crt"
}
)
```
The options given to `:ssl_params` are passed directly to the
`OpenSSL::SSL::SSLContext#set_params` method and can be any valid attribute
of the SSL context. Please see the [OpenSSL::SSL::SSLContext documentation]
for all of the available attributes.
Here is an example of passing in params that can be used for SSL client
certificate authentication (a.k.a. mutual TLS):
```ruby
redis = Redis.new(
:url => "rediss://:p4ssw0rd@10.0.1.1:6381/15",
:ssl_params => {
:ca_file => "/path/to/ca.crt",
:cert => OpenSSL::X509::Certificate.new(File.read("client.crt")),
:key => OpenSSL::PKey::RSA.new(File.read("client.key"))
}
)
```
[stunnel]: https://www.stunnel.org/
[hitch]: https://hitch-tls.org/
[ghostunnel]: https://github.com/square/ghostunnel
[OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.3.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
*NOTE:* SSL is only supported by the default "Ruby" driver
## Expert-Mode Options
- `inherit_socket: true`: disable safety check that prevents a forked child
from sharing a socket with its parent; this is potentially useful in order to mitigate connection churn when:
- many short-lived forked children of one process need to talk
to redis, AND
- your own code prevents the parent process from using the redis
connection while a child is alive
Improper use of `inherit_socket` will result in corrupted and/or incorrect
responses.
## Alternate drivers
By default, redis-rb uses Ruby's socket library to talk with Redis.
To use an alternative connection driver it should be specified as option
when instantiating the client object. These instructions are only valid
for **redis-rb 3.0**. For instructions on how to use alternate drivers from
**redis-rb 2.2**, please refer to an [older README][readme-2.2.2].
[readme-2.2.2]: https://github.com/redis/redis-rb/blob/v2.2.2/README.md
### hiredis
The hiredis driver uses the connection facility of hiredis-rb. In turn,
hiredis-rb is a binding to the official hiredis client library. It
optimizes for speed, at the cost of portability. Because it is a C
extension, JRuby is not supported (by default).
It is best to use hiredis when you have large replies (for example:
`LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or use big pipelines.
In your Gemfile, include hiredis:
```ruby
gem "redis", "~> 3.0.1"
gem "hiredis", "~> 0.4.5"
```
When instantiating the client object, specify hiredis:
```ruby
redis = Redis.new(:driver => :hiredis)
```
### synchrony
The synchrony driver adds support for [em-synchrony][em-synchrony].
This makes redis-rb work with EventMachine's asynchronous I/O, while not
changing the exposed API. The hiredis gem needs to be available as
well, because the synchrony driver uses hiredis for parsing the Redis
protocol.
[em-synchrony]: https://github.com/igrigorik/em-synchrony
In your Gemfile, include em-synchrony and hiredis:
```ruby
gem "redis", "~> 3.0.1"
gem "hiredis", "~> 0.4.5"
gem "em-synchrony"
```
When instantiating the client object, specify synchrony:
```ruby
redis = Redis.new(:driver => :synchrony)
```
## Testing
This library is tested using [Travis][travis-home], where it is tested
against the following interpreters and drivers:
* MRI 1.8.7 (drivers: ruby, hiredis)
* MRI 1.9.3 (drivers: ruby, hiredis, synchrony)
* MRI 2.0 (drivers: ruby, hiredis, synchrony)
* MRI 2.1 (drivers: ruby, hiredis, synchrony)
* MRI 2.2 (drivers: ruby, hiredis, synchrony)
* MRI 2.3 (drivers: ruby, hiredis, synchrony)
* JRuby 1.7 (1.8 mode) (drivers: ruby)
* JRuby 1.7 (1.9 mode) (drivers: ruby)
## Contributors
(ordered chronologically with more than 5 commits, see `git shortlog -sn` for
all contributors)
* Ezra Zygmuntowicz
* Taylor Weibley
* Matthew Clark
* Brian McKinney
* Luca Guidi
* Salvatore Sanfilippo
* Chris Wanstrath
* Damian Janowski
* Michel Martens
* Nick Quaranto
* Pieter Noordhuis
* Ilya Grigorik
## Contributing
[Fork the project](https://github.com/redis/redis-rb) and send pull
requests. You can also ask for help at `#redis-rb` on Freenode.
require "rake/testtask"
ENV["REDIS_BRANCH"] ||= "unstable"
REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
REDIS_CNF = File.join(REDIS_DIR, "test.conf")
REDIS_CNF_TEMPLATE = File.join(REDIS_DIR, "test.conf.erb")
REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
REDIS_LOG = File.join(REDIS_DIR, "db", "redis.log")
REDIS_SOCKET = File.join(REDIS_DIR, "db", "redis.sock")
BINARY = "tmp/redis-#{ENV["REDIS_BRANCH"]}/src/redis-server"
task :default => :run
desc "Run tests and manage server start/stop"
task :run => [:start, :test, :stop]
desc "Start the Redis server"
task :start => [BINARY, REDIS_CNF] do
sh "#{BINARY} --version"
redis_running = \
begin
File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
rescue Errno::ESRCH
FileUtils.rm REDIS_PID
false
end
unless redis_running
unless system("#{BINARY} #{REDIS_CNF}")
abort "could not start redis-server"
end
end
at_exit do
Rake::Task["stop"].invoke
end
end
desc "Stop the Redis server"
task :stop do
if File.exists?(REDIS_PID)
Process.kill "INT", File.read(REDIS_PID).to_i
FileUtils.rm REDIS_PID
end
end
desc "Clean up testing artifacts"
task :clean do
FileUtils.rm_f(BINARY)
FileUtils.rm_f(REDIS_CNF)
end
file BINARY do
branch = ENV.fetch("REDIS_BRANCH")
sh <<-SH
mkdir -p tmp;
cd tmp;
rm -rf redis-#{branch};
wget https://github.com/antirez/redis/archive/#{branch}.tar.gz -O #{branch}.tar.gz;
tar xf #{branch}.tar.gz;
cd redis-#{branch};
make
SH
end
file REDIS_CNF => [REDIS_CNF_TEMPLATE, __FILE__] do |t|
require 'erb'
erb = t.prerequisites[0]
template = File.read(erb)
File.open(REDIS_CNF, 'w') do |file|
file.puts "\# This file was auto-generated at #{Time.now}",
"\# from (#{erb})",
"\#"
conf = ERB.new(template).result
file << conf
end
end
Rake::TestTask.new do |t|
t.options = "-v" if $VERBOSE
t.test_files = FileList["test/*_test.rb"]
end
# Run with
#
# $ ruby -Ilib benchmarking/logging.rb
#
begin
require "bench"
rescue LoadError
$stderr.puts "`gem install bench` and try again."
exit 1
end
require "redis"
require "logger"
def log(level, namespace = nil)
logger = (namespace || Kernel).const_get(:Logger).new("/dev/null")
logger.level = (namespace || Logger).const_get(level)
logger
end
def stress(redis)
redis.flushdb
n = (ARGV.shift || 2000).to_i
n.times do |i|
key = "foo:#{i}"
redis.set key, i
redis.get key
end
end
default = Redis.new
logging_redises = [
Redis.new(:logger => log(:DEBUG)),
Redis.new(:logger => log(:INFO)),
]
begin
require "log4r"
logging_redises += [
Redis.new(:logger => log(:DEBUG, Log4r)),
Redis.new(:logger => log(:INFO, Log4r)),
]
rescue LoadError
$stderr.puts "Log4r not installed. `gem install log4r` if you want to compare it against Ruby's Logger (spoiler: it's much faster)."
end
benchmark "Default options (no logger)" do
stress(default)
end
logging_redises.each do |redis|
logger = redis.client.logger
case logger
when Logger
level = Logger::SEV_LABEL[logger.level]
when Log4r::Logger
level = logger.levels[logger.level]
end
benchmark "#{logger.class} on #{level}" do
stress(redis)
end
end
run 10
require "benchmark"
$:.push File.join(File.dirname(__FILE__), 'lib')
require 'redis'
ITERATIONS = 10000
@r = Redis.new
Benchmark.bmbm do |benchmark|
benchmark.report("set") do
@r.flushdb
ITERATIONS.times do |i|
@r.set("foo#{i}", "Hello world!")
@r.get("foo#{i}")
end
end
benchmark.report("set (pipelined)") do
@r.flushdb
@r.pipelined do
ITERATIONS.times do |i|
@r.set("foo#{i}", "Hello world!")
@r.get("foo#{i}")
end
end
end
benchmark.report("lpush+ltrim") do
@r.flushdb
ITERATIONS.times do |i|
@r.lpush "lpush#{i}", i
@r.ltrim "ltrim#{i}", 0, 30
end
end
benchmark.report("lpush+ltrim (pipelined)") do
@r.flushdb
@r.pipelined do
ITERATIONS.times do |i|
@r.lpush "lpush#{i}", i
@r.ltrim "ltrim#{i}", 0, 30
end
end
end
end
# Run with
#
# $ ruby -Ilib benchmarking/speed.rb
#
require "benchmark"
require "redis"
r = Redis.new
n = (ARGV.shift || 20000).to_i
elapsed = Benchmark.realtime do
# n sets, n gets
n.times do |i|
key = "foo#{i}"
r[key] = key * 10
r[key]
end
end
puts '%.2f Kops' % (2 * n / 1000 / elapsed)
require 'fileutils'
def run_in_background(command)
fork { system command }
end
def with_all_segments(&block)
0.upto(9) do |segment_number|
block_size = 100000
start_index = segment_number * block_size
end_index = start_index + block_size - 1
block.call(start_index, end_index)
end
end
#with_all_segments do |start_index, end_index|
# puts "Initializing keys from #{start_index} to #{end_index}"
# system "ruby worker.rb initialize #{start_index} #{end_index} 0"
#end
with_all_segments do |start_index, end_index|
run_in_background "ruby worker.rb write #{start_index} #{end_index} 10"
run_in_background "ruby worker.rb read #{start_index} #{end_index} 1"
end
BENCHMARK_ROOT = File.dirname(__FILE__)
REDIS_ROOT = File.join(BENCHMARK_ROOT, "..", "lib")
$: << REDIS_ROOT
require 'redis'
require 'benchmark'
def show_usage
puts <<-EOL
Usage: worker.rb [read:write] <start_index> <end_index> <sleep_msec>
EOL
end
def shift_from_argv
value = ARGV.shift
unless value
show_usage
exit -1
end
value
end
operation = shift_from_argv.to_sym
start_index = shift_from_argv.to_i
end_index = shift_from_argv.to_i
sleep_msec = shift_from_argv.to_i
sleep_duration = sleep_msec/1000.0
redis = Redis.new
case operation
when :initialize
start_index.upto(end_index) do |i|
redis[i] = 0
end
when :clear
start_index.upto(end_index) do |i|
redis.delete(i)
end
when :read, :write
puts "Starting to #{operation} at segment #{end_index + 1}"
loop do
t1 = Time.now
start_index.upto(end_index) do |i|
case operation
when :read
redis.get(i)
when :write
redis.incr(i)
else
raise "Unknown operation: #{operation}"
end
sleep sleep_duration
end
t2 = Time.now
requests_processed = end_index - start_index
time = t2 - t1
puts "#{t2.strftime("%H:%M")} [segment #{end_index + 1}] : Processed #{requests_processed} requests in #{time} seconds - #{(requests_processed/time).round} requests/sec"
end
else
raise "Unknown operation: #{operation}"
end
require 'redis'
r = Redis.new
r.del('foo')
puts
p'set foo to "bar"'
r['foo'] = 'bar'
puts
p 'value of foo'
p r['foo']
# This file implements a simple consistency test for Redis-rb (or any other
# Redis environment if you pass a different client object) where a client
# writes to the database using INCR in order to increment keys, but actively
# remember the value the key should have. Before every write a read is performed
# to check if the value in the database matches the value expected.
#
# In this way this program can check for lost writes, or acknowledged writes
# that were executed.
#
# Copyright (C) 2013-2014 Salvatore Sanfilippo <antirez@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'redis'
class ConsistencyTester
def initialize(redis)
@r = redis
@working_set = 10000
@keyspace = 100000
@writes = 0
@reads = 0
@failed_writes = 0
@failed_reads = 0
@lost_writes = 0
@not_ack_writes = 0
@delay = 0
@cached = {} # We take our view of data stored in the DB.
@prefix = [Process.pid.to_s,Time.now.usec,@r.object_id,""].join("|")
@errtime = {}
end
def genkey
# Write more often to a small subset of keys
ks = rand() > 0.5 ? @keyspace : @working_set
@prefix+"key_"+rand(ks).to_s
end
def check_consistency(key,value)
expected = @cached[key]
return if !expected # We lack info about previous state.
if expected > value
@lost_writes += expected-value
elsif expected < value
@not_ack_writes += value-expected
end
end
def puterr(msg)
if !@errtime[msg] || Time.now.to_i != @errtime[msg]
puts msg
end
@errtime[msg] = Time.now.to_i
end
def test
last_report = Time.now.to_i
while true
# Read
key = genkey
begin
val = @r.get(key)
check_consistency(key,val.to_i)
@reads += 1
rescue => e
puterr "Reading: #{e.class}: #{e.message} (#{e.backtrace.first})"
@failed_reads += 1
end
# Write
begin
@cached[key] = @r.incr(key).to_i
@writes += 1
rescue => e
puterr "Writing: #{e.class}: #{e.message} (#{e.backtrace.first})"
@failed_writes += 1
end
# Report
sleep @delay
if Time.now.to_i != last_report
report = "#{@reads} R (#{@failed_reads} err) | " +
"#{@writes} W (#{@failed_writes} err) | "
report += "#{@lost_writes} lost | " if @lost_writes > 0
report += "#{@not_ack_writes} noack | " if @not_ack_writes > 0
last_report = Time.now.to_i
puts report
end
end
end
end
Sentinels = [{:host => "127.0.0.1", :port => 26379},
{:host => "127.0.0.1", :port => 26380}]
r = Redis.new(:url => "redis://master1", :sentinels => Sentinels, :role => :master)
tester = ConsistencyTester.new(r)
tester.test
require "redis"
require "redis/distributed"
r = Redis::Distributed.new %w[redis://localhost:6379 redis://localhost:6380 redis://localhost:6381 redis://localhost:6382]
r.flushdb
r['urmom'] = 'urmom'
r['urdad'] = 'urdad'
r['urmom1'] = 'urmom1'
r['urdad1'] = 'urdad1'
r['urmom2'] = 'urmom2'
r['urdad2'] = 'urdad2'
r['urmom3'] = 'urmom3'
r['urdad3'] = 'urdad3'
p r['urmom']
p r['urdad']
p r['urmom1']
p r['urdad1']
p r['urmom2']
p r['urdad2']
p r['urmom3']
p r['urdad3']
r.rpush 'listor', 'foo1'
r.rpush 'listor', 'foo2'
r.rpush 'listor', 'foo3'
r.rpush 'listor', 'foo4'
r.rpush 'listor', 'foo5'
p r.rpop('listor')
p r.rpop('listor')
p r.rpop('listor')
p r.rpop('listor')
p r.rpop('listor')
puts "key distribution:"
r.ring.nodes.each do |node|
p [node.client, node.keys("*")]
end
r.flushdb
p r.keys('*')
require 'redis'
r = Redis.new
puts
p 'incr'
r.del 'counter'
p r.incr('counter')
p r.incr('counter')
p r.incr('counter')
puts
p 'decr'
p r.decr('counter')
p r.decr('counter')
p r.decr('counter')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment