redis 4.5.1 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Pubsub
6
+ # Post a message to a channel.
7
+ def publish(channel, message)
8
+ send_command([:publish, channel, message])
9
+ end
10
+
11
+ def subscribed?
12
+ synchronize do |client|
13
+ client.is_a? SubscribedClient
14
+ end
15
+ end
16
+
17
+ # Listen for messages published to the given channels.
18
+ def subscribe(*channels, &block)
19
+ synchronize do |_client|
20
+ _subscription(:subscribe, 0, channels, block)
21
+ end
22
+ end
23
+
24
+ # Listen for messages published to the given channels. Throw a timeout error
25
+ # if there is no messages for a timeout period.
26
+ def subscribe_with_timeout(timeout, *channels, &block)
27
+ synchronize do |_client|
28
+ _subscription(:subscribe_with_timeout, timeout, channels, block)
29
+ end
30
+ end
31
+
32
+ # Stop listening for messages posted to the given channels.
33
+ def unsubscribe(*channels)
34
+ synchronize do |client|
35
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
36
+
37
+ client.unsubscribe(*channels)
38
+ end
39
+ end
40
+
41
+ # Listen for messages published to channels matching the given patterns.
42
+ def psubscribe(*channels, &block)
43
+ synchronize do |_client|
44
+ _subscription(:psubscribe, 0, channels, block)
45
+ end
46
+ end
47
+
48
+ # Listen for messages published to channels matching the given patterns.
49
+ # Throw a timeout error if there is no messages for a timeout period.
50
+ def psubscribe_with_timeout(timeout, *channels, &block)
51
+ synchronize do |_client|
52
+ _subscription(:psubscribe_with_timeout, timeout, channels, block)
53
+ end
54
+ end
55
+
56
+ # Stop listening for messages posted to channels matching the given patterns.
57
+ def punsubscribe(*channels)
58
+ synchronize do |client|
59
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
60
+
61
+ client.punsubscribe(*channels)
62
+ end
63
+ end
64
+
65
+ # Inspect the state of the Pub/Sub subsystem.
66
+ # Possible subcommands: channels, numsub, numpat.
67
+ def pubsub(subcommand, *args)
68
+ send_command([:pubsub, subcommand] + args)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Scripting
6
+ # Control remote script registry.
7
+ #
8
+ # @example Load a script
9
+ # sha = redis.script(:load, "return 1")
10
+ # # => <sha of this script>
11
+ # @example Check if a script exists
12
+ # redis.script(:exists, sha)
13
+ # # => true
14
+ # @example Check if multiple scripts exist
15
+ # redis.script(:exists, [sha, other_sha])
16
+ # # => [true, false]
17
+ # @example Flush the script registry
18
+ # redis.script(:flush)
19
+ # # => "OK"
20
+ # @example Kill a running script
21
+ # redis.script(:kill)
22
+ # # => "OK"
23
+ #
24
+ # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
25
+ # @param [Array<String>] args depends on subcommand
26
+ # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
27
+ #
28
+ # @see #eval
29
+ # @see #evalsha
30
+ def script(subcommand, *args)
31
+ subcommand = subcommand.to_s.downcase
32
+
33
+ if subcommand == "exists"
34
+ arg = args.first
35
+
36
+ send_command([:script, :exists, arg]) do |reply|
37
+ reply = reply.map { |r| Boolify.call(r) }
38
+
39
+ if arg.is_a?(Array)
40
+ reply
41
+ else
42
+ reply.first
43
+ end
44
+ end
45
+ else
46
+ send_command([:script, subcommand] + args)
47
+ end
48
+ end
49
+
50
+ # Evaluate Lua script.
51
+ #
52
+ # @example EVAL without KEYS nor ARGV
53
+ # redis.eval("return 1")
54
+ # # => 1
55
+ # @example EVAL with KEYS and ARGV as array arguments
56
+ # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
57
+ # # => [["k1", "k2"], ["a1", "a2"]]
58
+ # @example EVAL with KEYS and ARGV in a hash argument
59
+ # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
60
+ # # => [["k1", "k2"], ["a1", "a2"]]
61
+ #
62
+ # @param [Array<String>] keys optional array with keys to pass to the script
63
+ # @param [Array<String>] argv optional array with arguments to pass to the script
64
+ # @param [Hash] options
65
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
66
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
67
+ # @return depends on the script
68
+ #
69
+ # @see #script
70
+ # @see #evalsha
71
+ def eval(*args)
72
+ _eval(:eval, args)
73
+ end
74
+
75
+ # Evaluate Lua script by its SHA.
76
+ #
77
+ # @example EVALSHA without KEYS nor ARGV
78
+ # redis.evalsha(sha)
79
+ # # => <depends on script>
80
+ # @example EVALSHA with KEYS and ARGV as array arguments
81
+ # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
82
+ # # => <depends on script>
83
+ # @example EVALSHA with KEYS and ARGV in a hash argument
84
+ # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
85
+ # # => <depends on script>
86
+ #
87
+ # @param [Array<String>] keys optional array with keys to pass to the script
88
+ # @param [Array<String>] argv optional array with arguments to pass to the script
89
+ # @param [Hash] options
90
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
91
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
92
+ # @return depends on the script
93
+ #
94
+ # @see #script
95
+ # @see #eval
96
+ def evalsha(*args)
97
+ _eval(:evalsha, args)
98
+ end
99
+
100
+ private
101
+
102
+ def _eval(cmd, args)
103
+ script = args.shift
104
+ options = args.pop if args.last.is_a?(Hash)
105
+ options ||= {}
106
+
107
+ keys = args.shift || options[:keys] || []
108
+ argv = args.shift || options[:argv] || []
109
+
110
+ send_command([cmd, script, keys.length] + keys + argv)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Server
6
+ # Asynchronously rewrite the append-only file.
7
+ #
8
+ # @return [String] `OK`
9
+ def bgrewriteaof
10
+ send_command([:bgrewriteaof])
11
+ end
12
+
13
+ # Asynchronously save the dataset to disk.
14
+ #
15
+ # @return [String] `OK`
16
+ def bgsave
17
+ send_command([:bgsave])
18
+ end
19
+
20
+ # Get or set server configuration parameters.
21
+ #
22
+ # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
23
+ # @return [String, Hash] string reply, or hash when retrieving more than one
24
+ # property with `CONFIG GET`
25
+ def config(action, *args)
26
+ send_command([:config, action] + args) do |reply|
27
+ if reply.is_a?(Array) && action == :get
28
+ Hashify.call(reply)
29
+ else
30
+ reply
31
+ end
32
+ end
33
+ end
34
+
35
+ # Manage client connections.
36
+ #
37
+ # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
38
+ # @return [String, Hash] depends on subcommand
39
+ def client(subcommand = nil, *args)
40
+ send_command([:client, subcommand] + args) do |reply|
41
+ if subcommand.to_s == "list"
42
+ reply.lines.map do |line|
43
+ entries = line.chomp.split(/[ =]/)
44
+ Hash[entries.each_slice(2).to_a]
45
+ end
46
+ else
47
+ reply
48
+ end
49
+ end
50
+ end
51
+
52
+ # Return the number of keys in the selected database.
53
+ #
54
+ # @return [Integer]
55
+ def dbsize
56
+ send_command([:dbsize])
57
+ end
58
+
59
+ # Remove all keys from all databases.
60
+ #
61
+ # @param [Hash] options
62
+ # - `:async => Boolean`: async flush (default: false)
63
+ # @return [String] `OK`
64
+ def flushall(options = nil)
65
+ if options && options[:async]
66
+ send_command(%i[flushall async])
67
+ else
68
+ send_command([:flushall])
69
+ end
70
+ end
71
+
72
+ # Remove all keys from the current database.
73
+ #
74
+ # @param [Hash] options
75
+ # - `:async => Boolean`: async flush (default: false)
76
+ # @return [String] `OK`
77
+ def flushdb(options = nil)
78
+ if options && options[:async]
79
+ send_command(%i[flushdb async])
80
+ else
81
+ send_command([:flushdb])
82
+ end
83
+ end
84
+
85
+ # Get information and statistics about the server.
86
+ #
87
+ # @param [String, Symbol] cmd e.g. "commandstats"
88
+ # @return [Hash<String, String>]
89
+ def info(cmd = nil)
90
+ send_command([:info, cmd].compact) do |reply|
91
+ if reply.is_a?(String)
92
+ reply = HashifyInfo.call(reply)
93
+
94
+ if cmd && cmd.to_s == "commandstats"
95
+ # Extract nested hashes for INFO COMMANDSTATS
96
+ reply = Hash[reply.map do |k, v|
97
+ v = v.split(",").map { |e| e.split("=") }
98
+ [k[/^cmdstat_(.*)$/, 1], Hash[v]]
99
+ end]
100
+ end
101
+ end
102
+
103
+ reply
104
+ end
105
+ end
106
+
107
+ # Get the UNIX time stamp of the last successful save to disk.
108
+ #
109
+ # @return [Integer]
110
+ def lastsave
111
+ send_command([:lastsave])
112
+ end
113
+
114
+ # Listen for all requests received by the server in real time.
115
+ #
116
+ # There is no way to interrupt this command.
117
+ #
118
+ # @yield a block to be called for every line of output
119
+ # @yieldparam [String] line timestamp and command that was executed
120
+ def monitor(&block)
121
+ synchronize do |client|
122
+ client.call_loop([:monitor], &block)
123
+ end
124
+ end
125
+
126
+ # Synchronously save the dataset to disk.
127
+ #
128
+ # @return [String]
129
+ def save
130
+ send_command([:save])
131
+ end
132
+
133
+ # Synchronously save the dataset to disk and then shut down the server.
134
+ def shutdown
135
+ synchronize do |client|
136
+ client.with_reconnect(false) do
137
+ begin
138
+ client.call([:shutdown])
139
+ rescue ConnectionError
140
+ # This means Redis has probably exited.
141
+ nil
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ # Make the server a slave of another instance, or promote it as master.
148
+ def slaveof(host, port)
149
+ send_command([:slaveof, host, port])
150
+ end
151
+
152
+ # Interact with the slowlog (get, len, reset)
153
+ #
154
+ # @param [String] subcommand e.g. `get`, `len`, `reset`
155
+ # @param [Integer] length maximum number of entries to return
156
+ # @return [Array<String>, Integer, String] depends on subcommand
157
+ def slowlog(subcommand, length = nil)
158
+ synchronize do |client|
159
+ args = [:slowlog, subcommand]
160
+ args << length if length
161
+ client.call args
162
+ end
163
+ end
164
+
165
+ # Internal command used for replication.
166
+ def sync
167
+ send_command([:sync])
168
+ end
169
+
170
+ # Return the server time.
171
+ #
172
+ # @example
173
+ # r.time # => [ 1333093196, 606806 ]
174
+ #
175
+ # @return [Array<Integer>] tuple of seconds since UNIX epoch and
176
+ # microseconds in the current second
177
+ def time
178
+ send_command([:time]) do |reply|
179
+ reply&.map(&:to_i)
180
+ end
181
+ end
182
+
183
+ def debug(*args)
184
+ send_command([:debug] + args)
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Sets
6
+ # Get the number of members in a set.
7
+ #
8
+ # @param [String] key
9
+ # @return [Integer]
10
+ def scard(key)
11
+ send_command([:scard, key])
12
+ end
13
+
14
+ # Add one or more members to a set.
15
+ #
16
+ # @param [String] key
17
+ # @param [String, Array<String>] member one member, or array of members
18
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
19
+ # holding whether or not adding the member succeeded, or `Integer` when an
20
+ # array of members is specified, holding the number of members that were
21
+ # successfully added
22
+ def sadd(key, member)
23
+ send_command([:sadd, key, member]) do |reply|
24
+ if member.is_a? Array
25
+ # Variadic: return integer
26
+ reply
27
+ else
28
+ # Single argument: return boolean
29
+ Boolify.call(reply)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Remove one or more members from a set.
35
+ #
36
+ # @param [String] key
37
+ # @param [String, Array<String>] member one member, or array of members
38
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
39
+ # holding whether or not removing the member succeeded, or `Integer` when an
40
+ # array of members is specified, holding the number of members that were
41
+ # successfully removed
42
+ def srem(key, member)
43
+ send_command([:srem, key, member]) do |reply|
44
+ if member.is_a? Array
45
+ # Variadic: return integer
46
+ reply
47
+ else
48
+ # Single argument: return boolean
49
+ Boolify.call(reply)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Remove and return one or more random member from a set.
55
+ #
56
+ # @param [String] key
57
+ # @return [String]
58
+ # @param [Integer] count
59
+ def spop(key, count = nil)
60
+ if count.nil?
61
+ send_command([:spop, key])
62
+ else
63
+ send_command([:spop, key, count])
64
+ end
65
+ end
66
+
67
+ # Get one or more random members from a set.
68
+ #
69
+ # @param [String] key
70
+ # @param [Integer] count
71
+ # @return [String]
72
+ def srandmember(key, count = nil)
73
+ if count.nil?
74
+ send_command([:srandmember, key])
75
+ else
76
+ send_command([:srandmember, key, count])
77
+ end
78
+ end
79
+
80
+ # Move a member from one set to another.
81
+ #
82
+ # @param [String] source source key
83
+ # @param [String] destination destination key
84
+ # @param [String] member member to move from `source` to `destination`
85
+ # @return [Boolean]
86
+ def smove(source, destination, member)
87
+ send_command([:smove, source, destination, member], &Boolify)
88
+ end
89
+
90
+ # Determine if a given value is a member of a set.
91
+ #
92
+ # @param [String] key
93
+ # @param [String] member
94
+ # @return [Boolean]
95
+ def sismember(key, member)
96
+ send_command([:sismember, key, member], &Boolify)
97
+ end
98
+
99
+ # Determine if multiple values are members of a set.
100
+ #
101
+ # @param [String] key
102
+ # @param [String, Array<String>] members
103
+ # @return [Array<Boolean>]
104
+ def smismember(key, *members)
105
+ send_command([:smismember, key, *members]) do |reply|
106
+ reply.map(&Boolify)
107
+ end
108
+ end
109
+
110
+ # Get all the members in a set.
111
+ #
112
+ # @param [String] key
113
+ # @return [Array<String>]
114
+ def smembers(key)
115
+ send_command([:smembers, key])
116
+ end
117
+
118
+ # Subtract multiple sets.
119
+ #
120
+ # @param [String, Array<String>] keys keys pointing to sets to subtract
121
+ # @return [Array<String>] members in the difference
122
+ def sdiff(*keys)
123
+ send_command([:sdiff, *keys])
124
+ end
125
+
126
+ # Subtract multiple sets and store the resulting set in a key.
127
+ #
128
+ # @param [String] destination destination key
129
+ # @param [String, Array<String>] keys keys pointing to sets to subtract
130
+ # @return [Integer] number of elements in the resulting set
131
+ def sdiffstore(destination, *keys)
132
+ send_command([:sdiffstore, destination, *keys])
133
+ end
134
+
135
+ # Intersect multiple sets.
136
+ #
137
+ # @param [String, Array<String>] keys keys pointing to sets to intersect
138
+ # @return [Array<String>] members in the intersection
139
+ def sinter(*keys)
140
+ send_command([:sinter, *keys])
141
+ end
142
+
143
+ # Intersect multiple sets and store the resulting set in a key.
144
+ #
145
+ # @param [String] destination destination key
146
+ # @param [String, Array<String>] keys keys pointing to sets to intersect
147
+ # @return [Integer] number of elements in the resulting set
148
+ def sinterstore(destination, *keys)
149
+ send_command([:sinterstore, destination, *keys])
150
+ end
151
+
152
+ # Add multiple sets.
153
+ #
154
+ # @param [String, Array<String>] keys keys pointing to sets to unify
155
+ # @return [Array<String>] members in the union
156
+ def sunion(*keys)
157
+ send_command([:sunion, *keys])
158
+ end
159
+
160
+ # Add multiple sets and store the resulting set in a key.
161
+ #
162
+ # @param [String] destination destination key
163
+ # @param [String, Array<String>] keys keys pointing to sets to unify
164
+ # @return [Integer] number of elements in the resulting set
165
+ def sunionstore(destination, *keys)
166
+ send_command([:sunionstore, destination, *keys])
167
+ end
168
+
169
+ # Scan a set
170
+ #
171
+ # @example Retrieve the first batch of keys in a set
172
+ # redis.sscan("set", 0)
173
+ #
174
+ # @param [String, Integer] cursor the cursor of the iteration
175
+ # @param [Hash] options
176
+ # - `:match => String`: only return keys matching the pattern
177
+ # - `:count => Integer`: return count keys at most per iteration
178
+ #
179
+ # @return [String, Array<String>] the next cursor and all found members
180
+ def sscan(key, cursor, **options)
181
+ _scan(:sscan, cursor, [key], **options)
182
+ end
183
+
184
+ # Scan a set
185
+ #
186
+ # @example Retrieve all of the keys in a set
187
+ # redis.sscan_each("set").to_a
188
+ # # => ["key1", "key2", "key3"]
189
+ #
190
+ # @param [Hash] options
191
+ # - `:match => String`: only return keys matching the pattern
192
+ # - `:count => Integer`: return count keys at most per iteration
193
+ #
194
+ # @return [Enumerator] an enumerator for all keys in the set
195
+ def sscan_each(key, **options, &block)
196
+ return to_enum(:sscan_each, key, **options) unless block_given?
197
+
198
+ cursor = 0
199
+ loop do
200
+ cursor, keys = sscan(key, cursor, **options)
201
+ keys.each(&block)
202
+ break if cursor == "0"
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end