Skip to content

[Feature brainstorm] Unlock-notify or retry logic #594

@undisputed-seraphim

Description

@undisputed-seraphim

I need to write to the same database from several threads. Sometimes, the threads encounter SQLITE_LOCKED and a std::system_error is thrown, but this is a recoverable issue and I would like to simply retry the step, perhaps with a backoff.

As per the sqlite3_unlock_notify page, perhaps it is possible to work the retry logic into this.

Currently, most of the code involving sqlite3_step() looks like this:

if(sqlite3_step(stmt) == SQLITE_DONE) {
	//  done..
} else {
	throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db));
}

Perhaps it can be changed into something like this.
We set a bool flag in storage_base to indicate if we should block on SQLITE_LOCKED.

int rc = 0;
if (blocking) {
    while ((rc = sqlite3_step(stmt)) == SQLITE_LOCKED) {
        rc = wait_for_unlock_notify(sqlite3_db_handle(stmt));
    }
} else {
    rc = sqlite3_step(stmt);
}
if (rc != SQLITE_DONE) {
    throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db));
}

Where wait_for_unlock_notify() is similar to the one described in the page above.

However, not all sqlite3 compilations have the sqlite3_unlock_notify() function. In that case, we can somehow fall back to a regular blocking loop:

#include <chrono>
using namespace std::chrono_literals;

int rc = 0;
if (blocking) {
    while ((rc = sqlite3_step(stmt)) == SQLITE_LOCKED) {
        std::this_thread::sleep_for(1s);
    }
} else {
    rc = sqlite3_step(stmt);
}
if (rc != SQLITE_DONE) {
    throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db));
}

Of course, sleep_for can be hidden inside wait_for_unlock_notify (with a better name I guess) and the existence of sqlite3_unlock_notify is detected with SFINAE.

What do you think?

As for the implementation of unlock_notify object, it can be achieved through std::condition_variable and std::mutex to keep it cross-platform, instead of relying on pthread.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions