/Users/deen/code/yugabyte-db/src/yb/yql/redis/redisserver/redisserver-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <chrono> |
15 | | #include <memory> |
16 | | #include <random> |
17 | | #include <string> |
18 | | #include <thread> |
19 | | #include <vector> |
20 | | |
21 | | #include <boost/algorithm/string.hpp> |
22 | | |
23 | | #include "yb/client/yb_table_name.h" |
24 | | |
25 | | #include "yb/gutil/strings/join.h" |
26 | | #include "yb/gutil/strings/substitute.h" |
27 | | |
28 | | #include "yb/integration-tests/redis_table_test_base.h" |
29 | | |
30 | | #include "yb/master/flush_manager.h" |
31 | | #include "yb/master/master_admin.pb.h" |
32 | | |
33 | | #include "yb/rpc/io_thread_pool.h" |
34 | | |
35 | | #include "yb/tserver/mini_tablet_server.h" |
36 | | #include "yb/tserver/tablet_server.h" |
37 | | |
38 | | #include "yb/util/cast.h" |
39 | | #include "yb/util/enums.h" |
40 | | #include "yb/util/metrics.h" |
41 | | #include "yb/util/net/socket.h" |
42 | | #include "yb/util/protobuf.h" |
43 | | #include "yb/util/ref_cnt_buffer.h" |
44 | | #include "yb/util/result.h" |
45 | | #include "yb/util/size_literals.h" |
46 | | #include "yb/util/status_log.h" |
47 | | #include "yb/util/test_util.h" |
48 | | #include "yb/util/tsan_util.h" |
49 | | #include "yb/util/value_changer.h" |
50 | | |
51 | | #include "yb/yql/redis/redisserver/redis_client.h" |
52 | | #include "yb/yql/redis/redisserver/redis_constants.h" |
53 | | #include "yb/yql/redis/redisserver/redis_encoding.h" |
54 | | #include "yb/yql/redis/redisserver/redis_server.h" |
55 | | |
56 | | DECLARE_uint64(redis_max_concurrent_commands); |
57 | | DECLARE_uint64(redis_max_batch); |
58 | | DECLARE_uint64(redis_max_read_buffer_size); |
59 | | DECLARE_uint64(redis_max_queued_bytes); |
60 | | DECLARE_int64(redis_rpc_block_size); |
61 | | DECLARE_bool(redis_safe_batch); |
62 | | DECLARE_bool(emulate_redis_responses); |
63 | | DECLARE_bool(enable_direct_local_tablet_server_call); |
64 | | DECLARE_bool(TEST_tserver_timeout); |
65 | | DECLARE_bool(TEST_enable_backpressure_mode_for_testing); |
66 | | DECLARE_bool(yedis_enable_flush); |
67 | | DECLARE_int32(redis_service_yb_client_timeout_millis); |
68 | | DECLARE_uint64(redis_max_value_size); |
69 | | DECLARE_uint64(redis_max_command_size); |
70 | | DECLARE_int32(redis_password_caching_duration_ms); |
71 | | DECLARE_uint64(rpc_max_message_size); |
72 | | DECLARE_uint64(consensus_max_batch_size_bytes); |
73 | | DECLARE_int32(consensus_rpc_timeout_ms); |
74 | | DECLARE_int64(max_time_in_queue_ms); |
75 | | |
76 | | DEFINE_uint64(test_redis_max_concurrent_commands, 20, |
77 | | "Value of redis_max_concurrent_commands for pipeline test"); |
78 | | DEFINE_uint64(test_redis_max_batch, 250, |
79 | | "Value of redis_max_batch for pipeline test"); |
80 | | |
81 | | METRIC_DECLARE_gauge_uint64(redis_available_sessions); |
82 | | METRIC_DECLARE_gauge_uint64(redis_allocated_sessions); |
83 | | METRIC_DECLARE_gauge_uint64(redis_monitoring_clients); |
84 | | |
85 | | using namespace std::literals; |
86 | | using namespace std::placeholders; |
87 | | |
88 | | namespace yb { |
89 | | namespace redisserver { |
90 | | |
91 | | using std::string; |
92 | | using std::unique_ptr; |
93 | | using std::vector; |
94 | | using strings::Substitute; |
95 | | using yb::integration_tests::RedisTableTestBase; |
96 | | using yb::util::ToRepeatedPtrField; |
97 | | |
98 | | #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) |
99 | | constexpr int kDefaultTimeoutMs = 100000; |
100 | | #else |
101 | | constexpr int kDefaultTimeoutMs = 10000; |
102 | | #endif |
103 | | |
104 | | typedef std::tuple<string, string> CollectionEntry; |
105 | | |
106 | | class TestRedisService : public RedisTableTestBase { |
107 | | public: |
108 | | void SetUp() override; |
109 | | void TearDown() override; |
110 | | |
111 | | void StartServer(); |
112 | | void StopServer(); |
113 | | void StartClient(); |
114 | | void StopClient(); |
115 | | void RestartClient(); |
116 | | void SendCommandAndExpectTimeout(const string& cmd); |
117 | | |
118 | | void SendCommandAndExpectResponse(int line, |
119 | | const string& cmd, |
120 | | const string& resp, |
121 | | bool partial = false); |
122 | | |
123 | | void SendCommandAndExpectResponse(int line, |
124 | | const RefCntBuffer& cmd, |
125 | | const RefCntBuffer& resp, |
126 | 0 | bool partial = false) { |
127 | 0 | SendCommandAndExpectResponse(line, cmd.ToBuffer(), resp.ToBuffer(), partial); |
128 | 0 | } |
129 | | |
130 | | template <class Callback> |
131 | | void DoRedisTest(int line, |
132 | | const std::vector<std::string>& command, |
133 | | RedisReplyType reply_type, |
134 | | const Callback& callback); |
135 | | |
136 | | void DoRedisTestString(int line, |
137 | | const std::vector<std::string>& command, |
138 | | const std::string& expected, |
139 | 0 | RedisReplyType type = RedisReplyType::kStatus) { |
140 | 0 | DoRedisTest(line, command, type, |
141 | 0 | [line, expected](const RedisReply& reply) { |
142 | 0 | ASSERT_EQ(expected, reply.as_string()) << "Originator: " << __FILE__ << ":" << line; |
143 | 0 | } |
144 | 0 | ); |
145 | 0 | } |
146 | | |
147 | | void DoRedisTestSimpleString(int line, |
148 | | const std::vector<std::string>& command, |
149 | 0 | const std::string& expected) { |
150 | 0 | DoRedisTestString(line, command, expected, RedisReplyType::kStatus); |
151 | 0 | } |
152 | | |
153 | | void DoRedisTestBulkString(int line, |
154 | | const std::vector<std::string>& command, |
155 | 0 | const std::string& expected) { |
156 | 0 | DoRedisTestString(line, command, expected, RedisReplyType::kString); |
157 | 0 | } |
158 | | |
159 | 0 | void DoRedisTestOk(int line, const std::vector<std::string>& command) { |
160 | 0 | DoRedisTestSimpleString(line, command, "OK"); |
161 | 0 | } |
162 | | |
163 | | void DoRedisTestExpectError(int line, const std::vector<std::string>& command, |
164 | 0 | const std::string& error_prefix = "") { |
165 | 0 | DoRedisTest(line, command, RedisReplyType::kError, |
166 | 0 | [line, error_prefix](const RedisReply& reply) { |
167 | 0 | if (!error_prefix.empty()) { |
168 | 0 | ASSERT_EQ(reply.error().find(error_prefix), 0) << "Error message has the wrong prefix " |
169 | 0 | << " expected : " << error_prefix << " got message " << reply.error() |
170 | 0 | << " Originator: " << __FILE__ << ":" << line; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | ); |
174 | 0 | } |
175 | | |
176 | | void DoRedisTestExpectSimpleStringEndingWith( |
177 | 0 | int line, const std::vector<std::string>& command, const std::string& suffix = "") { |
178 | 0 | DoRedisTest(line, command, RedisReplyType::kStatus, [line, suffix](const RedisReply& reply) { |
179 | 0 | if (!suffix.empty()) { |
180 | 0 | ASSERT_TRUE(boost::algorithm::ends_with(reply.as_string(), suffix)) |
181 | 0 | << "Reply has the wrong suffix. Expected '" << reply.as_string() << "' to end in " |
182 | 0 | << suffix << " Originator: " << __FILE__ << ":" << line; |
183 | 0 | } |
184 | 0 | }); |
185 | 0 | } |
186 | | |
187 | | void DoRedisTestExpectErrorMsg(int line, const std::vector<std::string>& command, |
188 | 0 | const std::string& error_msg) { |
189 | 0 | DoRedisTestString(line, command, error_msg, RedisReplyType::kError); |
190 | 0 | } |
191 | | |
192 | | void DoRedisTestInt(int line, |
193 | | const std::vector<std::string>& command, |
194 | 0 | int64_t expected) { |
195 | 0 | DoRedisTest(line, command, RedisReplyType::kInteger, |
196 | 0 | [line, expected](const RedisReply& reply) { |
197 | 0 | ASSERT_EQ(expected, reply.as_integer()) << "Originator: " << __FILE__ << ":" << line; |
198 | 0 | } |
199 | 0 | ); |
200 | 0 | } |
201 | | |
202 | | void DoRedisTestIntRange( |
203 | | int line, |
204 | | const std::vector<std::string>& command, |
205 | 0 | int64_t expected_min, int64_t expected_max) { |
206 | 0 | DoRedisTest(line, command, RedisReplyType::kInteger, |
207 | 0 | [line, expected_min, expected_max](const RedisReply& reply) { |
208 | 0 | ASSERT_LE(expected_min, reply.as_integer()) << "Originator: " << __FILE__ << ":" << line; |
209 | 0 | ASSERT_GE(expected_max, reply.as_integer()) << "Originator: " << __FILE__ << ":" << line; |
210 | 0 | } |
211 | 0 | ); |
212 | 0 | } |
213 | | |
214 | | void DoRedisTestApproxInt(int line, |
215 | | const std::vector<std::string>& command, |
216 | | int64_t expected, |
217 | 0 | int64_t err_bound) { |
218 | 0 | DoRedisTestIntRange(line, command, expected - err_bound, expected + err_bound); |
219 | 0 | } |
220 | | |
221 | | void DoRedisTestResultsArray( |
222 | 0 | int line, const std::vector<std::string>& command, const std::vector<RedisReply>& expected) { |
223 | 0 | DoRedisTest(line, command, RedisReplyType::kArray, |
224 | 0 | [line, expected](const RedisReply& reply) { |
225 | 0 | const auto& replies = reply.as_array(); |
226 | 0 | ASSERT_EQ(expected.size(), replies.size()) |
227 | 0 | << "Originator: " << __FILE__ << ":" << line << std::endl |
228 | 0 | << "Expected: " << yb::ToString(expected) << std::endl |
229 | 0 | << " Replies: " << Max500CharsPrinter(reply.ToString()); |
230 | 0 | for (size_t i = 0; i < expected.size(); i++) { |
231 | 0 | DVLOG(3) << "Checking " << replies[i].ToString(); |
232 | 0 | if (expected[i].get_type() == RedisReplyType::kString && |
233 | 0 | expected[i].as_string() == "IGNORED") { |
234 | 0 | continue; |
235 | 0 | } |
236 | 0 | ASSERT_EQ(expected[i], replies[i]) |
237 | 0 | << "Originator: " << __FILE__ << ":" << line << ", i: " << i << " expected[i] " |
238 | 0 | << yb::ToString(expected[i]) << " replies[i] " << yb::ToString(replies[i]); |
239 | 0 | } |
240 | 0 | } |
241 | 0 | ); |
242 | 0 | } |
243 | | |
244 | | // Note: expected empty string will check for null instead. |
245 | | void DoRedisTestArray( |
246 | 0 | int line, const std::vector<std::string>& command, const std::vector<std::string>& expected) { |
247 | 0 | std::vector<RedisReply> redis_replies; |
248 | 0 | for (size_t i = 0; i < expected.size(); i++) { |
249 | 0 | redis_replies.push_back(RedisReply(RedisReplyType::kString, expected[i])); |
250 | 0 | } |
251 | 0 | DoRedisTestResultsArray(line, command, redis_replies); |
252 | 0 | } |
253 | | |
254 | 0 | void DoRedisTestDouble(int line, const std::vector<std::string>& command, double expected) { |
255 | 0 | DoRedisTest(line, command, RedisReplyType::kString, |
256 | 0 | [line, expected](const RedisReply& reply) { |
257 | 0 | std::string::size_type sz; |
258 | 0 | double reply_score = std::stod(reply.as_string(), &sz); |
259 | 0 | ASSERT_EQ(reply_score, expected) << "Originator: " << __FILE__ << ":" << line; |
260 | 0 | } |
261 | 0 | ); |
262 | 0 | } |
263 | | |
264 | | // Used to check pairs of doubles and strings, for range scans withscores. |
265 | | void DoRedisTestScoreValueArray(int line, |
266 | | const std::vector<std::string>& command, |
267 | | const std::vector<double>& expected_scores, |
268 | 0 | const std::vector<std::string>& expected_values) { |
269 | 0 | ASSERT_EQ(expected_scores.size(), expected_values.size()); |
270 | 0 | DoRedisTest(line, command, RedisReplyType::kArray, |
271 | 0 | [line, expected_scores, expected_values](const RedisReply& reply) { |
272 | 0 | const auto& replies = reply.as_array(); |
273 | 0 | ASSERT_EQ(expected_scores.size() * 2, replies.size()) |
274 | 0 | << "Originator: " << __FILE__ << ":" << line; |
275 | 0 | for (size_t i = 0; i < expected_scores.size(); i++) { |
276 | 0 | ASSERT_EQ(expected_values[i], replies[2 * i].as_string()) |
277 | 0 | << "Originator: " << __FILE__ << ":" << line << ", i: " << i; |
278 | 0 | std::string::size_type sz; |
279 | 0 | double reply_score = std::stod(replies[2 * i + 1].as_string(), &sz); |
280 | 0 | ASSERT_EQ(expected_scores[i], reply_score) |
281 | 0 | << "Originator: " << __FILE__ << ":" << line << ", i: " << i; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | ); |
285 | 0 | } |
286 | | |
287 | | void DoRedisTestNull(int line, |
288 | 0 | const std::vector<std::string>& command) { |
289 | 0 | DoRedisTest(line, command, RedisReplyType::kNull, |
290 | 0 | [line](const RedisReply& reply) { |
291 | 0 | ASSERT_TRUE(reply.is_null()) << "Originator: " << __FILE__ << ":" << line; |
292 | 0 | } |
293 | 0 | ); |
294 | 0 | } |
295 | | |
296 | 0 | inline void CheckExpired(std::string* key) { |
297 | 0 | SyncClient(); |
298 | 0 | DoRedisTestInt(__LINE__, {"TTL", *key}, -2); |
299 | 0 | DoRedisTestInt(__LINE__, {"PTTL", *key}, -2); |
300 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", *key, "5"}, 0); |
301 | 0 | SyncClient(); |
302 | 0 | } |
303 | | |
304 | 0 | inline void CheckExpiredPrimitive(std::string* key) { |
305 | 0 | SyncClient(); |
306 | 0 | DoRedisTestNull(__LINE__, {"GET", *key}); |
307 | 0 | CheckExpired(key); |
308 | 0 | } |
309 | | |
310 | 0 | void SyncClient() { client().Commit(); } |
311 | | |
312 | | void VerifyCallbacks(); |
313 | | |
314 | 0 | Status FlushRedisTable() { |
315 | | // Flush the table |
316 | 0 | master::FlushTablesRequestPB req; |
317 | 0 | req.set_is_compaction(false); |
318 | 0 | table_name().SetIntoTableIdentifierPB(req.add_tables()); |
319 | 0 | master::FlushTablesResponsePB resp; |
320 | 0 | RETURN_NOT_OK(VERIFY_RESULT(mini_cluster()->GetLeaderMiniMaster())->flush_manager(). |
321 | 0 | FlushTables(&req, &resp)); |
322 | |
|
323 | 0 | master::IsFlushTablesDoneRequestPB wait_req; |
324 | | // Wait for table creation. |
325 | 0 | wait_req.set_flush_request_id(resp.flush_request_id()); |
326 | |
|
327 | 0 | for (int k = 0; k < 20; ++k) { |
328 | 0 | master::IsFlushTablesDoneResponsePB wait_resp; |
329 | 0 | RETURN_NOT_OK(VERIFY_RESULT(mini_cluster()->GetLeaderMiniMaster()) |
330 | 0 | ->flush_manager().IsFlushTablesDone(&wait_req, &wait_resp)); |
331 | 0 | if (wait_resp.done()) { |
332 | 0 | return Status::OK(); |
333 | 0 | } |
334 | 0 | SleepFor(MonoDelta::FromSeconds(1)); |
335 | 0 | } |
336 | 0 | return STATUS(IllegalState, "Could not flush redis table."); |
337 | 0 | } |
338 | | |
339 | 0 | int server_port() { return redis_server_port_; } |
340 | | |
341 | | CHECKED_STATUS Send(const std::string& cmd); |
342 | | |
343 | | CHECKED_STATUS SendCommandAndGetResponse( |
344 | | const string& cmd, size_t expected_resp_length, int timeout_in_millis = kDefaultTimeoutMs); |
345 | | |
346 | 0 | size_t CountSessions(const GaugePrototype<uint64_t>& proto) { |
347 | 0 | constexpr uint64_t kInitialValue = 0UL; |
348 | 0 | auto counter = server_->metric_entity()->FindOrCreateGauge(&proto, kInitialValue); |
349 | 0 | return counter->value(); |
350 | 0 | } |
351 | | |
352 | 0 | virtual Endpoint RedisProxyEndpoint() { |
353 | 0 | if (use_external_mini_cluster()) { |
354 | 0 | return Endpoint(IpAddress(), server_port()); |
355 | 0 | } |
356 | 0 | auto server = mini_cluster()->mini_tablet_server(0)->server(); |
357 | 0 | return Endpoint(server->first_rpc_address().address(), redis_server_port_); |
358 | 0 | } |
359 | | |
360 | | void TestTSTtl(const std::string& expire_command, int64_t ttl_sec, int64_t expire_val, |
361 | 0 | const std::string& redis_key) { |
362 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "10", "v1", "20", "v2", "30", "v3", expire_command, |
363 | 0 | strings::Substitute("$0", expire_val)}); |
364 | 0 | SyncClient(); |
365 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "40", "v4"}); |
366 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "10", "v5"}); |
367 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "50", "v6", expire_command, |
368 | 0 | std::to_string(expire_val + ttl_sec)}); |
369 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "60", "v7", expire_command, |
370 | 0 | std::to_string(expire_val - ttl_sec + kRedisMaxTtlSeconds)}); |
371 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "70", "v8", expire_command, |
372 | 0 | std::to_string(expire_val - ttl_sec + kRedisMinTtlSetExSeconds)}); |
373 | | // Same kv with different ttl (later one should win). |
374 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "80", "v9", expire_command, |
375 | 0 | std::to_string(expire_val)}); |
376 | 0 | DoRedisTestOk(__LINE__, {"TSADD", redis_key, "80", "v9", expire_command, |
377 | 0 | std::to_string(expire_val + ttl_sec)}); |
378 | 0 | SyncClient(); |
379 | | |
380 | | // Wait for min ttl to expire. |
381 | 0 | std::this_thread::sleep_for(std::chrono::seconds(kRedisMinTtlSetExSeconds + 1)); |
382 | |
|
383 | 0 | SyncClient(); |
384 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "10"}, "v5"); |
385 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "20"}, "v2"); |
386 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "30"}, "v3"); |
387 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "50"}, "v6"); |
388 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "60"}, "v7"); |
389 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "80"}, "v9"); |
390 | 0 | SyncClient(); |
391 | | |
392 | | // Wait for TTL expiry |
393 | 0 | std::this_thread::sleep_for(std::chrono::seconds(ttl_sec + 1)); |
394 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "10"}, "v5"); |
395 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "40"}, "v4"); |
396 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "50"}, "v6"); |
397 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "60"}, "v7"); |
398 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "80"}, "v9"); |
399 | 0 | DoRedisTestNull(__LINE__, {"TSGET", redis_key, "20"}); |
400 | 0 | DoRedisTestNull(__LINE__, {"TSGET", redis_key, "30"}); |
401 | 0 | SyncClient(); |
402 | | |
403 | | // Wait for next TTL expiry |
404 | 0 | std::this_thread::sleep_for(std::chrono::seconds(ttl_sec + 1)); |
405 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "10"}, "v5"); |
406 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "40"}, "v4"); |
407 | 0 | DoRedisTestNull(__LINE__, {"TSGET", redis_key, "20"}); |
408 | 0 | DoRedisTestNull(__LINE__, {"TSGET", redis_key, "30"}); |
409 | 0 | DoRedisTestNull(__LINE__, {"TSGET", redis_key, "50"}); |
410 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", redis_key, "60"}, "v7"); |
411 | 0 | SyncClient(); |
412 | 0 | VerifyCallbacks(); |
413 | | |
414 | | // Test invalid commands. |
415 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, |
416 | 0 | std::to_string(expire_val - 2 * ttl_sec)}); // Negative ttl. |
417 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", "20", "v2", "30", "v3", |
418 | 0 | expire_command}); |
419 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, "v2", "30", |
420 | 0 | "v3"}); |
421 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", expire_command, redis_key, "10", "v1", "30", "v3"}); |
422 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, "abc"}); |
423 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, "3.0"}); |
424 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, "123 "}); |
425 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, |
426 | 0 | "9223372036854775808"}); |
427 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, |
428 | 0 | "-9223372036854775809"}); |
429 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", redis_key, "10", "v1", expire_command, |
430 | 0 | std::to_string(expire_val - ttl_sec)}); // ttl of 0 not allowed. |
431 | 0 | } |
432 | | |
433 | 0 | void TestFlush(const string& flush_cmd, const bool allow_flush) { |
434 | 0 | FLAGS_yedis_enable_flush = allow_flush; |
435 | | // Populate keys. |
436 | 0 | const int kKeyCount = 100; |
437 | 0 | for (int i = 0; i < kKeyCount; i++) { |
438 | 0 | DoRedisTestOk(__LINE__, {"SET", Substitute("k$0", i), Substitute("v$0", i)}); |
439 | 0 | } |
440 | 0 | SyncClient(); |
441 | | |
442 | | // Verify keys. |
443 | 0 | for (int i = 0; i < kKeyCount; i++) { |
444 | 0 | DoRedisTestBulkString(__LINE__, {"GET", Substitute("k$0", i)}, Substitute("v$0", i)); |
445 | 0 | } |
446 | 0 | SyncClient(); |
447 | |
|
448 | 0 | if (!allow_flush) { |
449 | 0 | DoRedisTestExpectError(__LINE__, {flush_cmd}); |
450 | 0 | SyncClient(); |
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | | // Delete all keys in the database and verify keys are gone. |
455 | 0 | DoRedisTestOk(__LINE__, {flush_cmd}); |
456 | 0 | SyncClient(); |
457 | 0 | for (int i = 0; i < kKeyCount; i++) { |
458 | 0 | DoRedisTestNull(__LINE__, {"GET", Substitute("k$0", i)}); |
459 | 0 | } |
460 | 0 | SyncClient(); |
461 | | |
462 | | // Delete all keys in the database again (an NOOP) and verify there is no issue. |
463 | 0 | DoRedisTestOk(__LINE__, {flush_cmd}); |
464 | 0 | SyncClient(); |
465 | 0 | for (int i = 0; i < kKeyCount; i++) { |
466 | 0 | DoRedisTestNull(__LINE__, {"GET", Substitute("k$0", i)}); |
467 | 0 | } |
468 | 0 | SyncClient(); |
469 | 0 | } |
470 | | |
471 | | bool expected_no_sessions_ = false; |
472 | | |
473 | 0 | RedisClient& client() { |
474 | 0 | if (!test_client_) { |
475 | 0 | io_thread_pool_.emplace("test", 1); |
476 | 0 | test_client_ = CreateClient(); |
477 | 0 | } |
478 | 0 | return *test_client_; |
479 | 0 | } |
480 | | |
481 | 0 | std::shared_ptr<RedisClient> CreateClient() { |
482 | 0 | auto endpoint = RedisProxyEndpoint(); |
483 | 0 | return std::make_shared<RedisClient>(endpoint.address().to_string(), endpoint.port()); |
484 | 0 | } |
485 | | |
486 | 0 | void UseClient(std::shared_ptr<RedisClient> client) { |
487 | 0 | VLOG(1) << "Using " << client.get() << " replacing " << test_client_.get(); |
488 | 0 | test_client_ = client; |
489 | 0 | } |
490 | | |
491 | | void CloseRedisClient(); |
492 | | |
493 | | // Tests not repeated because they are already covered in the primitive TTL test: |
494 | | // Operating on a key that does not exist, EXPIRing with a TTL out of bounds, |
495 | | // Any (P) version of a command. |
496 | | template <typename T> |
497 | | void TestTtlCollection(std::string* collection_key, T* values, int val_size, |
498 | | std::function<void(std::string*, T*, int)> set_vals, |
499 | | std::function<void(std::string*, T*)> add_elems, |
500 | | std::function<void(std::string*, T*)> del_elems, |
501 | | std::function<void(std::string*, T*, bool)> get_check, |
502 | 0 | std::function<void(std::string*, int)> check_card) { |
503 | | |
504 | | // num_shifts is the number of times we call modify |
505 | 0 | int num_shifts = 7; |
506 | 0 | int size = val_size - num_shifts; |
507 | 0 | std::string key = *collection_key; |
508 | 0 | auto init = std::bind(set_vals, collection_key, std::placeholders::_1, size); |
509 | 0 | auto modify = [add_elems, del_elems, check_card, collection_key, size, this] |
510 | 0 | (T** values) { |
511 | 0 | SyncClient(); |
512 | 0 | if (std::rand() % 2) { |
513 | 0 | del_elems(collection_key, *values); |
514 | 0 | SyncClient(); |
515 | 0 | check_card(collection_key, size - 1); |
516 | 0 | SyncClient(); |
517 | 0 | add_elems(collection_key, *values + size); |
518 | 0 | } else { |
519 | 0 | add_elems(collection_key, *values + size); |
520 | 0 | SyncClient(); |
521 | 0 | check_card(collection_key, size + 1); |
522 | 0 | SyncClient(); |
523 | 0 | del_elems(collection_key, *values); |
524 | 0 | } |
525 | 0 | SyncClient(); |
526 | 0 | check_card(collection_key, size); |
527 | 0 | ++*values; |
528 | 0 | }; Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEvPS9_PT_iNS3_8functionIFvSA_SC_iEEENSD_IFvSA_SC_EEESH_NSD_IFvSA_SC_bEEENSD_IFvSA_iEEEENKUlPSA_E_clESM_ Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__15tupleIJNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_EEEEEvPSA_PT_iNS3_8functionIFvSC_SE_iEEENSF_IFvSC_SE_EEESJ_NSF_IFvSC_SE_bEEENSF_IFvSC_iEEEENKUlPPSB_E_clESP_ |
529 | 0 | auto check = [get_check, check_card, collection_key, size, val_size, values, this] |
530 | 0 | (T* curr_vals) { |
531 | 0 | T* it = values; |
532 | 0 | SyncClient(); |
533 | 0 | for ( ; it < curr_vals; ++it) |
534 | 0 | get_check(collection_key, it, false); |
535 | 0 | for ( ; it < curr_vals + size; ++it) |
536 | 0 | get_check(collection_key, it, true); |
537 | 0 | for ( ; it < values + val_size; ++it) |
538 | 0 | get_check(collection_key, it, false); |
539 | 0 | SyncClient(); |
540 | 0 | check_card(collection_key, size); |
541 | 0 | SyncClient(); |
542 | 0 | }; Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEvPS9_PT_iNS3_8functionIFvSA_SC_iEEENSD_IFvSA_SC_EEESH_NSD_IFvSA_SC_bEEENSD_IFvSA_iEEEENKUlSA_E_clESA_ Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__15tupleIJNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_EEEEEvPSA_PT_iNS3_8functionIFvSC_SE_iEEENSF_IFvSC_SE_EEESJ_NSF_IFvSC_SE_bEEENSF_IFvSC_iEEEENKUlPSB_E_clESO_ |
543 | 0 | auto expired = [get_check, collection_key, size, this](T* values) { |
544 | 0 | CheckExpired(collection_key); |
545 | 0 | for (T* it = values; it < values + size; ++it) |
546 | 0 | get_check(collection_key, it, false); |
547 | 0 | SyncClient(); |
548 | 0 | }; Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEvPS9_PT_iNS3_8functionIFvSA_SC_iEEENSD_IFvSA_SC_EEESH_NSD_IFvSA_SC_bEEENSD_IFvSA_iEEEENKUlSA_E0_clESA_ Unexecuted instantiation: _ZZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__15tupleIJNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_EEEEEvPSA_PT_iNS3_8functionIFvSC_SE_iEEENSF_IFvSC_SE_EEESJ_NSF_IFvSC_SE_bEEENSF_IFvSC_iEEEENKUlPSB_E0_clESO_ |
549 | | |
550 | | // Checking TTL and PERSIST on a persistent collection. |
551 | 0 | init(values); |
552 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, -1); |
553 | 0 | DoRedisTestInt(__LINE__, {"PTTL", key}, -1); |
554 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", key}, 0); |
555 | 0 | SyncClient(); |
556 | | // Checking that modification does not change anything. |
557 | 0 | modify(&values); |
558 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, -1); |
559 | 0 | DoRedisTestInt(__LINE__, {"PTTL", key}, -1); |
560 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", key}, 0); |
561 | 0 | check(values); |
562 | 0 | SyncClient(); |
563 | | // Adding TTL and checking that modification does not change anything. |
564 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "7"}, 1); |
565 | 0 | modify(&values); |
566 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, 7); |
567 | 0 | check(values); |
568 | 0 | SyncClient(); |
569 | | // Checking that everything is still there after some time. |
570 | 0 | std::this_thread::sleep_for(3s); |
571 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, 4); |
572 | 0 | check(values); |
573 | 0 | SyncClient(); |
574 | 0 | std::this_thread::sleep_for(5s); |
575 | 0 | expired(values); |
576 | 0 | SyncClient(); |
577 | | // Checking expiration changes for a later expiration. |
578 | 0 | init(values); |
579 | 0 | check(values); |
580 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "5"}, 1); |
581 | 0 | modify(&values); |
582 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "9"}, 1); |
583 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, 9); |
584 | 0 | check(values); |
585 | 0 | modify(&values); |
586 | 0 | SyncClient(); |
587 | 0 | std::this_thread::sleep_for(5s); |
588 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, 4); |
589 | 0 | check(values); |
590 | 0 | modify(&values); |
591 | 0 | SyncClient(); |
592 | 0 | std::this_thread::sleep_for(5s); |
593 | 0 | SyncClient(); |
594 | 0 | expired(values); |
595 | 0 | SyncClient(); |
596 | | // Checking expiration changes for an earlier expiration. |
597 | 0 | init(values); |
598 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "5"}, 1); |
599 | 0 | modify(&values); |
600 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "3"}, 1); |
601 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, 3); |
602 | 0 | check(values); |
603 | 0 | modify(&values); |
604 | 0 | SyncClient(); |
605 | 0 | std::this_thread::sleep_for(4s); |
606 | 0 | expired(values); |
607 | 0 | SyncClient(); |
608 | | // Checking persistence. |
609 | 0 | init(values); |
610 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "6"}, 1); |
611 | 0 | SyncClient(); |
612 | 0 | std::this_thread::sleep_for(3s); |
613 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", key}, 1); |
614 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, -1); |
615 | 0 | check(values); |
616 | 0 | SyncClient(); |
617 | 0 | std::this_thread::sleep_for(6s); |
618 | 0 | check(values); |
619 | 0 | SyncClient(); |
620 | | // Testing zero expiration. |
621 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "0"}, 1); |
622 | 0 | expired(values); |
623 | 0 | SyncClient(); |
624 | | // Testing negative expiration. |
625 | 0 | init(values); |
626 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", key, "-7"}, 1); |
627 | 0 | expired(values); |
628 | 0 | SyncClient(); |
629 | | // Testing SETEX turns the key back into a primitive. |
630 | 0 | init(values); |
631 | 0 | DoRedisTestOk(__LINE__, {"SETEX", key, "6", "17"}); |
632 | 0 | SyncClient(); |
633 | 0 | DoRedisTestBulkString(__LINE__, {"GET", key}, "17"); |
634 | 0 | SyncClient(); |
635 | 0 | std::this_thread::sleep_for(7s); |
636 | 0 | CheckExpired(&key); |
637 | 0 | SyncClient(); |
638 | 0 | VerifyCallbacks(); |
639 | 0 | } Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEvPS9_PT_iNS3_8functionIFvSA_SC_iEEENSD_IFvSA_SC_EEESH_NSD_IFvSA_SC_bEEENSD_IFvSA_iEEE Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService17TestTtlCollectionINSt3__15tupleIJNS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_EEEEEvPSA_PT_iNS3_8functionIFvSC_SE_iEEENSF_IFvSC_SE_EEESJ_NSF_IFvSC_SE_bEEENSF_IFvSC_iEEE |
640 | | |
641 | 0 | void TestTtlSet(std::string* collection_key, std::string* collection_values, int card) { |
642 | 0 | std::function<void(std::string*, std::string*, int)> set_init = |
643 | 0 | [this](std::string* key, std::string* values, int size) { |
644 | 0 | for (auto it = values; it < values + size; ++it) { |
645 | 0 | DoRedisTestInt(__LINE__, {"SADD", *key, *it}, 1); |
646 | 0 | SyncClient(); |
647 | 0 | } |
648 | 0 | SyncClient(); |
649 | 0 | }; |
650 | 0 | std::function<void(std::string*, std::string*)> set_add = |
651 | 0 | [this](std::string* key, std::string* value) { |
652 | 0 | DoRedisTestInt(__LINE__, {"SADD", *key, *value}, 1); |
653 | 0 | }; |
654 | 0 | std::function<void(std::string*, std::string*)> set_del = |
655 | 0 | [this](std::string* key, std::string* value) { |
656 | 0 | DoRedisTestInt(__LINE__, {"SREM", *key, *value}, 1); |
657 | 0 | }; |
658 | 0 | std::function<void(std::string*, std::string*, bool)> set_check = |
659 | 0 | [this](std::string* key, std::string* value, bool exists) { |
660 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", *key, *value}, exists); |
661 | 0 | }; |
662 | 0 | std::function<void(std::string*, int)> set_card = |
663 | 0 | [this](std::string* key, int size) { |
664 | 0 | DoRedisTestInt(__LINE__, {"SCARD", *key}, size); |
665 | 0 | }; |
666 | |
|
667 | 0 | TestTtlCollection(collection_key, collection_values, card, |
668 | 0 | set_init, set_add, set_del, set_check, set_card); |
669 | 0 | } |
670 | | |
671 | 0 | void TestTtlSortedSet(std::string* collection_key, CollectionEntry* collection_values, int card) { |
672 | 0 | std::function<void(std::string*, CollectionEntry*, int)> sorted_set_init = |
673 | 0 | [this](std::string* key, CollectionEntry* values, int size) { |
674 | 0 | for (auto it = values; it < values + size; ++it) { |
675 | 0 | DoRedisTestInt(__LINE__, {"ZADD", *key, std::get<0>(*it), std::get<1>(*it)}, 1); |
676 | 0 | SyncClient(); |
677 | 0 | } |
678 | 0 | SyncClient(); |
679 | 0 | }; |
680 | 0 | std::function<void(std::string*, CollectionEntry*)> sorted_set_add = |
681 | 0 | [this](std::string* key, CollectionEntry* value) { |
682 | 0 | DoRedisTestInt(__LINE__, {"ZADD", *key, std::get<0>(*value), std::get<1>(*value)}, 1); |
683 | 0 | }; |
684 | 0 | std::function<void(std::string*, CollectionEntry*)> sorted_set_del = |
685 | 0 | [this](std::string* key, CollectionEntry* value) { |
686 | 0 | DoRedisTestInt(__LINE__, {"ZREM", *key, std::get<1>(*value)}, 1); |
687 | 0 | }; |
688 | 0 | std::function<void(std::string*, CollectionEntry*, bool)> sorted_set_check = |
689 | 0 | [this](std::string* key, CollectionEntry* value, bool exists) { |
690 | 0 | if (exists) { |
691 | 0 | char buf[20]; |
692 | 0 | std::snprintf(buf, sizeof(buf), "%.6f", std::stof(std::get<0>(*value))); |
693 | 0 | DoRedisTestBulkString(__LINE__, {"ZSCORE", *key, std::get<1>(*value)}, |
694 | 0 | buf); |
695 | 0 | } else { |
696 | 0 | DoRedisTestNull(__LINE__, {"ZSCORE", *key, std::get<1>(*value)}); |
697 | 0 | } |
698 | 0 | }; |
699 | 0 | std::function<void(std::string*, int)> sorted_set_card = |
700 | 0 | [this](std::string* key, int size) { |
701 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", *key}, size); |
702 | 0 | }; |
703 | |
|
704 | 0 | TestTtlCollection(collection_key, collection_values, card, |
705 | 0 | sorted_set_init, sorted_set_add, sorted_set_del, |
706 | 0 | sorted_set_check, sorted_set_card); |
707 | 0 | } |
708 | | |
709 | 0 | void TestTtlHash(std::string* collection_key, CollectionEntry* collection_values, int card) { |
710 | 0 | std::function<void(std::string*, CollectionEntry*, int)> hash_init = |
711 | 0 | [this](std::string* key, CollectionEntry* values, int size) { |
712 | 0 | for (auto it = values; it < values + size; ++it) { |
713 | 0 | DoRedisTestInt(__LINE__, {"HSET", *key, std::get<0>(*it), std::get<1>(*it)}, 1); |
714 | 0 | SyncClient(); |
715 | 0 | } |
716 | 0 | SyncClient(); |
717 | 0 | }; |
718 | 0 | std::function<void(std::string*, CollectionEntry*)> hash_add = |
719 | 0 | [this](std::string* key, CollectionEntry* value) { |
720 | 0 | DoRedisTestInt(__LINE__, {"HSET", *key, std::get<0>(*value), std::get<1>(*value)}, 1); |
721 | 0 | }; |
722 | 0 | std::function<void(std::string*, CollectionEntry*)> hash_del = |
723 | 0 | [this](std::string* key, CollectionEntry* value) { |
724 | 0 | DoRedisTestInt(__LINE__, {"HDEL", *key, std::get<0>(*value)}, 1); |
725 | 0 | }; |
726 | 0 | std::function<void(std::string*, CollectionEntry*, bool)> hash_check = |
727 | 0 | [this](std::string* key, CollectionEntry* value, bool exists) { |
728 | 0 | if (exists) |
729 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", *key, std::get<0>(*value)}, |
730 | 0 | std::get<1>(*value)); |
731 | 0 | else |
732 | 0 | DoRedisTestNull(__LINE__, {"HGET", *key, std::get<0>(*value)}); |
733 | 0 | }; |
734 | 0 | std::function<void(std::string*, int)> hash_card = |
735 | 0 | [this](std::string* key, int size) { |
736 | 0 | DoRedisTestInt(__LINE__, {"HLEN", *key}, size); |
737 | 0 | }; |
738 | |
|
739 | 0 | TestTtlCollection(collection_key, collection_values, card, |
740 | 0 | hash_init, hash_add, hash_del, hash_check, hash_card); |
741 | 0 | } |
742 | | |
743 | | void TestAbort(const std::string& command); |
744 | | |
745 | | protected: |
746 | | std::string int64Max_ = std::to_string(std::numeric_limits<int64_t>::max()); |
747 | | std::string int64MaxExclusive_ = "(" + int64Max_; |
748 | | std::string int64Min_ = std::to_string(std::numeric_limits<int64_t>::min()); |
749 | | std::string int64MinExclusive_ = "(" + int64Min_; |
750 | | uint64_t redis_max_read_buffer_size_ = 512_MB; |
751 | | |
752 | | private: |
753 | | std::atomic<int> num_callbacks_called_{0}; |
754 | | int expected_callbacks_called_ = 0; |
755 | | Socket client_sock_; |
756 | | unique_ptr<RedisServer> server_; |
757 | | int redis_server_port_ = 0; |
758 | | unique_ptr<FileLock> redis_port_lock_; |
759 | | unique_ptr<FileLock> redis_webserver_lock_; |
760 | | std::vector<uint8_t> resp_; |
761 | | boost::optional<rpc::IoThreadPool> io_thread_pool_; |
762 | | std::shared_ptr<RedisClient> test_client_; |
763 | | }; |
764 | | |
765 | | class NoLocalCallsRedisServiceTest : public TestRedisService { |
766 | | public: |
767 | 0 | void SetUp() override { |
768 | 0 | FLAGS_enable_direct_local_tablet_server_call = false; |
769 | 0 | TestRedisService::SetUp(); |
770 | 0 | } |
771 | | }; |
772 | | |
773 | | |
774 | 0 | void TestRedisService::SetUp() { |
775 | 0 | FLAGS_redis_service_yb_client_timeout_millis = kDefaultTimeoutMs; |
776 | 0 | if (IsSanitizer()) { |
777 | 0 | FLAGS_redis_max_value_size = 1_MB; |
778 | 0 | FLAGS_rpc_max_message_size = FLAGS_redis_max_value_size * 4 - 1; |
779 | 0 | FLAGS_redis_max_command_size = FLAGS_rpc_max_message_size - 2_KB; |
780 | 0 | FLAGS_consensus_max_batch_size_bytes = FLAGS_rpc_max_message_size - 2_KB; |
781 | 0 | } else { |
782 | 0 | #ifndef NDEBUG |
783 | 0 | FLAGS_redis_max_value_size = 32_MB; |
784 | 0 | FLAGS_rpc_max_message_size = FLAGS_redis_max_value_size * 4 - 1; |
785 | 0 | FLAGS_redis_max_command_size = FLAGS_rpc_max_message_size - 2_KB; |
786 | 0 | FLAGS_consensus_max_batch_size_bytes = FLAGS_rpc_max_message_size - 2_KB; |
787 | 0 | FLAGS_consensus_rpc_timeout_ms = 3000; |
788 | 0 | #endif |
789 | 0 | } |
790 | 0 | FLAGS_redis_max_read_buffer_size = redis_max_read_buffer_size_; |
791 | 0 | LOG(INFO) << "FLAGS_redis_max_read_buffer_size=" << FLAGS_redis_max_read_buffer_size |
792 | 0 | << ", FLAGS_redis_max_queued_bytes=" << FLAGS_redis_max_queued_bytes; |
793 | |
|
794 | 0 | RedisTableTestBase::SetUp(); |
795 | |
|
796 | 0 | StartServer(); |
797 | 0 | StartClient(); |
798 | 0 | } |
799 | | |
800 | 0 | void TestRedisService::StartServer() { |
801 | 0 | if (use_external_mini_cluster()) { |
802 | 0 | redis_server_port_ = external_mini_cluster()->tablet_server(0)->redis_rpc_port(); |
803 | 0 | return; |
804 | 0 | } |
805 | | |
806 | 0 | redis_server_port_ = GetFreePort(&redis_port_lock_); |
807 | 0 | RedisServerOptions opts; |
808 | 0 | opts.rpc_opts.rpc_bind_addresses = AsString(RedisProxyEndpoint()); |
809 | | // No need to save the webserver port, as we don't plan on using it. Just use a unique free port. |
810 | 0 | opts.webserver_opts.port = GetFreePort(&redis_webserver_lock_); |
811 | 0 | string fs_root = GetTestPath("RedisServerTest-fsroot"); |
812 | 0 | opts.fs_opts.wal_paths = {fs_root}; |
813 | 0 | opts.fs_opts.data_paths = {fs_root}; |
814 | |
|
815 | 0 | auto master_rpc_addrs = master_rpc_addresses_as_strings(); |
816 | 0 | opts.master_addresses_flag = JoinStrings(master_rpc_addrs, ","); |
817 | |
|
818 | 0 | server_ = std::make_unique<RedisServer>(opts, mini_cluster()->mini_tablet_server(0)->server()); |
819 | 0 | LOG(INFO) << "Starting redis server..."; |
820 | 0 | CHECK_OK(server_->Start()); |
821 | 0 | LOG(INFO) << "Redis server successfully started."; |
822 | 0 | } |
823 | | |
824 | 0 | void TestRedisService::StopServer() { |
825 | 0 | if (!server_) { |
826 | 0 | return; |
827 | 0 | } |
828 | 0 | LOG(INFO) << "Shut down redis server..."; |
829 | 0 | server_->Shutdown(); |
830 | 0 | server_.reset(); |
831 | 0 | LOG(INFO) << "Redis server successfully shut down."; |
832 | 0 | } |
833 | | |
834 | 0 | void TestRedisService::StartClient() { |
835 | 0 | Endpoint remote = RedisProxyEndpoint(); |
836 | 0 | CHECK_OK(client_sock_.Init(0)); |
837 | 0 | CHECK_OK(client_sock_.SetNoDelay(false)); |
838 | 0 | LOG(INFO) << "Connecting to " << remote; |
839 | 0 | CHECK_OK(client_sock_.Connect(remote)); |
840 | 0 | Endpoint local; |
841 | 0 | CHECK_OK(client_sock_.GetSocketAddress(&local)); |
842 | 0 | CHECK_OK(client_sock_.GetPeerAddress(&remote)); |
843 | 0 | LOG(INFO) << "Connected: " << local << " => " << remote; |
844 | 0 | } |
845 | | |
846 | 0 | void TestRedisService::StopClient() { EXPECT_OK(client_sock_.Close()); } |
847 | | |
848 | 0 | void TestRedisService::RestartClient() { |
849 | 0 | StopClient(); |
850 | 0 | StartClient(); |
851 | 0 | } |
852 | | |
853 | 0 | void TestRedisService::CloseRedisClient() { |
854 | 0 | if (test_client_) { |
855 | 0 | test_client_->Disconnect(); |
856 | 0 | test_client_.reset(); |
857 | 0 | } |
858 | 0 | if (io_thread_pool_) { |
859 | 0 | io_thread_pool_->Shutdown(); |
860 | 0 | io_thread_pool_->Join(); |
861 | 0 | } |
862 | 0 | StopClient(); |
863 | 0 | } |
864 | | |
865 | 0 | void TestRedisService::TearDown() { |
866 | 0 | if (!use_external_mini_cluster()) { |
867 | 0 | size_t allocated_sessions = CountSessions(METRIC_redis_allocated_sessions); |
868 | 0 | if (!expected_no_sessions_) { |
869 | 0 | EXPECT_GT(allocated_sessions, 0); // Check that metric is sane. |
870 | 0 | } else { |
871 | 0 | EXPECT_EQ(0, allocated_sessions); |
872 | 0 | } |
873 | 0 | EXPECT_EQ(allocated_sessions, CountSessions(METRIC_redis_available_sessions)); |
874 | 0 | } |
875 | |
|
876 | 0 | CloseRedisClient(); |
877 | 0 | StopServer(); |
878 | 0 | RedisTableTestBase::TearDown(); |
879 | 0 | } |
880 | | |
881 | 0 | Status TestRedisService::Send(const std::string& cmd) { |
882 | | // Send the command. |
883 | 0 | auto bytes_written = EXPECT_RESULT(client_sock_.Write(to_uchar_ptr(cmd.c_str()), cmd.length())); |
884 | |
|
885 | 0 | EXPECT_EQ(cmd.length(), bytes_written); |
886 | |
|
887 | 0 | return Status::OK(); |
888 | 0 | } |
889 | | |
890 | | Status TestRedisService::SendCommandAndGetResponse( |
891 | 0 | const string& cmd, size_t expected_resp_length, int timeout_in_millis) { |
892 | 0 | RETURN_NOT_OK(Send(cmd)); |
893 | | |
894 | | // Receive the response. |
895 | 0 | MonoTime deadline = MonoTime::Now(); |
896 | 0 | deadline.AddDelta(MonoDelta::FromMilliseconds(timeout_in_millis)); |
897 | 0 | resp_.resize(expected_resp_length); |
898 | 0 | if (expected_resp_length) { |
899 | 0 | memset(resp_.data(), 0, expected_resp_length); |
900 | 0 | } |
901 | 0 | auto bytes_read = VERIFY_RESULT(client_sock_.BlockingRecv( |
902 | 0 | resp_.data(), expected_resp_length, deadline)); |
903 | 0 | resp_.resize(bytes_read); |
904 | 0 | if (expected_resp_length != bytes_read) { |
905 | 0 | return STATUS( |
906 | 0 | IOError, Substitute("Received $1 bytes instead of $2", bytes_read, expected_resp_length)); |
907 | 0 | } |
908 | 0 | return Status::OK(); |
909 | 0 | } |
910 | | |
911 | 0 | void TestRedisService::SendCommandAndExpectTimeout(const string& cmd) { |
912 | | // Don't expect to receive even 1 byte. |
913 | 0 | ASSERT_TRUE(SendCommandAndGetResponse(cmd, 1).IsTimedOut()); |
914 | 0 | } |
915 | | |
916 | | void TestRedisService::SendCommandAndExpectResponse(int line, |
917 | | const string& cmd, |
918 | | const string& expected, |
919 | 0 | bool partial) { |
920 | 0 | if (partial) { |
921 | 0 | auto seed = GetRandomSeed32(); |
922 | 0 | std::mt19937_64 rng(seed); |
923 | 0 | size_t last = cmd.length() - 2; |
924 | 0 | size_t splits = std::uniform_int_distribution<size_t>(1, 10)(rng); |
925 | 0 | std::vector<size_t> bounds(splits); |
926 | 0 | std::generate(bounds.begin(), bounds.end(), [&rng, last]{ |
927 | 0 | return std::uniform_int_distribution<size_t>(1, last)(rng); |
928 | 0 | }); |
929 | 0 | std::sort(bounds.begin(), bounds.end()); |
930 | 0 | bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end()); |
931 | 0 | size_t p = 0; |
932 | 0 | for (auto i : bounds) { |
933 | 0 | ASSERT_OK(Send(cmd.substr(p, i - p))); |
934 | 0 | p = i; |
935 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); |
936 | 0 | } |
937 | 0 | ASSERT_OK(SendCommandAndGetResponse(cmd.substr(p), expected.length())); |
938 | 0 | } else { |
939 | 0 | auto status = SendCommandAndGetResponse(cmd, expected.length()); |
940 | 0 | if (!status.ok()) { |
941 | 0 | LOG(INFO) << " Sent: " << Slice(cmd).ToDebugString(); |
942 | 0 | LOG(INFO) << "Received: " << Slice(resp_.data(), resp_.size()).ToDebugString(); |
943 | 0 | LOG(INFO) << "Expected: " << Slice(expected).ToDebugString(); |
944 | 0 | } |
945 | 0 | ASSERT_OK(status); |
946 | 0 | } |
947 | | |
948 | | // Verify that the response is as expected. |
949 | |
|
950 | 0 | std::string response(to_char_ptr(resp_.data()), expected.length()); |
951 | 0 | ASSERT_EQ(expected, response) |
952 | 0 | << "Command: " << Slice(cmd).ToDebugString() << std::endl |
953 | 0 | << "Originator: " << __FILE__ << ":" << line; |
954 | 0 | } |
955 | | |
956 | | template <class Callback> |
957 | | void TestRedisService::DoRedisTest(int line, |
958 | | const std::vector<std::string>& command, |
959 | | RedisReplyType reply_type, |
960 | 0 | const Callback& callback) { |
961 | 0 | expected_callbacks_called_++; |
962 | 0 | VLOG(4) << "Testing with line: " << __FILE__ << ":" << line; |
963 | 0 | client().Send(command, [this, line, reply_type, callback](const RedisReply& reply) { |
964 | 0 | VLOG(4) << "Received response for line: " << __FILE__ << ":" << line << " : " |
965 | 0 | << reply.as_string() << ", of type: " << to_underlying(reply.get_type()); |
966 | 0 | num_callbacks_called_++; |
967 | 0 | ASSERT_EQ(reply_type, reply.get_type()) |
968 | 0 | << "Originator: " << __FILE__ << ":" << line << ", reply: " |
969 | 0 | << Max500CharsPrinter(reply.ToString()); |
970 | 0 | callback(reply); |
971 | 0 | }); |
972 | 0 | } Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_17DoRedisTestStringEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEERKSA_NS0_14RedisReplyTypeEEUlRKNS0_10RedisReplyEE_EEviSE_SH_RKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_22DoRedisTestExpectErrorEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEERKSA_EUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_14DoRedisTestIntEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEExEUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_15DoRedisTestNullEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEEUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_39DoRedisTestExpectSimpleStringEndingWithEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEERKSA_EUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_23DoRedisTestResultsArrayEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEERKNS4_INS0_10RedisReplyENS8_ISF_EEEEEUlRKSF_E_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_26DoRedisTestScoreValueArrayEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEERKNS4_IdNS8_IdEEEESE_EUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_17DoRedisTestDoubleEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEdEUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: _ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS1_19DoRedisTestIntRangeEiRKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEExxEUlRKNS0_10RedisReplyEE_EEviSE_NS0_14RedisReplyTypeERKT_ Unexecuted instantiation: redisserver-test.cc:_ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS0_47TestRedisService_TestUsingOpenSourceClient_Test8TestBodyEvE3$_1EEviRKNSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEENS0_14RedisReplyTypeERKT_ Unexecuted instantiation: redisserver-test.cc:_ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS0_36TestRedisService_TestDummyLocal_Test8TestBodyEvE3$_2EEviRKNSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEENS0_14RedisReplyTypeERKT_ Unexecuted instantiation: redisserver-test.cc:_ZN2yb11redisserver16TestRedisService11DoRedisTestIZNS0_44TestRedisService_TestAdditionalCommands_Test8TestBodyEvE3$_3EEviRKNSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEENS0_14RedisReplyTypeERKT_ |
973 | | |
974 | 0 | void TestRedisService::VerifyCallbacks() { |
975 | 0 | ASSERT_EQ(expected_callbacks_called_, num_callbacks_called_.load(std::memory_order_acquire)); |
976 | 0 | } |
977 | | |
978 | 0 | TEST_F(TestRedisService, SimpleCommandInline) { |
979 | 0 | SendCommandAndExpectResponse(__LINE__, "set foo bar\r\n", "+OK\r\n"); |
980 | 0 | } |
981 | | |
982 | 0 | void TestRedisService::TestAbort(const std::string& command) { |
983 | 0 | ASSERT_OK(Send(command)); |
984 | 0 | std::this_thread::sleep_for(1000ms); |
985 | 0 | StopClient(); |
986 | | |
987 | | // TODO When reactor is shutting down, we cannot notify it that call is responded. |
988 | | // It is possible that it could happen not only with debug sleep. |
989 | 0 | std::this_thread::sleep_for(2000ms); |
990 | 0 | } |
991 | | |
992 | 0 | TEST_F(TestRedisService, AbortDuringProcessing) { |
993 | 0 | TestAbort("DEBUGSLEEP 2000\r\n"); |
994 | 0 | } |
995 | | |
996 | | class TestRedisServiceCleanQueueOnShutdown : public TestRedisService { |
997 | | public: |
998 | 0 | void SetUp() override { |
999 | 0 | saver_.emplace(); |
1000 | |
|
1001 | 0 | FLAGS_redis_max_concurrent_commands = 1; |
1002 | 0 | FLAGS_redis_max_batch = 1; |
1003 | 0 | TestRedisService::SetUp(); |
1004 | 0 | } |
1005 | | |
1006 | 0 | void TearDown() override { |
1007 | 0 | TestRedisService::TearDown(); |
1008 | 0 | saver_.reset(); |
1009 | 0 | } |
1010 | | |
1011 | | private: |
1012 | | boost::optional<google::FlagSaver> saver_; |
1013 | | }; |
1014 | | |
1015 | 0 | TEST_F_EX(TestRedisService, AbortQueueOnShutdown, TestRedisServiceCleanQueueOnShutdown) { |
1016 | 0 | TestAbort("DEBUGSLEEP 2000\r\nDEBUGSLEEP 999999999\r\n"); |
1017 | 0 | } |
1018 | | |
1019 | 0 | TEST_F(TestRedisService, AbortBatches) { |
1020 | 0 | TestAbort("DEBUGSLEEP 2000\r\nSET foo 1\r\nGET foo\r\nDEBUGSLEEP 999999999\r\n"); |
1021 | 0 | } |
1022 | | |
1023 | | class TestRedisServiceReceiveBufferOverflow : public TestRedisService { |
1024 | | public: |
1025 | 0 | void SetUp() override { |
1026 | 0 | saver_.emplace(); |
1027 | |
|
1028 | 0 | FLAGS_redis_max_concurrent_commands = 1; |
1029 | 0 | FLAGS_redis_max_batch = 1; |
1030 | | // TODO FLAGS_rpc_initial_buffer_size = 128; |
1031 | 0 | redis_max_read_buffer_size_changer_.Init(128, &redis_max_read_buffer_size_); |
1032 | 0 | FLAGS_redis_max_queued_bytes = 0; |
1033 | 0 | TestRedisService::SetUp(); |
1034 | 0 | } |
1035 | | |
1036 | 0 | void TearDown() override { |
1037 | 0 | TestRedisService::TearDown(); |
1038 | 0 | redis_max_read_buffer_size_changer_.Reset(); |
1039 | 0 | saver_.reset(); |
1040 | 0 | } |
1041 | | |
1042 | | private: |
1043 | | boost::optional<google::FlagSaver> saver_; |
1044 | | ValueChanger<uint64_t> redis_max_read_buffer_size_changer_; |
1045 | | }; |
1046 | | |
1047 | 0 | TEST_F_EX(TestRedisService, ReceiveBufferOverflow, TestRedisServiceReceiveBufferOverflow) { |
1048 | 0 | auto key = std::string(FLAGS_redis_max_read_buffer_size - 12, 'X'); |
1049 | 0 | SendCommandAndExpectResponse( |
1050 | 0 | __LINE__, Format("DEBUGSLEEP 2000\r\nSET key $0\r\n", key), "+OK\r\n+OK\r\n"); |
1051 | |
|
1052 | 0 | SendCommandAndExpectResponse( |
1053 | 0 | __LINE__, |
1054 | 0 | Format("DEBUGSLEEP 2000\r\nSET key1 $0\r\nSET key2 $0\r\n", key, key), |
1055 | 0 | "+OK\r\n+OK\r\n+OK\r\n"); |
1056 | 0 | } |
1057 | | |
1058 | | class TestTooBigCommand : public TestRedisService { |
1059 | 0 | void SetUp() override { |
1060 | 0 | FLAGS_redis_rpc_block_size = 32; |
1061 | 0 | redis_max_read_buffer_size_changer_.Init(1024, &redis_max_read_buffer_size_); |
1062 | 0 | TestRedisService::SetUp(); |
1063 | 0 | } |
1064 | | |
1065 | 0 | void TearDown() override { |
1066 | 0 | TestRedisService::TearDown(); |
1067 | 0 | redis_max_read_buffer_size_changer_.Reset(); |
1068 | 0 | } |
1069 | | |
1070 | | private: |
1071 | | ValueChanger<uint64_t> redis_max_read_buffer_size_changer_; |
1072 | | }; |
1073 | | |
1074 | | TEST_F_EX(TestRedisService, TooBigCommand, TestTooBigCommand) { |
1075 | | std::string small_key(FLAGS_redis_max_read_buffer_size / 2, 'X'); |
1076 | | SendCommandAndExpectResponse( |
1077 | | __LINE__, Format("SET key1 $0\r\nSET key2 $0\r\n", small_key), "+OK\r\n+OK\r\n"); |
1078 | | std::string big_key(FLAGS_redis_max_read_buffer_size, 'X'); |
1079 | | std::string key_suffix(FLAGS_redis_rpc_block_size, 'Y'); |
1080 | | auto status = SendCommandAndGetResponse(Format("SET key$0 $1\r\n", key_suffix, big_key), 1); |
1081 | | ASSERT_TRUE(status.IsNetworkError()) << "Status: " << status; |
1082 | | } |
1083 | | |
1084 | 0 | TEST_F_EX(TestRedisService, HugeCommandInline, NoLocalCallsRedisServiceTest) { |
1085 | | // Set a larger timeout for the yql layer : 1 min vs 10 min for tsan/asan. |
1086 | 0 | FLAGS_redis_service_yb_client_timeout_millis = 6 * kDefaultTimeoutMs; |
1087 | |
|
1088 | 0 | LOG(INFO) << "Creating a value of size " << FLAGS_redis_max_value_size; |
1089 | 0 | std::string value(FLAGS_redis_max_value_size, 'T'); |
1090 | 0 | DoRedisTestOk(__LINE__, {"SET", "foo", value}); |
1091 | 0 | SyncClient(); |
1092 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "foo"}, value); |
1093 | 0 | SyncClient(); |
1094 | 0 | DoRedisTestOk(__LINE__, {"SET", "foo", "Test"}); |
1095 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "foo"}, "Test"); |
1096 | 0 | SyncClient(); |
1097 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "foo", "Too much" + value}); |
1098 | 0 | SyncClient(); |
1099 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey1", value}, 1); |
1100 | 0 | SyncClient(); |
1101 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey2", value}, 1); |
1102 | 0 | SyncClient(); |
1103 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey3", value}, 1); |
1104 | 0 | SyncClient(); |
1105 | 0 | DoRedisTestArray(__LINE__, {"HGETALL", "map_key"}, {"subkey1", value, "subkey2", value, |
1106 | 0 | "subkey3", value}); |
1107 | 0 | SyncClient(); |
1108 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey4", value}, 1); |
1109 | 0 | DoRedisTestExpectError(__LINE__, {"HGETALL", "map_key"}); |
1110 | 0 | SyncClient(); |
1111 | 0 | DoRedisTestInt(__LINE__, {"DEL", "map_key"}, 1); |
1112 | 0 | SyncClient(); |
1113 | 0 | DoRedisTestInt(__LINE__, {"DEL", "foo"}, 1); |
1114 | 0 | SyncClient(); |
1115 | 0 | value[0] = 'A'; |
1116 | 0 | DoRedisTestOk( |
1117 | 0 | __LINE__, {"HMSET", "map_key1", "subkey1", value, "subkey2", value, "subkey3", value}); |
1118 | 0 | SyncClient(); |
1119 | 0 | DoRedisTestArray( |
1120 | 0 | __LINE__, {"HGETALL", "map_key1"}, {"subkey1", value, "subkey2", value, "subkey3", value}); |
1121 | 0 | SyncClient(); |
1122 | 0 | value[0] = 'B'; |
1123 | 0 | DoRedisTestExpectError( |
1124 | 0 | __LINE__, |
1125 | 0 | {"HMSET", "map_key1", "subkey1", value, "subkey2", value, "subkey3", value, |
1126 | 0 | "subkey4", value}); |
1127 | 0 | SyncClient(); |
1128 | 0 | VerifyCallbacks(); |
1129 | 0 | } |
1130 | | |
1131 | 0 | TEST_F(TestRedisService, SimpleCommandMulti) { |
1132 | 0 | SendCommandAndExpectResponse( |
1133 | 0 | __LINE__, "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1134 | 0 | } |
1135 | | |
1136 | 0 | TEST_F(TestRedisService, BatchedCommandsInline) { |
1137 | 0 | SendCommandAndExpectResponse( |
1138 | 0 | __LINE__, |
1139 | 0 | "set a 5\r\nset foo bar\r\nget foo\r\nget a\r\n", |
1140 | 0 | "+OK\r\n+OK\r\n$3\r\nbar\r\n$1\r\n5\r\n"); |
1141 | 0 | } |
1142 | | |
1143 | 0 | TEST_F(TestRedisService, TestTimedoutInQueue) { |
1144 | 0 | FLAGS_redis_max_batch = 1; |
1145 | 0 | SetAtomicFlag(true, &FLAGS_TEST_enable_backpressure_mode_for_testing); |
1146 | |
|
1147 | 0 | DoRedisTestOk(__LINE__, {"SET", "foo", "value"}); |
1148 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "foo"}, "value"); |
1149 | 0 | DoRedisTestOk(__LINE__, {"SET", "foo", "Test"}); |
1150 | | |
1151 | | // All calls past this call should fail. |
1152 | 0 | DoRedisTestOk(__LINE__, {"DEBUGSLEEP", yb::ToString(FLAGS_max_time_in_queue_ms)}); |
1153 | |
|
1154 | 0 | const string expected_message = |
1155 | 0 | "The server is overloaded. Call waited in the queue past max_time_in_queue."; |
1156 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "foo", "Test"}, expected_message); |
1157 | 0 | DoRedisTestExpectError(__LINE__, {"GET", "foo"}, expected_message); |
1158 | 0 | DoRedisTestExpectError(__LINE__, {"DEBUGSLEEP", "2000"}, expected_message); |
1159 | |
|
1160 | 0 | SyncClient(); |
1161 | 0 | VerifyCallbacks(); |
1162 | 0 | } |
1163 | | |
1164 | 0 | TEST_F(TestRedisService, BatchedCommandsInlinePartial) { |
1165 | 0 | for (int i = 0; i != 1000; ++i) { |
1166 | 0 | ASSERT_NO_FATAL_FAILURE( |
1167 | 0 | SendCommandAndExpectResponse( |
1168 | 0 | __LINE__, |
1169 | 0 | "set a 5\r\nset foo bar\r\nget foo\r\nget a\r\n", |
1170 | 0 | "+OK\r\n+OK\r\n$3\r\nbar\r\n$1\r\n5\r\n", |
1171 | 0 | /* partial */ true) |
1172 | 0 | ); |
1173 | 0 | } |
1174 | 0 | } |
1175 | | |
1176 | | namespace { |
1177 | | |
1178 | | class TestRedisServicePipelined : public TestRedisService { |
1179 | | public: |
1180 | 0 | void SetUp() override { |
1181 | 0 | FLAGS_redis_safe_batch = false; |
1182 | 0 | FLAGS_redis_max_concurrent_commands = FLAGS_test_redis_max_concurrent_commands; |
1183 | 0 | FLAGS_redis_max_batch = FLAGS_test_redis_max_batch; |
1184 | 0 | TestRedisService::SetUp(); |
1185 | 0 | } |
1186 | | }; |
1187 | | |
1188 | | #ifndef THREAD_SANITIZER |
1189 | | const size_t kPipelineKeys = 1000; |
1190 | | #else |
1191 | | const size_t kPipelineKeys = 100; |
1192 | | #endif |
1193 | | |
1194 | 0 | size_t ValueForKey(size_t key) { |
1195 | 0 | return key * 2; |
1196 | 0 | } |
1197 | | |
1198 | 0 | std::string PipelineSetCommand() { |
1199 | 0 | std::string command; |
1200 | 0 | for (size_t i = 0; i != kPipelineKeys; ++i) { |
1201 | 0 | command += yb::Format("set $0 $1\r\n", i, ValueForKey(i)); |
1202 | 0 | } |
1203 | 0 | return command; |
1204 | 0 | } |
1205 | | |
1206 | 0 | std::string PipelineSetResponse() { |
1207 | 0 | std::string response; |
1208 | 0 | for (size_t i = 0; i != kPipelineKeys; ++i) { |
1209 | 0 | response += "+OK\r\n"; |
1210 | 0 | } |
1211 | 0 | return response; |
1212 | 0 | } |
1213 | | |
1214 | 0 | std::string PipelineGetCommand() { |
1215 | 0 | std::string command; |
1216 | 0 | for (size_t i = 0; i != kPipelineKeys; ++i) { |
1217 | 0 | command += yb::Format("get $0\r\n", i); |
1218 | 0 | } |
1219 | 0 | return command; |
1220 | 0 | } |
1221 | | |
1222 | 0 | std::string PipelineGetResponse() { |
1223 | 0 | std::string response; |
1224 | 0 | for (size_t i = 0; i != kPipelineKeys; ++i) { |
1225 | 0 | std::string value = std::to_string(ValueForKey(i)); |
1226 | 0 | response += yb::Format("$$$0\r\n$1\r\n", value.length(), value); |
1227 | 0 | } |
1228 | 0 | return response; |
1229 | 0 | } |
1230 | | |
1231 | | } // namespace |
1232 | | |
1233 | 0 | TEST_F_EX(TestRedisService, Pipeline, TestRedisServicePipelined) { |
1234 | 0 | auto start = std::chrono::steady_clock::now(); |
1235 | 0 | SendCommandAndExpectResponse(__LINE__, PipelineSetCommand(), PipelineSetResponse()); |
1236 | 0 | auto mid = std::chrono::steady_clock::now(); |
1237 | 0 | SendCommandAndExpectResponse(__LINE__, PipelineGetCommand(), PipelineGetResponse()); |
1238 | 0 | auto end = std::chrono::steady_clock::now(); |
1239 | 0 | auto set_time = std::chrono::duration_cast<std::chrono::milliseconds>(mid - start); |
1240 | 0 | auto get_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - mid); |
1241 | 0 | LOG(INFO) << yb::Format("Unsafe set: $0ms, get: $1ms", set_time.count(), get_time.count()); |
1242 | 0 | } |
1243 | | |
1244 | 0 | TEST_F_EX(TestRedisService, PipelinePartial, TestRedisServicePipelined) { |
1245 | 0 | SendCommandAndExpectResponse(__LINE__, |
1246 | 0 | PipelineSetCommand(), |
1247 | 0 | PipelineSetResponse(), |
1248 | 0 | true /* partial */); |
1249 | 0 | SendCommandAndExpectResponse(__LINE__, |
1250 | 0 | PipelineGetCommand(), |
1251 | 0 | PipelineGetResponse(), |
1252 | 0 | true /* partial */); |
1253 | 0 | } |
1254 | | |
1255 | | namespace { |
1256 | | |
1257 | | class BatchGenerator { |
1258 | | public: |
1259 | 0 | explicit BatchGenerator(bool collisions) : collisions_(collisions), random_(293462970) {} |
1260 | | |
1261 | 0 | std::pair<std::string, std::string> Generate() { |
1262 | 0 | new_values_.clear(); |
1263 | 0 | requested_keys_.clear(); |
1264 | 0 | std::string command, response; |
1265 | 0 | size_t size = size_distribution_(random_); |
1266 | 0 | for (size_t j = 0; j != size; ++j) { |
1267 | 0 | bool get = !keys_.empty() && (bool_distribution_(random_) != 0); |
1268 | 0 | if (get) { |
1269 | 0 | int key = keys_[std::uniform_int_distribution<size_t>(0, keys_.size() - 1)(random_)]; |
1270 | 0 | if (!collisions_ && new_values_.count(key)) { |
1271 | 0 | continue; |
1272 | 0 | } |
1273 | 0 | command += yb::Format("get $0\r\n", key); |
1274 | 0 | auto value = std::to_string(values_[key]); |
1275 | 0 | response += yb::Format("$$$0\r\n$1\r\n", value.length(), value); |
1276 | 0 | requested_keys_.insert(key); |
1277 | 0 | } else { |
1278 | 0 | int value = value_distribution_(random_); |
1279 | 0 | for (;;) { |
1280 | 0 | int key = key_distribution_(random_); |
1281 | 0 | if (collisions_) { |
1282 | 0 | StoreValue(key, value); |
1283 | 0 | } else if(requested_keys_.count(key) || !new_values_.emplace(key, value).second) { |
1284 | 0 | continue; |
1285 | 0 | } |
1286 | 0 | command += yb::Format("set $0 $1\r\n", key, value); |
1287 | 0 | response += "+OK\r\n"; |
1288 | 0 | break; |
1289 | 0 | } |
1290 | 0 | } |
1291 | 0 | } |
1292 | |
|
1293 | 0 | for (const auto& p : new_values_) { |
1294 | 0 | StoreValue(p.first, p.second); |
1295 | 0 | } |
1296 | 0 | return std::make_pair(std::move(command), std::move(response)); |
1297 | 0 | } |
1298 | | private: |
1299 | 0 | void StoreValue(int key, int value) { |
1300 | 0 | auto it = values_.find(key); |
1301 | 0 | if (it == values_.end()) { |
1302 | 0 | values_.emplace(key, value); |
1303 | 0 | keys_.push_back(key); |
1304 | 0 | } else { |
1305 | 0 | it->second = value; |
1306 | 0 | } |
1307 | 0 | } |
1308 | | |
1309 | | static constexpr size_t kMinSize = 500; |
1310 | | static constexpr size_t kMaxSize = kMinSize + 511; |
1311 | | static constexpr int kMinKey = 0; |
1312 | | static constexpr int kMaxKey = 1023; |
1313 | | static constexpr int kMinValue = 0; |
1314 | | static constexpr int kMaxValue = 1023; |
1315 | | |
1316 | | const bool collisions_; |
1317 | | std::mt19937_64 random_; |
1318 | | std::uniform_int_distribution<int> bool_distribution_{0, 1}; |
1319 | | std::uniform_int_distribution<size_t> size_distribution_{kMinSize, kMaxSize}; |
1320 | | std::uniform_int_distribution<int> key_distribution_{kMinKey, kMaxKey}; |
1321 | | std::uniform_int_distribution<int> value_distribution_{kMinValue, kMaxValue}; |
1322 | | |
1323 | | std::unordered_map<int, int> values_; |
1324 | | std::unordered_map<int, int> new_values_; |
1325 | | std::unordered_set<int> requested_keys_; |
1326 | | std::vector<int> keys_; |
1327 | | }; |
1328 | | |
1329 | | } // namespace |
1330 | | |
1331 | 0 | TEST_F_EX(TestRedisService, MixedBatch, TestRedisServicePipelined) { |
1332 | 0 | constexpr size_t kBatches = 50; |
1333 | 0 | BatchGenerator generator(false); |
1334 | 0 | for (size_t i = 0; i != kBatches; ++i) { |
1335 | 0 | auto batch = generator.Generate(); |
1336 | 0 | SendCommandAndExpectResponse(__LINE__, batch.first, batch.second); |
1337 | 0 | } |
1338 | 0 | } |
1339 | | |
1340 | | class TestRedisServiceSafeBatch : public TestRedisService { |
1341 | | public: |
1342 | 0 | void SetUp() override { |
1343 | 0 | FLAGS_redis_max_concurrent_commands = 1; |
1344 | 0 | FLAGS_redis_max_batch = FLAGS_test_redis_max_batch; |
1345 | 0 | FLAGS_redis_safe_batch = true; |
1346 | 0 | TestRedisService::SetUp(); |
1347 | 0 | } |
1348 | | }; |
1349 | | |
1350 | 0 | TEST_F_EX(TestRedisService, SafeMixedBatch, TestRedisServiceSafeBatch) { |
1351 | 0 | constexpr size_t kBatches = 50; |
1352 | 0 | BatchGenerator generator(true); |
1353 | 0 | std::vector<decltype(generator.Generate())> batches; |
1354 | 0 | for (size_t i = 0; i != kBatches; ++i) { |
1355 | 0 | batches.push_back(generator.Generate()); |
1356 | 0 | } |
1357 | 0 | auto start = std::chrono::steady_clock::now(); |
1358 | 0 | for (const auto& batch : batches) { |
1359 | 0 | SendCommandAndExpectResponse(__LINE__, batch.first, batch.second); |
1360 | 0 | } |
1361 | 0 | auto total = std::chrono::steady_clock::now() - start; |
1362 | 0 | auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(total).count(); |
1363 | 0 | LOG(INFO) << Format("Total: $0ms, average: $1ms", ms, ms / kBatches); |
1364 | 0 | } |
1365 | | |
1366 | 0 | TEST_F_EX(TestRedisService, SafeBatchPipeline, TestRedisServiceSafeBatch) { |
1367 | 0 | auto start = std::chrono::steady_clock::now(); |
1368 | 0 | SendCommandAndExpectResponse(__LINE__, PipelineSetCommand(), PipelineSetResponse()); |
1369 | 0 | auto mid = std::chrono::steady_clock::now(); |
1370 | 0 | SendCommandAndExpectResponse(__LINE__, PipelineGetCommand(), PipelineGetResponse()); |
1371 | 0 | auto end = std::chrono::steady_clock::now(); |
1372 | 0 | auto set_time = std::chrono::duration_cast<std::chrono::milliseconds>(mid - start); |
1373 | 0 | auto get_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - mid); |
1374 | 0 | LOG(INFO) << yb::Format("Safe set: $0ms, get: $1ms", set_time.count(), get_time.count()); |
1375 | 0 | } |
1376 | | |
1377 | 0 | TEST_F(TestRedisService, BatchedCommandMulti) { |
1378 | 0 | SendCommandAndExpectResponse( |
1379 | 0 | __LINE__, |
1380 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n" |
1381 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n" |
1382 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", |
1383 | 0 | "+OK\r\n+OK\r\n+OK\r\n"); |
1384 | 0 | } |
1385 | | |
1386 | 0 | TEST_F(TestRedisService, BatchedCommandMultiPartial) { |
1387 | 0 | for (int i = 0; i != 1000; ++i) { |
1388 | 0 | ASSERT_NO_FATAL_FAILURE( |
1389 | 0 | SendCommandAndExpectResponse( |
1390 | 0 | __LINE__, |
1391 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$5\r\nTEST1\r\n" |
1392 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$5\r\nTEST2\r\n" |
1393 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$5\r\nTEST3\r\n" |
1394 | 0 | "*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n", |
1395 | 0 | "+OK\r\n+OK\r\n+OK\r\n$5\r\nTEST3\r\n", |
1396 | 0 | /* partial */ true) |
1397 | 0 | ); |
1398 | 0 | } |
1399 | 0 | } |
1400 | | |
1401 | 0 | TEST_F(TestRedisService, IncompleteCommandInline) { |
1402 | 0 | expected_no_sessions_ = true; |
1403 | 0 | SendCommandAndExpectTimeout("TEST"); |
1404 | 0 | } |
1405 | | |
1406 | 0 | TEST_F(TestRedisService, MalformedCommandsFollowedByAGoodOne) { |
1407 | 0 | expected_no_sessions_ = true; |
1408 | 0 | ASSERT_NOK(SendCommandAndGetResponse("*3\r\n.1\r\n", 1)); |
1409 | 0 | RestartClient(); |
1410 | 0 | ASSERT_NOK(SendCommandAndGetResponse("*0\r\n.2\r\n", 1)); |
1411 | 0 | RestartClient(); |
1412 | 0 | ASSERT_NOK(SendCommandAndGetResponse("*-4\r\n.3\r\n", 1)); |
1413 | 0 | RestartClient(); |
1414 | 0 | SendCommandAndExpectResponse(__LINE__, "*2\r\n$4\r\necho\r\n$3\r\nfoo\r\n", "$3\r\nfoo\r\n"); |
1415 | 0 | } |
1416 | | |
1417 | | namespace { |
1418 | | |
1419 | 0 | void TestBadCommand(std::string command, TestRedisService* test) { |
1420 | 0 | ASSERT_NOK(test->SendCommandAndGetResponse(command, 1)) << "Command: " << command; |
1421 | 0 | test->RestartClient(); |
1422 | |
|
1423 | 0 | command.erase(std::remove(command.begin(), command.end(), '\n'), command.end()); |
1424 | |
|
1425 | 0 | if (!command.empty()) { |
1426 | 0 | ASSERT_NOK(test->SendCommandAndGetResponse(command, 1)) << "Command: " << command; |
1427 | 0 | test->RestartClient(); |
1428 | 0 | } |
1429 | 0 | } |
1430 | | |
1431 | | } // namespace |
1432 | | |
1433 | 0 | TEST_F(TestRedisService, BadCommand) { |
1434 | 0 | expected_no_sessions_ = true; |
1435 | |
|
1436 | 0 | TestBadCommand("\n", this); |
1437 | 0 | TestBadCommand(" \r\n", this); |
1438 | 0 | TestBadCommand("*\r\n9\r\n", this); |
1439 | 0 | TestBadCommand("1\r\n\r\n", this); |
1440 | 0 | TestBadCommand("1\r\n \r\n", this); |
1441 | 0 | TestBadCommand("1\r\n*0\r\n", this); |
1442 | 0 | } |
1443 | | |
1444 | 0 | TEST_F(TestRedisService, BadRandom) { |
1445 | 0 | expected_no_sessions_ = true; |
1446 | 0 | const std::string allowed = " -$*\r\n0123456789"; |
1447 | 0 | std::string command; |
1448 | 0 | constexpr size_t kTotalProbes = 100; |
1449 | 0 | constexpr size_t kMinCommandLength = 1; |
1450 | 0 | constexpr size_t kMaxCommandLength = 100; |
1451 | 0 | constexpr int kTimeoutInMillis = 250; |
1452 | 0 | for (int i = 0; i != kTotalProbes; ++i) { |
1453 | 0 | size_t len = RandomUniformInt(kMinCommandLength, kMaxCommandLength); |
1454 | 0 | command.clear(); |
1455 | 0 | for (size_t idx = 0; idx != len; ++idx) { |
1456 | 0 | command += RandomElement(allowed); |
1457 | 0 | if (command[command.length() - 1] == '\r') { |
1458 | 0 | command += '\n'; |
1459 | 0 | } |
1460 | 0 | } |
1461 | |
|
1462 | 0 | LOG(INFO) << "Command: " << command; |
1463 | 0 | auto status = SendCommandAndGetResponse(command, 1, kTimeoutInMillis); |
1464 | | // We don't care about status here, because even usually it fails, |
1465 | | // sometimes it has non empty response. |
1466 | | // Our main goal is to test that server does not crash. |
1467 | 0 | LOG(INFO) << "Status: " << status; |
1468 | |
|
1469 | 0 | RestartClient(); |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | 0 | TEST_F(TestRedisService, IncompleteCommandMulti) { |
1474 | 0 | expected_no_sessions_ = true; |
1475 | 0 | SendCommandAndExpectTimeout("*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTE"); |
1476 | 0 | } |
1477 | | |
1478 | 0 | TEST_F(TestRedisService, Echo) { |
1479 | 0 | expected_no_sessions_ = true; |
1480 | 0 | SendCommandAndExpectResponse(__LINE__, "*2\r\n$4\r\necho\r\n$3\r\nfoo\r\n", "$3\r\nfoo\r\n"); |
1481 | 0 | SendCommandAndExpectResponse( |
1482 | 0 | __LINE__, "*2\r\n$4\r\necho\r\n$8\r\nfoo bar \r\n", "$8\r\nfoo bar \r\n"); |
1483 | 0 | SendCommandAndExpectResponse( |
1484 | 0 | __LINE__, |
1485 | 0 | EncodeAsArray({ // The request is sent as a multi bulk array. |
1486 | 0 | "echo"s, |
1487 | 0 | "foo bar"s |
1488 | 0 | }), |
1489 | 0 | EncodeAsBulkString("foo bar") // The response is in the bulk string format. |
1490 | 0 | ); |
1491 | 0 | } |
1492 | | |
1493 | 0 | TEST_F(TestRedisService, TestSetOnly) { |
1494 | 0 | SendCommandAndExpectResponse( |
1495 | 0 | __LINE__, "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1496 | 0 | SendCommandAndExpectResponse( |
1497 | 0 | __LINE__, "*3\r\n$3\r\nset\r\n$4\r\nfool\r\n$4\r\nBEST\r\n", "+OK\r\n"); |
1498 | 0 | } |
1499 | | |
1500 | 0 | TEST_F(TestRedisService, TestCaseInsensitiveness) { |
1501 | 0 | SendCommandAndExpectResponse( |
1502 | 0 | __LINE__, "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1503 | 0 | SendCommandAndExpectResponse( |
1504 | 0 | __LINE__, "*3\r\n$3\r\nSet\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1505 | 0 | SendCommandAndExpectResponse( |
1506 | 0 | __LINE__, "*3\r\n$3\r\nsEt\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1507 | 0 | SendCommandAndExpectResponse( |
1508 | 0 | __LINE__, "*3\r\n$3\r\nseT\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1509 | 0 | SendCommandAndExpectResponse( |
1510 | 0 | __LINE__, "*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1511 | 0 | } |
1512 | | |
1513 | 0 | TEST_F(TestRedisService, TestSetThenGet) { |
1514 | 0 | SendCommandAndExpectResponse(__LINE__, |
1515 | 0 | "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$4\r\nTEST\r\n", "+OK\r\n"); |
1516 | 0 | SendCommandAndExpectResponse(__LINE__, "*2\r\n$3\r\nget\r\n$3\r\nfoo\r\n", "$4\r\nTEST\r\n"); |
1517 | 0 | SendCommandAndExpectResponse( |
1518 | 0 | __LINE__, |
1519 | 0 | EncodeAsArray({ // The request is sent as a multi bulk array. |
1520 | 0 | "set"s, |
1521 | 0 | "name"s, |
1522 | 0 | "yugabyte"s |
1523 | 0 | }), |
1524 | 0 | EncodeAsSimpleString("OK") // The response is in the simple string format. |
1525 | 0 | ); |
1526 | 0 | SendCommandAndExpectResponse( |
1527 | 0 | __LINE__, |
1528 | 0 | EncodeAsArray({ // The request is sent as a multi bulk array. |
1529 | 0 | "get"s, |
1530 | 0 | "name"s |
1531 | 0 | }), |
1532 | 0 | EncodeAsBulkString("yugabyte") // The response is in the bulk string format. |
1533 | 0 | ); |
1534 | 0 | } |
1535 | | |
1536 | 0 | TEST_F(TestRedisService, TestUsingOpenSourceClient) { |
1537 | 0 | DoRedisTestOk(__LINE__, {"SET", "hello", "42"}); |
1538 | |
|
1539 | 0 | DoRedisTest(__LINE__, {"DECRBY", "hello", "12"}, |
1540 | 0 | RedisReplyType::kError, // TODO: fix error handling |
1541 | 0 | [](const RedisReply &reply) { |
1542 | | // TBD: ASSERT_EQ(30, reply.as_integer()); |
1543 | 0 | }); |
1544 | |
|
1545 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "hello"}, "42"); |
1546 | 0 | DoRedisTestOk(__LINE__, {"SET", "world", "72"}); |
1547 | |
|
1548 | 0 | SyncClient(); |
1549 | 0 | VerifyCallbacks(); |
1550 | 0 | } |
1551 | | |
1552 | 0 | TEST_F(TestRedisService, TestBinaryUsingOpenSourceClient) { |
1553 | 0 | const std::string kFooValue = "\001\002\r\n\003\004"s; |
1554 | 0 | const std::string kBarValue = "\013\010\000"s; |
1555 | |
|
1556 | 0 | DoRedisTestOk(__LINE__, {"SET", "foo", kFooValue}); |
1557 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "foo"}, kFooValue); |
1558 | 0 | DoRedisTestOk(__LINE__, {"SET", "bar", kBarValue}); |
1559 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "bar"}, kBarValue); |
1560 | |
|
1561 | 0 | SyncClient(); |
1562 | 0 | VerifyCallbacks(); |
1563 | 0 | } |
1564 | | |
1565 | 0 | TEST_F(TestRedisService, TestSingleCommand) { |
1566 | 0 | DoRedisTestOk(__LINE__, {"SET", "k1", ""}); |
1567 | 0 | DoRedisTestInt(__LINE__, {"HSET", "k2", "s1", ""}, 1); |
1568 | |
|
1569 | 0 | SyncClient(); |
1570 | |
|
1571 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, ""); |
1572 | 0 | SyncClient(); |
1573 | 0 | VerifyCallbacks(); |
1574 | 0 | } |
1575 | | |
1576 | 0 | TEST_F(TestRedisService, TestEmptyValue) { |
1577 | 0 | DoRedisTestOk(__LINE__, {"SET", "k1", ""}); |
1578 | 0 | DoRedisTestInt(__LINE__, {"HSET", "k2", "s1", ""}, 1); |
1579 | |
|
1580 | 0 | SyncClient(); |
1581 | |
|
1582 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, ""); |
1583 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "k2", "s1"}, ""); |
1584 | |
|
1585 | 0 | SyncClient(); |
1586 | 0 | VerifyCallbacks(); |
1587 | 0 | } |
1588 | | |
1589 | | void ConnectWithPassword( |
1590 | | TestRedisService* test, const char* password, bool auth_should_succeed, |
1591 | 0 | bool get_should_succeed) { |
1592 | 0 | auto rc1 = test->CreateClient(); |
1593 | 0 | test->UseClient(rc1); |
1594 | |
|
1595 | 0 | if (auth_should_succeed) { |
1596 | 0 | if (password != nullptr) test->DoRedisTestOk(__LINE__, {"AUTH", password}); |
1597 | 0 | } else { |
1598 | 0 | if (password != nullptr) test->DoRedisTestExpectError(__LINE__, {"AUTH", password}); |
1599 | 0 | } |
1600 | |
|
1601 | 0 | if (get_should_succeed) { |
1602 | 0 | test->DoRedisTestOk(__LINE__, {"SET", "k1", "5"}); |
1603 | 0 | test->DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "5"); |
1604 | 0 | } else { |
1605 | 0 | test->DoRedisTestExpectError(__LINE__, {"SET", "k1", "5"}); |
1606 | 0 | test->DoRedisTestExpectError(__LINE__, {"GET", "k1"}); |
1607 | 0 | } |
1608 | |
|
1609 | 0 | test->SyncClient(); |
1610 | 0 | test->UseClient(nullptr); |
1611 | 0 | } |
1612 | | |
1613 | 0 | TEST_F(TestRedisService, TestSelect) { |
1614 | 0 | auto rc1 = CreateClient(); |
1615 | 0 | auto rc2 = CreateClient(); |
1616 | 0 | auto rc3 = CreateClient(); |
1617 | |
|
1618 | 0 | const string default_db("0"); |
1619 | 0 | const string second_db("2"); |
1620 | |
|
1621 | 0 | UseClient(rc1); |
1622 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v1"}); |
1623 | 0 | SyncClient(); |
1624 | |
|
1625 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1626 | 0 | SyncClient(); |
1627 | | |
1628 | | // Select without creating a db should fail. |
1629 | 0 | DoRedisTestExpectError(__LINE__, {"SELECT", second_db.c_str()}); |
1630 | 0 | SyncClient(); |
1631 | | |
1632 | | // The connection would be closed upon a bad Select. |
1633 | 0 | DoRedisTestExpectError(__LINE__, {"PING"}); |
1634 | 0 | SyncClient(); |
1635 | | |
1636 | | // Use a different client. |
1637 | 0 | UseClient(rc2); |
1638 | | // Get the value from the default_db. |
1639 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1640 | 0 | SyncClient(); |
1641 | | |
1642 | | // Create DB. |
1643 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1644 | 0 | SyncClient(); |
1645 | | |
1646 | | // Select should now go through. |
1647 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1648 | 0 | SyncClient(); |
1649 | | |
1650 | | // Get should be empty. |
1651 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1652 | 0 | SyncClient(); |
1653 | | // Set a diffferent value |
1654 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v2"}); |
1655 | 0 | SyncClient(); |
1656 | | // Get that value |
1657 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1658 | 0 | SyncClient(); |
1659 | | // Select the original db and get the value. |
1660 | 0 | DoRedisTestOk(__LINE__, {"SELECT", default_db.c_str()}); |
1661 | 0 | SyncClient(); |
1662 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1663 | 0 | SyncClient(); |
1664 | |
|
1665 | 0 | UseClient(rc3); |
1666 | | // By default we should get the value from db-0 |
1667 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1668 | | // Select second db. |
1669 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1670 | | // Get that value |
1671 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1672 | 0 | SyncClient(); |
1673 | | |
1674 | | // List DB. |
1675 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1676 | 0 | SyncClient(); |
1677 | | |
1678 | | // Delete DB. |
1679 | 0 | DoRedisTestOk(__LINE__, {"DeleteDB", second_db.c_str()}); |
1680 | 0 | SyncClient(); |
1681 | | // Expect to not be able to read the value. |
1682 | 0 | DoRedisTestExpectError(__LINE__, {"GET", "key"}); |
1683 | 0 | SyncClient(); |
1684 | | // Expect to not be able to read the value. |
1685 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "key", "v2"}); |
1686 | 0 | SyncClient(); |
1687 | | |
1688 | | // List DB. |
1689 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db}); |
1690 | 0 | SyncClient(); |
1691 | |
|
1692 | 0 | rc1->Disconnect(); |
1693 | 0 | rc2->Disconnect(); |
1694 | 0 | rc3->Disconnect(); |
1695 | |
|
1696 | 0 | UseClient(nullptr); |
1697 | 0 | VerifyCallbacks(); |
1698 | 0 | } |
1699 | | |
1700 | 0 | TEST_F(TestRedisService, TestTruncate) { |
1701 | 0 | const string default_db("0"); |
1702 | 0 | const string second_db("2"); |
1703 | |
|
1704 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v1"}); |
1705 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1706 | 0 | SyncClient(); |
1707 | | |
1708 | | // Create DB. |
1709 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1710 | | // Select should now go through. |
1711 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1712 | 0 | SyncClient(); |
1713 | | |
1714 | | // Set a diffferent value |
1715 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v2"}); |
1716 | | // Get that value |
1717 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1718 | 0 | SyncClient(); |
1719 | | |
1720 | | // Select the original db and get the value. |
1721 | 0 | DoRedisTestOk(__LINE__, {"SELECT", default_db.c_str()}); |
1722 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1723 | 0 | SyncClient(); |
1724 | | |
1725 | | // Flush the default_db |
1726 | 0 | DoRedisTestOk(__LINE__, {"FLUSHDB"}); |
1727 | | |
1728 | | // Get should be empty. |
1729 | 0 | DoRedisTestOk(__LINE__, {"SELECT", default_db.c_str()}); |
1730 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1731 | 0 | SyncClient(); |
1732 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1733 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1734 | 0 | SyncClient(); |
1735 | |
|
1736 | 0 | DoRedisTestOk(__LINE__, {"SELECT", default_db.c_str()}); |
1737 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v1"}); |
1738 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1739 | 0 | SyncClient(); |
1740 | | |
1741 | | // Flush the default_db |
1742 | 0 | DoRedisTestOk(__LINE__, {"FLUSHALL"}); |
1743 | |
|
1744 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1745 | 0 | SyncClient(); |
1746 | |
|
1747 | 0 | DoRedisTestOk(__LINE__, {"SELECT", default_db.c_str()}); |
1748 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1749 | 0 | SyncClient(); |
1750 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1751 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1752 | 0 | SyncClient(); |
1753 | | |
1754 | | // List DB. |
1755 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1756 | 0 | SyncClient(); |
1757 | |
|
1758 | 0 | VerifyCallbacks(); |
1759 | 0 | } |
1760 | | |
1761 | 0 | TEST_F(TestRedisService, TestDeleteDB) { |
1762 | 0 | const string default_db("0"); |
1763 | 0 | const string second_db("2"); |
1764 | |
|
1765 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v1"}); |
1766 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v1"); |
1767 | 0 | SyncClient(); |
1768 | | |
1769 | | // Create DB. |
1770 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1771 | | // Select should now go through. |
1772 | 0 | DoRedisTestOk(__LINE__, {"SELECT", second_db.c_str()}); |
1773 | 0 | SyncClient(); |
1774 | | |
1775 | | // Set a diffferent value |
1776 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v2"}); |
1777 | | // Get that value |
1778 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1779 | 0 | SyncClient(); |
1780 | | |
1781 | | // Delete and recreate the DB. |
1782 | | // List DB. |
1783 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1784 | 0 | SyncClient(); |
1785 | 0 | DoRedisTestOk(__LINE__, {"DELETEDB", second_db.c_str()}); |
1786 | 0 | SyncClient(); |
1787 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db}); |
1788 | 0 | SyncClient(); |
1789 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1790 | 0 | SyncClient(); |
1791 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1792 | 0 | SyncClient(); |
1793 | | // With retries we should succeed immediately. |
1794 | 0 | DoRedisTestNull(__LINE__, {"GET", "key"}); |
1795 | 0 | SyncClient(); |
1796 | | // Set a diffferent value |
1797 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v2"}); |
1798 | 0 | SyncClient(); |
1799 | | // Get that value |
1800 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v2"); |
1801 | 0 | SyncClient(); |
1802 | | |
1803 | | // Delete and recreate the DB. Followed by a write. |
1804 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1805 | 0 | SyncClient(); |
1806 | 0 | DoRedisTestOk(__LINE__, {"DELETEDB", second_db.c_str()}); |
1807 | 0 | SyncClient(); |
1808 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db}); |
1809 | 0 | SyncClient(); |
1810 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1811 | 0 | SyncClient(); |
1812 | | // Set a value |
1813 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "v3"}); |
1814 | 0 | SyncClient(); |
1815 | | // Get that value |
1816 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "v3"); |
1817 | 0 | SyncClient(); |
1818 | | |
1819 | | // Delete and recreate the DB. Followed by a local op. |
1820 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db, second_db}); |
1821 | 0 | SyncClient(); |
1822 | 0 | DoRedisTestOk(__LINE__, {"DELETEDB", second_db.c_str()}); |
1823 | 0 | SyncClient(); |
1824 | 0 | DoRedisTestArray(__LINE__, {"LISTDB"}, {default_db}); |
1825 | 0 | SyncClient(); |
1826 | 0 | DoRedisTestOk(__LINE__, {"CREATEDB", second_db.c_str()}); |
1827 | 0 | SyncClient(); |
1828 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "cmd2"}, "cmd2"); |
1829 | 0 | SyncClient(); |
1830 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "cmd2"}, "cmd2"); |
1831 | 0 | SyncClient(); |
1832 | |
|
1833 | 0 | VerifyCallbacks(); |
1834 | 0 | } |
1835 | | |
1836 | 0 | TEST_F(TestRedisService, TestMonitor) { |
1837 | 0 | constexpr uint32 kDelayMs = NonTsanVsTsan(100, 1000); |
1838 | 0 | expected_no_sessions_ = true; |
1839 | 0 | auto rc1 = CreateClient(); |
1840 | 0 | auto rc2 = CreateClient(); |
1841 | 0 | auto mc1 = CreateClient(); |
1842 | 0 | auto mc2 = CreateClient(); |
1843 | |
|
1844 | 0 | UseClient(rc1); |
1845 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "cmd1"}, "cmd1"); // Excluded from both mc1 and mc2. |
1846 | 0 | SyncClient(); |
1847 | | |
1848 | | // Check number of monitoring clients. |
1849 | 0 | ASSERT_EQ(0, CountSessions(METRIC_redis_monitoring_clients)); |
1850 | |
|
1851 | 0 | UseClient(mc1); |
1852 | 0 | DoRedisTestOk(__LINE__, {"MONITOR"}); |
1853 | 0 | SyncClient(); |
1854 | | |
1855 | | // Wait for the server to realize that the connection is closed. |
1856 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1857 | | // Check number of monitoring clients. |
1858 | 0 | ASSERT_EQ(1, CountSessions(METRIC_redis_monitoring_clients)); |
1859 | |
|
1860 | 0 | UseClient(rc2); |
1861 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "cmd2"}, "cmd2"); // Included in mc1. |
1862 | 0 | SyncClient(); |
1863 | |
|
1864 | 0 | UseClient(mc2); |
1865 | 0 | DoRedisTestOk(__LINE__, {"MONITOR"}); |
1866 | 0 | SyncClient(); |
1867 | | |
1868 | | // Wait for the server to realize that the connection is closed. |
1869 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1870 | | // Check number of monitoring clients. |
1871 | 0 | ASSERT_EQ(2, CountSessions(METRIC_redis_monitoring_clients)); |
1872 | |
|
1873 | 0 | UseClient(rc1); |
1874 | 0 | const string really_long(100, 'x'); |
1875 | 0 | const string response_ending = string("\"PING\" \"").append(really_long).append("\""); |
1876 | 0 | DoRedisTestBulkString(__LINE__, {"PING", really_long}, really_long); // Included in mc1 and mc2. |
1877 | 0 | SyncClient(); |
1878 | |
|
1879 | 0 | UseClient(mc1); |
1880 | | // Check the responses for monitor on mc1. |
1881 | | // Responses are of the format |
1882 | | // <TS> {<db-id> <client-ip>:<port>} "CMD" "ARG1" .... |
1883 | | // We will check for the responses to end in "CMD" "ARG1" |
1884 | 0 | DoRedisTestExpectSimpleStringEndingWith(__LINE__, {}, "\"PING\" \"cmd2\""); |
1885 | 0 | DoRedisTestExpectSimpleStringEndingWith(__LINE__, {}, response_ending); |
1886 | 0 | SyncClient(); |
1887 | |
|
1888 | 0 | UseClient(mc2); |
1889 | | // Check the responses for monitor on mc2. |
1890 | 0 | DoRedisTestExpectSimpleStringEndingWith(__LINE__, {}, response_ending); |
1891 | 0 | SyncClient(); |
1892 | | |
1893 | | // Check number of monitoring clients. |
1894 | 0 | ASSERT_EQ(2, CountSessions(METRIC_redis_monitoring_clients)); |
1895 | | |
1896 | | // Close one monitoring clients. |
1897 | 0 | mc1->Disconnect(); |
1898 | | // Wait for the server to realize that the connection is closed. |
1899 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1900 | | |
1901 | | // Check number of monitoring clients. |
1902 | 0 | UseClient(rc1); |
1903 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "test"}, "test"); |
1904 | 0 | SyncClient(); |
1905 | | |
1906 | | // Wait for the server to realize that the connection is closed. |
1907 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1908 | | // Check number of monitoring clients. |
1909 | 0 | ASSERT_EQ(1, CountSessions(METRIC_redis_monitoring_clients)); |
1910 | |
|
1911 | 0 | mc2->Disconnect(); |
1912 | | // Wait for the server to realize that the connection is closed. |
1913 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1914 | | |
1915 | | // Check number of monitoring clients. |
1916 | 0 | UseClient(rc1); |
1917 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "test"}, "test"); |
1918 | 0 | SyncClient(); |
1919 | | |
1920 | | // Wait for the server to realize that the connection is closed. |
1921 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kDelayMs)); |
1922 | |
|
1923 | 0 | ASSERT_EQ(0, CountSessions(METRIC_redis_monitoring_clients)); |
1924 | |
|
1925 | 0 | UseClient(nullptr); |
1926 | 0 | VerifyCallbacks(); |
1927 | 0 | } |
1928 | | |
1929 | | void TestSubscribe( |
1930 | | TestRedisService* tester, |
1931 | | std::shared_ptr<RedisClient> ps0, // Used for PubSub command |
1932 | | std::shared_ptr<RedisClient> sc1, // Used for Subscribe |
1933 | | std::shared_ptr<RedisClient> sc2, |
1934 | | std::shared_ptr<RedisClient> sc3, |
1935 | | std::shared_ptr<RedisClient> pc1, // Used for Publish |
1936 | 0 | std::shared_ptr<RedisClient> pc2) { |
1937 | 0 | const string topic1 = "topic1", topic2 = "topic2"; |
1938 | 0 | const string msg1 = "msg1", msg2 = "msg2"; |
1939 | |
|
1940 | 0 | if (ps0) { |
1941 | 0 | tester->UseClient(ps0); |
1942 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
1943 | 0 | tester->DoRedisTestResultsArray( |
1944 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
1945 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
1946 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
1947 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
1948 | 0 | tester->SyncClient(); |
1949 | 0 | } |
1950 | |
|
1951 | 0 | tester->UseClient(sc1); |
1952 | 0 | tester->DoRedisTestResultsArray( |
1953 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
1954 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
1955 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
1956 | 0 | tester->SyncClient(); |
1957 | |
|
1958 | 0 | if (ps0) { |
1959 | 0 | tester->UseClient(ps0); |
1960 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic1}); |
1961 | 0 | tester->DoRedisTestResultsArray( |
1962 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
1963 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(1), |
1964 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
1965 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
1966 | 0 | tester->SyncClient(); |
1967 | 0 | } |
1968 | |
|
1969 | 0 | tester->UseClient(sc2); |
1970 | 0 | tester->DoRedisTestResultsArray( |
1971 | 0 | __LINE__, {"SUBSCRIBE", topic2}, |
1972 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
1973 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(1)}); |
1974 | 0 | tester->SyncClient(); |
1975 | |
|
1976 | 0 | if (ps0) { |
1977 | 0 | tester->UseClient(ps0); |
1978 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic1, topic2}); |
1979 | 0 | tester->DoRedisTestResultsArray( |
1980 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
1981 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(1), |
1982 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(1)}); |
1983 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
1984 | 0 | tester->SyncClient(); |
1985 | 0 | } |
1986 | |
|
1987 | 0 | tester->UseClient(sc3); |
1988 | 0 | tester->DoRedisTestResultsArray( |
1989 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
1990 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
1991 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
1992 | 0 | tester->DoRedisTestResultsArray( |
1993 | 0 | __LINE__, {"SUBSCRIBE", topic2}, |
1994 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
1995 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
1996 | 0 | tester->SyncClient(); |
1997 | |
|
1998 | 0 | if (ps0) { |
1999 | 0 | tester->UseClient(ps0); |
2000 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic1, topic2}); |
2001 | 0 | tester->DoRedisTestResultsArray( |
2002 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2003 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(2), |
2004 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
2005 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2006 | 0 | tester->SyncClient(); |
2007 | 0 | } |
2008 | | |
2009 | | // Now send msg1 to topic 1. |
2010 | 0 | tester->UseClient(pc1); |
2011 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg1}, 2); |
2012 | 0 | tester->SyncClient(); |
2013 | | |
2014 | | // Now send msg2 to topic 2. |
2015 | 0 | tester->UseClient(pc2); |
2016 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg2}, 2); |
2017 | 0 | tester->SyncClient(); |
2018 | | |
2019 | | // Verify the received messages. |
2020 | 0 | tester->UseClient(sc1); |
2021 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic1, msg1}); |
2022 | 0 | tester->SyncClient(); |
2023 | |
|
2024 | 0 | tester->UseClient(sc2); |
2025 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic2, msg2}); |
2026 | | // No more messages to receive. |
2027 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2028 | 0 | tester->SyncClient(); |
2029 | |
|
2030 | 0 | tester->UseClient(sc3); |
2031 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic1, msg1}); |
2032 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic2, msg2}); |
2033 | | // No more messages to receive. |
2034 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2035 | 0 | tester->SyncClient(); |
2036 | |
|
2037 | 0 | tester->UseClient(nullptr); |
2038 | 0 | tester->VerifyCallbacks(); |
2039 | 0 | } |
2040 | | |
2041 | | void TestUnsubscribe( |
2042 | | TestRedisService* tester, |
2043 | | std::shared_ptr<RedisClient> ps0, // Used for PubSub command |
2044 | | std::shared_ptr<RedisClient> sc1, // Used for Subscribe |
2045 | | std::shared_ptr<RedisClient> sc2, |
2046 | | std::shared_ptr<RedisClient> sc3, |
2047 | | std::shared_ptr<RedisClient> pc1, // Used for Publish |
2048 | 0 | std::shared_ptr<RedisClient> pc2) { |
2049 | 0 | const string topic1 = "topic1", topic2 = "topic2"; |
2050 | 0 | const string msg1 = "msg1", msg2 = "msg2", msg3 = "msg3", msg4 = "msg4"; |
2051 | |
|
2052 | 0 | tester->UseClient(sc1); |
2053 | 0 | tester->DoRedisTestResultsArray( |
2054 | 0 | __LINE__, {"SUBSCRIBE", topic1, topic2}, |
2055 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2056 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2057 | 0 | tester->DoRedisTestResultsArray( |
2058 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "subscribe"), |
2059 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
2060 | 0 | tester->SyncClient(); |
2061 | |
|
2062 | 0 | tester->UseClient(sc2); |
2063 | 0 | tester->DoRedisTestResultsArray( |
2064 | 0 | __LINE__, {"SUBSCRIBE", topic1, topic2}, |
2065 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2066 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2067 | 0 | tester->DoRedisTestResultsArray( |
2068 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "subscribe"), |
2069 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
2070 | 0 | tester->SyncClient(); |
2071 | |
|
2072 | 0 | tester->UseClient(sc3); |
2073 | 0 | tester->DoRedisTestResultsArray( |
2074 | 0 | __LINE__, {"SUBSCRIBE", topic1, topic2}, |
2075 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2076 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2077 | 0 | tester->DoRedisTestResultsArray( |
2078 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "subscribe"), |
2079 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
2080 | 0 | tester->SyncClient(); |
2081 | |
|
2082 | 0 | if (ps0) { |
2083 | 0 | tester->UseClient(ps0); |
2084 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic1, topic2}); |
2085 | 0 | tester->DoRedisTestResultsArray( |
2086 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2087 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(3), |
2088 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(3)}); |
2089 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2090 | 0 | tester->SyncClient(); |
2091 | 0 | } |
2092 | | |
2093 | | // sc1 will unsubscribe from topic1. Will still be subscribed to topic2. |
2094 | 0 | tester->UseClient(sc1); |
2095 | 0 | tester->DoRedisTestResultsArray( |
2096 | 0 | __LINE__, {"UNSUBSCRIBE", topic1}, |
2097 | 0 | {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2098 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2099 | 0 | tester->SyncClient(); |
2100 | | |
2101 | | // sc2 will unsubscribe from all topics. Will still be subscribed to none. |
2102 | 0 | tester->UseClient(sc2); |
2103 | 0 | tester->DoRedisTestResultsArray( |
2104 | 0 | __LINE__, {"UNSUBSCRIBE", topic1, topic2}, |
2105 | 0 | {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2106 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2107 | 0 | tester->DoRedisTestResultsArray( |
2108 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2109 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2110 | 0 | tester->SyncClient(); |
2111 | |
|
2112 | 0 | if (ps0) { |
2113 | 0 | tester->UseClient(ps0); |
2114 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic1, topic2}); |
2115 | 0 | tester->DoRedisTestResultsArray( |
2116 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2117 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(1), |
2118 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(2)}); |
2119 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2120 | 0 | tester->SyncClient(); |
2121 | 0 | } |
2122 | | |
2123 | | // Now send msg1 to topic 1. |
2124 | 0 | tester->UseClient(pc1); |
2125 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg1}, 1); |
2126 | 0 | tester->SyncClient(); |
2127 | | |
2128 | | // Now send msg2 to topic 2. |
2129 | 0 | tester->UseClient(pc2); |
2130 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg2}, 2); |
2131 | 0 | tester->SyncClient(); |
2132 | | |
2133 | | // Verify the received messages. |
2134 | 0 | tester->UseClient(sc1); |
2135 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic2, msg2}); |
2136 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2137 | 0 | tester->SyncClient(); |
2138 | |
|
2139 | 0 | tester->UseClient(sc2); |
2140 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2141 | 0 | tester->SyncClient(); |
2142 | |
|
2143 | 0 | tester->UseClient(sc3); |
2144 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic1, msg1}); |
2145 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic2, msg2}); |
2146 | | // No more messages to receive. |
2147 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2148 | 0 | tester->SyncClient(); |
2149 | | |
2150 | | // sc3 will unsubscribe from all topics. sc1 will still be subscribed to topic2. |
2151 | 0 | tester->UseClient(sc3); |
2152 | | // Redis does not specify a particular order. So, the following two messages |
2153 | | // could be in received in either order. |
2154 | 0 | tester->DoRedisTestResultsArray( |
2155 | 0 | __LINE__, {"UNSUBSCRIBE"}, {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2156 | 0 | RedisReply(RedisReplyType::kString, "IGNORED"), RedisReply(1)}); |
2157 | 0 | tester->DoRedisTestResultsArray( |
2158 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2159 | 0 | RedisReply(RedisReplyType::kString, "IGNORED"), RedisReply(0)}); |
2160 | 0 | tester->SyncClient(); |
2161 | |
|
2162 | 0 | if (ps0) { |
2163 | 0 | tester->UseClient(ps0); |
2164 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {topic2}); |
2165 | 0 | tester->DoRedisTestResultsArray( |
2166 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2167 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2168 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(1)}); |
2169 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2170 | 0 | tester->SyncClient(); |
2171 | 0 | } |
2172 | | |
2173 | | // Now send msg3 to topic 2. |
2174 | 0 | tester->UseClient(pc2); |
2175 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg3}, 1); |
2176 | 0 | tester->SyncClient(); |
2177 | | |
2178 | | // No one should receive the message except sc1. |
2179 | 0 | tester->UseClient(sc1); |
2180 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"message", topic2, msg3}); |
2181 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2182 | 0 | tester->SyncClient(); |
2183 | 0 | tester->UseClient(sc2); |
2184 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2185 | 0 | tester->SyncClient(); |
2186 | 0 | tester->UseClient(sc3); |
2187 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2188 | 0 | tester->SyncClient(); |
2189 | | |
2190 | | // sc1 will unsubscribe from topic2. No one left subscribed to any topic. |
2191 | 0 | tester->UseClient(sc1); |
2192 | 0 | tester->DoRedisTestResultsArray( |
2193 | 0 | __LINE__, {"UNSUBSCRIBE"}, {RedisReply(RedisReplyType::kString, "unsubscribe"), |
2194 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2195 | 0 | tester->SyncClient(); |
2196 | |
|
2197 | 0 | if (ps0) { |
2198 | 0 | tester->UseClient(ps0); |
2199 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2200 | 0 | tester->DoRedisTestResultsArray( |
2201 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2202 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2203 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2204 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2205 | 0 | tester->SyncClient(); |
2206 | 0 | } |
2207 | | |
2208 | | // Now send msg4 to topic 2. |
2209 | 0 | tester->UseClient(pc2); |
2210 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg4}, 0); |
2211 | 0 | tester->SyncClient(); |
2212 | | |
2213 | | // No one should receive the message. |
2214 | 0 | tester->UseClient(sc1); |
2215 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2216 | 0 | tester->SyncClient(); |
2217 | 0 | tester->UseClient(sc2); |
2218 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2219 | 0 | tester->SyncClient(); |
2220 | 0 | tester->UseClient(sc3); |
2221 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2222 | 0 | tester->SyncClient(); |
2223 | |
|
2224 | 0 | tester->UseClient(nullptr); |
2225 | 0 | tester->VerifyCallbacks(); |
2226 | 0 | } |
2227 | | |
2228 | | void TestPSubscribe( |
2229 | | TestRedisService* tester, |
2230 | | std::shared_ptr<RedisClient> ps0, // Used for PubSub command |
2231 | | std::shared_ptr<RedisClient> sc1, // Used for Subscribe |
2232 | | std::shared_ptr<RedisClient> sc2, |
2233 | | std::shared_ptr<RedisClient> sc3, |
2234 | | std::shared_ptr<RedisClient> pc1, // Used for Publish |
2235 | 0 | std::shared_ptr<RedisClient> pc2) { |
2236 | 0 | const string pattern1 = "t*1", pattern2 = "t*2", common_pattern = "t*"; |
2237 | 0 | const string topic1 = "topic1", topic2 = "topic2"; |
2238 | 0 | const string msg1 = "msg1", msg2 = "msg2"; |
2239 | |
|
2240 | 0 | if (ps0) { |
2241 | 0 | tester->UseClient(ps0); |
2242 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2243 | 0 | tester->DoRedisTestResultsArray( |
2244 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2245 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2246 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2247 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2248 | 0 | tester->SyncClient(); |
2249 | 0 | } |
2250 | |
|
2251 | 0 | tester->UseClient(sc1); |
2252 | 0 | tester->DoRedisTestResultsArray( |
2253 | 0 | __LINE__, {"PSUBSCRIBE", pattern1}, |
2254 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2255 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2256 | 0 | tester->SyncClient(); |
2257 | |
|
2258 | 0 | if (ps0) { |
2259 | 0 | tester->UseClient(ps0); |
2260 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2261 | 0 | tester->DoRedisTestResultsArray( |
2262 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2263 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2264 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2265 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 1); |
2266 | 0 | tester->SyncClient(); |
2267 | 0 | } |
2268 | |
|
2269 | 0 | tester->UseClient(sc2); |
2270 | 0 | tester->DoRedisTestResultsArray( |
2271 | 0 | __LINE__, {"psubscribe", pattern2}, |
2272 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2273 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(1)}); |
2274 | 0 | tester->SyncClient(); |
2275 | |
|
2276 | 0 | if (ps0) { |
2277 | 0 | tester->UseClient(ps0); |
2278 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2279 | 0 | tester->DoRedisTestResultsArray( |
2280 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2281 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2282 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2283 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 2); |
2284 | 0 | tester->SyncClient(); |
2285 | 0 | } |
2286 | |
|
2287 | 0 | tester->UseClient(sc3); |
2288 | 0 | tester->DoRedisTestResultsArray( |
2289 | 0 | __LINE__, {"psubscribe", common_pattern}, |
2290 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2291 | 0 | RedisReply(RedisReplyType::kString, common_pattern), RedisReply(1)}); |
2292 | 0 | tester->SyncClient(); |
2293 | | |
2294 | | // Now send msg1 to pattern 1. |
2295 | 0 | tester->UseClient(pc1); |
2296 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg1}, 2); |
2297 | 0 | tester->SyncClient(); |
2298 | | |
2299 | | // Now send msg2 to pattern 2. |
2300 | 0 | tester->UseClient(pc2); |
2301 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg2}, 2); |
2302 | 0 | tester->SyncClient(); |
2303 | | |
2304 | | // Verify the received messages. |
2305 | 0 | tester->UseClient(sc1); |
2306 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern1, topic1, msg1}); |
2307 | 0 | tester->SyncClient(); |
2308 | |
|
2309 | 0 | tester->UseClient(sc2); |
2310 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern2, topic2, msg2}); |
2311 | | // No more messages to receive. |
2312 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2313 | 0 | tester->SyncClient(); |
2314 | |
|
2315 | 0 | tester->UseClient(sc3); |
2316 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", common_pattern, topic1, msg1}); |
2317 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", common_pattern, topic2, msg2}); |
2318 | | // No more messages to receive. |
2319 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2320 | 0 | tester->SyncClient(); |
2321 | |
|
2322 | 0 | tester->UseClient(nullptr); |
2323 | 0 | tester->VerifyCallbacks(); |
2324 | 0 | } |
2325 | | |
2326 | | void TestPUnsubscribe( |
2327 | | TestRedisService* tester, |
2328 | | std::shared_ptr<RedisClient> ps0, // Used for PubSub command |
2329 | | std::shared_ptr<RedisClient> sc1, // Used for Subscribe |
2330 | | std::shared_ptr<RedisClient> sc2, |
2331 | | std::shared_ptr<RedisClient> sc3, |
2332 | | std::shared_ptr<RedisClient> pc1, // Used for Publish |
2333 | 0 | std::shared_ptr<RedisClient> pc2) { |
2334 | 0 | const string pattern1 = "to*1", pattern2 = "to*2"; |
2335 | 0 | const string topic1 = "topic1", topic2 = "topic2"; |
2336 | 0 | const string msg1 = "msg1", msg2 = "msg2", msg3 = "msg3"; |
2337 | |
|
2338 | 0 | if (ps0) { |
2339 | 0 | tester->UseClient(ps0); |
2340 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2341 | 0 | tester->DoRedisTestResultsArray( |
2342 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2343 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2344 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2345 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2346 | 0 | tester->SyncClient(); |
2347 | 0 | } |
2348 | |
|
2349 | 0 | tester->UseClient(sc1); |
2350 | 0 | tester->DoRedisTestResultsArray( |
2351 | 0 | __LINE__, {"psubscribe", pattern1, pattern2}, |
2352 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2353 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2354 | 0 | tester->DoRedisTestResultsArray( |
2355 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "psubscribe"), |
2356 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(2)}); |
2357 | 0 | tester->SyncClient(); |
2358 | |
|
2359 | 0 | if (ps0) { |
2360 | 0 | tester->UseClient(ps0); |
2361 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2362 | 0 | tester->DoRedisTestResultsArray( |
2363 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2364 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2365 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2366 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 2); |
2367 | 0 | tester->SyncClient(); |
2368 | 0 | } |
2369 | |
|
2370 | 0 | tester->UseClient(sc2); |
2371 | 0 | tester->DoRedisTestResultsArray( |
2372 | 0 | __LINE__, {"psubscribe", pattern1, pattern2}, |
2373 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2374 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2375 | 0 | tester->DoRedisTestResultsArray( |
2376 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "psubscribe"), |
2377 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(2)}); |
2378 | 0 | tester->SyncClient(); |
2379 | |
|
2380 | 0 | if (ps0) { |
2381 | 0 | tester->UseClient(ps0); |
2382 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2383 | 0 | tester->DoRedisTestResultsArray( |
2384 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2385 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2386 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2387 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 2); |
2388 | 0 | tester->SyncClient(); |
2389 | 0 | } |
2390 | |
|
2391 | 0 | tester->UseClient(sc3); |
2392 | 0 | tester->DoRedisTestResultsArray( |
2393 | 0 | __LINE__, {"psubscribe", pattern1, pattern2}, |
2394 | 0 | {RedisReply(RedisReplyType::kString, "psubscribe"), |
2395 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2396 | 0 | tester->DoRedisTestResultsArray( |
2397 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "psubscribe"), |
2398 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(2)}); |
2399 | 0 | tester->SyncClient(); |
2400 | |
|
2401 | 0 | if (ps0) { |
2402 | 0 | tester->UseClient(ps0); |
2403 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2404 | 0 | tester->DoRedisTestResultsArray( |
2405 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2406 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2407 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2408 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 2); |
2409 | 0 | tester->SyncClient(); |
2410 | 0 | } |
2411 | | |
2412 | | // sc1 will punsubscribe from pattern2. Will still be psubscribed to pattern1. |
2413 | 0 | tester->UseClient(sc1); |
2414 | 0 | tester->DoRedisTestResultsArray( |
2415 | 0 | __LINE__, {"punsubscribe", pattern1}, |
2416 | 0 | {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2417 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2418 | 0 | tester->SyncClient(); |
2419 | | |
2420 | | // sc2 will punsubscribe from all patterns. Will still be psubscribed to none. |
2421 | 0 | tester->UseClient(sc2); |
2422 | 0 | tester->DoRedisTestResultsArray( |
2423 | 0 | __LINE__, {"punsubscribe", pattern1, pattern2}, |
2424 | 0 | {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2425 | 0 | RedisReply(RedisReplyType::kString, pattern1), RedisReply(1)}); |
2426 | 0 | tester->DoRedisTestResultsArray( |
2427 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2428 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(0)}); |
2429 | 0 | tester->SyncClient(); |
2430 | | |
2431 | | // Now send msg1 to pattern 1. |
2432 | 0 | tester->UseClient(pc1); |
2433 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg1}, 1); |
2434 | 0 | tester->SyncClient(); |
2435 | | |
2436 | | // Now send msg2 to pattern 2. |
2437 | 0 | tester->UseClient(pc2); |
2438 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg2}, 2); |
2439 | 0 | tester->SyncClient(); |
2440 | | |
2441 | | // Verify the received pmessages. |
2442 | 0 | tester->UseClient(sc1); |
2443 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern2, topic2, msg2}); |
2444 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2445 | 0 | tester->SyncClient(); |
2446 | |
|
2447 | 0 | tester->UseClient(sc2); |
2448 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2449 | 0 | tester->SyncClient(); |
2450 | |
|
2451 | 0 | tester->UseClient(sc3); |
2452 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern1, topic1, msg1}); |
2453 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern2, topic2, msg2}); |
2454 | | // No more messages to receive. |
2455 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2456 | 0 | tester->SyncClient(); |
2457 | |
|
2458 | 0 | if (ps0) { |
2459 | 0 | tester->UseClient(ps0); |
2460 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2461 | 0 | tester->DoRedisTestResultsArray( |
2462 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2463 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2464 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2465 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 2); |
2466 | 0 | tester->SyncClient(); |
2467 | 0 | } |
2468 | | |
2469 | | // sc3 will punsubscribe from all patterns. |
2470 | 0 | tester->UseClient(sc3); |
2471 | | // Redis does not specify a particular order. So, the following two messages |
2472 | | // could be in received in either order. |
2473 | 0 | tester->DoRedisTestResultsArray( |
2474 | 0 | __LINE__, {"punsubscribe"}, {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2475 | 0 | RedisReply(RedisReplyType::kString, "IGNORED"), RedisReply(1)}); |
2476 | 0 | tester->DoRedisTestResultsArray( |
2477 | 0 | __LINE__, {}, {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2478 | 0 | RedisReply(RedisReplyType::kString, "IGNORED"), RedisReply(0)}); |
2479 | 0 | tester->SyncClient(); |
2480 | |
|
2481 | 0 | if (ps0) { |
2482 | 0 | tester->UseClient(ps0); |
2483 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2484 | 0 | tester->DoRedisTestResultsArray( |
2485 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2486 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2487 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2488 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 1); |
2489 | 0 | tester->SyncClient(); |
2490 | 0 | } |
2491 | | |
2492 | | // Now send msg3 to pattern 2. only sc1 receives it. |
2493 | 0 | tester->UseClient(pc2); |
2494 | 0 | tester->DoRedisTestInt(__LINE__, {"PUBLISH", topic2, msg3}, 1); |
2495 | 0 | tester->SyncClient(); |
2496 | | |
2497 | | // No one should receive the message. |
2498 | 0 | tester->UseClient(sc1); |
2499 | 0 | tester->DoRedisTestArray(__LINE__, {}, {"pmessage", pattern2, topic2, msg3}); |
2500 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2501 | 0 | tester->SyncClient(); |
2502 | 0 | tester->UseClient(sc2); |
2503 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2504 | 0 | tester->SyncClient(); |
2505 | 0 | tester->UseClient(sc3); |
2506 | 0 | tester->DoRedisTestArray(__LINE__, {"PING"}, {"pong", ""}); |
2507 | 0 | tester->SyncClient(); |
2508 | | |
2509 | | // Get sc1 to also punsubscribe from pattern 2. No one is subscribed to any patterns anymore. |
2510 | 0 | tester->UseClient(sc1); |
2511 | 0 | tester->DoRedisTestResultsArray( |
2512 | 0 | __LINE__, {"punsubscribe"}, {RedisReply(RedisReplyType::kString, "punsubscribe"), |
2513 | 0 | RedisReply(RedisReplyType::kString, pattern2), RedisReply(0)}); |
2514 | 0 | tester->SyncClient(); |
2515 | |
|
2516 | 0 | if (ps0) { |
2517 | 0 | tester->UseClient(ps0); |
2518 | 0 | tester->DoRedisTestArray(__LINE__, {"pubsub", "channels"}, {}); |
2519 | 0 | tester->DoRedisTestResultsArray( |
2520 | 0 | __LINE__, {"pubsub", "numsub", topic1, topic2}, |
2521 | 0 | {RedisReply(RedisReplyType::kString, topic1), RedisReply(0), |
2522 | 0 | RedisReply(RedisReplyType::kString, topic2), RedisReply(0)}); |
2523 | 0 | tester->DoRedisTestInt(__LINE__, {"pubsub", "numpat"}, 0); |
2524 | 0 | tester->SyncClient(); |
2525 | 0 | } |
2526 | |
|
2527 | 0 | tester->UseClient(nullptr); |
2528 | 0 | tester->VerifyCallbacks(); |
2529 | 0 | } |
2530 | | |
2531 | | // Utility for testing various combination(s). |
2532 | | YB_DEFINE_ENUM(SubOrUnsub, (kSubscribe)(kUnsubscribe)); |
2533 | | YB_DEFINE_ENUM(PatternOrChannel, (kChannel)(kPattern)); |
2534 | | YB_DEFINE_ENUM(LocalOrCluster, (kLocal)(kCluster)); |
2535 | | |
2536 | | class TestRedisServiceExternal : public TestRedisService { |
2537 | | protected: |
2538 | | void TestPubSub(LocalOrCluster ltype, SubOrUnsub stype, PatternOrChannel ptype); |
2539 | | |
2540 | 0 | void CustomizeExternalMiniCluster(ExternalMiniClusterOptions* opts) override { |
2541 | 0 | opts->extra_tserver_flags.push_back( |
2542 | 0 | "--redis_connection_soft_limit_grace_period_sec=" + |
2543 | 0 | AsString(static_cast<int>(kSoftLimitGracePeriod.ToSeconds()))); |
2544 | 0 | } |
2545 | | |
2546 | | static const MonoDelta kSoftLimitGracePeriod; |
2547 | | |
2548 | | private: |
2549 | 0 | Endpoint RedisProxyEndpoint() override { |
2550 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2551 | 0 | return Endpoint(IpAddress::from_string(ts0->bind_host()), ts0->redis_rpc_port()); |
2552 | 0 | } |
2553 | | |
2554 | 0 | bool use_external_mini_cluster() override { return true; } |
2555 | | }; |
2556 | | |
2557 | | const MonoDelta TestRedisServiceExternal::kSoftLimitGracePeriod = yb::NonTsanVsTsan(1s, 10s); |
2558 | | |
2559 | | void TestRedisServiceExternal::TestPubSub( |
2560 | 0 | LocalOrCluster ltype, SubOrUnsub stype, PatternOrChannel ptype) { |
2561 | 0 | std::shared_ptr<RedisClient> ps0, sc1, sc2, sc3, pc1, pc2; |
2562 | |
|
2563 | 0 | if (ltype == LocalOrCluster::kLocal) { |
2564 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2565 | 0 | auto host0 = ts0->bind_host(); |
2566 | 0 | auto port0 = ts0->redis_rpc_port(); |
2567 | |
|
2568 | 0 | sc1 = std::make_shared<RedisClient>(host0, port0); |
2569 | 0 | sc2 = std::make_shared<RedisClient>(host0, port0); |
2570 | 0 | sc3 = std::make_shared<RedisClient>(host0, port0); |
2571 | 0 | pc1 = std::make_shared<RedisClient>(host0, port0); |
2572 | 0 | pc2 = std::make_shared<RedisClient>(host0, port0); |
2573 | 0 | ps0 = std::make_shared<RedisClient>(host0, port0); |
2574 | 0 | } else { |
2575 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2576 | 0 | auto host0 = ts0->bind_host(); |
2577 | 0 | auto port0 = ts0->redis_rpc_port(); |
2578 | 0 | auto ts1 = external_mini_cluster()->tablet_server(1); |
2579 | 0 | auto host1 = ts1->bind_host(); |
2580 | 0 | auto port1 = ts1->redis_rpc_port(); |
2581 | |
|
2582 | 0 | sc1 = std::make_shared<RedisClient>(host0, port0); |
2583 | 0 | sc2 = std::make_shared<RedisClient>(host1, port1); |
2584 | 0 | sc3 = std::make_shared<RedisClient>(host0, port0); |
2585 | 0 | pc1 = std::make_shared<RedisClient>(host0, port0); |
2586 | 0 | pc2 = std::make_shared<RedisClient>(host1, port1); |
2587 | 0 | ps0 = nullptr; // Diabled. PubSub monitoring only queries the local proxy. |
2588 | 0 | } |
2589 | |
|
2590 | 0 | if (stype == SubOrUnsub::kSubscribe) { |
2591 | 0 | if (ptype == PatternOrChannel::kChannel) { |
2592 | 0 | TestSubscribe(this, ps0, sc1, sc2, sc3, pc1, pc2); |
2593 | 0 | } else { |
2594 | 0 | TestPSubscribe(this, ps0, sc1, sc2, sc3, pc1, pc2); |
2595 | 0 | } |
2596 | 0 | } else { |
2597 | 0 | if (ptype == PatternOrChannel::kChannel) { |
2598 | 0 | TestUnsubscribe(this, ps0, sc1, sc2, sc3, pc1, pc2); |
2599 | 0 | } else { |
2600 | 0 | TestPUnsubscribe(this, ps0, sc1, sc2, sc3, pc1, pc2); |
2601 | 0 | } |
2602 | 0 | } |
2603 | 0 | } |
2604 | | |
2605 | 0 | TEST_F(TestRedisServiceExternal, TestSubscribe) { |
2606 | 0 | expected_no_sessions_ = true; |
2607 | 0 | TestPubSub(LocalOrCluster::kLocal, SubOrUnsub::kSubscribe, PatternOrChannel::kChannel); |
2608 | 0 | } |
2609 | | |
2610 | 0 | TEST_F(TestRedisServiceExternal, TestSubscribeCluster) { |
2611 | 0 | expected_no_sessions_ = true; |
2612 | 0 | TestPubSub(LocalOrCluster::kCluster, SubOrUnsub::kSubscribe, PatternOrChannel::kChannel); |
2613 | 0 | } |
2614 | | |
2615 | 0 | TEST_F(TestRedisServiceExternal, TestUnsubscribe) { |
2616 | 0 | expected_no_sessions_ = true; |
2617 | 0 | TestPubSub(LocalOrCluster::kLocal, SubOrUnsub::kUnsubscribe, PatternOrChannel::kChannel); |
2618 | 0 | } |
2619 | | |
2620 | 0 | TEST_F(TestRedisServiceExternal, TestUnsubscribeCluster) { |
2621 | 0 | expected_no_sessions_ = true; |
2622 | 0 | TestPubSub(LocalOrCluster::kCluster, SubOrUnsub::kUnsubscribe, PatternOrChannel::kChannel); |
2623 | 0 | } |
2624 | | |
2625 | 0 | TEST_F(TestRedisServiceExternal, TestPSubscribe) { |
2626 | 0 | expected_no_sessions_ = true; |
2627 | 0 | TestPubSub(LocalOrCluster::kLocal, SubOrUnsub::kSubscribe, PatternOrChannel::kPattern); |
2628 | 0 | } |
2629 | | |
2630 | 0 | TEST_F(TestRedisServiceExternal, TestPSubscribeCluster) { |
2631 | 0 | expected_no_sessions_ = true; |
2632 | 0 | TestPubSub(LocalOrCluster::kCluster, SubOrUnsub::kSubscribe, PatternOrChannel::kPattern); |
2633 | 0 | } |
2634 | | |
2635 | 0 | TEST_F(TestRedisServiceExternal, TestPUnsubscribe) { |
2636 | 0 | expected_no_sessions_ = true; |
2637 | 0 | TestPubSub(LocalOrCluster::kLocal, SubOrUnsub::kUnsubscribe, PatternOrChannel::kPattern); |
2638 | 0 | } |
2639 | | |
2640 | 0 | TEST_F(TestRedisServiceExternal, TestPUnsubscribeCluster) { |
2641 | 0 | expected_no_sessions_ = true; |
2642 | 0 | TestPubSub(LocalOrCluster::kCluster, SubOrUnsub::kUnsubscribe, PatternOrChannel::kPattern); |
2643 | 0 | } |
2644 | | |
2645 | 0 | TEST_F(TestRedisServiceExternal, TestSlowSubscribersCatchingUp) { |
2646 | 0 | expected_no_sessions_ = true; |
2647 | |
|
2648 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2649 | 0 | auto host0 = ts0->bind_host(); |
2650 | 0 | auto port0 = ts0->redis_rpc_port(); |
2651 | |
|
2652 | 0 | auto sc1 = std::make_shared<RedisClient>(host0, port0); |
2653 | 0 | auto pc1 = std::make_shared<RedisClient>(host0, port0); |
2654 | |
|
2655 | 0 | const string topic1 = "topic1"; |
2656 | 0 | const string padding(1_MB, 'x'); |
2657 | |
|
2658 | 0 | UseClient(sc1); |
2659 | 0 | DoRedisTestResultsArray( |
2660 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
2661 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2662 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2663 | 0 | SyncClient(); |
2664 | |
|
2665 | 0 | constexpr int kNumLoops = 3; |
2666 | 0 | constexpr int kNumMsgs = 20; |
2667 | 0 | const auto kSoftLimitGracePeriodMinusDelta = |
2668 | 0 | MonoDelta::FromSeconds(kSoftLimitGracePeriod.ToSeconds() * 0.8); |
2669 | 0 | const auto kSoftLimitGracePeriodPlusDelta = |
2670 | 0 | MonoDelta::FromSeconds(kSoftLimitGracePeriod.ToSeconds() * 1.2); |
2671 | 0 | for (int loops = 0; loops < kNumLoops; loops++) { |
2672 | | // Write approx 20MB of data. More than the soft limit. But less than the hard limit. |
2673 | 0 | UseClient(pc1); |
2674 | 0 | for (int i = 0; i < kNumMsgs; i++) { |
2675 | | // Now send msg1 to topic 1. |
2676 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2677 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2678 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 1); |
2679 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2680 | 0 | } |
2681 | | |
2682 | | // Wait for less than what the server to enforce the soft limit. |
2683 | 0 | SleepFor(kSoftLimitGracePeriodMinusDelta); |
2684 | |
|
2685 | 0 | for (int i = 0; i < kNumMsgs; i++) { |
2686 | | // Verify the received messages. |
2687 | 0 | VLOG(2) << "Trial " << i << ". Receiving subscribed message"; |
2688 | 0 | UseClient(sc1); |
2689 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2690 | 0 | DoRedisTestArray(__LINE__, {}, {"message", topic1, msg}); |
2691 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2692 | 0 | } |
2693 | |
|
2694 | 0 | SleepFor(kSoftLimitGracePeriodPlusDelta); |
2695 | 0 | } |
2696 | |
|
2697 | 0 | const string big_padding(20_MB, 'x'); |
2698 | 0 | for (int loops = 0; loops < kNumLoops; loops++) { |
2699 | | // Write > soft limit sized data in one shot. |
2700 | 0 | UseClient(pc1); |
2701 | 0 | auto msg = Substitute("Big-$0", big_padding); |
2702 | 0 | VLOG(2) << loops << ". Publishing a big message of size " << msg.length(); |
2703 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 1); |
2704 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2705 | | |
2706 | | // Wait for less than what the server to enforce the soft limit. |
2707 | 0 | SleepFor(kSoftLimitGracePeriodMinusDelta); |
2708 | |
|
2709 | 0 | UseClient(sc1); |
2710 | 0 | DoRedisTestArray(__LINE__, {}, {"message", topic1, msg}); |
2711 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2712 | |
|
2713 | 0 | SleepFor(kSoftLimitGracePeriodPlusDelta); |
2714 | 0 | } |
2715 | 0 | } |
2716 | | |
2717 | 0 | TEST_F(TestRedisServiceExternal, TestSlowSubscribersSoftLimit) { |
2718 | 0 | expected_no_sessions_ = true; |
2719 | |
|
2720 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2721 | 0 | auto host0 = ts0->bind_host(); |
2722 | 0 | auto port0 = ts0->redis_rpc_port(); |
2723 | |
|
2724 | 0 | auto sc1 = std::make_shared<RedisClient>(host0, port0); |
2725 | 0 | auto pc1 = std::make_shared<RedisClient>(host0, port0); |
2726 | |
|
2727 | 0 | const string topic1 = "topic1"; |
2728 | 0 | const string padding(1_MB, 'x'); |
2729 | |
|
2730 | 0 | UseClient(sc1); |
2731 | 0 | DoRedisTestResultsArray( |
2732 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
2733 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2734 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2735 | 0 | SyncClient(); |
2736 | |
|
2737 | 0 | UseClient(pc1); |
2738 | | // Write approx 15MB of data. Something more than the soft limit. |
2739 | 0 | for (int i = 0; i < 15; i++) { |
2740 | | // Now send msg1 to topic 1. |
2741 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2742 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2743 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 1); |
2744 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2745 | 0 | } |
2746 | | |
2747 | | // Wait for the server to enforce the soft limit. |
2748 | 0 | SleepFor(kSoftLimitGracePeriod); |
2749 | |
|
2750 | 0 | DoRedisTestApproxInt(__LINE__, {"PUBLISH", topic1, "whatever"}, 1, 1); |
2751 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2752 | 0 | for (int i = 15; i < 30; i++) { |
2753 | | // Now send msg1 to topic 1. |
2754 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2755 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2756 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 0); |
2757 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2758 | 0 | } |
2759 | | |
2760 | | // sc1 should have already been disconnected. |
2761 | 0 | UseClient(sc1); |
2762 | 0 | DoRedisTestExpectError(__LINE__, {}); |
2763 | 0 | } |
2764 | | |
2765 | 0 | TEST_F(TestRedisServiceExternal, TestSlowSubscribersHardLimit) { |
2766 | 0 | expected_no_sessions_ = true; |
2767 | |
|
2768 | 0 | auto ts0 = external_mini_cluster()->tablet_server(0); |
2769 | 0 | auto host0 = ts0->bind_host(); |
2770 | 0 | auto port0 = ts0->redis_rpc_port(); |
2771 | |
|
2772 | 0 | auto sc1 = std::make_shared<RedisClient>(host0, port0); |
2773 | 0 | auto pc1 = std::make_shared<RedisClient>(host0, port0); |
2774 | |
|
2775 | 0 | const string topic1 = "topic1"; |
2776 | 0 | const string padding(1_MB, 'x'); |
2777 | |
|
2778 | 0 | UseClient(sc1); |
2779 | 0 | DoRedisTestResultsArray( |
2780 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
2781 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2782 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2783 | 0 | SyncClient(); |
2784 | |
|
2785 | 0 | UseClient(pc1); |
2786 | | // Write approx 32MB of data. |
2787 | 0 | for (int i = 0; i < 32; i++) { |
2788 | | // Now send msg1 to topic 1. |
2789 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2790 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2791 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 1); |
2792 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2793 | 0 | } |
2794 | | |
2795 | | // Let's allow for some msgs to be either sent to the subscriber or unsent, to account for |
2796 | | // buffering in the lower layers. |
2797 | 0 | for (int i = 32; i < 40; i++) { |
2798 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2799 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2800 | 0 | DoRedisTestApproxInt(__LINE__, {"PUBLISH", topic1, msg}, 1, 1); |
2801 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2802 | 0 | } |
2803 | | |
2804 | | // The slow subscriber should have been disconnected. Expect the msg to be sent to no one. |
2805 | 0 | for (int i = 40; i < 50; i++) { |
2806 | | // Now send msg1 to topic 1. |
2807 | 0 | auto msg = Substitute("trial-$0 : $1", i, padding); |
2808 | 0 | VLOG(2) << "Trial " << i << ". Publishing message of size " << msg.length() << " bytes"; |
2809 | 0 | DoRedisTestInt(__LINE__, {"PUBLISH", topic1, msg}, 0); |
2810 | 0 | ASSERT_NO_FATALS(SyncClient()); |
2811 | 0 | } |
2812 | |
|
2813 | 0 | UseClient(sc1); |
2814 | | // sc1 should have already been disconnected. |
2815 | 0 | DoRedisTestExpectError(__LINE__, {}); |
2816 | 0 | } |
2817 | | |
2818 | 0 | TEST_F(TestRedisServiceExternal, SubscribedClientMode) { |
2819 | 0 | expected_no_sessions_ = true; |
2820 | 0 | const string topic1 = "topic1"; |
2821 | 0 | const string value = "value"; |
2822 | |
|
2823 | 0 | DoRedisTestSimpleString(__LINE__, {"PING"}, "PONG"); |
2824 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "cmd2"}, "cmd2"); |
2825 | 0 | SyncClient(); |
2826 | |
|
2827 | 0 | DoRedisTestResultsArray( |
2828 | 0 | __LINE__, {"SUBSCRIBE", topic1}, |
2829 | 0 | {RedisReply(RedisReplyType::kString, "subscribe"), |
2830 | 0 | RedisReply(RedisReplyType::kString, topic1), RedisReply(1)}); |
2831 | 0 | SyncClient(); |
2832 | |
|
2833 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "foo", value}); |
2834 | 0 | SyncClient(); |
2835 | |
|
2836 | 0 | DoRedisTestArray(__LINE__, {"PING", "cmd2"}, {"pong", "cmd2"}); |
2837 | 0 | SyncClient(); |
2838 | |
|
2839 | 0 | DoRedisTestOk(__LINE__, {"QUIT"}); |
2840 | 0 | SyncClient(); |
2841 | 0 | } |
2842 | | |
2843 | 0 | TEST_F(TestRedisService, TestAuth) { |
2844 | 0 | FLAGS_redis_password_caching_duration_ms = 0; |
2845 | 0 | const char* kRedisAuthPassword = "redis-password"; |
2846 | | // Expect new connections to require authentication |
2847 | 0 | auto rc1 = CreateClient(); |
2848 | 0 | auto rc2 = CreateClient(); |
2849 | 0 | UseClient(rc1); |
2850 | 0 | DoRedisTestSimpleString(__LINE__, {"PING"}, "PONG"); |
2851 | 0 | SyncClient(); |
2852 | 0 | UseClient(rc2); |
2853 | 0 | DoRedisTestSimpleString(__LINE__, {"PING"}, "PONG"); |
2854 | 0 | SyncClient(); |
2855 | | |
2856 | | // Set require pass using one connection |
2857 | 0 | UseClient(rc1); |
2858 | 0 | DoRedisTestOk(__LINE__, {"CONFIG", "SET", "REQUIREPASS", kRedisAuthPassword}); |
2859 | 0 | DoRedisTestArray(__LINE__, {"CONFIG", "GET", "REQUIREPASS"}, {}); |
2860 | 0 | DoRedisTestArray(__LINE__, {"CONFIG", "GET", "FooBar"}, {}); |
2861 | 0 | SyncClient(); |
2862 | 0 | UseClient(nullptr); |
2863 | | // Other pre-established connections should still be able to work, without re-authentication. |
2864 | 0 | UseClient(rc2); |
2865 | 0 | DoRedisTestSimpleString(__LINE__, {"PING"}, "PONG"); |
2866 | 0 | SyncClient(); |
2867 | | |
2868 | | // Ensure that new connections need the correct password to authenticate. |
2869 | 0 | ConnectWithPassword(this, nullptr, false, false); |
2870 | 0 | ConnectWithPassword(this, "wrong-password", false, false); |
2871 | 0 | ConnectWithPassword(this, kRedisAuthPassword, true, true); |
2872 | | |
2873 | | // Set multiple passwords. |
2874 | 0 | UseClient(rc1); |
2875 | 0 | DoRedisTestOk(__LINE__, {"CONFIG", "SET", "REQUIREPASS", "passwordA,passwordB"}); |
2876 | 0 | SyncClient(); |
2877 | 0 | UseClient(nullptr); |
2878 | |
|
2879 | 0 | ConnectWithPassword(this, nullptr, false, false); |
2880 | 0 | ConnectWithPassword(this, "wrong-password", false, false); |
2881 | | // Old password should no longer work. |
2882 | 0 | ConnectWithPassword(this, kRedisAuthPassword, false, false); |
2883 | 0 | ConnectWithPassword(this, "passwordA", true, true); |
2884 | 0 | ConnectWithPassword(this, "passwordB", true, true); |
2885 | 0 | ConnectWithPassword(this, "passwordC", false, false); |
2886 | | // Need to provide one. Not both while authenticating. |
2887 | 0 | ConnectWithPassword(this, "passwordA,passwordB", false, false); |
2888 | | |
2889 | | // Setting more than 2 passwords should fail. |
2890 | 0 | UseClient(rc1); |
2891 | 0 | DoRedisTestExpectError( |
2892 | 0 | __LINE__, {"CONFIG", "SET", "REQUIREPASS", "passwordA,passwordB,passwordC"}); |
2893 | 0 | SyncClient(); |
2894 | | |
2895 | | // Now set no password. |
2896 | 0 | DoRedisTestOk(__LINE__, {"CONFIG", "SET", "REQUIREPASS", ""}); |
2897 | 0 | SyncClient(); |
2898 | 0 | UseClient(nullptr); |
2899 | | |
2900 | | // Setting wrong/old password(s) should fail. But set/get commands after that should succeed |
2901 | | // regardless. |
2902 | 0 | ConnectWithPassword(this, "wrong-password", false, true); |
2903 | 0 | ConnectWithPassword(this, kRedisAuthPassword, false, true); |
2904 | 0 | ConnectWithPassword(this, "passwordA", false, true); |
2905 | 0 | ConnectWithPassword(this, "passwordB", false, true); |
2906 | 0 | ConnectWithPassword(this, nullptr, true, true); |
2907 | |
|
2908 | 0 | VerifyCallbacks(); |
2909 | 0 | } |
2910 | | |
2911 | 0 | TEST_F(TestRedisService, TestPasswordChangeWithDelay) { |
2912 | 0 | constexpr uint32 kCachingDurationMs = 1000; |
2913 | 0 | FLAGS_redis_password_caching_duration_ms = kCachingDurationMs; |
2914 | 0 | const char* kRedisAuthPassword = "redis-password"; |
2915 | 0 | auto start = std::chrono::steady_clock::now(); |
2916 | 0 | auto rc1 = CreateClient(); |
2917 | |
|
2918 | 0 | UseClient(rc1); |
2919 | 0 | DoRedisTestOk(__LINE__, {"CONFIG", "SET", "REQUIREPASS", kRedisAuthPassword}); |
2920 | 0 | SyncClient(); |
2921 | 0 | UseClient(nullptr); |
2922 | | |
2923 | | // Proxy may not realize the password change immediately. |
2924 | | // Expect the old password to work only if we haven't taken too long to get here. |
2925 | 0 | const std::chrono::milliseconds kNotTooLong(kCachingDurationMs / 2); |
2926 | 0 | auto now = std::chrono::steady_clock::now(); |
2927 | 0 | if (std::chrono::duration_cast<std::chrono::milliseconds>(now - start) < kNotTooLong) { |
2928 | 0 | ConnectWithPassword(this, nullptr, true, true); |
2929 | 0 | ConnectWithPassword(this, kRedisAuthPassword, false, true); |
2930 | 0 | } |
2931 | | |
2932 | | // Wait for the cached redis credentials in the redis proxy to expire. |
2933 | 0 | constexpr uint32 kDelayMs = 100; |
2934 | 0 | std::this_thread::sleep_for(std::chrono::milliseconds(kCachingDurationMs + kDelayMs)); |
2935 | | |
2936 | | // Expect the proxy to realize the effect of the password change. |
2937 | 0 | ConnectWithPassword(this, nullptr, false, false); |
2938 | 0 | ConnectWithPassword(this, kRedisAuthPassword, true, true); |
2939 | |
|
2940 | 0 | VerifyCallbacks(); |
2941 | 0 | } |
2942 | | |
2943 | 0 | TEST_F(TestRedisService, TestRename) { |
2944 | 0 | DoRedisTestOk(__LINE__, {"SET", "k1", "5"}); |
2945 | 0 | SyncClient(); |
2946 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", "k1", "100"}, 1); |
2947 | 0 | SyncClient(); |
2948 | |
|
2949 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "5"); |
2950 | 0 | DoRedisTestNull(__LINE__, {"GET", "k2"}); |
2951 | 0 | SyncClient(); |
2952 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k1"}, 100, 5); |
2953 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k2"}, -2); |
2954 | 0 | SyncClient(); |
2955 | |
|
2956 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k1", "k2"}); |
2957 | 0 | SyncClient(); |
2958 | |
|
2959 | 0 | DoRedisTestNull(__LINE__, {"GET", "k1"}); |
2960 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k2"}, "5"); |
2961 | 0 | SyncClient(); |
2962 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k2"}, 100, 5); |
2963 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k1"}, -2); |
2964 | 0 | SyncClient(); |
2965 | | |
2966 | | // Degenerate case src == dest. |
2967 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k2", "k2"}); |
2968 | 0 | SyncClient(); |
2969 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k2"}, "5"); |
2970 | 0 | SyncClient(); |
2971 | | |
2972 | | // Failure cases. |
2973 | 0 | DoRedisTestExpectError(__LINE__, {"RENAME", "non-existent", "k2"}); |
2974 | 0 | SyncClient(); |
2975 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k2"}, "5"); |
2976 | 0 | SyncClient(); |
2977 | 0 | } |
2978 | | |
2979 | 0 | TEST_F(TestRedisService, TestRenameSameTablet) { |
2980 | | // Rename to a key in the same tablet |
2981 | | // specify prefix to ensure that the keys are on the same tablet. |
2982 | 0 | DoRedisTestOk(__LINE__, {"SET", "{k}0", "5"}); |
2983 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "{k}0", "{k}xxxxxx"}); |
2984 | 0 | SyncClient(); |
2985 | 0 | DoRedisTestNull(__LINE__, {"GET", "{k}0"}); |
2986 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "{k}xxxxxx"}, "5"); |
2987 | 0 | SyncClient(); |
2988 | 0 | } |
2989 | | |
2990 | 0 | TEST_F(TestRedisService, TestRenameSameTabletRandomized) { |
2991 | | // Rename to a key in the same tablet |
2992 | | // randomized 1/24 odds of being in the same tablet as k0. |
2993 | 0 | for (int i = 1; i < 100; i++) { |
2994 | 0 | const string dest = strings::Substitute("k$0", i); |
2995 | 0 | VLOG(1) << "Renaming from k0 to " << dest; |
2996 | 0 | DoRedisTestOk(__LINE__, {"SET", "k0", "5"}); |
2997 | 0 | SyncClient(); |
2998 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k0", dest}); |
2999 | 0 | SyncClient(); |
3000 | 0 | DoRedisTestNull(__LINE__, {"GET", "k0"}); |
3001 | 0 | DoRedisTestBulkString(__LINE__, {"GET", dest}, "5"); |
3002 | 0 | SyncClient(); |
3003 | 0 | } |
3004 | 0 | } |
3005 | | |
3006 | 0 | TEST_F(TestRedisService, TestRenamePipeline) { |
3007 | | // Pipeline case. |
3008 | 0 | DoRedisTestOk(__LINE__, {"SET", "ka", "4"}); |
3009 | 0 | SyncClient(); |
3010 | |
|
3011 | 0 | DoRedisTestOk(__LINE__, {"SET", "ka", "5"}); |
3012 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "ka", "kb"}); |
3013 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "kb"}, "5"); |
3014 | 0 | DoRedisTestNull(__LINE__, {"GET", "ka"}); |
3015 | 0 | SyncClient(); |
3016 | 0 | } |
3017 | | |
3018 | 0 | TEST_F(TestRedisService, TestRenameHash) { |
3019 | 0 | DoRedisTestInt(__LINE__, {"HSET", "k1", "s1", "5"}, 1); |
3020 | 0 | DoRedisTestInt(__LINE__, {"HSET", "k1", "s2", "6"}, 1); |
3021 | 0 | SyncClient(); |
3022 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", "k1", "100"}, 1); |
3023 | |
|
3024 | 0 | SyncClient(); |
3025 | 0 | DoRedisTestOk(__LINE__, {"SET", "k2", "x"}); |
3026 | 0 | SyncClient(); |
3027 | |
|
3028 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "k1", "s1"}, "5"); |
3029 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "k1", "s2"}, "6"); |
3030 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k2"}, "x"); |
3031 | 0 | SyncClient(); |
3032 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k1"}, 100, 5); |
3033 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k2"}, -1); |
3034 | 0 | SyncClient(); |
3035 | |
|
3036 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k1", "k2"}); |
3037 | 0 | SyncClient(); |
3038 | |
|
3039 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "k2", "s1"}, "5"); |
3040 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "k2", "s2"}, "6"); |
3041 | 0 | DoRedisTestNull(__LINE__, {"GET", "k1"}); |
3042 | 0 | DoRedisTestNull(__LINE__, {"HGET", "k1", "s1"}); |
3043 | 0 | SyncClient(); |
3044 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k2"}, 100, 5); |
3045 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k1"}, -2); |
3046 | 0 | SyncClient(); |
3047 | 0 | } |
3048 | | |
3049 | 0 | TEST_F(TestRedisService, TestRenameSet) { |
3050 | 0 | DoRedisTestInt(__LINE__, {"SADD", "k1", "s1", "s2"}, 2); |
3051 | 0 | SyncClient(); |
3052 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", "k1", "100"}, 1); |
3053 | 0 | SyncClient(); |
3054 | |
|
3055 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "k1"}, {"s1", "s2"}); |
3056 | 0 | DoRedisTestNull(__LINE__, {"GET", "k2"}); |
3057 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "k2"}, {}); |
3058 | 0 | SyncClient(); |
3059 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k1"}, 100, 5); |
3060 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k2"}, -2); |
3061 | 0 | SyncClient(); |
3062 | |
|
3063 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k1", "k2"}); |
3064 | 0 | SyncClient(); |
3065 | |
|
3066 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "k2"}, {"s1", "s2"}); |
3067 | 0 | DoRedisTestNull(__LINE__, {"GET", "k1"}); |
3068 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "k1"}, {}); |
3069 | 0 | SyncClient(); |
3070 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k2"}, 100, 5); |
3071 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k1"}, -2); |
3072 | 0 | SyncClient(); |
3073 | 0 | } |
3074 | | |
3075 | 0 | TEST_F(TestRedisService, TestRenameSortedSet) { |
3076 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "k1", "-2", "sk1", "2", "sk2"}, 2); |
3077 | 0 | SyncClient(); |
3078 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", "k1", "100"}, 1); |
3079 | 0 | SyncClient(); |
3080 | |
|
3081 | 0 | DoRedisTestScoreValueArray( |
3082 | 0 | __LINE__, {"ZRANGEBYSCORE", "k1", "-inf", "+inf", "WITHSCORES"}, {-2, 2}, {"sk1", "sk2"}); |
3083 | 0 | DoRedisTestNull(__LINE__, {"GET", "k2"}); |
3084 | 0 | SyncClient(); |
3085 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k1"}, 100, 5); |
3086 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k2"}, -2); |
3087 | 0 | SyncClient(); |
3088 | |
|
3089 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k1", "k2"}); |
3090 | 0 | SyncClient(); |
3091 | |
|
3092 | 0 | DoRedisTestScoreValueArray( |
3093 | 0 | __LINE__, {"ZRANGEBYSCORE", "k2", "-inf", "+inf", "WITHSCORES"}, {-2, 2}, {"sk1", "sk2"}); |
3094 | 0 | DoRedisTestNull(__LINE__, {"GET", "k1"}); |
3095 | 0 | SyncClient(); |
3096 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", "k2"}, 100, 5); |
3097 | 0 | DoRedisTestInt(__LINE__, {"TTL", "k1"}, -2); |
3098 | 0 | SyncClient(); |
3099 | 0 | } |
3100 | | |
3101 | 0 | TEST_F(TestRedisService, TestRenameTSType) { |
3102 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "k1", "2", "sk1", "-2", "sk2"}); |
3103 | 0 | SyncClient(); |
3104 | |
|
3105 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "k1", "-inf", "+inf"}, {"-2", "sk2", "2", "sk1"}); |
3106 | 0 | DoRedisTestNull(__LINE__, {"GET", "k2"}); |
3107 | 0 | SyncClient(); |
3108 | |
|
3109 | 0 | DoRedisTestOk(__LINE__, {"RENAME", "k1", "k2"}); |
3110 | 0 | SyncClient(); |
3111 | |
|
3112 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "k2", "-inf", "+inf"}, {"-2", "sk2", "2", "sk1"}); |
3113 | 0 | DoRedisTestNull(__LINE__, {"GET", "k1"}); |
3114 | 0 | SyncClient(); |
3115 | 0 | } |
3116 | | |
3117 | 0 | TEST_F(TestRedisService, TestIncr) { |
3118 | 0 | DoRedisTestOk(__LINE__, {"SET", "k1", "5"}); |
3119 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "5"); |
3120 | |
|
3121 | 0 | DoRedisTestInt(__LINE__, {"INCR", "k1"}, 6); |
3122 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "6"); |
3123 | |
|
3124 | 0 | DoRedisTestInt(__LINE__, {"INCRBY", "k1", "4"}, 10); |
3125 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "10"); |
3126 | |
|
3127 | 0 | DoRedisTestInt(__LINE__, {"INCRBY", "k1", "-5"}, 5); |
3128 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "5"); |
3129 | |
|
3130 | 0 | DoRedisTestNull(__LINE__, {"GET", "kne1"}); |
3131 | 0 | DoRedisTestInt(__LINE__, {"INCR", "kne1"}, 1); |
3132 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "kne1"}, "1"); |
3133 | |
|
3134 | 0 | DoRedisTestNull(__LINE__, {"GET", "kne2"}); |
3135 | 0 | DoRedisTestInt(__LINE__, {"INCRBY", "kne2", "5"}, 5); |
3136 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "kne2"}, "5"); |
3137 | 0 | SyncClient(); |
3138 | |
|
3139 | 0 | DoRedisTestInt(__LINE__, {"HSET", "h1", "f1", "5"}, 1); |
3140 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "f1"}, "5"); |
3141 | 0 | SyncClient(); |
3142 | |
|
3143 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "h1", "f1", "1"}, 6); |
3144 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "f1"}, "6"); |
3145 | 0 | SyncClient(); |
3146 | |
|
3147 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "h1", "f1", "4"}, 10); |
3148 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "f1"}, "10"); |
3149 | 0 | SyncClient(); |
3150 | |
|
3151 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "h1", "f1", "-5"}, 5); |
3152 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "f1"}, "5"); |
3153 | |
|
3154 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "h1", "fne", "6"}, 6); |
3155 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "fne"}, "6"); |
3156 | 0 | SyncClient(); |
3157 | |
|
3158 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "hstr", "fstr", "5"}, 5); |
3159 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "hstr", "fstr"}, "5"); |
3160 | 0 | SyncClient(); |
3161 | |
|
3162 | 0 | DoRedisTestNull(__LINE__, {"GET", "hne1"}); |
3163 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "hne1", "fne", "6"}, 6); |
3164 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "hne1", "fne"}, "6"); |
3165 | |
|
3166 | 0 | DoRedisTestInt(__LINE__, {"HINCRBY", "hne1", "fne", "-16"}, -10); |
3167 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "hne1", "fne"}, "-10"); |
3168 | |
|
3169 | 0 | SyncClient(); |
3170 | 0 | LOG(INFO) << "Done with the test"; |
3171 | 0 | VerifyCallbacks(); |
3172 | 0 | } |
3173 | | |
3174 | 0 | TEST_F(TestRedisService, TestKeysPipeline) { |
3175 | 0 | DoRedisTestOk(__LINE__, {"SET", "xa", "5"}); |
3176 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*"}, {"xa"}); |
3177 | 0 | DoRedisTestNull(__LINE__, {"GET", "xb"}); |
3178 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "xa"}, "5"); |
3179 | 0 | SyncClient(); |
3180 | 0 | VerifyCallbacks(); |
3181 | 0 | } |
3182 | | |
3183 | 0 | TEST_F(TestRedisService, TestIncrCorner) { |
3184 | 0 | DoRedisTestOk(__LINE__, {"SET", "kstr", "str"}); |
3185 | 0 | SyncClient(); |
3186 | |
|
3187 | 0 | DoRedisTestExpectError(__LINE__, {"INCR", "kstr"}, "ERR"); |
3188 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "kstr"}, "str"); |
3189 | 0 | DoRedisTestExpectError(__LINE__, {"INCRBY", "kstr", "5"}, "ERR"); |
3190 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "kstr"}, "str"); |
3191 | 0 | SyncClient(); |
3192 | |
|
3193 | 0 | DoRedisTestInt(__LINE__, {"HSET", "h1", "f1", "5"}, 1); |
3194 | 0 | DoRedisTestInt(__LINE__, {"HSET", "h1", "fstr", "str"}, 1); |
3195 | 0 | SyncClient(); |
3196 | | |
3197 | | // over 32 bit |
3198 | 0 | DoRedisTestOk(__LINE__, {"SET", "novar", "17179869184"}); |
3199 | 0 | SyncClient(); |
3200 | 0 | DoRedisTestInt(__LINE__, {"INCR", "novar"}, 17179869185); |
3201 | 0 | SyncClient(); |
3202 | 0 | DoRedisTestInt(__LINE__, {"INCRBY", "novar", "17179869183"}, 34359738368); |
3203 | 0 | SyncClient(); |
3204 | | |
3205 | | // over 32 bit |
3206 | 0 | DoRedisTestOk(__LINE__, {"SET", "novar64", "9223372036854775807"}); // 2 ** 63 - 1 |
3207 | 0 | SyncClient(); |
3208 | 0 | DoRedisTestExpectError(__LINE__, {"INCR", "novar64"}, "Increment would overflow"); |
3209 | 0 | SyncClient(); |
3210 | | |
3211 | | // INCRBY on a Hash type should fail. |
3212 | 0 | DoRedisTestExpectError(__LINE__, {"INCRBY", "h1", "5"}, "WRONGTYPE"); |
3213 | 0 | SyncClient(); |
3214 | | // HINCRBY should fail on a normal key. |
3215 | 0 | DoRedisTestExpectError(__LINE__, {"HINCRBY", "kstr", "fstr", "5"}, "WRONGTYPE"); |
3216 | 0 | SyncClient(); |
3217 | | // HINCRBY too many arguments. |
3218 | 0 | DoRedisTestExpectError(__LINE__, {"HINCRBY", "h1", "f1", "5", "extra_arg"}); |
3219 | 0 | SyncClient(); |
3220 | |
|
3221 | 0 | DoRedisTestExpectError(__LINE__, {"HINCRBY", "h1", "fstr", "5"}, "ERR"); |
3222 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "h1", "fstr"}, "str"); |
3223 | 0 | SyncClient(); |
3224 | |
|
3225 | 0 | VerifyCallbacks(); |
3226 | 0 | } |
3227 | | |
3228 | | // This test also uses the open source client |
3229 | 0 | TEST_F(TestRedisService, TestTtlSetEx) { |
3230 | |
|
3231 | 0 | DoRedisTestOk(__LINE__, {"SET", "k1", "v1"}); |
3232 | 0 | DoRedisTestOk(__LINE__, {"SET", "k2", "v2", "EX", "1"}); |
3233 | 0 | DoRedisTestOk(__LINE__, {"SET", "k3", "v3", "EX", NonTsanVsTsan("20", "100")}); |
3234 | 0 | DoRedisTestOk(__LINE__, {"SET", "k4", "v4", "EX", std::to_string(kRedisMaxTtlSeconds)}); |
3235 | 0 | DoRedisTestOk(__LINE__, {"SET", "k5", "v5", "EX", std::to_string(kRedisMinTtlSetExSeconds)}); |
3236 | | |
3237 | | // Invalid ttl. |
3238 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "k6", "v6", "EX", |
3239 | 0 | std::to_string(kRedisMaxTtlSeconds + 1)}); |
3240 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "k7", "v7", "EX", |
3241 | 0 | std::to_string(kRedisMinTtlSetExSeconds - 1)}); |
3242 | | |
3243 | | // Commands are pipelined and only sent when client.commit() is called. |
3244 | | // sync_commit() waits until all responses are received. |
3245 | 0 | SyncClient(); |
3246 | 0 | std::this_thread::sleep_for(2s); |
3247 | |
|
3248 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k1"}, "v1"); |
3249 | 0 | DoRedisTestNull(__LINE__, {"GET", "k2"}); |
3250 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k3"}, "v3"); |
3251 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k4"}, "v4"); |
3252 | 0 | DoRedisTestNull(__LINE__, {"GET", "k5"}); |
3253 | |
|
3254 | 0 | SyncClient(); |
3255 | 0 | DoRedisTestOk(__LINE__, {"SET", "k10", "v10", "EX", "5", "NX"}); |
3256 | 0 | SyncClient(); |
3257 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k10"}, "v10"); |
3258 | 0 | SyncClient(); |
3259 | |
|
3260 | 0 | std::this_thread::sleep_for(10s); |
3261 | 0 | DoRedisTestOk(__LINE__, {"SET", "k10", "v10", "EX", "5", "NX"}); |
3262 | |
|
3263 | 0 | SyncClient(); |
3264 | 0 | VerifyCallbacks(); |
3265 | 0 | } |
3266 | | |
3267 | 0 | TEST_F(TestRedisService, TestDummyLocal) { |
3268 | 0 | expected_no_sessions_ = true; |
3269 | 0 | DoRedisTestBulkString(__LINE__, {"INFO"}, kInfoResponse); |
3270 | 0 | DoRedisTestBulkString(__LINE__, {"INFO", "Replication"}, kInfoResponse); |
3271 | 0 | DoRedisTestBulkString(__LINE__, {"INFO", "foo", "bar", "whatever", "whatever"}, kInfoResponse); |
3272 | 0 | DoRedisTest(__LINE__, {"INFO"}, RedisReplyType::kString, [] (const RedisReply& reply) { |
3273 | 0 | ASSERT_NE(reply.as_string().find("redis_version"), string::npos); |
3274 | 0 | } |
3275 | 0 | ); |
3276 | |
|
3277 | 0 | DoRedisTestOk(__LINE__, {"COMMAND"}); |
3278 | 0 | DoRedisTestExpectError(__LINE__, {"EVAL"}); |
3279 | |
|
3280 | 0 | SyncClient(); |
3281 | 0 | VerifyCallbacks(); |
3282 | 0 | } |
3283 | | |
3284 | 0 | TEST_F(TestRedisService, TestTimeSeriesTtl) { |
3285 | 0 | FLAGS_emulate_redis_responses = true; |
3286 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "key", "10", "v", "EXPIRE_IN", "5"}); |
3287 | 0 | SyncClient(); |
3288 | |
|
3289 | 0 | std::this_thread::sleep_for(std::chrono::seconds(10)); |
3290 | 0 | DoRedisTestNull(__LINE__, {"TSGET", "key", "10"}); |
3291 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "key", "2", "val"}); |
3292 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "key", "20", "v"}); |
3293 | |
|
3294 | 0 | SyncClient(); |
3295 | 0 | VerifyCallbacks(); |
3296 | 0 | } |
3297 | | |
3298 | 0 | TEST_F(TestRedisService, TestTimeSeries) { |
3299 | | // The default value is true, but we explicitly set this here for clarity. |
3300 | 0 | FLAGS_emulate_redis_responses = true; |
3301 | | |
3302 | | // Need an int for timeseries as a score. |
3303 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "42.0", "42"}); |
3304 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "12.0", "42"}); |
3305 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "subkey1", "42"}); |
3306 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "subkey2", "12"}); |
3307 | 0 | DoRedisTestExpectError(__LINE__, {"TSGET", "ts_key", "subkey1"}); |
3308 | 0 | DoRedisTestExpectError(__LINE__, {"TSGET", "ts_key", "subkey2"}); |
3309 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", "3.0", "v3"}); |
3310 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", "abc", "v3"}); |
3311 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", "123abc", "v3"}); |
3312 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", " 123", "v3"}); |
3313 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", "0xff", "v3"}); |
3314 | | |
3315 | | // Incorrect number of arguments. |
3316 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "subkey1"}); |
3317 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "subkey2"}); |
3318 | 0 | DoRedisTestExpectError(__LINE__, {"TSGET", "ts_key"}); |
3319 | 0 | DoRedisTestExpectError(__LINE__, {"TSGET", "ts_key"}); |
3320 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "ts_key", "1", "v1", "2", "v2", "3"}); |
3321 | | |
3322 | | // Valid statements. |
3323 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "-10", "value1"}); |
3324 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "-20", "value2"}); |
3325 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "-30", "value3"}); |
3326 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "10", "value4"}); |
3327 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "20", "value5"}); |
3328 | | // For duplicate keys, the last one is picked up. |
3329 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "30", "value100", "30", "value6"}); |
3330 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", int64Max_, "valuemax"}); |
3331 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", int64Min_, "valuemin"}); |
3332 | 0 | SyncClient(); |
3333 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "30", "value7"}); |
3334 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_multi", "10", "v1", "20", "v2", "30", "v3", "40", "v4"}); |
3335 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_multi", "10", "v5", "50", "v6", "30", "v7", "60", "v8"}); |
3336 | 0 | SyncClient(); |
3337 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_multi", "10", "v9", "70", "v10", "30", "v11", "80", "v12"}); |
3338 | 0 | SyncClient(); |
3339 | | |
3340 | | // Ensure we retrieve appropriate results. |
3341 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "-10"}, "value1"); |
3342 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "-20"}, "value2"); |
3343 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "-30"}, "value3"); |
3344 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "10"}, "value4"); |
3345 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "20"}, "value5"); |
3346 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", "30"}, "value7"); |
3347 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", int64Max_}, "valuemax"); |
3348 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_key", int64Min_}, "valuemin"); |
3349 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "10"}, "v9"); |
3350 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "20"}, "v2"); |
3351 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "30"}, "v11"); |
3352 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "40"}, "v4"); |
3353 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "50"}, "v6"); |
3354 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "60"}, "v8"); |
3355 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "70"}, "v10"); |
3356 | 0 | DoRedisTestBulkString(__LINE__, {"TSGET", "ts_multi", "80"}, "v12"); |
3357 | | |
3358 | | // Keys that are not present. |
3359 | 0 | DoRedisTestNull(__LINE__, {"TSGET", "ts_key", "40"}); |
3360 | 0 | DoRedisTestNull(__LINE__, {"TSGET", "abc", "30"}); |
3361 | | |
3362 | | // HGET/SISMEMBER/GET should not work with this. |
3363 | 0 | DoRedisTestExpectError(__LINE__, {"HGET", "ts_key", "30"}); |
3364 | 0 | DoRedisTestExpectError(__LINE__, {"SISMEMBER", "ts_key", "30"}); |
3365 | 0 | DoRedisTestExpectError(__LINE__, {"HEXISTS", "ts_key", "30"}); |
3366 | 0 | DoRedisTestExpectError(__LINE__, {"GET", "ts_key"}); |
3367 | | |
3368 | | // TSGET should not work with HSET. |
3369 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "30", "v1"}, 1); |
3370 | 0 | DoRedisTestExpectError(__LINE__, {"TSGET", "map_key", "30"}); |
3371 | |
|
3372 | 0 | SyncClient(); |
3373 | 0 | VerifyCallbacks(); |
3374 | 0 | } |
3375 | | |
3376 | 0 | TEST_F(TestRedisService, TestSortedSets) { |
3377 | | // The default value is true, but we explicitly set this here for clarity. |
3378 | 0 | FLAGS_emulate_redis_responses = true; |
3379 | | |
3380 | | // Need an double for sorted sets as a score. |
3381 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "subkey1", "42"}); |
3382 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "subkey2", "12"}); |
3383 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "1", "v1", "2", "v2", "abc", "v3"}); |
3384 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "1", "v1", "2", "v2", "123abc", "v3"}); |
3385 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "1", "v1", "2", "v2", " 123", "v3"}); |
3386 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "1", " 2"}); |
3387 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "1", "abc"}); |
3388 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "abc", "2"}); |
3389 | | |
3390 | | // Incorrect number of arguments. |
3391 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "subkey1"}); |
3392 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "1", "v1", "2", "v2", "3"}); |
3393 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "1"}); |
3394 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "1", "2", "3"}); |
3395 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "1", "2", "WITHSCORES", "abc"}); |
3396 | 0 | DoRedisTestExpectError(__LINE__, {"ZREM", "z_key"}); |
3397 | | |
3398 | | // Valid statements |
3399 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "-30.0", "v1"}, 1); |
3400 | 0 | SyncClient(); |
3401 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "-20.0", "v2"}, 1); |
3402 | 0 | SyncClient(); |
3403 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "-10.0", "v3"}, 1); |
3404 | 0 | SyncClient(); |
3405 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "10.0", "v4"}, 1); |
3406 | 0 | SyncClient(); |
3407 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "20.0", "v5"}, 1); |
3408 | 0 | SyncClient(); |
3409 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "30.0", "v6"}, 1); |
3410 | 0 | SyncClient(); |
3411 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", |
3412 | 0 | strings::Substitute("$0", std::numeric_limits<double>::max()), "vmax"}, 1); |
3413 | 0 | SyncClient(); |
3414 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", |
3415 | 0 | strings::Substitute("$0", -std::numeric_limits<double>::max()), "vmin"}, 1); |
3416 | 0 | SyncClient(); |
3417 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "40.0", "v6"}, 0); |
3418 | 0 | SyncClient(); |
3419 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "0x1e", "v6"}, 0); |
3420 | 0 | SyncClient(); |
3421 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "-20", "v1"}, 0); |
3422 | 0 | SyncClient(); |
3423 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "-30", "v1"}, 0); |
3424 | 0 | SyncClient(); |
3425 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "30.000001", "v7"}, 1); |
3426 | 0 | SyncClient(); |
3427 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "30.000001", "v8"}, 1); |
3428 | 0 | SyncClient(); |
3429 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_key"}, 10); |
3430 | 0 | DoRedisTestOk(__LINE__, {"SET", "s_key", "s_val"}); |
3431 | 0 | SyncClient(); |
3432 | 0 | DoRedisTestExpectError(__LINE__, {"ZCARD", "s_key"}); |
3433 | 0 | DoRedisTestExpectErrorMsg(__LINE__, {"ZCARD", "s_key"}, |
3434 | 0 | "WRONGTYPE Operation against a key holding the wrong kind of value"); |
3435 | |
|
3436 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "-10.0", "v3", "-20.0", "v2", "-30.0", "v1", |
3437 | 0 | "10.0", "v4", "20.0", "v5", "30.0", "v6"}, 6); |
3438 | 0 | SyncClient(); |
3439 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "40.0", "v6", "0x1e", "v6", "-20", "v1", "-30", "v1", |
3440 | 0 | "30.000001", "v7", "30.000001", "v8"}, 2); |
3441 | 0 | SyncClient(); |
3442 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", |
3443 | 0 | strings::Substitute("$0", std::numeric_limits<double>::max()), "vmax", |
3444 | 0 | strings::Substitute("$0", -std::numeric_limits<double>::max()), "vmin"}, 2); |
3445 | 0 | SyncClient(); |
3446 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_multi"}, 10); |
3447 | | |
3448 | | // Ensure we retrieve appropriate results. |
3449 | 0 | LOG(INFO) << "Starting ZRANGE queries"; |
3450 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "+inf", "-inf"}, {}); |
3451 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf"}, |
3452 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "vmax"}); |
3453 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "(-inf", "(+inf"}, |
3454 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "vmax"}); |
3455 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "20.0", "30.0"}, {"v5", "v6"}); |
3456 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "20.0", "30.000001"}, |
3457 | 0 | {"v5", "v6", "v7", "v8"}); |
3458 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "20.0", "(30.000001"}, {"v5", "v6"}); |
3459 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "(20.0", "30.000001"}, {"v6", "v7", "v8"}); |
3460 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "-20.0", "-10.0"}, {"v2", "v3"}); |
3461 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "(-20.0", "(-10.0"}, {}); |
3462 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "+inf", "-inf"}, {}); |
3463 | |
|
3464 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "20.0", "30.0", "WITHSCORES"}, |
3465 | 0 | {20.0, 30.0}, {"v5", "v6"}); |
3466 | 0 | DoRedisTestScoreValueArray(__LINE__, |
3467 | 0 | {"ZRANGEBYSCORE", "z_key", "20.0", "30.000001", "withscores"}, |
3468 | 0 | {20.0, 30.0, 30.000001, 30.000001}, {"v5", "v6", "v7", "v8"}); |
3469 | |
|
3470 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "-inf", "+inf"}, |
3471 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "vmax"}); |
3472 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "(-inf", "(+inf"}, |
3473 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "vmax"}); |
3474 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "20.0", "30.0"}, {"v5", "v6"}); |
3475 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "20.0", "30.000001"}, |
3476 | 0 | {"v5", "v6", "v7", "v8"}); |
3477 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "20.0", "(30.000001"}, {"v5", "v6"}); |
3478 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "(20.0", "30.000001"}, |
3479 | 0 | {"v6", "v7", "v8"}); |
3480 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "-20.0", "-10.0"}, {"v2", "v3"}); |
3481 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "(-20.0", "(-10.0"}, {}); |
3482 | |
|
3483 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "20.0", "30.0", "WITHSCORES"}, |
3484 | 0 | {20.0, 30.0}, {"v5", "v6"}); |
3485 | 0 | DoRedisTestScoreValueArray(__LINE__, |
3486 | 0 | {"ZRANGEBYSCORE", "z_multi", "20.0", "30.000001", "withscores"}, |
3487 | 0 | {20.0, 30.0, 30.000001, 30.000001}, {"v5", "v6", "v7", "v8"}); |
3488 | |
|
3489 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_key", "v6"}, 1); |
3490 | 0 | SyncClient(); |
3491 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_key", "v6"}, 0); |
3492 | 0 | SyncClient(); |
3493 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_key", "v7"}, 1); |
3494 | 0 | SyncClient(); |
3495 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_key", "v9"}, 0); |
3496 | 0 | SyncClient(); |
3497 | |
|
3498 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_multi", "v6", "v7", "v9"}, 2); |
3499 | 0 | SyncClient(); |
3500 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "z_multi", "v6", "v7", "v9"}, 0); |
3501 | 0 | SyncClient(); |
3502 | |
|
3503 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf"}, |
3504 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v8", "vmax"}); |
3505 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_key"}, 8); |
3506 | |
|
3507 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_multi", "-inf", "+inf"}, |
3508 | 0 | {"vmin", "v1", "v2", "v3", "v4", "v5", "v8", "vmax"}); |
3509 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_multi"}, 8); |
3510 | | |
3511 | | // Test NX/CH option. |
3512 | 0 | LOG(INFO) << "Starting ZADD with options"; |
3513 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "NX", "0", "v8"}, 0); |
3514 | 0 | SyncClient(); |
3515 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "NX", "CH", "0", "v8"}, 0); |
3516 | 0 | SyncClient(); |
3517 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "NX", "0", "v9"}, 1); |
3518 | 0 | SyncClient(); |
3519 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "NX", "40", "v9"}, 0); |
3520 | 0 | SyncClient(); |
3521 | | |
3522 | | // Make sure that only v9 exists at 0 and not at 40. |
3523 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "0.0", "0.0"}, {"v9"}); |
3524 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "40.0", "40.0"}, {}); |
3525 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_key"}, 9); |
3526 | | |
3527 | | // Test XX/CH option. |
3528 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "XX", "CH", "0", "v8"}, 1); |
3529 | 0 | SyncClient(); |
3530 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "XX", "30.000001", "v8"}, 0); |
3531 | 0 | SyncClient(); |
3532 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "XX", "0", "v10"}, 0); |
3533 | 0 | SyncClient(); |
3534 | | |
3535 | | // Make sure that only v9 exists at 0 and v8 exists at 30.000001. |
3536 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "0.0", "0.0", "WITHSCORES"}, |
3537 | 0 | {0.0}, {"v9"}); |
3538 | 0 | DoRedisTestScoreValueArray(__LINE__, |
3539 | 0 | {"ZRANGEBYSCORE", "z_key", "30.000001", "30.000001", "WITHSCORES"}, |
3540 | 0 | {30.000001}, {"v8"}); |
3541 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_key"}, 9); |
3542 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "does_not_exist"}, 0); |
3543 | | |
3544 | | // Test NX/XX/CH option for multi. |
3545 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "NX", "0", "v8", "40", "v9"}, 1); |
3546 | 0 | SyncClient(); |
3547 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "CH", "0", "v8", "0", "v9"}, 2); |
3548 | 0 | SyncClient(); |
3549 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "XX", "CH", "30.000001", "v8", "0", "v10"}, 1); |
3550 | 0 | SyncClient(); |
3551 | | |
3552 | | // Make sure that only v9 exists and 0 and v8 exists at 30.000001. |
3553 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "0.0", "0.0", "WITHSCORES"}, |
3554 | 0 | {0.0}, {"v9"}); |
3555 | 0 | DoRedisTestScoreValueArray(__LINE__, |
3556 | 0 | {"ZRANGEBYSCORE", "z_key", "30.000001", "30.000001", "WITHSCORES"}, |
3557 | 0 | {30.000001}, {"v8"}); |
3558 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_multi"}, 9); |
3559 | | |
3560 | | // Test incr option. |
3561 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "INCR", "10", "v8"}, 0); |
3562 | 0 | SyncClient(); |
3563 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "INCR", "XX", "CH", "10", "v8"}, 1); |
3564 | 0 | SyncClient(); |
3565 | | // This shouldn't do anything, since NX option is specified. |
3566 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "INCR", "NX", "10", "v8"}, 0); |
3567 | 0 | SyncClient(); |
3568 | | |
3569 | | // Make sure v8 has been incremented by 20. |
3570 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "30.000001", "30.000001"}, {}); |
3571 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "50.000001", "50.000001"}, {"v8"}); |
3572 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "z_key"}, 9); |
3573 | | |
3574 | | // HGET/SISMEMBER/GET/TS should not work with this. |
3575 | 0 | DoRedisTestExpectError(__LINE__, {"SISMEMBER", "z_key", "30"}); |
3576 | 0 | DoRedisTestExpectError(__LINE__, {"HEXISTS", "z_key", "30"}); |
3577 | 0 | DoRedisTestExpectError(__LINE__, {"GET", "z_key"}); |
3578 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGE", "z_key", "1", "a"}); |
3579 | | |
3580 | | // ZADD should not work with HSET. |
3581 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "30", "v1"}, 1); |
3582 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "map_key", "40", "v2"}); |
3583 | | |
3584 | | // Cannot have both NX and XX options. |
3585 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "CH", "NX", "XX", "0", "v1"}); |
3586 | |
|
3587 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "CH", "NX", "INCR"}); |
3588 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "XX"}); |
3589 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "CH", "NX", "0", "v1", "1"}); |
3590 | | // Cannot have incr with multiple score value pairs. |
3591 | 0 | DoRedisTestExpectError(__LINE__, {"ZADD", "z_key", "INCR", "0", "v1", "1", "v2"}); |
3592 | | |
3593 | | // Test ZREM on non-existent key and then add the same key. |
3594 | 0 | DoRedisTestInt(__LINE__, {"ZREM", "my_z_set", "v1"}, 0); |
3595 | 0 | SyncClient(); |
3596 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "my_z_set"}, 0); |
3597 | 0 | SyncClient(); |
3598 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "my_z_set", "1", "v1"}, 1); |
3599 | 0 | SyncClient(); |
3600 | 0 | DoRedisTestInt(__LINE__, {"ZCARD", "my_z_set"}, 1); |
3601 | 0 | SyncClient(); |
3602 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "my_z_set", "1", "1"}, {"v1"}); |
3603 | 0 | } |
3604 | | |
3605 | 0 | TEST_F(TestRedisService, ZRangeByScoreInvalidOptions) { |
3606 | 0 | expected_no_sessions_ = true; |
3607 | | |
3608 | | // Not enough args to ZRANGEBYSCORE should throw an error. |
3609 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf", "LIMIT"}); |
3610 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf", "LIMIT", "2"}); |
3611 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf", "LIMIT", "a", "1"}); |
3612 | 0 | DoRedisTestExpectError(__LINE__, { |
3613 | 0 | "ZRANGEBYSCORE", "z_key", "-inf", "+inf", "LIMIT", "1", "2", "3"}); |
3614 | 0 | DoRedisTestExpectError(__LINE__, { |
3615 | 0 | "ZRANGEBYSCORE", "z_key", "-inf", "+inf", "LIMIT", "WITHSCORES", "2", "3"}); |
3616 | 0 | DoRedisTestExpectError(__LINE__, { |
3617 | 0 | "ZRANGEBYSCORE", "z_key", "-inf", "+inf", "WITHSCORES", "2", "3"}); |
3618 | 0 | SyncClient(); |
3619 | 0 | VerifyCallbacks(); |
3620 | 0 | } |
3621 | | |
3622 | 0 | TEST_F(TestRedisService, TestZRevRange) { |
3623 | | // The default value is true, but we explicitly set this here for clarity. |
3624 | 0 | FLAGS_emulate_redis_responses = true; |
3625 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v1", "0", "v2", |
3626 | 0 | "1", "v3", "1", "v4", "1", "v5"}, 6); |
3627 | 0 | SyncClient(); |
3628 | |
|
3629 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "0", "5"}, |
3630 | 0 | {"v5", "v4", "v3", "v2", "v1", "v0"}); |
3631 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZREVRANGE", "z_multi", "0", "-1", "WITHSCORES"}, |
3632 | 0 | {1, 1, 1, 0, 0, 0}, |
3633 | 0 | {"v5", "v4", "v3", "v2", "v1", "v0"}); |
3634 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "0", "1"}, {"v5", "v4"}); |
3635 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "2", "3"}, {"v3", "v2"}); |
3636 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "6", "7"}, {}); |
3637 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "0", "-1"}, |
3638 | 0 | {"v5", "v4", "v3", "v2", "v1", "v0"}); |
3639 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "-2", "-1"}, {"v1", "v0"}); |
3640 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "-3", "-2"}, {"v2", "v1"}); |
3641 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_multi", "-3", "5"}, {"v2", "v1", "v0"}); |
3642 | | |
3643 | | // Test empty key. |
3644 | 0 | DoRedisTestArray(__LINE__, {"ZREVRANGE", "z_key", "0", "1"}, {}); |
3645 | |
|
3646 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "0"}); |
3647 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "1", "2", "3"}); |
3648 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "1", "2", "WITHSCORES", "1"}); |
3649 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "1.0", "2.0"}); |
3650 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "1", "2"}); |
3651 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "0", "(2"}); |
3652 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "(0", "2"}); |
3653 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "z_multi", "(0", "(2"}); |
3654 | | |
3655 | | // Test key with wrong type. |
3656 | 0 | DoRedisTestOk(__LINE__, {"SET", "s_key", "s_val"}); |
3657 | 0 | DoRedisTestExpectError(__LINE__, {"ZREVRANGE", "s_key", "1", "2"}); |
3658 | |
|
3659 | 0 | SyncClient(); |
3660 | 0 | VerifyCallbacks(); |
3661 | 0 | } |
3662 | | |
3663 | 0 | TEST_F(TestRedisService, TestZRange) { |
3664 | | // The default value is true, but we explicitly set this here for clarity. |
3665 | 0 | FLAGS_emulate_redis_responses = true; |
3666 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v1", "0", "v2", |
3667 | 0 | "1", "v3", "1", "v4", "1", "v5"}, 6); |
3668 | 0 | SyncClient(); |
3669 | |
|
3670 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "0", "5"}, {"v0", "v1", "v2", "v3", "v4", "v5"}); |
3671 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGE", "z_multi", "0", "-1", "WITHSCORES"}, |
3672 | 0 | {0, 0, 0, 1, 1, 1}, |
3673 | 0 | {"v0", "v1", "v2", "v3", "v4", "v5"}); |
3674 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "0", "1"}, {"v0", "v1"}); |
3675 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "2", "3"}, {"v2", "v3"}); |
3676 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "6", "7"}, {}); |
3677 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "0", "-1"}, |
3678 | 0 | {"v0", "v1", "v2", "v3", "v4", "v5"}); |
3679 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "-2", "-1"}, {"v4", "v5"}); |
3680 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "-3", "-2"}, {"v3", "v4"}); |
3681 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "-3", "5"}, {"v3", "v4", "v5"}); |
3682 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "0", "100"}, |
3683 | 0 | {"v0", "v1", "v2", "v3", "v4", "v5"}); |
3684 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "-100", "100"}, |
3685 | 0 | {"v0", "v1", "v2", "v3", "v4", "v5"}); |
3686 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "10", "100"}, {}); |
3687 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_multi", "-100", "-10"}, {}); |
3688 | | |
3689 | | // Test empty key. |
3690 | 0 | DoRedisTestArray(__LINE__, {"ZRANGE", "z_key", "0", "1"}, {}); |
3691 | |
|
3692 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "z_multi", "0"}); |
3693 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "z_multi", "1", "2", "3"}); |
3694 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "z_multi", "1", "2", "WITHSCORES", "1"}); |
3695 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "z_multi", "1.0", "2.0"}); |
3696 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "1", "2"}); |
3697 | | |
3698 | | // Test key with wrong type. |
3699 | 0 | DoRedisTestOk(__LINE__, {"SET", "s_key", "s_val"}); |
3700 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGE", "s_key", "1", "2"}); |
3701 | |
|
3702 | 0 | SyncClient(); |
3703 | 0 | VerifyCallbacks(); |
3704 | 0 | } |
3705 | | |
3706 | 0 | TEST_F(TestRedisService, TestZScore) { |
3707 | | // The default value is true, but we explicitly set this here for clarity. |
3708 | 0 | FLAGS_emulate_redis_responses = true; |
3709 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v0_copy", "1", "v1", |
3710 | 0 | "2", "v2", "3", "v3", "4.5", "v4"}, 6); |
3711 | 0 | SyncClient(); |
3712 | |
|
3713 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v0"}, 0.0); |
3714 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v0_copy"}, 0.0); |
3715 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v1"}, 1.0); |
3716 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v2"}, 2.0); |
3717 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v3"}, 3.0); |
3718 | 0 | DoRedisTestDouble(__LINE__, {"ZSCORE", "z_multi", "v4"}, 4.5); |
3719 | |
|
3720 | 0 | DoRedisTestNull(__LINE__, {"ZSCORE", "z_no_exist", "v4"}); |
3721 | |
|
3722 | 0 | SyncClient(); |
3723 | 0 | VerifyCallbacks(); |
3724 | 0 | } |
3725 | | |
3726 | 0 | TEST_F(TestRedisService, TestTimeSeriesTTL) { |
3727 | 0 | int64_t ttl_sec = 10; |
3728 | 0 | TestTSTtl("EXPIRE_IN", ttl_sec, ttl_sec, "test_expire_in"); |
3729 | 0 | int64_t curr_time_sec = GetCurrentTimeMicros() / MonoTime::kMicrosecondsPerSecond; |
3730 | 0 | TestTSTtl("EXPIRE_AT", ttl_sec, curr_time_sec + ttl_sec, "test_expire_at"); |
3731 | |
|
3732 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_in", "10", "v1", "EXPIRE_IN", |
3733 | 0 | std::to_string(kRedisMinTtlSetExSeconds - 1)}); |
3734 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_in", "10", "v1", "EXPIRE_IN", |
3735 | 0 | std::to_string(kRedisMaxTtlSeconds + 1)}); |
3736 | |
|
3737 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "EXPIRE_AT", |
3738 | 0 | std::to_string(curr_time_sec - 10)}); |
3739 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "expire_at", |
3740 | 0 | std::to_string(curr_time_sec - 10)}); |
3741 | |
|
3742 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "EXPIRE_AT", |
3743 | 0 | std::to_string(curr_time_sec + kRedisMinTtlSetExSeconds - 1)}); |
3744 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "expire_at", |
3745 | 0 | std::to_string(curr_time_sec + kRedisMinTtlSetExSeconds - 1)}); |
3746 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "exPiRe_aT", |
3747 | 0 | std::to_string(curr_time_sec + kRedisMinTtlSetExSeconds - 1)}); |
3748 | |
|
3749 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "EXPIRE_IN", |
3750 | 0 | std::to_string(curr_time_sec + kRedisMaxTtlSeconds + 1)}); |
3751 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "expire_in", |
3752 | 0 | std::to_string(curr_time_sec + kRedisMaxTtlSeconds + 1)}); |
3753 | 0 | DoRedisTestExpectError(__LINE__, {"TSADD", "test_expire_at", "10", "v1", "eXpIrE_In", |
3754 | 0 | std::to_string(curr_time_sec + kRedisMaxTtlSeconds + 1)}); |
3755 | 0 | } |
3756 | | |
3757 | 0 | TEST_F(TestRedisService, TestTsCard) { |
3758 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
3759 | 0 | "-50", "v1", |
3760 | 0 | "-40", "v2", |
3761 | 0 | "-30", "v3", |
3762 | 0 | "-20", "v4", |
3763 | 0 | "-10", "v5", |
3764 | 0 | "10", "v6", |
3765 | 0 | "20", "v7", |
3766 | 0 | "30", "v8", |
3767 | 0 | "40", "v9", |
3768 | 0 | "50", "v10", |
3769 | 0 | }); |
3770 | |
|
3771 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key1", |
3772 | 0 | "10", "v6", |
3773 | 0 | "20", "v7", |
3774 | 0 | "30", "v8", |
3775 | 0 | "40", "v9", |
3776 | 0 | "50", "v10", |
3777 | 0 | }); |
3778 | |
|
3779 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key2", "10", "v6"}); |
3780 | 0 | SyncClient(); |
3781 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key2", "11", "v7", "EXPIRE_IN", "10"}); |
3782 | 0 | SyncClient(); |
3783 | |
|
3784 | 0 | DoRedisTestInt(__LINE__, {"TSCARD", "ts_key"}, 10); |
3785 | 0 | SyncClient(); |
3786 | 0 | DoRedisTestInt(__LINE__, {"TSCARD", "ts_key1"}, 5); |
3787 | 0 | SyncClient(); |
3788 | 0 | DoRedisTestInt(__LINE__, {"TSCARD", "ts_key2"}, 2); |
3789 | 0 | SyncClient(); |
3790 | 0 | DoRedisTestInt(__LINE__, {"TSCARD", "invalid_key"}, 0); |
3791 | 0 | SyncClient(); |
3792 | | |
3793 | | // After TTL expiry. |
3794 | 0 | std::this_thread::sleep_for(std::chrono::seconds(11)); |
3795 | 0 | DoRedisTestInt(__LINE__, {"TSCARD", "ts_key2"}, 1); |
3796 | | |
3797 | | // Test errors. |
3798 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v1", "0", "v2", |
3799 | 0 | "1", "v3", "1", "v4", "1", "v5"}, 6); |
3800 | 0 | DoRedisTestExpectError(__LINE__, {"TSCARD", "z_multi"}); // incorrect type. |
3801 | |
|
3802 | 0 | SyncClient(); |
3803 | 0 | VerifyCallbacks(); |
3804 | 0 | } |
3805 | | |
3806 | 0 | TEST_F(TestRedisService, TestTsLastN) { |
3807 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
3808 | 0 | "-50", "v1", |
3809 | 0 | "-40", "v2", |
3810 | 0 | "-30", "v3", |
3811 | 0 | "-20", "v4", |
3812 | 0 | "-10", "v5", |
3813 | 0 | "10", "v6", |
3814 | 0 | "20", "v7", |
3815 | 0 | "30", "v8", |
3816 | 0 | "40", "v9", |
3817 | 0 | "50", "v10", |
3818 | 0 | }); |
3819 | |
|
3820 | 0 | SyncClient(); |
3821 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "5"}, |
3822 | 0 | {"10", "v6", "20", "v7", "30", "v8", "40", "v9", "50", "v10"}); |
3823 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "4"}, |
3824 | 0 | {"20", "v7", "30", "v8", "40", "v9", "50", "v10"}); |
3825 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "3"}, |
3826 | 0 | {"30", "v8", "40", "v9", "50", "v10"}); |
3827 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "2"}, |
3828 | 0 | {"40", "v9", "50", "v10"}); |
3829 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "10"}, |
3830 | 0 | {"-50", "v1", "-40", "v2", "-30", "v3", "-20", "v4", "-10", "v5", "10", "v6", |
3831 | 0 | "20", "v7", "30", "v8", "40", "v9", "50", "v10"}); |
3832 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", "20"}, |
3833 | 0 | {"-50", "v1", "-40", "v2", "-30", "v3", "-20", "v4", "-10", "v5", "10", "v6", |
3834 | 0 | "20", "v7", "30", "v8", "40", "v9", "50", "v10"}); |
3835 | 0 | DoRedisTestArray(__LINE__, {"TSLASTN", "ts_key", |
3836 | 0 | std::to_string(std::numeric_limits<int32>::max())}, |
3837 | 0 | {"-50", "v1", "-40", "v2", "-30", "v3", "-20", "v4", "-10", "v5", "10", "v6", |
3838 | 0 | "20", "v7", "30", "v8", "40", "v9", "50", "v10"}); |
3839 | |
|
3840 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "abc"}); |
3841 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "3.0"}); |
3842 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "999999999999"}); // out of bounds. |
3843 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "-999999999999"}); // out of bounds. |
3844 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "0"}); // out of bounds. |
3845 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "ts_key", "-1"}); // out of bounds. |
3846 | 0 | DoRedisTestNull(__LINE__, {"TSLASTN", "randomkey", "10"}); // invalid key. |
3847 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_multi", "0", "v0", "0", "v1", "0", "v2", |
3848 | 0 | "1", "v3", "1", "v4", "1", "v5"}, 6); |
3849 | 0 | DoRedisTestExpectError(__LINE__, {"TSLASTN", "z_multi", "10"}); // incorrect type. |
3850 | 0 | SyncClient(); |
3851 | 0 | VerifyCallbacks(); |
3852 | 0 | } |
3853 | | |
3854 | 0 | TEST_F(TestRedisService, TestTsRangeByTime) { |
3855 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
3856 | 0 | "-50", "v1", |
3857 | 0 | "-40", "v2", |
3858 | 0 | "-30", "v3", |
3859 | 0 | "-20", "v4", |
3860 | 0 | "-10", "v5", |
3861 | 0 | "10", "v6", |
3862 | 0 | "20", "v7", |
3863 | 0 | "30", "v8", |
3864 | 0 | "40", "v9", |
3865 | 0 | "50", "v10", |
3866 | 0 | }); |
3867 | |
|
3868 | 0 | SyncClient(); |
3869 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-35", "25"}, |
3870 | 0 | {"-30", "v3", "-20", "v4", "-10", "v5", "10", "v6", "20", "v7"}); |
3871 | | |
3872 | | // Overwrite and test. |
3873 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
3874 | 0 | "-50", "v11", |
3875 | 0 | "-40", "v22", |
3876 | 0 | "-30", "v33", |
3877 | 0 | "-20", "v44", |
3878 | 0 | "-10", "v55", |
3879 | 0 | "10", "v66", |
3880 | 0 | "20", "v77", |
3881 | 0 | "30", "v88", |
3882 | 0 | "40", "v99", |
3883 | 0 | "50", "v110", |
3884 | 0 | }); |
3885 | |
|
3886 | 0 | SyncClient(); |
3887 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-55", "-10"}, |
3888 | 0 | {"-50", "v11", "-40", "v22", "-30", "v33", "-20", "v44", "-10", "v55"}); |
3889 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-20", "55"}, |
3890 | 0 | {"-20", "v44", "-10", "v55", "10", "v66", "20", "v77", "30", "v88", "40", "v99", |
3891 | 0 | "50", "v110"}); |
3892 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-55", "55"}, |
3893 | 0 | {"-50", "v11", "-40", "v22", "-30", "v33", "-20", "v44", "-10", "v55", |
3894 | 0 | "10", "v66", "20", "v77", "30", "v88", "40", "v99", "50", "v110"}); |
3895 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-15", "-5"}, {"-10", "v55"}); |
3896 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "10"}, {"10", "v66"}); |
3897 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-10", "-10"}, {"-10", "v55"}); |
3898 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-57", "-55"}, {}); |
3899 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "55", "60"}, {}); |
3900 | | |
3901 | | // Test with ttl. |
3902 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
3903 | 0 | "-30", "v333", |
3904 | 0 | "-10", "v555", |
3905 | 0 | "20", "v777", |
3906 | 0 | "30", "v888", |
3907 | 0 | "50", "v1110", |
3908 | 0 | "EXPIRE_IN", "5", |
3909 | 0 | }); |
3910 | 0 | SyncClient(); |
3911 | 0 | std::this_thread::sleep_for(std::chrono::seconds(6)); |
3912 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-55", "-10"}, |
3913 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44"}); |
3914 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-20", "55"}, |
3915 | 0 | {"-20", "v44", "10", "v66", "40", "v99"}); |
3916 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-55", "60"}, |
3917 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66", "40", "v99"}); |
3918 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-15", "-5"}, {}); |
3919 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "10"}, {"10", "v66"}); |
3920 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-25", "-15"}, {"-20", "v44"}); |
3921 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-5", "-15"}, {}); |
3922 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-45", "-55"}, {}); |
3923 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "45", "55"}, {}); |
3924 | | |
3925 | | // Test exclusive ranges. |
3926 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-20", "(40"}, {"10", "v66"}); |
3927 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-20", "(-20"}, {}); |
3928 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-20", "-10"}, {}); |
3929 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-10", "(10"}, {}); |
3930 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-50", "(-40"}, {}); |
3931 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-55", "(11"}, |
3932 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66"}); |
3933 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-50", "10"}, |
3934 | 0 | {"-40", "v22", "-20", "v44", "10", "v66"}); |
3935 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-51", "10"}, |
3936 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66"}); |
3937 | | |
3938 | | // Test infinity. |
3939 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-10", "+inf"}, |
3940 | 0 | {"10", "v66", "40", "v99"}); |
3941 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-inf", "10"}, |
3942 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66"}); |
3943 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-10", "(+inf"}, |
3944 | 0 | {"10", "v66", "40", "v99"}); |
3945 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-inf", "10"}, |
3946 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66"}); |
3947 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-inf", "+inf"}, |
3948 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66", "40", "v99"}); |
3949 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "(-inf", "(+inf"}, |
3950 | 0 | {"-50", "v11", "-40", "v22", "-20", "v44", "10", "v66", "40", "v99"}); |
3951 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "+inf", "-inf"}, {}); |
3952 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "+inf", "10"}, {}); |
3953 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "+inf", "+inf"}, {}); |
3954 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "-inf"}, {}); |
3955 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-inf", "-inf"}, {}); |
3956 | 0 | SyncClient(); |
3957 | | |
3958 | | // Test infinity with int64 min, max. |
3959 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_inf", |
3960 | 0 | int64Min_, "v1", |
3961 | 0 | "-10", "v2", |
3962 | 0 | "10", "v3", |
3963 | 0 | int64Max_, "v4", |
3964 | 0 | }); |
3965 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "-inf", "+inf"}, |
3966 | 0 | {int64Min_, "v1", "-10", "v2", "10", "v3", int64Max_, "v4"}); |
3967 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "(-inf", "(+inf"}, |
3968 | 0 | {int64Min_, "v1", "-10", "v2", "10", "v3", int64Max_, "v4"}); |
3969 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "-inf", "-inf"}, |
3970 | 0 | {}); |
3971 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "+inf", "+inf"}, |
3972 | 0 | {}); |
3973 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "-10", "(+inf"}, |
3974 | 0 | {"-10", "v2", "10", "v3", int64Max_, "v4"}); |
3975 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "-10", "+inf"}, |
3976 | 0 | {"-10", "v2", "10", "v3", int64Max_, "v4"}); |
3977 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "(-inf", "10"}, |
3978 | 0 | {int64Min_, "v1", "-10", "v2", "10", "v3"}); |
3979 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", "-inf", "10"}, |
3980 | 0 | {int64Min_, "v1", "-10", "v2", "10", "v3"}); |
3981 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64Min_, int64Max_}, |
3982 | 0 | {int64Min_, "v1", "-10", "v2", "10", "v3", int64Max_, "v4"}); |
3983 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64MaxExclusive_, int64Max_}, |
3984 | 0 | {}); |
3985 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64MaxExclusive_, int64MaxExclusive_}, |
3986 | 0 | {}); |
3987 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64Max_, int64MaxExclusive_}, |
3988 | 0 | {}); |
3989 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64MinExclusive_, int64MinExclusive_}, |
3990 | 0 | {}); |
3991 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64MinExclusive_, int64Min_}, |
3992 | 0 | {}); |
3993 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64Min_, int64MinExclusive_}, |
3994 | 0 | {}); |
3995 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64Min_, int64Min_}, |
3996 | 0 | {int64Min_, "v1"}); |
3997 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_inf", int64Max_, int64Max_}, |
3998 | 0 | {int64Max_, "v4"}); |
3999 | | |
4000 | | // Test invalid requests. |
4001 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "20", "30"}); |
4002 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "abc"}); |
4003 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "20.1"}); |
4004 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", |
4005 | 0 | "1", "v100", |
4006 | 0 | "2", "v200", |
4007 | 0 | "3", "v300", |
4008 | 0 | "4", "v400", |
4009 | 0 | "5", "v500", |
4010 | 0 | }); |
4011 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", |
4012 | 0 | "6", "v600" |
4013 | 0 | }); |
4014 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value"}); |
4015 | 0 | SyncClient(); |
4016 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "map_key", "1", "5"}); |
4017 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "key"}); |
4018 | |
|
4019 | 0 | SyncClient(); |
4020 | 0 | VerifyCallbacks(); |
4021 | 0 | } |
4022 | | |
4023 | 0 | TEST_F(TestRedisService, TestTsRevRangeByTime) { |
4024 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4025 | 0 | "-50", "v1", |
4026 | 0 | "-40", "v2", |
4027 | 0 | "-30", "v3", |
4028 | 0 | "-20", "v4", |
4029 | 0 | "-10", "v5", |
4030 | 0 | "10", "v6", |
4031 | 0 | "20", "v7", |
4032 | 0 | "30", "v8", |
4033 | 0 | "40", "v9", |
4034 | 0 | "50", "v10", |
4035 | 0 | }); |
4036 | |
|
4037 | 0 | SyncClient(); |
4038 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-35", "25"}, |
4039 | 0 | {"20", "v7", "10", "v6", "-10", "v5", "-20", "v4", "-30", "v3"}); |
4040 | | |
4041 | | // Overwrite and test. |
4042 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4043 | 0 | "-50", "v11", |
4044 | 0 | "-40", "v22", |
4045 | 0 | "-30", "v33", |
4046 | 0 | "-20", "v44", |
4047 | 0 | "-10", "v55", |
4048 | 0 | "10", "v66", |
4049 | 0 | "20", "v77", |
4050 | 0 | "30", "v88", |
4051 | 0 | "40", "v99", |
4052 | 0 | "50", "v110", |
4053 | 0 | }); |
4054 | |
|
4055 | 0 | SyncClient(); |
4056 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "-10"}, |
4057 | 0 | {"-10", "v55", "-20", "v44", "-30", "v33", "-40", "v22", "-50", "v11"}); |
4058 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-20", "55"}, |
4059 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77", "10", "v66", "-10", |
4060 | 0 | "v55", "-20", "v44"}); |
4061 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "55"}, |
4062 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77", "10", "v66", "-10", |
4063 | 0 | "v55", "-20", "v44", "-30", "v33", "-40", "v22", "-50", "v11"}); |
4064 | |
|
4065 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-15", "-5"}, {"-10", "v55"}); |
4066 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "10"}, {"10", "v66"}); |
4067 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-10", "-10"}, {"-10", "v55"}); |
4068 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-57", "-55"}, {}); |
4069 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "55", "60"}, {}); |
4070 | | |
4071 | | // Test with limit. |
4072 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "-10", "LIMIT", "1"}, |
4073 | 0 | {"-10", "v55"}); |
4074 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "-10", "LIMIT", "2"}, |
4075 | 0 | {"-10", "v55", "-20", "v44"}); |
4076 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-20", "55", "LIMIT", "4"}, |
4077 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77"}); |
4078 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "55", "LIMIT", "5"}, |
4079 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77", "10", "v66"}); |
4080 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "+inf", "LIMIT", "1"}, |
4081 | 0 | {"50", "v110"}); |
4082 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(50", "LIMIT", "1"}, |
4083 | 0 | {"40", "v99"}); |
4084 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(40", "LIMIT", "1"}, |
4085 | 0 | {"30", "v88"}); |
4086 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(30", "LIMIT", "1"}, |
4087 | 0 | {"20", "v77"}); |
4088 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(20", "LIMIT", "1"}, |
4089 | 0 | {"10", "v66"}); |
4090 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(10", "LIMIT", "1"}, |
4091 | 0 | {"-10", "v55"}); |
4092 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(-10", "LIMIT", "1"}, |
4093 | 0 | {"-20", "v44"}); |
4094 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(-20", "LIMIT", "1"}, |
4095 | 0 | {"-30", "v33"}); |
4096 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(-30", "LIMIT", "1"}, |
4097 | 0 | {"-40", "v22"}); |
4098 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(-40", "LIMIT", "1"}, |
4099 | 0 | {"-50", "v11"}); |
4100 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "(-50", "LIMIT", "1"}, |
4101 | 0 | {}); |
4102 | | |
4103 | | // Test with a limit larger than the total number of elements. |
4104 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "-10", "LIMIT", "300"}, |
4105 | 0 | {"-10", "v55", "-20", "v44", "-30", "v33", "-40", "v22", "-50", "v11"}); |
4106 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-20", "55", "LIMIT", "121"}, |
4107 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77", "10", "v66", "-10", |
4108 | 0 | "v55", "-20", "v44"}); |
4109 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "55", "LIMIT", "34"}, |
4110 | 0 | {"50", "v110", "40", "v99", "30", "v88", "20", "v77", "10", "v66", "-10", |
4111 | 0 | "v55", "-20", "v44", "-30", "v33", "-40", "v22", "-50", "v11"}); |
4112 | |
|
4113 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-15", "-5"}, {"-10", "v55"}); |
4114 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "10"}, {"10", "v66"}); |
4115 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-10", "-10"}, {"-10", "v55"}); |
4116 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-57", "-55"}, {}); |
4117 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "55", "60"}, {}); |
4118 | | |
4119 | | // Test with ttl. |
4120 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4121 | 0 | "-30", "v333", |
4122 | 0 | "-10", "v555", |
4123 | 0 | "20", "v777", |
4124 | 0 | "30", "v888", |
4125 | 0 | "50", "v1110", |
4126 | 0 | "EXPIRE_IN", "5", |
4127 | 0 | }); |
4128 | 0 | SyncClient(); |
4129 | 0 | std::this_thread::sleep_for(std::chrono::seconds(6)); |
4130 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "-10"}, |
4131 | 0 | {"-20", "v44", "-40", "v22", "-50", "v11"}); |
4132 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-20", "55"}, |
4133 | 0 | {"40", "v99", "10", "v66", "-20", "v44"}); |
4134 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "60"}, |
4135 | 0 | {"40", "v99", "10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4136 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-15", "-5"}, {}); |
4137 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "10"}, {"10", "v66"}); |
4138 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-25", "-15"}, {"-20", "v44"}); |
4139 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-5", "-15"}, {}); |
4140 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-45", "-55"}, {}); |
4141 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "45", "55"}, {}); |
4142 | | |
4143 | | // Test exclusive ranges. |
4144 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-20", "(40"}, {"10", "v66"}); |
4145 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-20", "(-20"}, {}); |
4146 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-20", "-10"}, {}); |
4147 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-10", "(10"}, {}); |
4148 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-50", "(-40"}, {}); |
4149 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-55", "(11"}, |
4150 | 0 | {"10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4151 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-50", "10"}, |
4152 | 0 | {"10", "v66", "-20", "v44", "-40", "v22"}); |
4153 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-51", "10"}, |
4154 | 0 | {"10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4155 | | |
4156 | | // Test infinity. |
4157 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-10", "+inf"}, |
4158 | 0 | {"40", "v99", "10", "v66"}); |
4159 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "10"}, |
4160 | 0 | {"10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4161 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-10", "(+inf"}, |
4162 | 0 | {"40", "v99", "10", "v66"}); |
4163 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-inf", "10"}, |
4164 | 0 | {"10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4165 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "+inf"}, |
4166 | 0 | {"40", "v99", "10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4167 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "(-inf", "(+inf"}, |
4168 | 0 | {"40", "v99", "10", "v66", "-20", "v44", "-40", "v22", "-50", "v11"}); |
4169 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "+inf", "-inf"}, {}); |
4170 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "+inf", "10"}, {}); |
4171 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "+inf", "+inf"}, {}); |
4172 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "-inf"}, {}); |
4173 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "-inf", "-inf"}, {}); |
4174 | 0 | SyncClient(); |
4175 | | |
4176 | | // Test infinity with int64 min, max. |
4177 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_inf", |
4178 | 0 | int64Min_, "v1", |
4179 | 0 | "-10", "v2", |
4180 | 0 | "10", "v3", |
4181 | 0 | int64Max_, "v4", |
4182 | 0 | }); |
4183 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "-inf", "+inf"}, |
4184 | 0 | {int64Max_, "v4", "10", "v3", "-10", "v2", int64Min_, "v1"}); |
4185 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "(-inf", "(+inf"}, |
4186 | 0 | {int64Max_, "v4", "10", "v3", "-10", "v2", int64Min_, "v1"}); |
4187 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "-inf", "-inf"}, |
4188 | 0 | {}); |
4189 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "+inf", "+inf"}, |
4190 | 0 | {}); |
4191 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "-10", "(+inf"}, |
4192 | 0 | {int64Max_, "v4", "10", "v3", "-10", "v2"}); |
4193 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "-10", "+inf"}, |
4194 | 0 | {int64Max_, "v4", "10", "v3", "-10", "v2"}); |
4195 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "(-inf", "10"}, |
4196 | 0 | {"10", "v3", "-10", "v2", int64Min_, "v1"}); |
4197 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", "-inf", "10"}, |
4198 | 0 | {"10", "v3", "-10", "v2", int64Min_, "v1"}); |
4199 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64Min_, int64Max_}, |
4200 | 0 | {int64Max_, "v4", "10", "v3", "-10", "v2", int64Min_, "v1"}); |
4201 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64MaxExclusive_, int64Max_}, |
4202 | 0 | {}); |
4203 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64MaxExclusive_, int64MaxExclusive_}, |
4204 | 0 | {}); |
4205 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64Max_, int64MaxExclusive_}, |
4206 | 0 | {}); |
4207 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64MinExclusive_, int64MinExclusive_}, |
4208 | 0 | {}); |
4209 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64MinExclusive_, int64Min_}, |
4210 | 0 | {}); |
4211 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64Min_, int64MinExclusive_}, |
4212 | 0 | {} ); |
4213 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64Min_, int64Min_}, |
4214 | 0 | {int64Min_, "v1"}); |
4215 | 0 | DoRedisTestArray(__LINE__, {"TSREVRANGEBYTIME", "ts_inf", int64Max_, int64Max_}, |
4216 | 0 | {int64Max_, "v4"}); |
4217 | | |
4218 | | // Test invalid requests. |
4219 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20", "30"}); |
4220 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "abc"}); |
4221 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20.1"}); |
4222 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20", "LIMIT"}); |
4223 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20", "LIMIT", "BC"}); |
4224 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20", "LIMIT", "1.3"}); |
4225 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "ts_key", "10", "20", "SOMETHING", "3"}); |
4226 | |
|
4227 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", |
4228 | 0 | "1", "v100", |
4229 | 0 | "2", "v200", |
4230 | 0 | "3", "v300", |
4231 | 0 | "4", "v400", |
4232 | 0 | "5", "v500", |
4233 | 0 | }); |
4234 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", |
4235 | 0 | "6", "v600" |
4236 | 0 | }); |
4237 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value"}); |
4238 | 0 | SyncClient(); |
4239 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "map_key", "1", "5"}); |
4240 | 0 | DoRedisTestExpectError(__LINE__, {"TSREVRANGEBYTIME", "key"}); |
4241 | |
|
4242 | 0 | SyncClient(); |
4243 | 0 | VerifyCallbacks(); |
4244 | 0 | } |
4245 | | |
4246 | 0 | TEST_F(TestRedisService, TestTsRem) { |
4247 | | |
4248 | | // Try some deletes before inserting any data. |
4249 | 0 | DoRedisTestOk(__LINE__, {"TSREM", "invalid_key", "20", "40", "70", "90"}); |
4250 | |
|
4251 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4252 | 0 | "10", "v1", |
4253 | 0 | "20", "v2", |
4254 | 0 | "30", "v3", |
4255 | 0 | "40", "v4", |
4256 | 0 | "50", "v5", |
4257 | 0 | "60", "v6", |
4258 | 0 | "70", "v7", |
4259 | 0 | "80", "v8", |
4260 | 0 | "90", "v9", |
4261 | 0 | "100", "v10", |
4262 | 0 | }); |
4263 | | |
4264 | | // Try some deletes. |
4265 | 0 | SyncClient(); |
4266 | 0 | DoRedisTestOk(__LINE__, {"TSREM", "ts_key", "20", "40", "70", "90"}); |
4267 | 0 | SyncClient(); |
4268 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "100"}, |
4269 | 0 | {"10", "v1", "30", "v3", "50", "v5", "60", "v6", "80", "v8", "100", "v10"}); |
4270 | 0 | DoRedisTestOk(__LINE__, {"TSREM", "ts_key", "30", "60", "70", "80", "90"}); |
4271 | 0 | SyncClient(); |
4272 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "100"}, |
4273 | 0 | {"10", "v1", "50", "v5", "100", "v10"}); |
4274 | | |
4275 | | // Now add some data and try some more deletes. |
4276 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4277 | 0 | "25", "v25", |
4278 | 0 | "35", "v35", |
4279 | 0 | "45", "v45", |
4280 | 0 | "55", "v55", |
4281 | 0 | "75", "v75", |
4282 | 0 | "85", "v85", |
4283 | 0 | "95", "v95", |
4284 | 0 | }); |
4285 | 0 | SyncClient(); |
4286 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "100"}, |
4287 | 0 | {"10", "v1", "25", "v25", "35", "v35", "45", "v45", "50", "v5", "55", "v55", |
4288 | 0 | "75", "v75", "85", "v85", "95", "v95", "100", "v10"}); |
4289 | 0 | DoRedisTestOk(__LINE__, {"TSREM", "ts_key", "10", "25", "30", "45", "50", "65", "70", "85", |
4290 | 0 | "90"}); |
4291 | 0 | SyncClient(); |
4292 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "100"}, |
4293 | 0 | {"35", "v35", "55", "v55", "75", "v75", "95", "v95", "100", "v10"}); |
4294 | | |
4295 | | // Delete top level, then add some values and verify. |
4296 | 0 | DoRedisTestInt(__LINE__, {"DEL", "ts_key"}, 1); |
4297 | 0 | SyncClient(); |
4298 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", |
4299 | 0 | "22", "v22", |
4300 | 0 | "33", "v33", |
4301 | 0 | "44", "v44", |
4302 | 0 | "55", "v55", |
4303 | 0 | "77", "v77", |
4304 | 0 | "88", "v88", |
4305 | 0 | "99", "v99", |
4306 | 0 | }); |
4307 | 0 | SyncClient(); |
4308 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "10", "100"}, |
4309 | 0 | {"22", "v22", "33", "v33", "44", "v44", "55", "v55", "77", "v77", "88", "v88", |
4310 | 0 | "99", "v99"}); |
4311 | | |
4312 | | // Now try invalid commands. |
4313 | 0 | DoRedisTestExpectError(__LINE__, {"TSREM", "ts_key"}); // Not enough arguments. |
4314 | 0 | DoRedisTestExpectError(__LINE__, {"TSREM", "ts_key", "v1", "10"}); // wrong type for timestamp. |
4315 | 0 | DoRedisTestExpectError(__LINE__, {"TSREM", "ts_key", "1.0", "10"}); // wrong type for timestamp. |
4316 | 0 | DoRedisTestExpectError(__LINE__, {"HDEL", "ts_key", "22"}); // wrong delete type. |
4317 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "hkey", "10", "v1", "20", "v2"}); |
4318 | 0 | DoRedisTestExpectError(__LINE__, {"TSREM", "hkey", "10", "20"}); // wrong delete type. |
4319 | |
|
4320 | 0 | SyncClient(); |
4321 | 0 | VerifyCallbacks(); |
4322 | 0 | } |
4323 | | |
4324 | 0 | TEST_F(TestRedisService, TestOverwrites) { |
4325 | | // The default value is true, but we explicitly set this here for clarity. |
4326 | 0 | FLAGS_emulate_redis_responses = true; |
4327 | | |
4328 | | // Test Upsert. |
4329 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey1", "42"}, 1); |
4330 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "map_key", "subkey1"}, "42"); |
4331 | | // Overwrite the same key. Using Set. |
4332 | 0 | DoRedisTestOk(__LINE__, {"SET", "map_key", "new_value"}); |
4333 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "map_key"}, "new_value"); |
4334 | 0 | SyncClient(); |
4335 | | |
4336 | | // Test NX. |
4337 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value1", "NX"}); |
4338 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "value1"); |
4339 | 0 | DoRedisTestNull(__LINE__, {"SET", "key", "value2", "NX"}); |
4340 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "value1"); |
4341 | | |
4342 | | // Test XX. |
4343 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value2", "XX"}); |
4344 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "value2"); |
4345 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value3", "XX"}); |
4346 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "value3"); |
4347 | 0 | DoRedisTestNull(__LINE__, {"SET", "unknown_key", "value", "XX"}); |
4348 | |
|
4349 | 0 | SyncClient(); |
4350 | 0 | VerifyCallbacks(); |
4351 | 0 | } |
4352 | | |
4353 | 0 | TEST_F(TestRedisService, TestSetNX) { |
4354 | | // Test Insert. |
4355 | 0 | DoRedisTestInt(__LINE__, {"SETNX", "key1", "value1"}, 1); |
4356 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "value1"); |
4357 | | // Overwrite the same key. Using SetNX. |
4358 | 0 | DoRedisTestInt(__LINE__, {"SETNX", "key1", "new_value"}, 0); |
4359 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "value1"); |
4360 | | // Test a new key. |
4361 | 0 | DoRedisTestInt(__LINE__, {"SETNX", "key2", "value2"}, 1); |
4362 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key2"}, "value2"); |
4363 | | |
4364 | | // Test `SET key value NX`. |
4365 | 0 | DoRedisTestOk(__LINE__, {"SET", "key3", "value3", "NX"}); |
4366 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key3"}, "value3"); |
4367 | 0 | DoRedisTestNull(__LINE__, {"SET", "key3", "new_value", "NX"}); |
4368 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key3"}, "value3"); |
4369 | | |
4370 | | // Test `SET key value NX` after SETNX. |
4371 | 0 | DoRedisTestNull(__LINE__, {"SET", "key1", "new_value", "NX"}); |
4372 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "value1"); |
4373 | | |
4374 | | // Test SETNX after `SET key value NX`. |
4375 | 0 | DoRedisTestInt(__LINE__, {"SETNX", "key3", "new_value"}, 0); |
4376 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key3"}, "value3"); |
4377 | | // Test a new key. |
4378 | 0 | DoRedisTestInt(__LINE__, {"SETNX", "key4", "value4"}, 1); |
4379 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key4"}, "value4"); |
4380 | | |
4381 | | // Now try invalid commands. |
4382 | 0 | DoRedisTestExpectError(__LINE__, {"SETNX"}); // Not enough arguments. |
4383 | 0 | DoRedisTestExpectError(__LINE__, {"SETNX", "key"}); // Not enough arguments. |
4384 | 0 | DoRedisTestExpectError(__LINE__, {"SETNX", "key", "score", "value"}); // Too many arguments. |
4385 | 0 | DoRedisTestExpectError(__LINE__, {"SETNX", "key", "score1", "value1", "score2", |
4386 | 0 | "value2"}); // Too many arguments. |
4387 | |
|
4388 | 0 | SyncClient(); |
4389 | 0 | VerifyCallbacks(); |
4390 | 0 | } |
4391 | | |
4392 | 0 | TEST_F(TestRedisService, TestAdditionalCommands) { |
4393 | | |
4394 | | // The default value is true, but we explicitly set this here for clarity. |
4395 | 0 | FLAGS_emulate_redis_responses = true; |
4396 | |
|
4397 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey1", "42"}, 1); |
4398 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey2", "12"}, 1); |
4399 | |
|
4400 | 0 | SyncClient(); |
4401 | | |
4402 | | // With emulate_redis_responses flag = true, we expect an int response 0 because the subkey |
4403 | | // already existed. If flag is false, we'll get an OK response, which is tested later. |
4404 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey1", "41"}, 0); |
4405 | |
|
4406 | 0 | SyncClient(); |
4407 | |
|
4408 | 0 | DoRedisTestBulkString(__LINE__, {"HGET", "map_key", "subkey1"}, "41"); |
4409 | |
|
4410 | 0 | DoRedisTestResultsArray( |
4411 | 0 | __LINE__, {"HMGET", "map_key", "subkey1", "subkey3", "subkey2"}, |
4412 | 0 | {RedisReply(RedisReplyType::kString, "41"), RedisReply(), |
4413 | 0 | RedisReply(RedisReplyType::kString, "12")}); |
4414 | |
|
4415 | 0 | DoRedisTestArray(__LINE__, {"HGETALL", "map_key"}, {"subkey1", "41", "subkey2", "12"}); |
4416 | |
|
4417 | 0 | DoRedisTestOk(__LINE__, {"SET", "key1", "30"}); |
4418 | |
|
4419 | 0 | SyncClient(); |
4420 | | |
4421 | | // Should be error due to wrong type. |
4422 | 0 | DoRedisTestExpectError(__LINE__, {"HGET", "key1", "subkey1"}); |
4423 | |
|
4424 | 0 | DoRedisTestBulkString(__LINE__, {"GETSET", "key1", "val1"}, "30"); |
4425 | 0 | DoRedisTestNull(__LINE__, {"GETSET", "non_existent", "val2"}); |
4426 | |
|
4427 | 0 | SyncClient(); |
4428 | |
|
4429 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "val1"); |
4430 | 0 | DoRedisTestInt(__LINE__, {"APPEND", "key1", "extra1"}, 10); |
4431 | |
|
4432 | 0 | SyncClient(); |
4433 | |
|
4434 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "val1extra1"); |
4435 | |
|
4436 | 0 | DoRedisTestNull(__LINE__, {"GET", "key2"}); |
4437 | | // Deleting an empty key should return 0 |
4438 | 0 | DoRedisTestInt(__LINE__, {"DEL", "key2"}, 0); |
4439 | | // Appending to an empty key should work |
4440 | 0 | DoRedisTestInt(__LINE__, {"APPEND", "key2", "val2"}, 4); |
4441 | |
|
4442 | 0 | SyncClient(); |
4443 | |
|
4444 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key2"}, "val2"); |
4445 | |
|
4446 | 0 | SyncClient(); |
4447 | |
|
4448 | 0 | DoRedisTestInt(__LINE__, {"DEL", "key2"}, 1); |
4449 | |
|
4450 | 0 | SyncClient(); |
4451 | |
|
4452 | 0 | DoRedisTestNull(__LINE__, {"GET", "key2"}); |
4453 | 0 | DoRedisTestInt(__LINE__, {"SETRANGE", "key1", "2", "xyz3"}, 10); |
4454 | 0 | DoRedisTestInt(__LINE__, {"SETRANGE", "sr1", "2", "abcd"}, 6); |
4455 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "sr1"}, "\0\0abcd"s); |
4456 | |
|
4457 | 0 | SyncClient(); |
4458 | |
|
4459 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key1"}, "vaxyz3tra1"); |
4460 | 0 | DoRedisTestOk(__LINE__, {"SET", "key3", "23"}); |
4461 | |
|
4462 | 0 | SyncClient(); |
4463 | |
|
4464 | 0 | DoRedisTestInt(__LINE__, {"INCR", "key3"}, 24); |
4465 | | // If no value is present, 0 is the default |
4466 | 0 | DoRedisTestInt(__LINE__, {"INCR", "key4"}, 1); |
4467 | |
|
4468 | 0 | SyncClient(); |
4469 | |
|
4470 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key3"}, "24"); |
4471 | |
|
4472 | 0 | DoRedisTestInt(__LINE__, {"STRLEN", "key1"}, 10); |
4473 | 0 | DoRedisTestInt(__LINE__, {"STRLEN", "key2"}, 0); |
4474 | 0 | DoRedisTestInt(__LINE__, {"STRLEN", "key3"}, 2); |
4475 | |
|
4476 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "key1"}, 1); |
4477 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "key2"}, 0); |
4478 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "key3"}, 1); |
4479 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "map_key"}, 1); |
4480 | 0 | DoRedisTestBulkString(__LINE__, {"GETRANGE", "key1", "1", "-1"}, "axyz3tra1"); |
4481 | 0 | DoRedisTestBulkString(__LINE__, {"GETRANGE", "key5", "1", "4"}, ""); |
4482 | 0 | DoRedisTestBulkString(__LINE__, {"GETRANGE", "key1", "-12", "4"}, "vaxyz"); |
4483 | |
|
4484 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", "subkey5", "19", "subkey6", "14"}); |
4485 | | // The last value for a duplicate key is picked up. |
4486 | 0 | DoRedisTestOk(__LINE__, {"HMSET", "map_key", "hashkey1", "v1", "hashkey2", "v2", |
4487 | 0 | "hashkey1", "v3"}); |
4488 | |
|
4489 | 0 | SyncClient(); |
4490 | |
|
4491 | 0 | DoRedisTestArray(__LINE__, {"HGETALL", "map_key"}, |
4492 | 0 | {"hashkey1", "v3", "hashkey2", "v2", "subkey1", "41", "subkey2", "12", "subkey5", "19", |
4493 | 0 | "subkey6", "14"}); |
4494 | 0 | DoRedisTestArray(__LINE__, {"HKEYS", "map_key"}, |
4495 | 0 | {"hashkey1", "hashkey2", "subkey1", "subkey2", "subkey5", "subkey6"}); |
4496 | 0 | DoRedisTestArray(__LINE__, {"HVALS", "map_key"}, |
4497 | 0 | {"v3", "v2", "41", "12", "19", "14"}); |
4498 | 0 | DoRedisTestInt(__LINE__, {"HLEN", "map_key"}, 6); |
4499 | 0 | DoRedisTestInt(__LINE__, {"HLEN", "does_not_exist"}, 0); |
4500 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey1"}, 1); |
4501 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey2"}, 1); |
4502 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey3"}, 0); |
4503 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey4"}, 0); |
4504 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey5"}, 1); |
4505 | 0 | DoRedisTestInt(__LINE__, {"HEXISTS", "map_key", "subkey6"}, 1); |
4506 | | // HSTRLEN |
4507 | 0 | DoRedisTestInt(__LINE__, {"HSTRLEN", "map_key", "subkey1"}, 2); |
4508 | 0 | DoRedisTestInt(__LINE__, {"HSTRLEN", "map_key", "does_not_exist"}, 0); |
4509 | 0 | SyncClient(); |
4510 | | |
4511 | | // HDEL |
4512 | | // subkey7 doesn't exists |
4513 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "subkey2", "subkey7", "subkey5"}, 2); |
4514 | 0 | SyncClient(); |
4515 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "subkey9"}, 0); |
4516 | 0 | SyncClient(); |
4517 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "map_key"}, 1); |
4518 | 0 | DoRedisTestArray(__LINE__, {"HGETALL", "map_key"}, {"hashkey1", "v3", "hashkey2", "v2", |
4519 | 0 | "subkey1", "41", "subkey6", "14"}); |
4520 | 0 | DoRedisTestInt(__LINE__, {"DEL", "map_key"}, 1); // Delete the whole map with a del |
4521 | 0 | SyncClient(); |
4522 | |
|
4523 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "map_key"}, 0); |
4524 | 0 | DoRedisTestArray(__LINE__, {"HGETALL", "map_key"}, {}); |
4525 | |
|
4526 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "set1"}, 0); |
4527 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "val1"}, 1); |
4528 | 0 | SyncClient(); |
4529 | 0 | DoRedisTestInt(__LINE__, {"DEL", "set1"}, 1); |
4530 | 0 | SyncClient(); |
4531 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "val1"}, 1); |
4532 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set2", "val5", "val5", "val5"}, 1); |
4533 | 0 | SyncClient(); |
4534 | 0 | DoRedisTestInt(__LINE__, {"EXISTS", "set1"}, 1); |
4535 | |
|
4536 | 0 | SyncClient(); |
4537 | |
|
4538 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "val2", "val1", "val3"}, 2); |
4539 | |
|
4540 | 0 | SyncClient(); |
4541 | |
|
4542 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "set1"}, {"val1", "val2", "val3"}); |
4543 | 0 | DoRedisTestInt(__LINE__, {"SCARD", "set1"}, 3); |
4544 | 0 | DoRedisTestInt(__LINE__, {"SCARD", "does_not_exist"}, 0); |
4545 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "val1"}, 1); |
4546 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "val2"}, 1); |
4547 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "val3"}, 1); |
4548 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "val4"}, 0); |
4549 | 0 | SyncClient(); |
4550 | | |
4551 | | // SREM remove val1 and val3. val4 doesn't exist. |
4552 | 0 | DoRedisTestInt(__LINE__, {"SREM", "set1", "val1", "val3", "val4"}, 2); |
4553 | 0 | SyncClient(); |
4554 | 0 | DoRedisTestArray(__LINE__, {"SMEMBERS", "set1"}, {"val2"}); |
4555 | | |
4556 | | // AUTH accepts 1 argument. |
4557 | 0 | DoRedisTestExpectError(__LINE__, {"AUTH", "foo", "subkey5", "19", "subkey6", "14"}); |
4558 | 0 | DoRedisTestExpectError(__LINE__, {"AUTH"}); |
4559 | | // CONFIG should be dummy implementations, that respond OK irrespective of the arguments |
4560 | 0 | DoRedisTestOk(__LINE__, {"CONFIG", "foo", "subkey5", "19", "subkey6", "14"}); |
4561 | 0 | DoRedisTestOk(__LINE__, {"CONFIG"}); |
4562 | | // Commands are pipelined and only sent when client.commit() is called. |
4563 | | // sync_commit() waits until all responses are received. |
4564 | 0 | SyncClient(); |
4565 | |
|
4566 | 0 | DoRedisTest(__LINE__, {"ROLE"}, RedisReplyType::kArray, |
4567 | 0 | [](const RedisReply& reply) { |
4568 | 0 | const auto& replies = reply.as_array(); |
4569 | 0 | ASSERT_EQ(3, replies.size()); |
4570 | 0 | ASSERT_EQ("master", replies[0].as_string()); |
4571 | 0 | ASSERT_EQ(0, replies[1].as_integer()); |
4572 | 0 | ASSERT_TRUE(replies[2].is_array()) << "replies[2]: " << replies[2].ToString(); |
4573 | 0 | ASSERT_EQ(0, replies[2].as_array().size()); |
4574 | 0 | } |
4575 | 0 | ); |
4576 | |
|
4577 | 0 | DoRedisTestBulkString(__LINE__, {"PING", "foo"}, "foo"); |
4578 | 0 | DoRedisTestSimpleString(__LINE__, {"PING"}, "PONG"); |
4579 | |
|
4580 | 0 | DoRedisTestOk(__LINE__, {"QUIT"}); |
4581 | |
|
4582 | 0 | DoRedisTestOk(__LINE__, {"FLUSHDB"}); |
4583 | |
|
4584 | 0 | SyncClient(); |
4585 | |
|
4586 | 0 | VerifyCallbacks(); |
4587 | 0 | } |
4588 | | |
4589 | 0 | TEST_F(TestRedisService, TestDel) { |
4590 | | // The default value is true, but we explicitly set this here for clarity. |
4591 | 0 | FLAGS_emulate_redis_responses = true; |
4592 | |
|
4593 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value"}); |
4594 | 0 | DoRedisTestInt(__LINE__, {"DEL", "key"}, 1); |
4595 | 0 | DoRedisTestInt(__LINE__, {"DEL", "key"}, 0); |
4596 | 0 | DoRedisTestInt(__LINE__, {"DEL", "non_existent"}, 0); |
4597 | 0 | SyncClient(); |
4598 | 0 | VerifyCallbacks(); |
4599 | 0 | } |
4600 | | |
4601 | 0 | TEST_F(TestRedisService, TestHDel) { |
4602 | | // The default value is true, but we explicitly set this here for clarity. |
4603 | 0 | FLAGS_emulate_redis_responses = true; |
4604 | |
|
4605 | 0 | DoRedisTestInt(__LINE__, {"HSET", "map_key", "subkey1", "42"}, 1); |
4606 | 0 | SyncClient(); |
4607 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "subkey1", "non_existent_1", "non_existent_2"}, 1); |
4608 | 0 | SyncClient(); |
4609 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "non_existent_1"}, 0); |
4610 | 0 | SyncClient(); |
4611 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "non_existent_1", "non_existent_2"}, 0); |
4612 | 0 | SyncClient(); |
4613 | 0 | DoRedisTestInt(__LINE__, {"HDEL", "map_key", "non_existent_1", "non_existent_1"}, 0); |
4614 | 0 | SyncClient(); |
4615 | 0 | VerifyCallbacks(); |
4616 | 0 | } |
4617 | | |
4618 | 0 | TEST_F(TestRedisService, TestSADDBatch) { |
4619 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "10"}, 1); |
4620 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "20"}, 1); |
4621 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "30"}, 1); |
4622 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set1", "30"}, 0); |
4623 | 0 | SyncClient(); |
4624 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "10"}, 1); |
4625 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "20"}, 1); |
4626 | 0 | DoRedisTestInt(__LINE__, {"SISMEMBER", "set1", "30"}, 1); |
4627 | 0 | SyncClient(); |
4628 | 0 | VerifyCallbacks(); |
4629 | 0 | } |
4630 | | |
4631 | 0 | TEST_F(TestRedisService, TestSRem) { |
4632 | | // The default value is true, but we explicitly set this here for clarity. |
4633 | 0 | FLAGS_emulate_redis_responses = true; |
4634 | |
|
4635 | 0 | DoRedisTestInt(__LINE__, {"SADD", "set_key", "subkey1"}, 1); |
4636 | 0 | SyncClient(); |
4637 | 0 | DoRedisTestInt(__LINE__, {"SREM", "set_key", "subkey1", "non_existent_1", "non_existent_2"}, 1); |
4638 | 0 | SyncClient(); |
4639 | 0 | DoRedisTestInt(__LINE__, {"SREM", "set_key", "non_existent_1"}, 0); |
4640 | 0 | SyncClient(); |
4641 | 0 | DoRedisTestInt(__LINE__, {"SREM", "set_key", "non_existent_1", "non_existent_2"}, 0); |
4642 | 0 | SyncClient(); |
4643 | 0 | DoRedisTestInt(__LINE__, {"SREM", "set_key", "non_existent_1", "non_existent_1"}, 0); |
4644 | 0 | SyncClient(); |
4645 | 0 | VerifyCallbacks(); |
4646 | 0 | } |
4647 | | |
4648 | 0 | TEST_F(TestRedisService, TestEmulateFlagFalse) { |
4649 | 0 | FLAGS_emulate_redis_responses = false; |
4650 | |
|
4651 | 0 | DoRedisTestOk(__LINE__, {"HSET", "map_key", "subkey1", "42"}); |
4652 | |
|
4653 | 0 | DoRedisTestOk(__LINE__, {"SADD", "set_key", "val1", "val2", "val1"}); |
4654 | |
|
4655 | 0 | DoRedisTestOk(__LINE__, {"HDEL", "map_key", "subkey1", "subkey2"}); |
4656 | |
|
4657 | 0 | SyncClient(); |
4658 | |
|
4659 | 0 | VerifyCallbacks(); |
4660 | 0 | } |
4661 | | |
4662 | 0 | TEST_F(TestRedisService, TestHMGetTiming) { |
4663 | 0 | const int num_keys = 50; |
4664 | | // For small hset size will not get consistent result. |
4665 | 0 | const int size_hset = 1000; |
4666 | 0 | const int num_subkeys = 1000; |
4667 | 0 | const int num_hmgets = 10; |
4668 | 0 | const bool is_random = true; |
4669 | 0 | const bool is_serial = true; // Sequentially sync client to measure latency |
4670 | 0 | const bool test_nonexisting = true; |
4671 | |
|
4672 | 0 | auto start = std::chrono::steady_clock::now(); |
4673 | |
|
4674 | 0 | for (int i = 0; i < num_keys; i++) { |
4675 | 0 | for (int j = 0; j < size_hset; j++) { |
4676 | 0 | string si = std::to_string(i); |
4677 | 0 | string sj = std::to_string(j); |
4678 | 0 | DoRedisTestInt(__LINE__, {"HSET", "parent_" + si, "subkey_" + sj, "value_" + sj}, 1); |
4679 | 0 | } |
4680 | 0 | if (IsSanitizer() || ((i & 0x7) == 0)) { |
4681 | 0 | SyncClient(); |
4682 | 0 | } |
4683 | 0 | } |
4684 | |
|
4685 | 0 | SyncClient(); |
4686 | |
|
4687 | 0 | auto mid = std::chrono::steady_clock::now(); |
4688 | |
|
4689 | 0 | int max_query_subkey = test_nonexisting ? size_hset * 2 : size_hset; |
4690 | |
|
4691 | 0 | for (int i = 0; i < num_hmgets; i++) { |
4692 | 0 | string si = std::to_string(i % num_keys); |
4693 | 0 | vector<string> command = {"HMGET", "parent_" + si}; |
4694 | 0 | vector<RedisReply> expected; |
4695 | 0 | for (int j = 0; j < num_subkeys; j++) { |
4696 | 0 | int idx = is_random ? |
4697 | 0 | RandomUniformInt(0, max_query_subkey) : |
4698 | 0 | (j * max_query_subkey) / num_subkeys; |
4699 | 0 | string sj = std::to_string(idx); |
4700 | 0 | command.push_back("subkey_" + sj); |
4701 | 0 | expected.push_back( |
4702 | 0 | idx >= size_hset ? RedisReply() : RedisReply(RedisReplyType::kString, "value_" + sj)); |
4703 | 0 | } |
4704 | 0 | DoRedisTestResultsArray(__LINE__, command, expected); |
4705 | 0 | if (is_serial) { |
4706 | 0 | SyncClient(); |
4707 | 0 | } |
4708 | 0 | } |
4709 | |
|
4710 | 0 | SyncClient(); |
4711 | |
|
4712 | 0 | auto end = std::chrono::steady_clock::now(); |
4713 | |
|
4714 | 0 | auto set_time = std::chrono::duration_cast<std::chrono::milliseconds>(mid - start).count(); |
4715 | 0 | auto get_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - mid).count(); |
4716 | |
|
4717 | 0 | LOG(INFO) << yb::Format("Total HSET time: $0ms Total HMGET time: $1ms", set_time, get_time); |
4718 | |
|
4719 | 0 | VerifyCallbacks(); |
4720 | 0 | } |
4721 | | |
4722 | 0 | TEST_F(TestRedisService, TestTtlSet) { |
4723 | 0 | std::string collection_key = "russell"; |
4724 | 0 | std::string values[10] = {"the", "set", "of", "all", "sets", |
4725 | 0 | "that", "do", "not", "contain", "themselves"}; |
4726 | 0 | int card = 10; |
4727 | 0 | TestTtlSet(&collection_key, values, card); |
4728 | 0 | } |
4729 | | |
4730 | 0 | TEST_F(TestRedisService, TestTtlSortedSet) { |
4731 | 0 | std::string collection_key = "sort_me_up"; |
4732 | 0 | CollectionEntry values[10] = { std::make_tuple("5.4223", "insertion"), |
4733 | 0 | std::make_tuple("-1", "bogo"), |
4734 | 0 | std::make_tuple("8", "selection"), |
4735 | 0 | std::make_tuple("3.1415926", "heap"), |
4736 | 0 | std::make_tuple("2.718", "quick"), |
4737 | 0 | std::make_tuple("1", "merge"), |
4738 | 0 | std::make_tuple("9.9", "bubble"), |
4739 | 0 | std::make_tuple("0", "radix"), |
4740 | 0 | std::make_tuple("9.9", "shell"), |
4741 | 0 | std::make_tuple("11", "comb") }; |
4742 | 0 | int card = 10; |
4743 | 0 | TestTtlSortedSet(&collection_key, values, card); |
4744 | 0 | } |
4745 | | |
4746 | 0 | TEST_F(TestRedisService, TestTtlHash) { |
4747 | 0 | std::string collection_key = "hash_browns"; |
4748 | 0 | CollectionEntry values[10] = { std::make_tuple("eggs", "hyperloglog"), |
4749 | 0 | std::make_tuple("bagel", "bloom"), |
4750 | 0 | std::make_tuple("ham", "quotient"), |
4751 | 0 | std::make_tuple("salmon", "cuckoo"), |
4752 | 0 | std::make_tuple("porridge", "lp_norm_sketch"), |
4753 | 0 | std::make_tuple("muffin", "count_sketch"), |
4754 | 0 | std::make_tuple("doughnut", "hopscotch"), |
4755 | 0 | std::make_tuple("oatmeal", "fountain_codes"), |
4756 | 0 | std::make_tuple("fruit", "linear_probing"), |
4757 | 0 | std::make_tuple("toast", "chained") }; |
4758 | 0 | int card = 10; |
4759 | 0 | TestTtlHash(&collection_key, values, card); |
4760 | 0 | } |
4761 | | |
4762 | 0 | TEST_F(TestRedisService, TestTtlTimeseries) { |
4763 | 0 | std::string key = "timeseries"; |
4764 | 0 | DoRedisTestOk(__LINE__, {"TSADD", key, "1", "hello", "2", "how", "3", "are", "5", "you"}); |
4765 | | // Checking TTL on timeseries. |
4766 | 0 | DoRedisTestInt(__LINE__, {"TTL", key}, -1); |
4767 | 0 | DoRedisTestInt(__LINE__, {"PTTL", key}, -1); |
4768 | 0 | SyncClient(); |
4769 | | // Checking PERSIST and (P)EXPIRE do not work. |
4770 | 0 | DoRedisTestExpectError(__LINE__, {"PERSIST", key}); |
4771 | 0 | DoRedisTestExpectError(__LINE__, {"EXPIRE", key, "13"}); |
4772 | 0 | DoRedisTestExpectError(__LINE__, {"PEXPIRE", key, "16384"}); |
4773 | 0 | SyncClient(); |
4774 | | // Checking SETEX turns it back into a normal key. |
4775 | 0 | DoRedisTestOk(__LINE__, {"SETEX", key, "6", "17"}); |
4776 | 0 | SyncClient(); |
4777 | 0 | DoRedisTestBulkString(__LINE__, {"GET", key}, "17"); |
4778 | 0 | SyncClient(); |
4779 | 0 | std::this_thread::sleep_for(7s); |
4780 | 0 | CheckExpired(&key); |
4781 | 0 | SyncClient(); |
4782 | 0 | VerifyCallbacks(); |
4783 | 0 | } |
4784 | | |
4785 | | // For testing commands where the value is overwritten, but TTL is not. |
4786 | 0 | TEST_F(TestRedisService, TestTtlModifyNoOverwrite) { |
4787 | | // TODO: when we support RENAME, it should also be added here. |
4788 | 0 | std::string k1 = "key"; |
4789 | 0 | std::string k2 = "keyy"; |
4790 | 0 | const int64_t millisecond_error = 500; |
4791 | | // Test integer modify |
4792 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, "3"}); |
4793 | 0 | SyncClient(); |
4794 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k1, "14"}, 1); |
4795 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 14); |
4796 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 14000, millisecond_error); |
4797 | 0 | DoRedisTestInt(__LINE__, {"INCR", k1}, 4); |
4798 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 14); |
4799 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 14000, millisecond_error); |
4800 | 0 | SyncClient(); |
4801 | 0 | std::this_thread::sleep_for(5s); |
4802 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 9); |
4803 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 9000, millisecond_error); |
4804 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, "4"); |
4805 | 0 | DoRedisTestInt(__LINE__, {"INCRBY", k1, "3"}, 7); |
4806 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 9); |
4807 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 9000, millisecond_error); |
4808 | 0 | SyncClient(); |
4809 | 0 | std::this_thread::sleep_for(4s); |
4810 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, "7"); |
4811 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 5); |
4812 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 5000, millisecond_error); |
4813 | 0 | SyncClient(); |
4814 | 0 | std::this_thread::sleep_for(5s); |
4815 | 0 | CheckExpired(&k1); |
4816 | | // Test string modify |
4817 | 0 | DoRedisTestOk(__LINE__, {"SETEX", k2, "12", "from what I've tasted of desire "}); |
4818 | 0 | SyncClient(); |
4819 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 12); |
4820 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 12000, millisecond_error); |
4821 | 0 | DoRedisTestInt(__LINE__, {"APPEND", k2, "I hold with those who favor fire."}, 65); |
4822 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 12); |
4823 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 12000, millisecond_error); |
4824 | 0 | SyncClient(); |
4825 | 0 | std::this_thread::sleep_for(5s); |
4826 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 7); |
4827 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 7000, millisecond_error); |
4828 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k2}, "from what I've tasted of desire " |
4829 | 0 | "I hold with those who favor fire."); |
4830 | 0 | SyncClient(); |
4831 | 0 | std::this_thread::sleep_for(3s); |
4832 | 0 | DoRedisTestInt(__LINE__, {"SETRANGE", k2, "5", "the beginning of time, sir"}, 65); |
4833 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 4); |
4834 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 4000, millisecond_error); |
4835 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k2}, "from the beginning of time, " |
4836 | 0 | "sir I hold with those who favor fire."); |
4837 | 0 | SyncClient(); |
4838 | 0 | std::this_thread::sleep_for(2s); |
4839 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 2); |
4840 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 2000, millisecond_error); |
4841 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k2}, "from the beginning of time, " |
4842 | 0 | "sir I hold with those who favor fire."); |
4843 | 0 | SyncClient(); |
4844 | 0 | std::this_thread::sleep_for(3s); |
4845 | 0 | CheckExpired(&k2); |
4846 | | // Test Persist |
4847 | 0 | DoRedisTestOk(__LINE__, {"SETEX", k1, "13", "we've been pulling out the nails that hold up"}); |
4848 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 13); |
4849 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 13000, millisecond_error); |
4850 | 0 | DoRedisTestInt(__LINE__, {"APPEND", k1, " everything you've known"}, 69); |
4851 | 0 | SyncClient(); |
4852 | 0 | std::this_thread::sleep_for(5s); |
4853 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 8); |
4854 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 8000, millisecond_error); |
4855 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, "we've been pulling out the nails " |
4856 | 0 | "that hold up everything you've known"); |
4857 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k1}, 1); |
4858 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
4859 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
4860 | 0 | SyncClient(); |
4861 | 0 | std::this_thread::sleep_for(9s); |
4862 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, "we've been pulling out the nails " |
4863 | 0 | "that hold up everything you've known"); |
4864 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
4865 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
4866 | 0 | } |
4867 | | |
4868 | | // For testing TTL-related commands on primitives. |
4869 | 0 | TEST_F(TestRedisService, TestTtlPrimitive) { |
4870 | 0 | std::string k1 = "foo"; |
4871 | 0 | std::string k2 = "fu"; |
4872 | 0 | std::string k3 = "phu"; |
4873 | 0 | std::string value = "bar"; |
4874 | 0 | int64_t millisecond_error = 500; |
4875 | | // Checking expected behavior on a key with no ttl. |
4876 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value}); |
4877 | 0 | SyncClient(); |
4878 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4879 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, -2); |
4880 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k2}, -2); |
4881 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
4882 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
4883 | 0 | SyncClient(); |
4884 | | // Setting a TTL and checking expected return values. |
4885 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k1, "4"}, 1); |
4886 | 0 | SyncClient(); |
4887 | 0 | { |
4888 | 0 | int attempt = 1; |
4889 | 0 | while (true) { |
4890 | 0 | const auto ttl_set_at = std::chrono::system_clock::now(); |
4891 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 4); |
4892 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 4000, millisecond_error); |
4893 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4894 | 0 | SyncClient(); |
4895 | 0 | auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
4896 | 0 | std::chrono::system_clock::now() - ttl_set_at).count(); |
4897 | 0 | std::this_thread::sleep_for(std::max( |
4898 | 0 | static_cast<int64_t>(0), static_cast<int64_t>(2500 - elapsed_ms)) * 1ms); |
4899 | | // By this point there should be about 1.4 seconds left until the key's expiration. |
4900 | 0 | auto total_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( |
4901 | 0 | std::chrono::system_clock::now() - ttl_set_at).count(); |
4902 | 0 | if (total_elapsed_ms > 2550) { |
4903 | 0 | if (attempt < 10) { |
4904 | 0 | LOG(INFO) << "TTL test took too long, re-trying (attempt: " |
4905 | 0 | << attempt << ")"; |
4906 | 0 | attempt++; |
4907 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value}); |
4908 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k1, "4"}, 1); |
4909 | 0 | continue; |
4910 | 0 | } else { |
4911 | 0 | LOG(WARNING) << "TTL test took too long, not re-trying: attempt=" << attempt; |
4912 | 0 | } |
4913 | 0 | } |
4914 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 1); |
4915 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 1000, millisecond_error); |
4916 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4917 | 0 | SyncClient(); |
4918 | | // Checking expected return values after expiration. |
4919 | 0 | std::this_thread::sleep_for(2s); |
4920 | 0 | CheckExpiredPrimitive(&k1); |
4921 | 0 | break; // Success. |
4922 | 0 | } |
4923 | 0 | } |
4924 | | // Testing functionality with SETEX. |
4925 | 0 | DoRedisTestOk(__LINE__, {"SETEX", k1, "5", value}); |
4926 | 0 | SyncClient(); |
4927 | 0 | DoRedisTestIntRange(__LINE__, {"TTL", k1}, 4, 5); |
4928 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 4500, millisecond_error); |
4929 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4930 | 0 | SyncClient(); |
4931 | | // Set a new, earlier expiration. |
4932 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k1, "2"}, 1); |
4933 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 2); |
4934 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 1500, millisecond_error); |
4935 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4936 | 0 | SyncClient(); |
4937 | | // Check that the value expires as expected. |
4938 | 0 | std::this_thread::sleep_for(2s); |
4939 | 0 | CheckExpiredPrimitive(&k1); |
4940 | | // Initialize with SET using the EX flag. |
4941 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value, "EX", "2"}); |
4942 | 0 | SyncClient(); |
4943 | | // Set a new, later, expiration. |
4944 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k1, "8"}, 1); |
4945 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 8); |
4946 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 8000, millisecond_error); |
4947 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4948 | 0 | SyncClient(); |
4949 | | // Checking expected return values after a while, before expiration. |
4950 | 0 | std::this_thread::sleep_for(4s); |
4951 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, 4); |
4952 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k1}, 4000, millisecond_error); |
4953 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4954 | 0 | SyncClient(); |
4955 | | // Persisting the key and checking expected return values. |
4956 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k1}, 1); |
4957 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
4958 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
4959 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4960 | 0 | SyncClient(); |
4961 | | // Check that the key and value are still there after a while. |
4962 | 0 | std::this_thread::sleep_for(30s); |
4963 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
4964 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
4965 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
4966 | 0 | SyncClient(); |
4967 | | // Persist a key that does not exist. |
4968 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k2}, 0); |
4969 | 0 | SyncClient(); |
4970 | | // Persist a key that has no TTL. |
4971 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k1}, 0); |
4972 | 0 | SyncClient(); |
4973 | | // Vanilla set on a key and persisting it. |
4974 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
4975 | 0 | SyncClient(); |
4976 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k2}, 0); |
4977 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, -1); |
4978 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k2}, -1); |
4979 | 0 | SyncClient(); |
4980 | | // Expiring with an invalid TTL. We do not check the minimum, |
4981 | | // because any negative value leads to an immediate deletion. |
4982 | 0 | DoRedisTestExpectError(__LINE__, {"PEXPIRE", k2, |
4983 | 0 | std::to_string(kRedisMaxTtlMillis + 1)}); |
4984 | 0 | DoRedisTestExpectError(__LINE__, {"EXPIRE", k2, |
4985 | 0 | std::to_string(kRedisMaxTtlMillis / MonoTime::kMillisecondsPerSecond + 1)}); |
4986 | 0 | SyncClient(); |
4987 | | // Test that setting a zero-valued TTL properly expires the value. |
4988 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k2, "0"}, 1); |
4989 | 0 | CheckExpiredPrimitive(&k2); |
4990 | | // One more time with a negative TTL. |
4991 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
4992 | 0 | SyncClient(); |
4993 | 0 | DoRedisTestInt(__LINE__, {"EXPIRE", k2, "-7"}, 1); |
4994 | 0 | CheckExpiredPrimitive(&k2); |
4995 | 0 | DoRedisTestOk(__LINE__, {"SETEX", k2, "-7", value}); |
4996 | 0 | CheckExpiredPrimitive(&k2); |
4997 | | // Test PExpire |
4998 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
4999 | 0 | SyncClient(); |
5000 | 0 | DoRedisTestInt(__LINE__, {"PEXPIRE", k2, "3200"}, 1); |
5001 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 3); |
5002 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 3200, millisecond_error); |
5003 | 0 | SyncClient(); |
5004 | 0 | std::this_thread::sleep_for(1s); |
5005 | 0 | DoRedisTestInt(__LINE__, {"TTL", k2}, 2); |
5006 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 2200, millisecond_error); |
5007 | 0 | SyncClient(); |
5008 | 0 | std::this_thread::sleep_for(3s); |
5009 | 0 | CheckExpiredPrimitive(&k2); |
5010 | | // Test PSetEx |
5011 | 0 | DoRedisTestOk(__LINE__, {"PSETEX", k3, "2300", value}); |
5012 | 0 | SyncClient(); |
5013 | 0 | std::this_thread::sleep_for(1s); |
5014 | 0 | DoRedisTestInt(__LINE__, {"TTL", k3}, 1); |
5015 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k3}, 1300, millisecond_error); |
5016 | 0 | SyncClient(); |
5017 | 0 | std::this_thread::sleep_for(2s); |
5018 | 0 | CheckExpiredPrimitive(&k3); |
5019 | 0 | VerifyCallbacks(); |
5020 | 0 | } |
5021 | | |
5022 | | // For testing TestExpireAt |
5023 | 0 | TEST_F(TestRedisService, TestExpireAt) { |
5024 | 0 | std::string k1 = "foo"; |
5025 | 0 | std::string k2 = "fu"; |
5026 | 0 | std::string k3 = "phu"; |
5027 | 0 | std::string value = "bar"; |
5028 | 0 | int64_t millisecond_error = 500; |
5029 | 0 | int64_t second_error = 1; |
5030 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value}); |
5031 | 0 | SyncClient(); |
5032 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, std::to_string(std::time(0) + 5)}, 1); |
5033 | 0 | SyncClient(); |
5034 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k1}, 5, second_error); |
5035 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5036 | 0 | SyncClient(); |
5037 | 0 | std::this_thread::sleep_for(2s); |
5038 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k1}, 3, second_error); |
5039 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5040 | 0 | SyncClient(); |
5041 | | // Setting a new, later expiration. |
5042 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, std::to_string(std::time(0) + 7)}, 1); |
5043 | 0 | SyncClient(); |
5044 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k1}, 7, second_error); |
5045 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5046 | 0 | SyncClient(); |
5047 | | // Checking expected return values after expiration. |
5048 | 0 | std::this_thread::sleep_for(8s); |
5049 | 0 | CheckExpiredPrimitive(&k1); |
5050 | | |
5051 | | // Again, but with an earlier expiration. |
5052 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value}); |
5053 | 0 | SyncClient(); |
5054 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, std::to_string(std::time(0) + 13)}, 1); |
5055 | 0 | SyncClient(); |
5056 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k1}, 13, second_error); |
5057 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5058 | 0 | SyncClient(); |
5059 | | // Setting a new, earlier expiration. |
5060 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, std::to_string(std::time(0) + 5)}, 1); |
5061 | 0 | SyncClient(); |
5062 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k1}, 5, second_error); |
5063 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5064 | 0 | SyncClient(); |
5065 | | // Check that the value expires as expected. |
5066 | 0 | std::this_thread::sleep_for(6s); |
5067 | 0 | CheckExpiredPrimitive(&k1); |
5068 | | |
5069 | | // Persisting the key and checking expected return values. |
5070 | 0 | DoRedisTestOk(__LINE__, {"SET", k1, value}); |
5071 | 0 | SyncClient(); |
5072 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, std::to_string(std::time(0) + 3)}, 1); |
5073 | 0 | SyncClient(); |
5074 | 0 | DoRedisTestInt(__LINE__, {"PERSIST", k1}, 1); |
5075 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
5076 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
5077 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5078 | 0 | SyncClient(); |
5079 | | // Check that the key and value are still there after a while. |
5080 | 0 | std::this_thread::sleep_for(30s); |
5081 | 0 | DoRedisTestInt(__LINE__, {"TTL", k1}, -1); |
5082 | 0 | DoRedisTestInt(__LINE__, {"PTTL", k1}, -1); |
5083 | 0 | DoRedisTestBulkString(__LINE__, {"GET", k1}, value); |
5084 | 0 | SyncClient(); |
5085 | | // Test that setting a zero-valued time properly expires the value. |
5086 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k1, "0"}, 1); |
5087 | 0 | CheckExpiredPrimitive(&k1); |
5088 | | // One more time with a negative expiration time. |
5089 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
5090 | 0 | SyncClient(); |
5091 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k2, "-7"}, 1); |
5092 | 0 | CheckExpiredPrimitive(&k2); |
5093 | | // Again with times before the current time. |
5094 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
5095 | 0 | SyncClient(); |
5096 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k2, std::to_string(std::time(0) - 3)}, 1); |
5097 | 0 | CheckExpiredPrimitive(&k2); |
5098 | | // Again with the current time. |
5099 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
5100 | 0 | SyncClient(); |
5101 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k2, std::to_string(std::time(0))}, 1); |
5102 | 0 | CheckExpiredPrimitive(&k2); |
5103 | | // Test PExpireAt |
5104 | 0 | DoRedisTestOk(__LINE__, {"SET", k2, value}); |
5105 | 0 | SyncClient(); |
5106 | 0 | DoRedisTestInt(__LINE__, {"PEXPIREAT", k2, std::to_string(std::time(0) * 1000 + 3200)}, 1); |
5107 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k2}, 3, second_error); |
5108 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 3200, 2 * millisecond_error); |
5109 | 0 | SyncClient(); |
5110 | 0 | std::this_thread::sleep_for(1s); |
5111 | 0 | DoRedisTestApproxInt(__LINE__, {"TTL", k2}, 2, second_error); |
5112 | 0 | DoRedisTestApproxInt(__LINE__, {"PTTL", k2}, 2200, 2 * millisecond_error); |
5113 | 0 | SyncClient(); |
5114 | 0 | std::this_thread::sleep_for(3s); |
5115 | 0 | CheckExpiredPrimitive(&k2); |
5116 | | // Test ExpireAt on nonexistent key |
5117 | 0 | DoRedisTestInt(__LINE__, {"EXPIREAT", k3, std::to_string(std::time(0) + 4)}, 0); |
5118 | 0 | SyncClient(); |
5119 | 0 | DoRedisTestNull(__LINE__, {"GET", k3}); |
5120 | 0 | SyncClient(); |
5121 | 0 | VerifyCallbacks(); |
5122 | 0 | } |
5123 | | |
5124 | 0 | TEST_F(TestRedisService, TestQuit) { |
5125 | 0 | DoRedisTestOk(__LINE__, {"SET", "key", "value"}); |
5126 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "key"}, "value"); |
5127 | 0 | DoRedisTestInt(__LINE__, {"DEL", "key"}, 1); |
5128 | 0 | DoRedisTestOk(__LINE__, {"QUIT"}); |
5129 | 0 | SyncClient(); |
5130 | 0 | VerifyCallbacks(); |
5131 | | // Connection closed so following command fails |
5132 | 0 | DoRedisTestExpectError(__LINE__, {"SET", "key", "value"}); |
5133 | 0 | SyncClient(); |
5134 | 0 | VerifyCallbacks(); |
5135 | 0 | } |
5136 | | |
5137 | 0 | TEST_F(TestRedisService, TestFlushAll) { |
5138 | 0 | TestFlush("FLUSHALL", false); |
5139 | 0 | TestFlush("FLUSHALL", true); |
5140 | 0 | } |
5141 | | |
5142 | 0 | TEST_F(TestRedisService, TestFlushDb) { |
5143 | 0 | TestFlush("FLUSHDB", false); |
5144 | 0 | TestFlush("FLUSHDB", true); |
5145 | 0 | } |
5146 | | |
5147 | | // Test deque functionality of the list. |
5148 | 0 | TEST_F(TestRedisService, TestListBasic) { |
5149 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "letters", "florea", "elena", "dumitru"}, 3); |
5150 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 3); |
5151 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "letters", "constantin", "barbu"}, 5); |
5152 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 5); |
5153 | 0 | DoRedisTestBulkString(__LINE__, {"LPOP", "letters"}, "barbu"); |
5154 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 4); |
5155 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "letters", "ana"}, 5); |
5156 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 5); |
5157 | 0 | DoRedisTestInt(__LINE__, {"RPUSH", "letters", "lazar", "maria", "nicolae"}, 8); |
5158 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 8); |
5159 | 0 | DoRedisTestBulkString(__LINE__, {"LPOP", "letters"}, "ana"); |
5160 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 7); |
5161 | 0 | DoRedisTestBulkString(__LINE__, {"RPOP", "letters"}, "nicolae"); |
5162 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 6); |
5163 | 0 | DoRedisTestInt(__LINE__, {"RPUSH", "letters", "gheorghe", "haralambie", "ion"}, 9); |
5164 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 9); |
5165 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "letters", "vasile", "udrea", "tudor", "sandu"}, 13); |
5166 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 13); |
5167 | 0 | DoRedisTestInt(__LINE__, {"RPUSH", "letters", "jiu", "kilogram"}, 15); |
5168 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 15); |
5169 | 0 | DoRedisTestBulkString(__LINE__, {"RPOP", "letters"}, "kilogram"); |
5170 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 14); |
5171 | 0 | DoRedisTestBulkString(__LINE__, {"LPOP", "letters"}, "sandu"); |
5172 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 13); |
5173 | 0 | DoRedisTestInt(__LINE__, {"RPUSH", "letters", "dublu v", "xenia", "i grec"}, 16); |
5174 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 16); |
5175 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "letters", "radu", "q", "petre", "olga"}, 20); |
5176 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 20); |
5177 | 0 | DoRedisTestBulkString(__LINE__, {"RPOP", "letters"}, "i grec"); |
5178 | 0 | DoRedisTestInt(__LINE__, {"LLEN", "letters"}, 19); |
5179 | 0 | DoRedisTestInt(__LINE__, {"RPUSH", "letters", "zamfir"}, 20); |
5180 | 0 | SyncClient(); |
5181 | | |
5182 | | // Degenerate cases |
5183 | 0 | DoRedisTestOk(__LINE__, {"SET", "bravo", "alpha"}); |
5184 | 0 | DoRedisTestNull(__LINE__, {"LPOP", "november"}); |
5185 | 0 | DoRedisTestNull(__LINE__, {"RPOP", "kilo"}); |
5186 | 0 | DoRedisTestInt(__LINE__, {"LPUSH", "sierra", "yankee"}, 1); |
5187 | 0 | SyncClient(); |
5188 | 0 | DoRedisTestExpectError(__LINE__, {"LPOP", "bravo"}); |
5189 | 0 | DoRedisTestBulkString(__LINE__, {"RPOP", "sierra"}, "yankee"); |
5190 | 0 | DoRedisTestExpectError(__LINE__, {"RPOP", "bravo"}); |
5191 | 0 | SyncClient(); |
5192 | 0 | DoRedisTestNull(__LINE__, {"LPOP", "sierra"}); |
5193 | 0 | DoRedisTestNull(__LINE__, {"RPOP", "sierra"}); |
5194 | 0 | } |
5195 | | |
5196 | 0 | TEST_F(TestRedisService, Keys) { |
5197 | | // The default value is true, but we explicitly set this here for clarity. |
5198 | 0 | FLAGS_emulate_redis_responses = true; |
5199 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key_0", "1", "a"}, 1); |
5200 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key_0", "2", "b"}, 1); |
5201 | 0 | DoRedisTestOk(__LINE__, {"SET", "z_key_1", "v1"}); |
5202 | 0 | DoRedisTestOk(__LINE__, {"SET", "z_key_1", "v2"}); |
5203 | 0 | SyncClient(); |
5204 | |
|
5205 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*"}, {"z_key_1", "z_key_0"}); |
5206 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*key*1"}, {"z_key_1"}); |
5207 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*key\\*1"}, {}); |
5208 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_[^1]"}, {"z_key_0"}); |
5209 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_[02]"}, {"z_key_0"}); |
5210 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_k?y_?"}, {"z_key_1", "z_key_0"}); |
5211 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_\\?"}, {}); |
5212 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_["}, {}); |
5213 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_."}, {}); |
5214 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_[]"}, {}); |
5215 | 0 | SyncClient(); |
5216 | |
|
5217 | 0 | DoRedisTestInt(__LINE__, {"HSET", "z_key_\0", "f", "v"}, 1); |
5218 | 0 | SyncClient(); |
5219 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "z_key_\0"}, {"z_key_\0"}); |
5220 | |
|
5221 | 0 | SyncClient(); |
5222 | 0 | VerifyCallbacks(); |
5223 | 0 | } |
5224 | | |
5225 | 0 | TEST_F(TestRedisService, KeysZeroChar) { |
5226 | 0 | FLAGS_emulate_redis_responses = true; |
5227 | 0 | string s("foo\0bar", 6); |
5228 | 0 | string s1("foo\0bars", 7); |
5229 | 0 | DoRedisTestInt(__LINE__, {"HSET", s, "1", "a"}, 1); |
5230 | 0 | SyncClient(); |
5231 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "foo"}, {}); |
5232 | 0 | DoRedisTestArray(__LINE__, {"KEYS", s}, {s}); |
5233 | 0 | DoRedisTestArray(__LINE__, {"KEYS", s1}, {}); |
5234 | 0 | SyncClient(); |
5235 | 0 | VerifyCallbacks(); |
5236 | 0 | } |
5237 | | |
5238 | 0 | TEST_F(TestRedisService, RangeScanTimeout) { |
5239 | | // Test SortedSets. |
5240 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "1.0", "v1"}, 1); |
5241 | 0 | SyncClient(); |
5242 | 0 | DoRedisTestArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf"}, {"v1"}); |
5243 | 0 | SyncClient(); |
5244 | |
|
5245 | 0 | FLAGS_TEST_tserver_timeout = true; |
5246 | 0 | DoRedisTestExpectError(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf"}, |
5247 | 0 | "Deadline for query passed."); |
5248 | 0 | SyncClient(); |
5249 | 0 | FLAGS_TEST_tserver_timeout = false; |
5250 | | |
5251 | | // Test TimeSeries. |
5252 | 0 | DoRedisTestOk(__LINE__, {"TSADD", "ts_key", "1", "v1"}); |
5253 | 0 | SyncClient(); |
5254 | 0 | DoRedisTestArray(__LINE__, {"TSRANGEBYTIME", "ts_key", "-inf", "+inf"}, {"1", "v1"}); |
5255 | 0 | SyncClient(); |
5256 | |
|
5257 | 0 | FLAGS_TEST_tserver_timeout = true; |
5258 | 0 | DoRedisTestExpectError(__LINE__, {"TSRANGEBYTIME", "ts_key", "-inf", "+inf"}, |
5259 | 0 | "Deadline for query passed."); |
5260 | 0 | SyncClient(); |
5261 | 0 | FLAGS_TEST_tserver_timeout = false; |
5262 | | |
5263 | | // Test a point read doesn't time out. |
5264 | 0 | DoRedisTestOk(__LINE__, {"SET", "k", "v"}); |
5265 | 0 | SyncClient(); |
5266 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k"}, "v"); |
5267 | 0 | SyncClient(); |
5268 | |
|
5269 | 0 | FLAGS_TEST_tserver_timeout = true; |
5270 | 0 | DoRedisTestBulkString(__LINE__, {"GET", "k"}, "v"); |
5271 | |
|
5272 | 0 | SyncClient(); |
5273 | 0 | VerifyCallbacks(); |
5274 | 0 | } |
5275 | | |
5276 | 0 | TEST_F(TestRedisService, KeysTimeout) { |
5277 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "1.0", "v1"}, 1); |
5278 | 0 | SyncClient(); |
5279 | 0 | FLAGS_TEST_tserver_timeout = true; |
5280 | 0 | DoRedisTestExpectError(__LINE__, {"KEYS", "*"}, |
5281 | 0 | "Errors occurred while reaching out to the tablet servers"); |
5282 | 0 | SyncClient(); |
5283 | 0 | FLAGS_TEST_tserver_timeout = false; |
5284 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*"}, {"z_key"}); |
5285 | 0 | SyncClient(); |
5286 | 0 | VerifyCallbacks(); |
5287 | 0 | } |
5288 | | |
5289 | 0 | TEST_F(TestRedisService, KeysWithFlush) { |
5290 | 0 | for (int i = 0; i < 2; i++) { |
5291 | 0 | DoRedisTestOk(__LINE__, {"SET", Format("k$0", i), Format("v$0", i)}); |
5292 | 0 | SyncClient(); |
5293 | 0 | ASSERT_OK(FlushRedisTable()); |
5294 | 0 | } |
5295 | |
|
5296 | 0 | DoRedisTestArray(__LINE__, {"KEYS", "*"}, {"k0", "k1"}); |
5297 | 0 | SyncClient(); |
5298 | 0 | VerifyCallbacks(); |
5299 | 0 | } |
5300 | | |
5301 | 0 | TEST_F(TestRedisService, SortedSetsIncr) { |
5302 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "1", "v1"}, 1); |
5303 | 0 | SyncClient(); |
5304 | 0 | DoRedisTestInt(__LINE__, {"ZADD", "z_key", "incr", "1", "v1"}, 0); |
5305 | 0 | SyncClient(); |
5306 | 0 | DoRedisTestScoreValueArray(__LINE__, {"ZRANGEBYSCORE", "z_key", "-inf", "+inf", "WITHSCORES"}, |
5307 | 0 | {2}, {"v1"}); |
5308 | 0 | SyncClient(); |
5309 | 0 | } |
5310 | | |
5311 | | } // namespace redisserver |
5312 | | } // namespace yb |