-
Notifications
You must be signed in to change notification settings - Fork 845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
redis from mruby #1152
redis from mruby #1152
Conversation
if you forget to call |
works fine. however there is a bug when multiple values are sent back from redis:
crashes
also, is there any reason the connect block is not inside |
there might still be a leak: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on the PR.
I have left my thoughts, mostly on the changes in the C side. To me it seems that several refactors have been made to the redis binding. By asking questions, I am trying to understand why the changes are necessary (or what the benefits are).
include/h2o/mruby_.h
Outdated
@@ -117,12 +119,14 @@ typedef struct st_h2o_mruby_generator_t { | |||
} refs; | |||
} h2o_mruby_generator_t; | |||
|
|||
#define H2O_MRUBY_CALLBACK_ID_NOOP 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if 0
as a callback ID is allowed. See https://github.com/h2o/h2o/blob/5b71e0a/lib/handler/mruby.c#L773.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In current implementation, 0
is actually not meaningful and I believe there're no users who actively uses it because it only raises runtime error which means programming error. OTOH we need a way to exit h2o_mruby_run_fiber
lib/common/redis.c
Outdated
return; | ||
} | ||
|
||
if (command->type == H2O_REDIS_COMMAND_TYPE_MONITOR) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to have code for "monitor" assuming that we always return a error?
lib/common/redis.c
Outdated
return; | ||
|
||
int err = H2O_REDIS_ERROR_NONE; | ||
char *errstr = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need err
and errstr
?
I would appreciate it if you could merge them instead of having two. One way would be to only use const char *
and have specific pointer values defined in the header file. Please consult http1client for details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At 3ead0da I remoted int err
, but still I'm feeling that it may be necessary to report better error message to the client, regarding the fact that error message may came from strerror
lib/common/redis.c
Outdated
h2o_timeout_unlink(entry); | ||
|
||
conn->_did_connect_timeout = 1; | ||
invoke_deferred(conn, &conn->_defer_timeout_entry, on_connect_timeout_deferred); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would apppreciate if you could rather not make deferred calls within timeout callbacks. I believe that there is no reason to do so. Doing more things at once is generally better for performance (better locality) and debugging (gives us better trace).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that there is no reason to do so.
At that time there're some reasons to do workaround with hiredis async API, but at 3ead0da I refactored to eliminate such tricks
lib/common/redis.c
Outdated
} | ||
} | ||
|
||
static void on_disconnect(const redisAsyncContext *redis, int status) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the introduction of the function mean that we now have a "disconnecting" (or "closing") state?
If yes, is there a need to add the state to h2o_redis_connection_state_t
? If no, why do we need this function?
I would also appreciate it if you could consider unifying the term being used. At the moment I see "close" and "disconnect".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the introduction of the function mean that we now have a "disconnecting" (or "closing") state?
In our design, disconnecting hiredis object cannot be touched because once disconnecting procedure is fired our redis client immediately detach hiredis object and try to create new connection if another request comes. Regarding that, I don't think we have to have disconnecting
state.
lib/common/redis.c
Outdated
{ | ||
h2o_redis_conn_t *conn = H2O_STRUCT_FROM_MEMBER(h2o_redis_conn_t, _defer_timeout_entry, entry); | ||
|
||
assert((conn->_redis->c.flags & REDIS_CONNECTED) == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I correct in assuming that there is a value that represents the connection state in the redisAsyncContext
? If so, can we use the value instead of having h2o_redis_connection_state_t
?
Having two states that represents the same thing is a source of confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a value that represents the connection state in the redisAsyncContext?
Yes, but it's a bit redundant and seems to be not intended to be used directly from outside of hiredis. And also considering that we have a bit different state model described in #1152 (comment), I think we should have our own state representation.
it would be great if there would be a way to re-initialize the redis connector if it got killed afair: tearing up the redis connection outside of the request handler proc leaves the handler in a non working state if e.g. redis is restarted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the changes. I have left my tentative thoughts. Please let me know what you think.
lib/common/redis.c
Outdated
if (h2o_timeout_is_linked(&conn->_connect_timeout_entry)) | ||
h2o_timeout_unlink(&conn->_connect_timeout_entry); | ||
if (h2o_linklist_is_linked(&conn->_connect_timeout._link)) | ||
h2o_timeout_dispose(conn->loop, &conn->_connect_timeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hasn't _connect_timeout
been initialized regardless of if it's has something linked?
In other words, I wonder if it is necessary have a surrounding if
when calling the dispose function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
conn->_connect_timeout
may be uninitialized if the user set conn->connect_timeout
to zero. so we have to check it by h2o_linklist_is_linked(&conn->_connect_timeout._link)
@@ -224,7 +224,7 @@ static h2o_iovec_t build_redis_value(h2o_iovec_t session_data) | |||
|
|||
#undef BASE64_LENGTH | |||
|
|||
static void redis_resumption_on_get(redisReply *reply, void *_accept_data) | |||
static void redis_resumption_on_get(redisReply *reply, void *_accept_data, const char *errstr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we can receive a timeout error through errstr
, is it possible to get rid of the timeout handling logic specific to redis-based session resumption that uses on_redis_resumption_get_failed
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I recall correctly, on_redis_resumption_get_failed
is introduced with zero timeout because h2o_socket_ssl_resume_server_handshake
or something like that is not reentrant.
lib/handler/mruby/redis.c
Outdated
static void on_gc_dispose_redis(mrb_state *mrb, void *_conn) | ||
{ | ||
struct st_h2o_mruby_redis_conn_t *conn = _conn; | ||
if (conn == NULL) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any chance that conn
becomes NULL? Unless there's such case, this early return is redundant with the potential of hiding a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed at c3d0f85
lib/handler/mruby/redis.c
Outdated
static void on_gc_dispose_command(mrb_state *mrb, void *_ctx) | ||
{ | ||
struct st_h2o_mruby_redis_command_context_t *ctx = _ctx; | ||
if (ctx == NULL) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed at c3d0f85
include/h2o/redis.h
Outdated
h2o_timeout_entry_t _timeout_entry; | ||
h2o_timeout_entry_t _defer_timeout_entry; | ||
h2o_timeout_t _connect_timeout; | ||
h2o_timeout_entry_t _connect_timeout_entry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please elaborate the lifecycle of h2o_redis_conn_t::_redis
?
I expected that it is set to non-NULL when redisAsyncConnect
is called, and is set to NULL when redisAsyncFree
is being called.
But that does not seem to be the case. For example, I do not see redisAsyncFree
called when on_connect
with a status code other than REDIS_OK
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically the lifecycle of redis object is managed by hiredis internal. For example, if on_connect
gets called with REDIS_ERR
, immediately after it hiredis will call __redisAsyncDisconnect
which will finally dispose redis object.
https://github.com/h2o/h2o/blob/master/deps/hiredis/async.c#L503-L504
@yannick |
…back calling code into it
@i110 ah right, i forgot that it autoconnects, my bad sorry. |
Thank you for your hard work and your patience! |
This PR makes it able to access redis from mruby hanlder, like the following:
TODOs:
handler/mruby/redis.c
? (I feel it'll reduce maintainability, so let's discuss)