Conversation
…a memory leak would have occured if the methods format() or formatArgv() were to be used more than once on the same object. This change simply ensures that currently allocated memory gets freed before new memory gets allocated.
|
can you separate the bug fix from this pr? we can review and merge it separately. |
| { | ||
| redisFreeCommand(temp); | ||
| temp = nullptr; | ||
| } |
There was a problem hiding this comment.
This is a valid bug fix. Could you please add a unit test, so it will fail on old code but pass with your fix? #Closed
There was a problem hiding this comment.
Hi @qiluo-msft . I'm not sure how to add a unit test for this. The leak will only happen if someone calls RedisCommand::format() (or RedisCommand::formatArgv()) more than once. The pointer to the leaked memory, temp, is a private member and cannot be tested outside the class RedisCommand itself. I'm running out of ideas on how to write a unit test for this. Any suggestions would be greatly appreciated.
There was a problem hiding this comment.
BTW, while looking at the existing unit tests, I found a place where a memory leak would occur. The leak is in file tests/redis_state_ut.cpp, line 738, as follows:
738) RedisCommand keys;
739) keys.format("KEYS %s*", (c.getStateHashPrefix() + tableName).c_str());
740) RedisReply r(&db, keys, REDIS_REPLY_ARRAY);
741) EXPECT_EQ(r.getContext()->elements, (size_t) 0);
742) // Verify number of objects
743) keys.format("KEYS %s:*", tableName.c_str());
744) RedisReply r2(&db, keys, REDIS_REPLY_ARRAY);
745) EXPECT_EQ(r2.getContext()->elements, (size_t) numOfKeys);
At line 739 we invoke keys.format() a first time. At line 743 we invoke keys.format() a second time. The second time the function is invoked, the memory that was allocated the first time the function was invoked is not freed and will leak.
There was a problem hiding this comment.
Understand the difficulty. Could you separate the bug fix from this pr? The bug fix could be merged sooner.
We need more effort to review the new feature.
In reply to: 501238042 [](ancestors = 501238042)
There was a problem hiding this comment.
Ok I will submit a separate pull request
There was a problem hiding this comment.
Submitted this pull request that only contains the memory leak fix.
#392
|
@qiluo-msft, any update on this pr? |
common/dbconnector.h
Outdated
| int formatted_command(redisCallbackFn * cb_func_p, void * cb_data_p, const char * cmd_p, size_t len); | ||
|
|
||
| private: | ||
| const std::string db_name_m; |
There was a problem hiding this comment.
db_name_m [](start = 25, length = 9)
We are using m_cameCase as member variable name convention. For this case, suggest m_dbName #Closed
common/dbconnector.h
Outdated
| * | ||
| * @return User context pointer. | ||
| */ | ||
| void * get_user_ctx() const { return user_ctx_pm; } |
There was a problem hiding this comment.
get_user_ctx [](start = 11, length = 12)
Naming convention: cameCaseForFunctionName. So suggest getUserContext() #Closed
common/dbconnector.h
Outdated
| * @endcode | ||
| * | ||
| */ | ||
| redisAsyncContext * context() const { return ac_pm; } |
There was a problem hiding this comment.
[](start = 23, length = 1)
Suggest remove extra blank after * #Closed
common/dbconnector.h
Outdated
| * | ||
| * @return The DB name associated with this connector. | ||
| */ | ||
| const std::string & db_name() const { return db_name_m; } |
There was a problem hiding this comment.
[](start = 21, length = 1)
Suggest remove extra blank before & #Closed
common/dbconnector.h
Outdated
| * | ||
| * @return The DB name associated with this connector. | ||
| */ | ||
| const std::string & db_name() const { return db_name_m; } |
There was a problem hiding this comment.
string [](start = 15, length = 6)
Better return non reference string, so caller does not worry about scope.
There was a problem hiding this comment.
Sorry for misleading, I propose to return basic std::string.
In reply to: 532125270 [](ancestors = 532125270)
| * that allows the event loop to dispatch the "events" (i.e. replies | ||
| * from the REDIS server) to their corresponding callback functions. | ||
| */ | ||
| class DBConnector_async |
There was a problem hiding this comment.
DBConnector_async [](start = 6, length = 17)
Could you provide use cases? We also require unit test cases for new classes.
There was a problem hiding this comment.
The use cases are described in the Doxygen text above. Basically, this allows designing processes around an event loop such as GLib, libevent, qt, etc. as described in hiredis documentation: https://github.com/redis/hiredis/tree/master/adapters
There was a problem hiding this comment.
Thanks I read the Doxygen text.
I am asking about how do you plan to use the new classes in downstream repoes? Do you already have a PR following?
Require unit test cases in this PR. That also serves as use case.
In reply to: 532125398 [](ancestors = 532125398)
There was a problem hiding this comment.
This will be used by hamd (pull request: sonic-net/sonic-buildimage#5553).
hamd is the Host Account Management Daemon. It is designed as an asynchronous, non-blocking, event-driven process. It uses the GLib event-driven main-loop framework (ref. https://www.freedesktop.org/software/gstreamer-sdk/data/docs/latest/glib/glib-The-Main-Event-Loop.html).
The hiredis library provides GLib bindings. hamd uses the hiredis asynchronous APIs to make non-blocking, asynchronous, requests to the REDIS server. When responses or events are received from the REDIS server, they get dispatched to callback functions by the GLib main-loop.
The following files show how hamd uses the new async APIs:
https://github.com/martin-belanger/sonic-buildimage/blob/master/src/ham/hamd/hamd_redis.h
https://github.com/martin-belanger/sonic-buildimage/blob/master/src/ham/hamd/hamd_redis.cpp
This shows how hamd's GLib Main-loop is created:
https://github.com/martin-belanger/sonic-buildimage/blob/master/src/ham/hamd/hamd_main.cpp
common/dbconnector.cpp
Outdated
| return status; | ||
| } | ||
|
|
||
| int DBConnector_async::formatted_command(redisCallbackFn * cb_func_p, void * cb_data_p, const char * cmd_p, size_t len) |
There was a problem hiding this comment.
formatted_command [](start = 23, length = 17)
command() and formatted_command fit better in the class RedisReply, or its subclass.
Is it a good idea to have a redisCallbackFn member in RedisReply, or its subclass?
There was a problem hiding this comment.
The RedisReply class is designed specifically for synchronous calls (i.e. blocking calls). When using the Async library (i.e. non-blocking) we do not wait for a reply. The reply comes back at a later time and is dispatched by the event loop (implemented by GLib, libevent, qt, or others) to a callback function.
Async is a completely different approach and the RedisReply does not apply.
common/dbconnector.h
Outdated
| const std::string db_name_m; | ||
| const int db_id_m = -1; | ||
| std::string sock_addr_m; | ||
| redisAsyncContext * ac_pm = nullptr; |
There was a problem hiding this comment.
redisAsyncContext [](start = 4, length = 17)
Suggest create a wrapper class: RedisAsyncContext derived from RedisContext.
m_conn should point to ac_pm->c
There was a problem hiding this comment.
I cannot derive RedisAsyncContext from RedisContext. RedisAsyncContext is not my definition. It is defined in the hiredis library (a 3rd party library).
There was a problem hiding this comment.
Sorry for misleading. I did not mean redisAsyncContext in hiredis repo.
I mean in this repo, create a wrapper class: RedisAsyncContext derived from RedisContext (not redisContext in hiredis repo).
In reply to: 532126556 [](ancestors = 532126556)
There was a problem hiding this comment.
I see that a new class named RedisContext was introduced after I initially submitted my pull request. I understand what you are asking.
By the way, since I submitted this pull request (2 months ago) I have been reassigned to a new project. I have very little time for SONiC now, but let me see see what I can do. Maybe one of my colleagues can take this over.
common/table.cpp
Outdated
| cmd.formatHSET(key, field_r, value_r); | ||
| return dbconn_rm.formatted_command(cb_func_p, cb_data_p, cmd.c_str(), cmd.length()); | ||
| } | ||
|
|
There was a problem hiding this comment.
Remove extra blank line
| return dbconn_rm.formatted_command(cb_func_p, cb_data_p, cmd.c_str(), cmd.length()); | ||
| } | ||
|
|
||
| int Table_async::hget(redisCallbackFn * cb_func_p, void * cb_data_p, const std::string & key_r, const std::string & field_r) |
There was a problem hiding this comment.
hget [](start = 17, length = 4)
Functions in this file fit better in the class DBConnector or its subclass.
It has less relationship with the Table concept. #Closed
There was a problem hiding this comment.
I'm just following the same model that was used for class Table. Just like class Table defines Table::hget(), Table::hset(), etc., class Table_async defines Table_async::hget(), Table_async::hset(), etc.
qiluo-msft
left a comment
There was a problem hiding this comment.
As comments. Please also resolve conflicts and add testcase.
|
|
||
| int Table_async::hget(redisCallbackFn * cb_func_p, void * cb_data_p, const std::string & key_r, const std::string & field_r) | ||
| { | ||
| std::string key = getKeyName(key_r); |
There was a problem hiding this comment.
[](start = 15, length = 2)
Use one blank character
|
I just added a new commit to address the syntax issues (camel-casing, blanks, etc.) I will look into adding a class |
* Add new SSD type support * Fix review comment --------- Co-authored-by: Prince George <[email protected]>
This pull request is mainly to add support for REDIS asynchronous APIs (from the hiredis library).
I also fixed a potential memory leak in common/rediscommand.cpp.