finsync_redis 3.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis/Gemfile +11 -0
  4. data/.travis.yml +89 -0
  5. data/.yardopts +3 -0
  6. data/CHANGELOG.md +373 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +410 -0
  10. data/Rakefile +87 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/consistency.rb +114 -0
  18. data/examples/dist_redis.rb +43 -0
  19. data/examples/incr-decr.rb +17 -0
  20. data/examples/list.rb +26 -0
  21. data/examples/pubsub.rb +37 -0
  22. data/examples/sentinel/sentinel.conf +9 -0
  23. data/examples/sentinel/start +49 -0
  24. data/examples/sentinel.rb +41 -0
  25. data/examples/sets.rb +36 -0
  26. data/examples/unicorn/config.ru +3 -0
  27. data/examples/unicorn/unicorn.rb +20 -0
  28. data/lib/redis/client.rb +590 -0
  29. data/lib/redis/connection/command_helper.rb +44 -0
  30. data/lib/redis/connection/hiredis.rb +66 -0
  31. data/lib/redis/connection/registry.rb +12 -0
  32. data/lib/redis/connection/ruby.rb +429 -0
  33. data/lib/redis/connection/synchrony.rb +133 -0
  34. data/lib/redis/connection.rb +9 -0
  35. data/lib/redis/distributed.rb +873 -0
  36. data/lib/redis/errors.rb +40 -0
  37. data/lib/redis/hash_ring.rb +132 -0
  38. data/lib/redis/pipeline.rb +141 -0
  39. data/lib/redis/subscribe.rb +91 -0
  40. data/lib/redis/version.rb +3 -0
  41. data/lib/redis.rb +2788 -0
  42. data/redis.gemspec +44 -0
  43. data/test/bitpos_test.rb +69 -0
  44. data/test/blocking_commands_test.rb +42 -0
  45. data/test/client_test.rb +59 -0
  46. data/test/command_map_test.rb +30 -0
  47. data/test/commands_on_hashes_test.rb +21 -0
  48. data/test/commands_on_hyper_log_log_test.rb +21 -0
  49. data/test/commands_on_lists_test.rb +20 -0
  50. data/test/commands_on_sets_test.rb +77 -0
  51. data/test/commands_on_sorted_sets_test.rb +137 -0
  52. data/test/commands_on_strings_test.rb +101 -0
  53. data/test/commands_on_value_types_test.rb +133 -0
  54. data/test/connection_handling_test.rb +277 -0
  55. data/test/connection_test.rb +57 -0
  56. data/test/db/.gitkeep +0 -0
  57. data/test/distributed_blocking_commands_test.rb +46 -0
  58. data/test/distributed_commands_on_hashes_test.rb +10 -0
  59. data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
  60. data/test/distributed_commands_on_lists_test.rb +22 -0
  61. data/test/distributed_commands_on_sets_test.rb +83 -0
  62. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  63. data/test/distributed_commands_on_strings_test.rb +59 -0
  64. data/test/distributed_commands_on_value_types_test.rb +95 -0
  65. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  66. data/test/distributed_connection_handling_test.rb +23 -0
  67. data/test/distributed_internals_test.rb +79 -0
  68. data/test/distributed_key_tags_test.rb +52 -0
  69. data/test/distributed_persistence_control_commands_test.rb +26 -0
  70. data/test/distributed_publish_subscribe_test.rb +92 -0
  71. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  72. data/test/distributed_scripting_test.rb +102 -0
  73. data/test/distributed_sorting_test.rb +20 -0
  74. data/test/distributed_test.rb +58 -0
  75. data/test/distributed_transactions_test.rb +32 -0
  76. data/test/encoding_test.rb +18 -0
  77. data/test/error_replies_test.rb +59 -0
  78. data/test/fork_safety_test.rb +65 -0
  79. data/test/helper.rb +232 -0
  80. data/test/helper_test.rb +24 -0
  81. data/test/internals_test.rb +417 -0
  82. data/test/lint/blocking_commands.rb +150 -0
  83. data/test/lint/hashes.rb +162 -0
  84. data/test/lint/hyper_log_log.rb +60 -0
  85. data/test/lint/lists.rb +143 -0
  86. data/test/lint/sets.rb +140 -0
  87. data/test/lint/sorted_sets.rb +316 -0
  88. data/test/lint/strings.rb +260 -0
  89. data/test/lint/value_types.rb +122 -0
  90. data/test/persistence_control_commands_test.rb +26 -0
  91. data/test/pipelining_commands_test.rb +242 -0
  92. data/test/publish_subscribe_test.rb +282 -0
  93. data/test/remote_server_control_commands_test.rb +118 -0
  94. data/test/scanning_test.rb +413 -0
  95. data/test/scripting_test.rb +78 -0
  96. data/test/sentinel_command_test.rb +80 -0
  97. data/test/sentinel_test.rb +255 -0
  98. data/test/sorting_test.rb +59 -0
  99. data/test/ssl_test.rb +73 -0
  100. data/test/support/connection/hiredis.rb +1 -0
  101. data/test/support/connection/ruby.rb +1 -0
  102. data/test/support/connection/synchrony.rb +17 -0
  103. data/test/support/redis_mock.rb +130 -0
  104. data/test/support/ssl/gen_certs.sh +31 -0
  105. data/test/support/ssl/trusted-ca.crt +25 -0
  106. data/test/support/ssl/trusted-ca.key +27 -0
  107. data/test/support/ssl/trusted-cert.crt +81 -0
  108. data/test/support/ssl/trusted-cert.key +28 -0
  109. data/test/support/ssl/untrusted-ca.crt +26 -0
  110. data/test/support/ssl/untrusted-ca.key +27 -0
  111. data/test/support/ssl/untrusted-cert.crt +82 -0
  112. data/test/support/ssl/untrusted-cert.key +28 -0
  113. data/test/support/wire/synchrony.rb +24 -0
  114. data/test/support/wire/thread.rb +5 -0
  115. data/test/synchrony_driver.rb +88 -0
  116. data/test/test.conf.erb +9 -0
  117. data/test/thread_safety_test.rb +62 -0
  118. data/test/transactions_test.rb +264 -0
  119. data/test/unknown_commands_test.rb +14 -0
  120. data/test/url_param_test.rb +138 -0
  121. metadata +202 -0
