Skip to content
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

Introduce RESET request #435

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/gateway.c
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,45 @@ static int handle_weight(struct handle *req)
return 0;
}

static void resetCb(struct exec *exec, int status)
{
tracef("reset cb status:%d", status);
struct gateway *g = exec->data;
struct handle *req = g->req;
struct response_empty response = {0};
g->req = NULL;
if (status != 0) {
failure(req, status, "database reset failed");
return;
}
/* status and exec->status should always match. */
assert(exec->status == 0);
SUCCESS(empty, EMPTY);
}

static int handle_reset(struct handle *req)
{
tracef("handle reset");
struct cursor *cursor = &req->cursor;
struct gateway *g = req->gateway;
int rc;

START_V0(reset, empty);
(void)response;
CHECK_LEADER(req);
LOOKUP_DB(request.db_id);
FAIL_IF_CHECKPOINTING;

g->req = req;
rc = leader__reset(g->leader, &g->exec, resetCb);
if (rc != 0) {
tracef("leader reset failed %d", rc);
g->req = NULL;
return rc;
}
return 0;
}

int gateway__handle(struct gateway *g,
struct handle *req,
int type,
Expand Down
102 changes: 102 additions & 0 deletions src/leader.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,105 @@ int leader__barrier(struct leader *l, struct barrier *barrier, barrier_cb cb)
}
return 0;
}

static void resetBarrierCb(struct barrier *barrier, int status)
{
tracef("reset barrier cb status:%d", status);
struct exec *exec = barrier->data;
struct leader *l = exec->leader;
sqlite3_vfs *vfs;
dqlite_vfs_frame *frames;
struct vfs_database_memory_usage usage;
unsigned n, i;
int rv;

if (status != 0) {
rv = status;
goto finish;
}

#if SQLITE_VERSION_NUMBER >= 302400
vfs = sqlite3_vfs_find(l->db->config->name);
assert(vfs != NULL);
usage = VfsDatabaseMemoryUsage(vfs, l->db->filename);
tracef("reset barrier usage before database_n_pages=%u wal_n_frames=%u wal_n_tx=%u shm_n_regions=%u",
usage.database_n_pages,
usage.wal_n_frames,
usage.wal_n_tx,
usage.shm_n_regions);

rv = sqlite3_db_config(l->conn, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
assert(rv == 0);
exec->status = sqlite3_exec(l->conn, "VACUUM", NULL, NULL, NULL);
rv = sqlite3_db_config(l->conn, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, does dqlite's memory footprint shrink after resetting a DB? If it's not shrinking, we should look into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be a good thing to measure. Let me see if I can add some basic tracing of memory consumption before and after the VACUUM.

Copy link
Contributor Author

@cole-miller cole-miller Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't see any change in number of DB pages, number of WAL frames, or number of shm regions after leaderApplyFrames returns vs. before VACUUM. @freeekanayaka, can you think of anything that would be preventing our VFS from dropping allocated memory when SQLite is done using it? (I could also just be looking in the wrong places -- I don't fully understand the VFS subsystem yet.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the VACUUM actually succeed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does -- I just tried adding an extra tracef to confirm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't see any change in number of DB pages, number of WAL frames, or number of shm regions after leaderApplyFrames returns vs. before VACUUM. @freeekanayaka, can you think of anything that would be preventing our VFS from dropping allocated memory when SQLite is done using it? (I could also just be looking in the wrong places -- I don't fully understand the VFS subsystem yet.)

Not on the top of my head, but I also don't have a clear picture of the changes that are being made. The SQLite memory allocator has some helper functions to track the amount of allocated memory, perhaps that could be useful to check.

assert(rv == 0);

rv = VfsPoll(vfs, l->db->filename, &frames, &n);
if (rv != 0 || n == 0) {
goto finish;
}

rv = leaderApplyFrames(exec, frames, n);
for (i = 0; i < n; i++) {
sqlite3_free(frames[i].data);
}
sqlite3_free(frames);
if (rv != 0) {
VfsAbort(vfs, l->db->filename);
goto finish;
}

usage = VfsDatabaseMemoryUsage(vfs, l->db->filename);
tracef("reset barrier usage after database_n_pages=%u wal_n_frames=%u wal_n_tx=%u shm_n_regions=%u",
usage.database_n_pages,
usage.wal_n_frames,
usage.wal_n_tx,
usage.shm_n_regions);
return;
#else
tracef("reset barrier cb not supported");
(void)vfs;
(void)frames;
(void)n;
(void)i;
rv = DQLITE_ERROR;
#endif

finish:
if (rv != 0) {
l->exec->status = rv;
}
leaderExecDone(l->exec);
}

int leader__reset(struct leader *l, struct exec *exec, exec_cb cb)
{
tracef("leader reset");
int version = sqlite3_libversion_number();
int rv;

if (version < 302400) {
tracef("leader reset not supported");
return DQLITE_ERROR;
}

exec->leader = l;
exec->barrier.data = exec;
exec->barrier.cb = NULL;
exec->stmt = NULL;
exec->cb = cb;

if (l->exec != NULL) {
tracef("leader reset busy");
return SQLITE_BUSY;
}
l->exec = exec;

rv = leader__barrier(l, &exec->barrier, resetBarrierCb);
if (rv != 0) {
tracef("raft barrier failed %d", rv);
l->exec = NULL;
return rv;
}
return 0;
}
2 changes: 2 additions & 0 deletions src/leader.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,6 @@ int leader__exec(struct leader *l,
*/
int leader__barrier(struct leader *l, struct barrier *barrier, barrier_cb cb);

int leader__reset(struct leader *l, struct exec *exec, exec_cb cb);

#endif /* LEADER_H_*/
3 changes: 2 additions & 1 deletion src/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ enum {
DQLITE_REQUEST_CLUSTER,
DQLITE_REQUEST_TRANSFER,
DQLITE_REQUEST_DESCRIBE,
DQLITE_REQUEST_WEIGHT
DQLITE_REQUEST_WEIGHT,
DQLITE_REQUEST_RESET
};

#define DQLITE_REQUEST_CLUSTER_FORMAT_V0 0 /* ID and address */
Expand Down
4 changes: 3 additions & 1 deletion src/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#define REQUEST_TRANSFER(X, ...) X(uint64, id, ##__VA_ARGS__)
#define REQUEST_DESCRIBE(X, ...) X(uint64, format, ##__VA_ARGS__)
#define REQUEST_WEIGHT(X, ...) X(uint64, weight, ##__VA_ARGS__)
#define REQUEST_RESET(X, ...) X(uint64, db_id, ##__VA_ARGS__)

#define REQUEST__DEFINE(LOWER, UPPER, _) \
SERIALIZE__DEFINE(request_##LOWER, REQUEST_##UPPER);
Expand All @@ -64,7 +65,8 @@
X(cluster, CLUSTER, __VA_ARGS__) \
X(transfer, TRANSFER, __VA_ARGS__) \
X(describe, DESCRIBE, __VA_ARGS__) \
X(weight, WEIGHT, __VA_ARGS__)
X(weight, WEIGHT, __VA_ARGS__) \
X(reset, RESET, __VA_ARGS__)

REQUEST__TYPES(REQUEST__DEFINE);

Expand Down
16 changes: 16 additions & 0 deletions src/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2702,3 +2702,19 @@ int VfsRestore(sqlite3_vfs *vfs,

return 0;
}

struct vfs_database_memory_usage VfsDatabaseMemoryUsage(sqlite3_vfs *vfs, const char *filename)
{
struct vfs *v;
struct vfsDatabase *d;
struct vfs_database_memory_usage usage = {0};

v = (struct vfs *)(vfs->pAppData);
d = vfsDatabaseLookup(v, filename);
assert(d != NULL);
usage.database_n_pages = d->n_pages;
usage.wal_n_frames = d->wal.n_frames;
usage.wal_n_tx = d->wal.n_tx;
usage.shm_n_regions = d->shm.n_regions;
return usage;
}
11 changes: 11 additions & 0 deletions src/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,15 @@ int VfsDatabaseNumPages(sqlite3_vfs *vfs,
const char* filename,
uint32_t *n);

struct vfs_database_memory_usage
{
unsigned database_n_pages;
unsigned wal_n_frames;
unsigned wal_n_tx;
unsigned shm_n_regions;
};

struct vfs_database_memory_usage VfsDatabaseMemoryUsage(sqlite3_vfs *vfs,
const char *filename);

#endif /* VFS_H_ */
58 changes: 58 additions & 0 deletions test/unit/test_gateway.c
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,64 @@ TEST_CASE(request_cluster, unrecognizedFormat, NULL)
return MUNIT_OK;
}

#if SQLITE_VERSION_NUMBER >= 302400

/******************************************************************************
*
* reset
*
******************************************************************************/

struct reset_fixture
{
FIXTURE;
struct request_reset request;
struct response_empty response;
};

TEST_SUITE(reset);
TEST_SETUP(reset)
{
struct reset_fixture *f = munit_malloc(sizeof *f);
SETUP;
CLUSTER_ELECT(0);
OPEN;
return f;
}
TEST_TEAR_DOWN(reset)
{
struct reset_fixture *f = data;
TEAR_DOWN;
free(f);
}

TEST_CASE(reset, simple, NULL)
{
struct reset_fixture *f = data;
char buf[100];
int i;
(void)params;
EXEC("CREATE TABLE test (n INT)");
for (i = 0; i < 100; i += 1) {
snprintf(buf, 100, "INSERT INTO test VALUES (%d)", i);
EXEC(buf);
}
f->request.db_id = 0;
ENCODE(&f->request, reset);
HANDLE(RESET);
WAIT;
ASSERT_CALLBACK(0, EMPTY);

CLUSTER_DEPOSE;
SELECT(1);
CLUSTER_ELECT(1);
OPEN;
EXEC("CREATE TABLE test (n INT)");
return MUNIT_OK;
}

#endif /* SQLITE_VERSION_NUMBER >= 302400 */

/******************************************************************************
*
* invalid
Expand Down