-
Notifications
You must be signed in to change notification settings - Fork 260
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
Improve sqlite usage and test stability #31
Improve sqlite usage and test stability #31
Conversation
8e7e2bd
to
be2f5a2
Compare
d6105bd
to
9da009e
Compare
9da009e
to
bbe6171
Compare
Also already links write integration test against the default plugins.
Query the underlying db directly in tests to determine the amount of recorded messages.
- Always open db with threading mode multi-thread. This forbids sharing database connections across threads. Db access from multiple threads is possible when each thread uses its own connection. - Open sqlite db accordingly to given io flags. Readonly open works only with already existing database. - Set journal mode pragma to WAL (write ahead log) and synchronous pragma to NORMAL. This should yield good write performance. - Small fix: use *.db3 as db name in tests.
bbe6171
to
0621595
Compare
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 think rosbag2 shouldn't have any dependencies (even if only test) to any specific storage format.
We could introduce a new package for testing in order to have to integration tested thoroughly.
@@ -42,7 +42,7 @@ class Rosbag2TestFixture : public Test | |||
{ | |||
public: | |||
Rosbag2TestFixture() | |||
: database_name_(UnitTest::GetInstance()->current_test_info()->name()) | |||
: database_name_(std::string(UnitTest::GetInstance()->current_test_info()->name()) + ".db3") |
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 this imply dependencies to sqlite storage? I feel like all tests within rosbag2
should be independent of any storage format.
@@ -24,6 +24,8 @@ | |||
#include "rclcpp/rclcpp.hpp" | |||
#include "rosbag2/rosbag2.hpp" | |||
#include "rosbag2_storage/serialized_bag_message.hpp" | |||
#include "rosbag2_storage_default_plugins/sqlite/sqlite_exception.hpp" |
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 think this implies a strong dependency from rosbag2
to rosbag2_storage_default_plugins
. Ideally all these tests should perform equivally with all plugins.
We can introduce a new package for integration or system tests like these.
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 order to write integration tests for rosbag2
we need a storage plugin. Which storage plugin is used for the tests is irrelevant and hidden inside the test fixtures.
Having rosbag2_storage_default_plugins
as test dependency for rosbag2
or having a separate test package depending on rosbag2_storage_default_plugins
and rosbag2
is for me practically equivalent. From a developers point of view I would prefer the test dependency as then the tests reside in the same package as the code they test.
When the rosbag2_transport
package is introduced the tests will move there. If we choose to move the tests into a package of their own I suggest to do so after/ with the introduction of rosbag2_transport
.
std::this_thread::sleep_for(50ms); | ||
} | ||
} | ||
)); | ||
} | ||
|
||
size_t count_stored_messages(rosbag2_storage_plugins::SqliteWrapper & db, std::string topic_name) |
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.
const std::string &
try { | ||
return count_stored_messages_in_db(db, topic_name); | ||
} catch (const rosbag2_storage_plugins::SqliteException & e) { | ||
(void) e; |
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.
shouldn't this break the while loop? Is there a way to have the loop conditioned otherwise than while (true)
? This seems just dangerous.
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.
The test runner already provides a timeout so the while (true)
while never run forever.
If a query fails to lock the sqlite db the wrapper throws (see also comment below). This loop simply retries the query until it succeeds (or the test wrapper kills and fails the test). It could be rewritten but this would only hide the intention of looping until the query succeeds.
std::this_thread::sleep_for(50ms); | ||
} | ||
} | ||
)); | ||
} | ||
|
||
size_t count_stored_messages(rosbag2_storage_plugins::SqliteWrapper & db, std::string topic_name) | ||
{ | ||
// protect against concurrent writes (from recording) that may make the count query throw. |
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 called concurrently, shouldn't this be locked then?
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.
We decided no for the following reason: the rosbag2_storage_plugins::SqliteWrapper
is not designed to handle db locking more gracefully than throwing an exception as for the "record" and "play" usecases there will be no concurrent usage of the SqliteWrapper
. Even for a later "convert" usage concurrent usage of the same db is (almost certain) not needed. So the concurrent use is only needed in tests the handling of db locks is put in the test fixture.
Retry is the most common strategy for dealing with failures of obtaining a db lock. Every other strategy would yield much more complicated code and the test runners timeout protects against infinite retrying.
|
||
#ifdef _WIN32 | ||
# pragma warning(push) | ||
# pragma warning(disable:4275) |
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.
what's that warning? Can you maybe put a little comment on this to trace it later on?
|
||
#ifdef _WIN32 | ||
# pragma warning(push) | ||
# pragma warning(disable:4251) |
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. Comment could be helpful here.
|
||
namespace rosbag2_storage_plugins | ||
{ | ||
|
||
class SqliteStatementWrapper : public std::enable_shared_from_this<SqliteStatementWrapper> | ||
class ROSBAG2_STORAGE_DEFAULT_PLUGINS_PUBLIC SqliteStatementWrapper | ||
: public std::enable_shared_from_this<SqliteStatementWrapper> | ||
{ | ||
public: | ||
SqliteStatementWrapper(sqlite3 * database, std::string query); |
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.
const ref?
@@ -51,8 +51,9 @@ SqliteStatementWrapper::~SqliteStatementWrapper() | |||
std::shared_ptr<SqliteStatementWrapper> SqliteStatementWrapper::execute_and_reset() | |||
{ | |||
int return_code = sqlite3_step(statement_); | |||
if (return_code != SQLITE_OK && return_code != SQLITE_DONE) { | |||
throw SqliteException("Error processing SQLite statement."); | |||
if (return_code != SQLITE_OK && return_code != SQLITE_DONE && return_code != SQLITE_ROW) { |
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.
nitpick: Maybe for readability, these checks can be excluded in a little inline function is_valid
or something. So ease up the condition.
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 agree.
@@ -25,7 +25,8 @@ | |||
|
|||
#include "rosbag2_storage/serialized_bag_message.hpp" | |||
#include "../logging.hpp" | |||
#include "sqlite_statement_wrapper.hpp" | |||
#include "rosbag2_storage_default_plugins/sqlite/sqlite_statement_wrapper.hpp" |
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.
nit: alphabetical order with ../logging.hpp
@@ -51,13 +51,18 @@ SqliteStatementWrapper::~SqliteStatementWrapper() | |||
std::shared_ptr<SqliteStatementWrapper> SqliteStatementWrapper::execute_and_reset() | |||
{ | |||
int return_code = sqlite3_step(statement_); | |||
if (return_code != SQLITE_OK && return_code != SQLITE_DONE && return_code != SQLITE_ROW) { | |||
if (!isQueryOk(return_code)) { |
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.
nitpick: ros2 convention is lower case and underscore
throw SqliteException("Error processing SQLite statement. Return code: " + | ||
std::to_string(return_code)); | ||
} | ||
return reset(); | ||
} | ||
|
||
bool SqliteStatementWrapper::isQueryOk(int return_code) |
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 be inline or set in a lamdba
* remove unnecessary block Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * use target_link_libraries instead of ament_target_dependencies Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * remove ros environment Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * add prefix to compile definition Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp>
* remove unnecessary block Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * use target_link_libraries instead of ament_target_dependencies Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * remove ros environment Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> * add prefix to compile definition Signed-off-by: wep21 <border_goldenmarket@yahoo.co.jp> Signed-off-by: James Smith <james@foxglove.dev>
This pr is based on #30.
Improved handling of sqlite:
io_flag
, i.e. read-only or read-write. Only an existing db can be opened read-only.Other: