Skip to content

Commit

Permalink
vinyl: don't exempt dropped indexes from dump and compaction
Browse files Browse the repository at this point in the history
We remove an LSM tree from the scheduler queues as soon as it is
dropped, even though the tree may hang around for a while after
that, e.g. because it is pinned by an iterator. As a result, once
an index is dropped, it won't be dumped anymore - its memory level
will simply disappear without a trace. This is okay for now, but
to implement snapshot iterators we must make sure that an index
will stay valid as long as there's an iterator that references it.

That said, let's delay removal of an index from the scheduler queues
until it is about to be destroyed.
  • Loading branch information
locker committed Aug 20, 2019
1 parent 1314b95 commit d7387ec
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 80 deletions.
16 changes: 1 addition & 15 deletions src/box/vinyl.c
Expand Up @@ -755,18 +755,8 @@ vinyl_index_open(struct index *index)
/*
* Add the new LSM tree to the scheduler so that it can
* be dumped and compacted.
*
* Note, during local recovery an LSM tree may be marked
* as dropped, which means that it will be dropped before
* recovery is complete. In this case there's no need in
* letting the scheduler know about it.
*/
if (!lsm->is_dropped)
vy_scheduler_add_lsm(&env->scheduler, lsm);
else
assert(env->status == VINYL_INITIAL_RECOVERY_LOCAL ||
env->status == VINYL_FINAL_RECOVERY_LOCAL);
return 0;
return vy_scheduler_add_lsm(&env->scheduler, lsm);
}

static void
Expand Down Expand Up @@ -836,8 +826,6 @@ vinyl_index_abort_create(struct index *index)
return;
}

vy_scheduler_remove_lsm(&env->scheduler, lsm);

lsm->is_dropped = true;

vy_log_tx_begin();
Expand Down Expand Up @@ -891,8 +879,6 @@ vinyl_index_commit_drop(struct index *index, int64_t lsn)
if (env->status == VINYL_FINAL_RECOVERY_LOCAL && lsm->is_dropped)
return;

vy_scheduler_remove_lsm(&env->scheduler, lsm);

lsm->is_dropped = true;

vy_log_tx_begin();
Expand Down
4 changes: 4 additions & 0 deletions src/box/vy_lsm.c
Expand Up @@ -45,6 +45,7 @@
#include "say.h"
#include "schema.h"
#include "tuple.h"
#include "trigger.h"
#include "vy_log.h"
#include "vy_mem.h"
#include "vy_range.h"
Expand Down Expand Up @@ -207,6 +208,7 @@ vy_lsm_new(struct vy_lsm_env *lsm_env, struct vy_cache_env *cache_env,
lsm->group_id = group_id;
lsm->opts = index_def->opts;
vy_lsm_read_set_new(&lsm->read_set);
rlist_create(&lsm->on_destroy);

lsm_env->lsm_count++;
return lsm;
Expand Down Expand Up @@ -244,6 +246,8 @@ vy_range_tree_free_cb(vy_range_tree_t *t, struct vy_range *range, void *arg)
void
vy_lsm_delete(struct vy_lsm *lsm)
{
trigger_run(&lsm->on_destroy, lsm);

assert(heap_node_is_stray(&lsm->in_dump));
assert(heap_node_is_stray(&lsm->in_compaction));
assert(vy_lsm_read_set_empty(&lsm->read_set));
Expand Down
14 changes: 14 additions & 0 deletions src/box/vy_lsm.h
Expand Up @@ -312,6 +312,20 @@ struct vy_lsm {
* this LSM tree.
*/
vy_lsm_read_set_t read_set;
/**
* Triggers run when the last reference to this LSM tree
* is dropped and the LSM tree is about to be destroyed.
* A pointer to this LSM tree is passed to the trigger
* callback in the 'event' argument.
*
* For instance, this trigger is used to remove a dropped
* LSM tree from the scheduler before it gets destroyed.
* Since each dump/compaction task takes a reference to
* the target index, this means that a dropped index will
* not get destroyed until all tasks scheduled for it have
* been completed.
*/
struct rlist on_destroy;
};

/** Extract vy_lsm from an index object. */
Expand Down
95 changes: 37 additions & 58 deletions src/box/vy_scheduler.c
Expand Up @@ -510,35 +510,47 @@ vy_scheduler_reset_stat(struct vy_scheduler *scheduler)
stat->compaction_output = 0;
}

void
static void
vy_scheduler_on_delete_lsm(struct trigger *trigger, void *event)
{
struct vy_lsm *lsm = event;
struct vy_scheduler *scheduler = trigger->data;
assert(! heap_node_is_stray(&lsm->in_dump));
assert(! heap_node_is_stray(&lsm->in_compaction));
vy_dump_heap_delete(&scheduler->dump_heap, lsm);
vy_compaction_heap_delete(&scheduler->compaction_heap, lsm);
trigger_clear(trigger);
free(trigger);
}

int
vy_scheduler_add_lsm(struct vy_scheduler *scheduler, struct vy_lsm *lsm)
{
assert(!lsm->is_dropped);
assert(heap_node_is_stray(&lsm->in_dump));
assert(heap_node_is_stray(&lsm->in_compaction));
/*
* Register a trigger that will remove this LSM tree from
* the scheduler queues on destruction.
*/
struct trigger *trigger = malloc(sizeof(*trigger));
if (trigger == NULL) {
diag_set(OutOfMemory, sizeof(*trigger), "malloc", "trigger");
return -1;
}
trigger_create(trigger, vy_scheduler_on_delete_lsm, scheduler, NULL);
trigger_add(&lsm->on_destroy, trigger);
/*
* Add this LSM tree to the scheduler queues so that it
* can be dumped and compacted in a timely manner.
*/
vy_dump_heap_insert(&scheduler->dump_heap, lsm);
vy_compaction_heap_insert(&scheduler->compaction_heap, lsm);
}

void
vy_scheduler_remove_lsm(struct vy_scheduler *scheduler, struct vy_lsm *lsm)
{
assert(!lsm->is_dropped);
assert(! heap_node_is_stray(&lsm->in_dump));
assert(! heap_node_is_stray(&lsm->in_compaction));
vy_dump_heap_delete(&scheduler->dump_heap, lsm);
vy_compaction_heap_delete(&scheduler->compaction_heap, lsm);
return 0;
}

static void
vy_scheduler_update_lsm(struct vy_scheduler *scheduler, struct vy_lsm *lsm)
{
if (lsm->is_dropped) {
/* Dropped LSM trees are exempted from scheduling. */
assert(heap_node_is_stray(&lsm->in_dump));
assert(heap_node_is_stray(&lsm->in_compaction));
return;
}
assert(! heap_node_is_stray(&lsm->in_dump));
assert(! heap_node_is_stray(&lsm->in_compaction));
vy_dump_heap_update(&scheduler->dump_heap, lsm);
Expand Down Expand Up @@ -1267,15 +1279,9 @@ vy_task_dump_abort(struct vy_task *task)
/* The iterator has been cleaned up in a worker thread. */
task->wi->iface->close(task->wi);

/*
* It's no use alerting the user if the server is
* shutting down or the LSM tree was dropped.
*/
if (!lsm->is_dropped) {
struct error *e = diag_last_error(&task->diag);
error_log(e);
say_error("%s: dump failed", vy_lsm_name(lsm));
}
struct error *e = diag_last_error(&task->diag);
error_log(e);
say_error("%s: dump failed", vy_lsm_name(lsm));

vy_run_discard(task->new_run);

Expand All @@ -1287,18 +1293,6 @@ vy_task_dump_abort(struct vy_task *task)

assert(scheduler->dump_task_count > 0);
scheduler->dump_task_count--;

/*
* If the LSM tree was dropped during dump, we abort
* the dump task, but we should still poke the scheduler
* to check if the current dump round is complete.
* If we don't and this LSM tree happens to be the last
* one of the current generation, the scheduler will
* never be notified about dump completion and hence
* memory will never be released.
*/
if (lsm->is_dropped)
vy_scheduler_complete_dump(scheduler);
}

/**
Expand All @@ -1317,7 +1311,6 @@ vy_task_dump_new(struct vy_scheduler *scheduler, struct vy_worker *worker,
.abort = vy_task_dump_abort,
};

assert(!lsm->is_dropped);
assert(!lsm->is_dumping);
assert(lsm->pin_count == 0);
assert(vy_lsm_generation(lsm) == scheduler->dump_generation);
Expand Down Expand Up @@ -1602,16 +1595,10 @@ vy_task_compaction_abort(struct vy_task *task)
/* The iterator has been cleaned up in worker. */
task->wi->iface->close(task->wi);

/*
* It's no use alerting the user if the server is
* shutting down or the LSM tree was dropped.
*/
if (!lsm->is_dropped) {
struct error *e = diag_last_error(&task->diag);
error_log(e);
say_error("%s: failed to compact range %s",
vy_lsm_name(lsm), vy_range_str(range));
}
struct error *e = diag_last_error(&task->diag);
error_log(e);
say_error("%s: failed to compact range %s",
vy_lsm_name(lsm), vy_range_str(range));

vy_run_discard(task->new_run);

Expand All @@ -1629,7 +1616,6 @@ vy_task_compaction_new(struct vy_scheduler *scheduler, struct vy_worker *worker,
.complete = vy_task_compaction_complete,
.abort = vy_task_compaction_abort,
};
assert(!lsm->is_dropped);

struct vy_range *range = vy_range_heap_top(&lsm->range_heap);
assert(range != NULL);
Expand Down Expand Up @@ -1945,12 +1931,6 @@ vy_task_complete(struct vy_task *task)
assert(scheduler->stat.tasks_inprogress > 0);
scheduler->stat.tasks_inprogress--;

if (task->lsm->is_dropped) {
if (task->ops->abort)
task->ops->abort(task);
goto out;
}

struct diag *diag = &task->diag;
if (task->is_failed) {
assert(!diag_is_empty(diag));
Expand All @@ -1967,7 +1947,6 @@ vy_task_complete(struct vy_task *task)
diag_move(diag_get(), diag);
goto fail;
}
out:
scheduler->stat.tasks_completed++;
return 0;
fail:
Expand Down
10 changes: 3 additions & 7 deletions src/box/vy_scheduler.h
Expand Up @@ -194,16 +194,12 @@ vy_scheduler_reset_stat(struct vy_scheduler *scheduler);

/**
* Add an LSM tree to scheduler dump/compaction queues.
* When the LSM tree is destroyed, it will be removed
* from the queues automatically.
*/
void
int
vy_scheduler_add_lsm(struct vy_scheduler *, struct vy_lsm *);

/**
* Remove an LSM tree from scheduler dump/compaction queues.
*/
void
vy_scheduler_remove_lsm(struct vy_scheduler *, struct vy_lsm *);

/**
* Trigger dump of all currently existing in-memory trees.
*/
Expand Down

0 comments on commit d7387ec

Please sign in to comment.