hiredis-client 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/redis_client/hiredis/extconf.rb +12 -4
- data/ext/redis_client/hiredis/hiredis_connection.c +195 -129
- data/lib/redis_client/hiredis_connection.rb +30 -15
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3506adf6e5b2e5da54f4fc75e762a0fd4f23b9bd9ea225a468b2ae96360d0273
|
4
|
+
data.tar.gz: 484b29d8fb080fad9076e71f1db9a1703b9b2a3a5ae173b6a5073d5abb2bfbe6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e238fe1e94a808f6c1966c8c2ce1136fa173594cfa824156199c3509b3b61c8b9f97a75336458edf7c50dcddc7a7d08833358a9f34bf64f141587fb1f19fa74
|
7
|
+
data.tar.gz: a6ddda3ab9d41f591cd82c1319fd43fc701cbb0c515768e0d940bc095fc69c69334bbf932c860ab37944a7f73e141a5e6b25fc4a2de454fb726f4dd199fadf48
|
@@ -3,6 +3,10 @@
|
|
3
3
|
require "mkmf"
|
4
4
|
|
5
5
|
class HiredisConnectionExtconf
|
6
|
+
def initialize(debug)
|
7
|
+
@debug = debug
|
8
|
+
end
|
9
|
+
|
6
10
|
def configure
|
7
11
|
if RUBY_ENGINE == "ruby" && !RUBY_PLATFORM.match?(/mswin/)
|
8
12
|
configure_extension
|
@@ -18,12 +22,16 @@ class HiredisConnectionExtconf
|
|
18
22
|
have_func("rb_hash_new_capa", "ruby.h")
|
19
23
|
|
20
24
|
$CFLAGS = concat_flags($CFLAGS, "-I#{hiredis_dir}", "-std=c99", "-fvisibility=hidden")
|
21
|
-
$CFLAGS = if
|
22
|
-
concat_flags($CFLAGS, "-Werror", "-g")
|
25
|
+
$CFLAGS = if @debug
|
26
|
+
concat_flags($CFLAGS, "-Werror", "-g", RbConfig::CONFIG["debugflags"])
|
23
27
|
else
|
24
28
|
concat_flags($CFLAGS, "-O3")
|
25
29
|
end
|
26
30
|
|
31
|
+
if @debug
|
32
|
+
$CPPFLAGS = concat_flags($CPPFLAGS, "-DRUBY_DEBUG=1")
|
33
|
+
end
|
34
|
+
|
27
35
|
append_cflags("-Wno-declaration-after-statement") # Older compilers
|
28
36
|
append_cflags("-Wno-compound-token-split-by-macro") # Older rubies on macos
|
29
37
|
end
|
@@ -33,7 +41,7 @@ class HiredisConnectionExtconf
|
|
33
41
|
"USE_SSL" => 1,
|
34
42
|
"CFLAGS" => concat_flags(ENV["CFLAGS"], "-fvisibility=hidden"),
|
35
43
|
}
|
36
|
-
env["OPTIMIZATION"] = "-g" if
|
44
|
+
env["OPTIMIZATION"] = "-g" if @debug
|
37
45
|
|
38
46
|
env = configure_openssl(env)
|
39
47
|
|
@@ -97,4 +105,4 @@ class HiredisConnectionExtconf
|
|
97
105
|
end
|
98
106
|
end
|
99
107
|
|
100
|
-
HiredisConnectionExtconf.new.configure
|
108
|
+
HiredisConnectionExtconf.new(ENV["EXT_PEDANTIC"]).configure
|
@@ -37,8 +37,13 @@
|
|
37
37
|
#include <sys/socket.h>
|
38
38
|
#include <stdbool.h>
|
39
39
|
#include "hiredis.h"
|
40
|
+
#include "net.h"
|
40
41
|
#include "hiredis_ssl.h"
|
41
42
|
|
43
|
+
#if !defined(RUBY_ASSERT)
|
44
|
+
# define RUBY_ASSERT(condition) ((void)0)
|
45
|
+
#endif
|
46
|
+
|
42
47
|
#if !defined(HAVE_RB_HASH_NEW_CAPA)
|
43
48
|
static inline VALUE rb_hash_new_capa(long capa)
|
44
49
|
{
|
@@ -46,6 +51,16 @@ static inline VALUE rb_hash_new_capa(long capa)
|
|
46
51
|
}
|
47
52
|
#endif
|
48
53
|
|
54
|
+
static void redis_set_error(redisContext *c, int type, const char *str) {
|
55
|
+
size_t len;
|
56
|
+
|
57
|
+
c->err = type;
|
58
|
+
len = strlen(str);
|
59
|
+
len = len < (sizeof(c->errstr) - 1) ? len : (sizeof(c->errstr) - 1);
|
60
|
+
memcpy(c->errstr, str, len);
|
61
|
+
c->errstr[len] = '\0';
|
62
|
+
}
|
63
|
+
|
49
64
|
static VALUE rb_eRedisClientCommandError, rb_eRedisClientConnectionError, rb_eRedisClientCannotConnectError, rb_eRedisClientProtocolError;
|
50
65
|
static VALUE rb_eRedisClientReadTimeoutError, rb_eRedisClientWriteTimeoutError;
|
51
66
|
static VALUE Redis_Qfalse;
|
@@ -60,11 +75,11 @@ typedef struct {
|
|
60
75
|
#define SSL_CONTEXT(from, name) \
|
61
76
|
hiredis_ssl_context_t *name = NULL; \
|
62
77
|
TypedData_Get_Struct(from, hiredis_ssl_context_t, &hiredis_ssl_context_data_type, name); \
|
63
|
-
if(name == NULL) { \
|
78
|
+
if (name == NULL) { \
|
64
79
|
rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
|
65
80
|
}
|
66
81
|
|
67
|
-
void hiredis_ssl_context_free(void *ptr) {
|
82
|
+
static void hiredis_ssl_context_free(void *ptr) {
|
68
83
|
hiredis_ssl_context_t *ssl_context = (hiredis_ssl_context_t *)ptr;
|
69
84
|
if (ssl_context->context) {
|
70
85
|
redisFreeSSLContext(ssl_context->context);
|
@@ -84,9 +99,6 @@ static const rb_data_type_t hiredis_ssl_context_data_type = {
|
|
84
99
|
.dmark = NULL,
|
85
100
|
.dfree = hiredis_ssl_context_free,
|
86
101
|
.dsize = hiredis_ssl_context_memsize,
|
87
|
-
#ifdef HAS_GC_COMPACT
|
88
|
-
.dcompact = NULL
|
89
|
-
#endif
|
90
102
|
},
|
91
103
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
92
104
|
};
|
@@ -120,10 +132,18 @@ static VALUE hiredis_ssl_context_init(VALUE self, VALUE ca_file, VALUE ca_path,
|
|
120
132
|
return Qnil;
|
121
133
|
}
|
122
134
|
|
135
|
+
typedef struct {
|
136
|
+
VALUE stack;
|
137
|
+
int *task_index;
|
138
|
+
} hiredis_reader_state_t;
|
139
|
+
|
123
140
|
static void *reply_append(const redisReadTask *task, VALUE value) {
|
124
|
-
|
125
|
-
|
141
|
+
hiredis_reader_state_t *state = (hiredis_reader_state_t *)task->privdata;
|
142
|
+
int task_index = *state->task_index;
|
126
143
|
|
144
|
+
if (task->parent) {
|
145
|
+
RUBY_ASSERT(task_index > 0);
|
146
|
+
VALUE parent = rb_ary_entry(state->stack, task_index - 1);
|
127
147
|
switch (task->parent->type) {
|
128
148
|
case REDIS_REPLY_ARRAY:
|
129
149
|
case REDIS_REPLY_SET:
|
@@ -132,11 +152,10 @@ static void *reply_append(const redisReadTask *task, VALUE value) {
|
|
132
152
|
break;
|
133
153
|
case REDIS_REPLY_MAP:
|
134
154
|
if (task->idx % 2) {
|
135
|
-
VALUE key = (
|
136
|
-
task->parent->privdata = NULL;
|
155
|
+
VALUE key = rb_ary_pop(state->stack);
|
137
156
|
rb_hash_aset(parent, key, value);
|
138
157
|
} else {
|
139
|
-
|
158
|
+
rb_ary_push(state->stack, value);
|
140
159
|
}
|
141
160
|
break;
|
142
161
|
default:
|
@@ -144,6 +163,7 @@ static void *reply_append(const redisReadTask *task, VALUE value) {
|
|
144
163
|
break;
|
145
164
|
}
|
146
165
|
}
|
166
|
+
rb_ary_store(state->stack, task_index, value);
|
147
167
|
return (void*)value;
|
148
168
|
}
|
149
169
|
|
@@ -219,13 +239,78 @@ static redisReplyObjectFunctions reply_functions = {
|
|
219
239
|
reply_free,
|
220
240
|
};
|
221
241
|
|
242
|
+
typedef struct {
|
243
|
+
redisContext *context;
|
244
|
+
int return_value;
|
245
|
+
} hiredis_buffer_read_args_t;
|
246
|
+
|
247
|
+
static void *hiredis_buffer_read_safe(void *_args) {
|
248
|
+
hiredis_buffer_read_args_t *args = _args;
|
249
|
+
args->return_value = redisBufferRead(args->context);
|
250
|
+
return NULL;
|
251
|
+
}
|
252
|
+
static int hiredis_buffer_read_nogvl(redisContext *context) {
|
253
|
+
hiredis_buffer_read_args_t args = {
|
254
|
+
.context = context,
|
255
|
+
};
|
256
|
+
rb_thread_call_without_gvl(hiredis_buffer_read_safe, &args, RUBY_UBF_IO, 0);
|
257
|
+
return args.return_value;
|
258
|
+
}
|
259
|
+
|
260
|
+
typedef struct {
|
261
|
+
redisContext *context;
|
262
|
+
int *done;
|
263
|
+
int return_value;
|
264
|
+
} hiredis_buffer_write_args_t;
|
265
|
+
|
266
|
+
static void *hiredis_buffer_write_safe(void *_args) {
|
267
|
+
hiredis_buffer_write_args_t *args = _args;
|
268
|
+
args->return_value = redisBufferWrite(args->context, args->done);
|
269
|
+
return NULL;
|
270
|
+
}
|
271
|
+
static int hiredis_buffer_write_nogvl(redisContext *context, int *done) {
|
272
|
+
hiredis_buffer_write_args_t args = {
|
273
|
+
.context = context,
|
274
|
+
.done = done,
|
275
|
+
};
|
276
|
+
rb_thread_call_without_gvl(hiredis_buffer_write_safe, &args, RUBY_UBF_IO, 0);
|
277
|
+
return args.return_value;
|
278
|
+
}
|
279
|
+
|
222
280
|
#define CONNECTION(from, name) \
|
223
281
|
hiredis_connection_t *name = NULL; \
|
224
282
|
TypedData_Get_Struct(from, hiredis_connection_t, &hiredis_connection_data_type, name); \
|
225
|
-
if(name == NULL) { \
|
283
|
+
if (name == NULL) { \
|
226
284
|
rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \
|
227
285
|
}
|
228
286
|
|
287
|
+
typedef struct {
|
288
|
+
redisContext *context;
|
289
|
+
int return_value;
|
290
|
+
} hiredis_reconnect_args_t;
|
291
|
+
|
292
|
+
static void *hiredis_reconnect_safe(void *_args) {
|
293
|
+
hiredis_reconnect_args_t *args = _args;
|
294
|
+
args->return_value = redisReconnect(args->context);
|
295
|
+
return NULL;
|
296
|
+
}
|
297
|
+
|
298
|
+
static int hiredis_reconnect_nogvl(redisContext *context) {
|
299
|
+
hiredis_reconnect_args_t args = {
|
300
|
+
.context = context,
|
301
|
+
.return_value = 0
|
302
|
+
};
|
303
|
+
rb_thread_call_without_gvl(hiredis_reconnect_safe, &args, RUBY_UBF_IO, 0);
|
304
|
+
return args.return_value;
|
305
|
+
}
|
306
|
+
|
307
|
+
static void *hiredis_connect_with_options_safe(void *options) {
|
308
|
+
return (void *)redisConnectWithOptions(options);
|
309
|
+
}
|
310
|
+
|
311
|
+
static redisContext *hiredis_connect_with_options_nogvl(redisOptions *options) {
|
312
|
+
return (redisContext *)rb_thread_call_without_gvl(hiredis_connect_with_options_safe, options, RUBY_UBF_IO, 0);
|
313
|
+
}
|
229
314
|
|
230
315
|
typedef struct {
|
231
316
|
redisContext *context;
|
@@ -234,42 +319,19 @@ typedef struct {
|
|
234
319
|
struct timeval write_timeout;
|
235
320
|
} hiredis_connection_t;
|
236
321
|
|
237
|
-
void
|
238
|
-
while (task) {
|
239
|
-
if (task->obj) { rb_gc_mark((VALUE)task->obj); }
|
240
|
-
if (task->privdata) { rb_gc_mark((VALUE)task->privdata); }
|
241
|
-
task = task->parent;
|
242
|
-
}
|
243
|
-
}
|
244
|
-
|
245
|
-
void hiredis_connection_mark(void *ptr) {
|
246
|
-
hiredis_connection_t *connection = ptr;
|
247
|
-
if (connection->context) {
|
248
|
-
redisReader *reader = connection->context->reader;
|
249
|
-
for (int index = 0; index < reader->tasks; index++) {
|
250
|
-
hiredis_connection_mark_task(reader->task[index]);
|
251
|
-
}
|
252
|
-
}
|
253
|
-
}
|
254
|
-
void hiredis_connection_free(void *ptr) {
|
322
|
+
static void hiredis_connection_free(void *ptr) {
|
255
323
|
hiredis_connection_t *connection = ptr;
|
256
324
|
if (connection) {
|
257
325
|
if (connection->context) {
|
258
|
-
|
326
|
+
// redisFree may calls close() if we're still connected, but
|
327
|
+
// it shoudln't be considered a "blocking" call given we don't
|
328
|
+
// use any socket option that may make it a blocking operation.
|
329
|
+
redisFree(connection->context);
|
259
330
|
}
|
260
331
|
xfree(connection);
|
261
332
|
}
|
262
333
|
}
|
263
334
|
|
264
|
-
static size_t hiredis_connection_task_memsize(redisReadTask *task) {
|
265
|
-
size_t size = 0;
|
266
|
-
while (task) {
|
267
|
-
size += sizeof(redisReadTask);
|
268
|
-
task = task->parent;
|
269
|
-
}
|
270
|
-
return size;
|
271
|
-
}
|
272
|
-
|
273
335
|
static size_t hiredis_connection_memsize(const void *ptr) {
|
274
336
|
hiredis_connection_t *connection = (hiredis_connection_t *)ptr;
|
275
337
|
|
@@ -281,10 +343,7 @@ static size_t hiredis_connection_memsize(const void *ptr) {
|
|
281
343
|
redisReader *reader = connection->context->reader;
|
282
344
|
size += sizeof(redisReader);
|
283
345
|
size += reader->maxbuf;
|
284
|
-
|
285
|
-
for (int index = 0; index < reader->tasks; index++) {
|
286
|
-
size += hiredis_connection_task_memsize(reader->task[index]);
|
287
|
-
}
|
346
|
+
size += reader->tasks * sizeof(redisReadTask);
|
288
347
|
}
|
289
348
|
}
|
290
349
|
|
@@ -294,14 +353,11 @@ static size_t hiredis_connection_memsize(const void *ptr) {
|
|
294
353
|
static const rb_data_type_t hiredis_connection_data_type = {
|
295
354
|
.wrap_struct_name = "redis-client:hiredis_connection",
|
296
355
|
.function = {
|
297
|
-
.dmark =
|
356
|
+
.dmark = NULL,
|
298
357
|
.dfree = hiredis_connection_free,
|
299
358
|
.dsize = hiredis_connection_memsize,
|
300
|
-
#ifdef HAS_GC_COMPACT
|
301
|
-
.dcompact = NULL
|
302
|
-
#endif
|
303
359
|
},
|
304
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
360
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
305
361
|
};
|
306
362
|
|
307
363
|
static VALUE hiredis_alloc(VALUE klass) {
|
@@ -309,7 +365,7 @@ static VALUE hiredis_alloc(VALUE klass) {
|
|
309
365
|
return TypedData_Make_Struct(klass, hiredis_connection_t, &hiredis_connection_data_type, connection);
|
310
366
|
}
|
311
367
|
|
312
|
-
void redis_set_io_error(redisContext *context, int err) {
|
368
|
+
static void redis_set_io_error(redisContext *context, int err) {
|
313
369
|
if (err) {
|
314
370
|
errno = err;
|
315
371
|
}
|
@@ -325,10 +381,10 @@ static inline void redis_raise_error_and_disconnect(redisContext *context, VALUE
|
|
325
381
|
if (context->err) {
|
326
382
|
strncpy(errstr, context->errstr, 128);
|
327
383
|
}
|
328
|
-
|
384
|
+
redisNetClose(context);
|
329
385
|
|
330
386
|
if (!err) {
|
331
|
-
|
387
|
+
rb_raise(timeout_error, "Unknown Error (redis_raise_error_and_disconnect)");
|
332
388
|
}
|
333
389
|
|
334
390
|
// OpenSSL bug: The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected EOF from the peer.
|
@@ -437,6 +493,12 @@ static int hiredis_wait_writable(int fd, const struct timeval *timeout, int *iss
|
|
437
493
|
}
|
438
494
|
|
439
495
|
static VALUE hiredis_connect_finish(hiredis_connection_t *connection, redisContext *context) {
|
496
|
+
if (connection->context && connection->context != context) {
|
497
|
+
redisFree(context);
|
498
|
+
rb_raise(rb_eRuntimeError, "HiredisConnection is already connected, must be a bug");
|
499
|
+
}
|
500
|
+
connection->context = context;
|
501
|
+
|
440
502
|
if (context->err) {
|
441
503
|
redis_raise_error_and_disconnect(context, rb_eRedisClientCannotConnectError);
|
442
504
|
}
|
@@ -470,34 +532,10 @@ static VALUE hiredis_connect_finish(hiredis_connection_t *connection, redisConte
|
|
470
532
|
|
471
533
|
context->reader->fn = &reply_functions;
|
472
534
|
redisSetPushCallback(context, NULL);
|
473
|
-
connection->context = context;
|
474
535
|
return Qtrue;
|
475
536
|
}
|
476
537
|
|
477
|
-
static
|
478
|
-
CONNECTION(self, connection);
|
479
|
-
if (connection->context) {
|
480
|
-
redisFree(connection->context);
|
481
|
-
connection->context = NULL;
|
482
|
-
}
|
483
|
-
redisContext *context = redisConnectNonBlock(StringValuePtr(host), NUM2INT(port));
|
484
|
-
if (context) {
|
485
|
-
redisEnableKeepAlive(context);
|
486
|
-
}
|
487
|
-
return hiredis_connect_finish(connection, context);
|
488
|
-
}
|
489
|
-
|
490
|
-
static VALUE hiredis_connect_unix(VALUE self, VALUE path) {
|
491
|
-
CONNECTION(self, connection);
|
492
|
-
if (connection->context) {
|
493
|
-
redisFree(connection->context);
|
494
|
-
connection->context = NULL;
|
495
|
-
}
|
496
|
-
return hiredis_connect_finish(connection, redisConnectUnixNonBlock(StringValuePtr(path)));
|
497
|
-
}
|
498
|
-
|
499
|
-
static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
|
500
|
-
CONNECTION(self, connection);
|
538
|
+
static void hiredis_init_ssl(hiredis_connection_t *connection, VALUE ssl_param) {
|
501
539
|
SSL_CONTEXT(ssl_param, ssl_context)
|
502
540
|
|
503
541
|
if (redisInitiateSSLWithContext(connection->context, ssl_context->context) != REDIS_OK) {
|
@@ -505,7 +543,7 @@ static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
|
|
505
543
|
}
|
506
544
|
|
507
545
|
redisSSL *redis_ssl = redisGetSSLSocket(connection->context);
|
508
|
-
|
546
|
+
|
509
547
|
if (redis_ssl->wantRead) {
|
510
548
|
int readable = 0;
|
511
549
|
if (hiredis_wait_readable(connection->context->fd, &connection->connect_timeout, &readable) < 0) {
|
@@ -513,21 +551,81 @@ static VALUE hiredis_init_ssl(VALUE self, VALUE ssl_param) {
|
|
513
551
|
}
|
514
552
|
if (!readable) {
|
515
553
|
errno = EAGAIN;
|
516
|
-
|
554
|
+
redis_set_error(connection->context, REDIS_ERR_IO, "SSL Connection Timeout");
|
555
|
+
hiredis_raise_error_and_disconnect(connection, rb_eRedisClientReadTimeoutError);
|
517
556
|
}
|
518
557
|
|
519
558
|
if (redisInitiateSSLContinue(connection->context) != REDIS_OK) {
|
520
559
|
hiredis_raise_error_and_disconnect(connection, rb_eRedisClientCannotConnectError);
|
521
560
|
};
|
522
561
|
}
|
562
|
+
}
|
523
563
|
|
524
|
-
|
564
|
+
static VALUE hiredis_connect(VALUE self, VALUE path, VALUE host, VALUE port, VALUE ssl_param) {
|
565
|
+
CONNECTION(self, connection);
|
566
|
+
if (connection->context) {
|
567
|
+
rb_raise(rb_eRuntimeError, "HiredisConnection is already connected, must be a bug");
|
568
|
+
}
|
569
|
+
|
570
|
+
redisOptions options = {
|
571
|
+
.connect_timeout = &connection->connect_timeout,
|
572
|
+
.options = REDIS_OPT_NONBLOCK
|
573
|
+
};
|
574
|
+
if (RTEST(path)) {
|
575
|
+
REDIS_OPTIONS_SET_UNIX(&options, StringValuePtr(path));
|
576
|
+
} else {
|
577
|
+
REDIS_OPTIONS_SET_TCP(&options, StringValuePtr(host), NUM2INT(port));
|
578
|
+
}
|
579
|
+
|
580
|
+
redisContext *context = hiredis_connect_with_options_nogvl(&options);
|
581
|
+
if (context && !RTEST(path)) {
|
582
|
+
redisEnableKeepAlive(context);
|
583
|
+
}
|
584
|
+
|
585
|
+
VALUE success = hiredis_connect_finish(connection, context);
|
586
|
+
|
587
|
+
if (RTEST(success) && !NIL_P(ssl_param)) {
|
588
|
+
hiredis_init_ssl(connection, ssl_param);
|
589
|
+
}
|
590
|
+
|
591
|
+
return success;
|
592
|
+
}
|
593
|
+
|
594
|
+
static VALUE hiredis_reconnect(VALUE self, VALUE unix, VALUE ssl_param) {
|
595
|
+
CONNECTION(self, connection);
|
596
|
+
if (!connection->context) {
|
597
|
+
return Qfalse;
|
598
|
+
}
|
599
|
+
|
600
|
+
hiredis_reconnect_nogvl(connection->context);
|
601
|
+
|
602
|
+
VALUE success = hiredis_connect_finish(connection, connection->context);
|
603
|
+
|
604
|
+
if (RTEST(success)) {
|
605
|
+
if (!RTEST(unix)) {
|
606
|
+
redisEnableKeepAlive(connection->context);
|
607
|
+
}
|
608
|
+
|
609
|
+
if (RTEST(ssl_param)) {
|
610
|
+
hiredis_init_ssl(connection, ssl_param);
|
611
|
+
}
|
612
|
+
}
|
613
|
+
|
614
|
+
return success;
|
525
615
|
}
|
526
616
|
|
527
617
|
static VALUE hiredis_connected_p(VALUE self) {
|
528
618
|
CONNECTION(self, connection);
|
529
619
|
|
530
|
-
|
620
|
+
if (!connection->context) {
|
621
|
+
return Qfalse;
|
622
|
+
}
|
623
|
+
|
624
|
+
if (connection->context->fd == REDIS_INVALID_FD) {
|
625
|
+
return Qfalse;
|
626
|
+
}
|
627
|
+
|
628
|
+
return Qtrue;
|
531
629
|
}
|
532
630
|
|
533
631
|
static VALUE hiredis_write(VALUE self, VALUE command) {
|
@@ -554,44 +652,6 @@ static VALUE hiredis_write(VALUE self, VALUE command) {
|
|
554
652
|
return Qnil;
|
555
653
|
}
|
556
654
|
|
557
|
-
typedef struct {
|
558
|
-
redisContext *context;
|
559
|
-
int return_value;
|
560
|
-
} hiredis_buffer_read_args_t;
|
561
|
-
|
562
|
-
void *hiredis_buffer_read_safe(void *_args) {
|
563
|
-
hiredis_buffer_read_args_t *args = _args;
|
564
|
-
args->return_value = redisBufferRead(args->context);
|
565
|
-
return NULL;
|
566
|
-
}
|
567
|
-
int hiredis_buffer_read_nogvl(redisContext *context) {
|
568
|
-
hiredis_buffer_read_args_t args = {
|
569
|
-
.context = context,
|
570
|
-
};
|
571
|
-
rb_thread_call_without_gvl(hiredis_buffer_read_safe, &args, RUBY_UBF_IO, 0);
|
572
|
-
return args.return_value;
|
573
|
-
}
|
574
|
-
|
575
|
-
typedef struct {
|
576
|
-
redisContext *context;
|
577
|
-
int *done;
|
578
|
-
int return_value;
|
579
|
-
} hiredis_buffer_write_args_t;
|
580
|
-
|
581
|
-
void *hiredis_buffer_write_safe(void *_args) {
|
582
|
-
hiredis_buffer_write_args_t *args = _args;
|
583
|
-
args->return_value = redisBufferWrite(args->context, args->done);
|
584
|
-
return NULL;
|
585
|
-
}
|
586
|
-
int hiredis_buffer_write_nogvl(redisContext *context, int *done) {
|
587
|
-
hiredis_buffer_write_args_t args = {
|
588
|
-
.context = context,
|
589
|
-
.done = done,
|
590
|
-
};
|
591
|
-
rb_thread_call_without_gvl(hiredis_buffer_write_safe, &args, RUBY_UBF_IO, 0);
|
592
|
-
return args.return_value;
|
593
|
-
}
|
594
|
-
|
595
655
|
static VALUE hiredis_flush(VALUE self) {
|
596
656
|
CONNECTION(self, connection);
|
597
657
|
ENSURE_CONNECTED(connection);
|
@@ -628,6 +688,16 @@ static int hiredis_read_internal(hiredis_connection_t *connection, VALUE *reply)
|
|
628
688
|
void *redis_reply = NULL;
|
629
689
|
int wdone = 0;
|
630
690
|
|
691
|
+
// This struct being on the stack, the GC won't move nor collect that `stack` RArray.
|
692
|
+
// We use that to avoid having to have a `mark` function with write barriers.
|
693
|
+
// Not that it would be too hard, but if we mark the response objects, we'll likely end up
|
694
|
+
// promoting them to the old generation which isn't desirable.
|
695
|
+
hiredis_reader_state_t reader_state = {
|
696
|
+
.stack = rb_ary_new(),
|
697
|
+
.task_index = &connection->context->reader->ridx,
|
698
|
+
};
|
699
|
+
connection->context->reader->privdata = &reader_state;
|
700
|
+
|
631
701
|
/* Try to read pending replies */
|
632
702
|
if (redisGetReplyFromReader(connection->context, &redis_reply) == REDIS_ERR) {
|
633
703
|
return HIREDIS_FATAL_CONNECTION_ERROR; // Protocol error
|
@@ -722,18 +792,15 @@ static VALUE hiredis_read(VALUE self) {
|
|
722
792
|
static VALUE hiredis_close(VALUE self) {
|
723
793
|
CONNECTION(self, connection);
|
724
794
|
if (connection->context) {
|
725
|
-
|
726
|
-
connection->context = NULL;
|
795
|
+
redisNetClose(connection->context);
|
727
796
|
}
|
728
797
|
return Qnil;
|
729
798
|
}
|
730
799
|
|
731
800
|
RUBY_FUNC_EXPORTED void Init_hiredis_connection(void) {
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
RUBY_ASSERT((void *)Qnil != NULL);
|
736
|
-
#endif
|
801
|
+
// Qfalse == NULL, so we can't return Qfalse in `reply_create_bool()`
|
802
|
+
RUBY_ASSERT((void *)Qfalse == NULL);
|
803
|
+
RUBY_ASSERT((void *)Qnil != NULL);
|
737
804
|
|
738
805
|
redisInitOpenSSL();
|
739
806
|
|
@@ -768,15 +835,14 @@ RUBY_FUNC_EXPORTED void Init_hiredis_connection(void) {
|
|
768
835
|
rb_define_private_method(rb_cHiredisConnection, "read_timeout_us=", hiredis_set_read_timeout, 1);
|
769
836
|
rb_define_private_method(rb_cHiredisConnection, "write_timeout_us=", hiredis_set_write_timeout, 1);
|
770
837
|
|
771
|
-
rb_define_private_method(rb_cHiredisConnection, "
|
772
|
-
rb_define_private_method(rb_cHiredisConnection, "
|
773
|
-
rb_define_private_method(rb_cHiredisConnection, "init_ssl", hiredis_init_ssl, 1);
|
838
|
+
rb_define_private_method(rb_cHiredisConnection, "_connect", hiredis_connect, 4);
|
839
|
+
rb_define_private_method(rb_cHiredisConnection, "_reconnect", hiredis_reconnect, 2);
|
774
840
|
rb_define_method(rb_cHiredisConnection, "connected?", hiredis_connected_p, 0);
|
775
841
|
|
776
842
|
rb_define_private_method(rb_cHiredisConnection, "_write", hiredis_write, 1);
|
777
843
|
rb_define_private_method(rb_cHiredisConnection, "_read", hiredis_read, 0);
|
778
844
|
rb_define_private_method(rb_cHiredisConnection, "flush", hiredis_flush, 0);
|
779
|
-
|
845
|
+
rb_define_private_method(rb_cHiredisConnection, "_close", hiredis_close, 0);
|
780
846
|
|
781
847
|
VALUE rb_cHiredisSSLContext = rb_define_class_under(rb_cHiredisConnection, "SSLContext", rb_cObject);
|
782
848
|
rb_define_alloc_func(rb_cHiredisSSLContext, hiredis_ssl_context_alloc);
|
@@ -30,27 +30,32 @@ class RedisClient
|
|
30
30
|
|
31
31
|
def initialize(config, connect_timeout:, read_timeout:, write_timeout:)
|
32
32
|
super()
|
33
|
+
@config = config
|
33
34
|
self.connect_timeout = connect_timeout
|
34
35
|
self.read_timeout = read_timeout
|
35
36
|
self.write_timeout = write_timeout
|
37
|
+
connect
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
40
|
+
def close
|
41
|
+
_close
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def reconnect
|
46
|
+
reconnected = begin
|
47
|
+
_reconnect(@config.path, @config.ssl_context)
|
48
|
+
rescue SystemCallError => error
|
49
|
+
host = @config.path || "#{@config.host}:#{@config.port}"
|
50
|
+
error_code = error.class.name.split("::").last
|
51
|
+
raise CannotConnectError, "Failed to reconnect to #{host} (#{error_code})"
|
50
52
|
end
|
51
53
|
|
52
|
-
if
|
53
|
-
|
54
|
+
if reconnected
|
55
|
+
true
|
56
|
+
else
|
57
|
+
# Reusing the hiredis connection didn't work let's create a fresh one
|
58
|
+
super
|
54
59
|
end
|
55
60
|
end
|
56
61
|
|
@@ -100,5 +105,15 @@ class RedisClient
|
|
100
105
|
rescue SystemCallError, IOError => error
|
101
106
|
raise ConnectionError, error.message
|
102
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def connect
|
112
|
+
_connect(@config.path, @config.host, @config.port, @config.ssl_context)
|
113
|
+
rescue SystemCallError => error
|
114
|
+
host = @config.path || "#{@config.host}:#{@config.port}"
|
115
|
+
error_code = error.class.name.split("::").last
|
116
|
+
raise CannotConnectError, "Failed to connect to #{host} (#{error_code})"
|
117
|
+
end
|
103
118
|
end
|
104
119
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hiredis-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.14.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.14.1
|
27
27
|
description:
|
28
28
|
email:
|
29
29
|
- jean.boussier@gmail.com
|