@@ -0,0 +1,40 @@
1
+ class Redis
2
+ # Base error for all redis-rb errors.
3
+ class BaseError < RuntimeError
4
+ end
5
+
6
+ # Raised by the connection when a protocol error occurs.
7
+ class ProtocolError < BaseError
8
+ def initialize(reply_type)
9
+ super(<<-EOS.gsub(/(?:^|\n)\s*/, " "))
10
+ Got '#{reply_type}' as initial reply byte.
11
+ If you're in a forking environment, such as Unicorn, you need to
12
+ connect to Redis after forking.
13
+ EOS
14
+ end
15
+ end
16
+
17
+ # Raised by the client when command execution returns an error reply.
18
+ class CommandError < BaseError
19
+ end
20
+
21
+ # Base error for connection related errors.
22
+ class BaseConnectionError < BaseError
23
+ end
24
+
25
+ # Raised when connection to a Redis server cannot be made.
26
+ class CannotConnectError < BaseConnectionError
27
+ end
28
+
29
+ # Raised when connection to a Redis server is lost.
30
+ class ConnectionError < BaseConnectionError
31
+ end
32
+
33
+ # Raised when performing I/O times out.
34
+ class TimeoutError < BaseConnectionError
35
+ end
36
+
37
+ # Raised when the connection was inherited by a child process.
38
+ class InheritedError < BaseConnectionError
39
+ end
40
+ end
@@ -0,0 +1,132 @@
1
+ require 'zlib'
2
+
3
+ class Redis
4
+ class HashRing
5
+
6
+ POINTS_PER_SERVER = 160 # this is the default in libmemcached
7
+
8
+ attr_reader :ring, :sorted_keys, :replicas, :nodes
9
+
10
+ # nodes is a list of objects that have a proper to_s representation.
11
+ # replicas indicates how many virtual points should be used pr. node,
12
+ # replicas are required to improve the distribution.
13
+ def initialize(nodes=[], replicas=POINTS_PER_SERVER)
14
+ @replicas = replicas
15
+ @ring = {}
16
+ @nodes = []
17
+ @sorted_keys = []
18
+ nodes.each do |node|
19
+ add_node(node)
20
+ end
21
+ end
22
+
23
+ # Adds a `node` to the hash ring (including a number of replicas).
24
+ def add_node(node)
25
+ @nodes << node
26
+ @replicas.times do |i|
27
+ key = Zlib.crc32("#{node.id}:#{i}")
28
+ raise "Node ID collision" if @ring.has_key?(key)
29
+ @ring[key] = node
30
+ @sorted_keys << key
31
+ end
32
+ @sorted_keys.sort!
33
+ end
34
+
35
+ def remove_node(node)
36
+ @nodes.reject!{|n| n.id == node.id}
37
+ @replicas.times do |i|
38
+ key = Zlib.crc32("#{node.id}:#{i}")
39
+ @ring.delete(key)
40
+ @sorted_keys.reject! {|k| k == key}
41
+ end
42
+ end
43
+
44
+ # get the node in the hash ring for this key
45
+ def get_node(key)
46
+ get_node_pos(key)[0]
47
+ end
48
+
49
+ def get_node_pos(key)
50
+ return [nil,nil] if @ring.size == 0
51
+ crc = Zlib.crc32(key)
52
+ idx = HashRing.binary_search(@sorted_keys, crc)
53
+ return [@ring[@sorted_keys[idx]], idx]
54
+ end
55
+
56
+ def iter_nodes(key)
57
+ return [nil,nil] if @ring.size == 0
58
+ _, pos = get_node_pos(key)
59
+ @ring.size.times do |n|
60
+ yield @ring[@sorted_keys[(pos+n) % @ring.size]]
61
+ end
62
+ end
63
+
64
+ class << self
65
+
66
+ # gem install RubyInline to use this code
67
+ # Native extension to perform the binary search within the hashring.
68
+ # There's a pure ruby version below so this is purely optional
69
+ # for performance. In testing 20k gets and sets, the native
70
+ # binary search shaved about 12% off the runtime (9sec -> 8sec).
71
+ begin
72
+ require 'inline'
73
+ inline do |builder|
74
+ builder.c <<-EOM
75
+ int binary_search(VALUE ary, unsigned int r) {
76
+ int upper = RARRAY_LEN(ary) - 1;
77
+ int lower = 0;
78
+ int idx = 0;
79
+
80
+ while (lower <= upper) {
81
+ idx = (lower + upper) / 2;
82
+
83
+ VALUE continuumValue = RARRAY_PTR(ary)[idx];
84
+ unsigned int l = NUM2UINT(continuumValue);
85
+ if (l == r) {
86
+ return idx;
87
+ }
88
+ else if (l > r) {
89
+ upper = idx - 1;
90
+ }
91
+ else {
92
+ lower = idx + 1;
93
+ }
94
+ }
95
+ if (upper < 0) {
96
+ upper = RARRAY_LEN(ary) - 1;
97
+ }
98
+ return upper;
99
+ }
100
+ EOM
101
+ end
102
+ rescue Exception
103
+ # Find the closest index in HashRing with value <= the given value
104
+ def binary_search(ary, value, &block)
105
+ upper = ary.size - 1
106
+ lower = 0
107
+ idx = 0
108
+
109
+ while(lower <= upper) do
110
+ idx = (lower + upper) / 2
111
+ comp = ary[idx] <=> value
112
+
113
+ if comp == 0
114
+ return idx
115
+ elsif comp > 0
116
+ upper = idx - 1
117
+ else
118
+ lower = idx + 1
119
+ end
120
+ end
121
+
122
+ if upper < 0
123
+ upper = ary.size - 1
124
+ end
125
+ return upper
126
+ end
127
+
128
+ end
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,141 @@
1
+ class Redis
2
+ unless defined?(::BasicObject)
3
+ class BasicObject
4
+ instance_methods.each { |meth| undef_method(meth) unless meth =~ /\A(__|instance_eval)/ }
5
+ end
6
+ end
7
+
8
+ class Pipeline
9
+ attr_accessor :db
10
+
11
+ attr :futures
12
+
13
+ def initialize
14
+ @with_reconnect = true
15
+ @shutdown = false
16
+ @futures = []
17
+ end
18
+
19
+ def with_reconnect?
20
+ @with_reconnect
21
+ end
22
+
23
+ def without_reconnect?
24
+ !@with_reconnect
25
+ end
26
+
27
+ def shutdown?
28
+ @shutdown
29
+ end
30
+
31
+ def call(command, &block)
32
+ # A pipeline that contains a shutdown should not raise ECONNRESET when
33
+ # the connection is gone.
34
+ @shutdown = true if command.first == :shutdown
35
+ future = Future.new(command, block)
36
+ @futures << future
37
+ future
38
+ end
39
+
40
+ def call_pipeline(pipeline)
41
+ @shutdown = true if pipeline.shutdown?
42
+ @futures.concat(pipeline.futures)
43
+ @db = pipeline.db
44
+ nil
45
+ end
46
+
47
+ def commands
48
+ @futures.map { |f| f._command }
49
+ end
50
+
51
+ def with_reconnect(val=true)
52
+ @with_reconnect = false unless val
53
+ yield
54
+ end
55
+
56
+ def without_reconnect(&blk)
57
+ with_reconnect(false, &blk)
58
+ end
59
+
60
+ def finish(replies, &blk)
61
+ if blk
62
+ futures.each_with_index.map do |future, i|
63
+ future._set(blk.call(replies[i]))
64
+ end
65
+ else
66
+ futures.each_with_index.map do |future, i|
67
+ future._set(replies[i])
68
+ end
69
+ end
70
+ end
71
+
72
+ class Multi < self
73
+ def finish(replies)
74
+ exec = replies.last
75
+
76
+ return if exec.nil? # The transaction failed because of WATCH.
77
+
78
+ # EXEC command failed.
79
+ raise exec if exec.is_a?(CommandError)
80
+
81
+ if exec.size < futures.size
82
+ # Some command wasn't recognized by Redis.
83
+ raise replies.detect { |r| r.is_a?(CommandError) }
84
+ end
85
+
86
+ super(exec) do |reply|
87
+ # Because an EXEC returns nested replies, hiredis won't be able to
88
+ # convert an error reply to a CommandError instance itself. This is
89
+ # specific to MULTI/EXEC, so we solve this here.
90
+ reply.is_a?(::RuntimeError) ? CommandError.new(reply.message) : reply
91
+ end
92
+ end
93
+
94
+ def commands
95
+ [[:multi]] + super + [[:exec]]
96
+ end
97
+ end
98
+ end
99
+
100
+ class FutureNotReady < RuntimeError
101
+ def initialize
102
+ super("Value will be available once the pipeline executes.")
103
+ end
104
+ end
105
+
106
+ class Future < BasicObject
107
+ FutureNotReady = ::Redis::FutureNotReady.new
108
+
109
+ def initialize(command, transformation)
110
+ @command = command
111
+ @transformation = transformation
112
+ @object = FutureNotReady
113
+ end
114
+
115
+ def inspect
116
+ "<Redis::Future #{@command.inspect}>"
117
+ end
118
+
119
+ def _set(object)
120
+ @object = @transformation ? @transformation.call(object) : object
121
+ value
122
+ end
123
+
124
+ def _command
125
+ @command
126
+ end
127
+
128
+ def value
129
+ ::Kernel.raise(@object) if @object.kind_of?(::RuntimeError)
130
+ @object
131
+ end
132
+
133
+ def is_a?(other)
134
+ self.class.ancestors.include?(other)
135
+ end
136
+
137
+ def class
138
+ Future
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,91 @@
1
+ class Redis
2
+ class SubscribedClient
3
+ def initialize(client)
4
+ @client = client
5
+ end
6
+
7
+ def call(command)
8
+ @client.process([command])
9
+ end
10
+
11
+ def subscribe(*channels, &block)
12
+ subscription("subscribe", "unsubscribe", channels, block)
13
+ end
14
+
15
+ def subscribe_with_timeout(timeout, *channels, &block)
16
+ subscription("subscribe", "unsubscribe", channels, block, timeout)
17
+ end
18
+
19
+ def psubscribe(*channels, &block)
20
+ subscription("psubscribe", "punsubscribe", channels, block)
21
+ end
22
+
23
+ def psubscribe_with_timeout(timeout, *channels, &block)
24
+ subscription("psubscribe", "punsubscribe", channels, block, timeout)
25
+ end
26
+
27
+ def unsubscribe(*channels)
28
+ call([:unsubscribe, *channels])
29
+ end
30
+
31
+ def punsubscribe(*channels)
32
+ call([:punsubscribe, *channels])
33
+ end
34
+
35
+ protected
36
+
37
+ def subscription(start, stop, channels, block, timeout = 0)
38
+ sub = Subscription.new(&block)
39
+
40
+ unsubscribed = false
41
+
42
+ begin
43
+ @client.call_loop([start, *channels], timeout) do |line|
44
+ type, *rest = line
45
+ sub.callbacks[type].call(*rest)
46
+ unsubscribed = type == stop && rest.last == 0
47
+ break if unsubscribed
48
+ end
49
+ ensure
50
+ # No need to unsubscribe here. The real client closes the connection
51
+ # whenever an exception is raised (see #ensure_connected).
52
+ end
53
+ end
54
+ end
55
+
56
+ class Subscription
57
+ attr :callbacks
58
+
59
+ def initialize
60
+ @callbacks = Hash.new do |hash, key|
61
+ hash[key] = lambda { |*_| }
62
+ end
63
+
64
+ yield(self)
65
+ end
66
+
67
+ def subscribe(&block)
68
+ @callbacks["subscribe"] = block
69
+ end
70
+
71
+ def unsubscribe(&block)
72
+ @callbacks["unsubscribe"] = block
73
+ end
74
+
75
+ def message(&block)
76
+ @callbacks["message"] = block
77
+ end
78
+
79
+ def psubscribe(&block)
80
+ @callbacks["psubscribe"] = block
81
+ end
82
+
83
+ def punsubscribe(&block)
84
+ @callbacks["punsubscribe"] = block
85
+ end
86
+
87
+ def pmessage(&block)
88
+ @callbacks["pmessage"] = block
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ class Redis
2
+ VERSION = "3.3.5"
3
+ end