From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Vladimir Davydov Subject: [PATCH] memtx: don't delay deletion of temporary tuples during snapshot Date: Mon, 4 Jun 2018 14:42:28 +0300 Message-Id: To: kostja@tarantool.org Cc: tarantool-patches@freelists.org List-ID: Since tuples stored in temporary spaces are never written to disk, we can always delete them immediately, even when a snapshot is in progress. Closes #3432 --- https://github.com/tarantool/tarantool/issues/3432 https://github.com/tarantool/tarantool/commits/gh-3432-memtx-dont-delay-free-temp-tuples src/box/memtx_engine.c | 8 +++- src/box/memtx_space.c | 1 + src/box/tuple_format.c | 1 + src/box/tuple_format.h | 6 +++ src/errinj.h | 1 + test/box/errinj.result | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ test/box/errinj.test.lua | 44 ++++++++++++++++++++++ 7 files changed, 156 insertions(+), 1 deletion(-) diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c index fac84ce1..675ebb59 100644 --- a/src/box/memtx_engine.c +++ b/src/box/memtx_engine.c @@ -463,6 +463,11 @@ memtx_engine_bootstrap(struct engine *engine) static int checkpoint_write_row(struct xlog *l, struct xrow_header *row) { + struct errinj *errinj = errinj(ERRINJ_SNAP_WRITE_ROW_TIMEOUT, + ERRINJ_DOUBLE); + if (errinj != NULL && errinj->dparam > 0) + usleep(errinj->dparam * 1000000); + static ev_tstamp last = 0; if (last == 0) { ev_now_update(loop()); @@ -1138,7 +1143,8 @@ memtx_tuple_delete(struct tuple_format *format, struct tuple *tuple) struct memtx_tuple *memtx_tuple = container_of(tuple, struct memtx_tuple, base); if (memtx->alloc.free_mode != SMALL_DELAYED_FREE || - memtx_tuple->version == memtx->snapshot_version) + memtx_tuple->version == memtx->snapshot_version || + format->temporary) smfree(&memtx->alloc, memtx_tuple, total); else smfree_delayed(&memtx->alloc, memtx_tuple, total); diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c index f17df58c..aef7e788 100644 --- a/src/box/memtx_space.c +++ b/src/box/memtx_space.c @@ -896,6 +896,7 @@ memtx_space_new(struct memtx_engine *memtx, return NULL; } format->engine = memtx; + format->temporary = def->opts.temporary; format->exact_field_count = def->exact_field_count; tuple_format_ref(format); diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c index 277d9e7f..486646ea 100644 --- a/src/box/tuple_format.c +++ b/src/box/tuple_format.c @@ -270,6 +270,7 @@ tuple_format_new(struct tuple_format_vtab *vtab, struct key_def * const *keys, format->vtab = *vtab; format->engine = NULL; format->extra_size = extra_size; + format->temporary = false; if (tuple_format_register(format) < 0) { tuple_format_destroy(format); free(format); diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h index d8c898b8..9da9be3e 100644 --- a/src/box/tuple_format.h +++ b/src/box/tuple_format.h @@ -124,6 +124,12 @@ struct tuple_format { /** Reference counter */ int refs; /** + * Tuples of this format belong to a temporary space and + * hence can be freed immediately while checkpointing is + * in progress. + */ + bool temporary; + /** * The number of extra bytes to reserve in tuples before * field map. * \sa struct tuple diff --git a/src/errinj.h b/src/errinj.h index ab578274..4998fdcd 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -112,6 +112,7 @@ struct errinj { _(ERRINJ_HTTPC_EXECUTE, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_LOG_ROTATE, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_SNAP_COMMIT_DELAY, ERRINJ_BOOL, {.bparam = 0}) \ + _(ERRINJ_SNAP_WRITE_ROW_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index e25a4594..d3765f11 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -20,6 +20,8 @@ errinj.info() state: false ERRINJ_VYRUN_DATA_READ: state: false + ERRINJ_SNAP_WRITE_ROW_TIMEOUT: + state: 0 ERRINJ_VY_SCHED_TIMEOUT: state: 0 ERRINJ_WAL_WRITE_PARTIAL: @@ -1309,3 +1311,97 @@ s:select() s:drop() --- ... +-- +-- gh-3432: check that deletion of temporary tuples is not delayed +-- if snapshot is in progress. +-- +test_run:cmd("create server test with script='box/lua/cfg_memory.lua'") +--- +- true +... +test_run:cmd(string.format("start server test with args='%d'", 100 * 1024 * 1024)) +--- +- true +... +test_run:cmd("switch test") +--- +- true +... +fiber = require('fiber') +--- +... +-- Create a persistent space. +_ = box.schema.space.create('test') +--- +... +_ = box.space.test:create_index('pk') +--- +... +for i = 1, 100 do box.space.test:insert{i} end +--- +... +-- Create a temporary space. +count = 500 +--- +... +pad = string.rep('x', 100 * 1024) +--- +... +_ = box.schema.space.create('tmp', {temporary = true}) +--- +... +_ = box.space.tmp:create_index('pk') +--- +... +for i = 1, count do box.space.tmp:insert{i, pad} end +--- +... +-- Start background snapshot. +c = fiber.channel(1) +--- +... +box.error.injection.set('ERRINJ_SNAP_WRITE_ROW_TIMEOUT', 0.01) +--- +- ok +... +_ = fiber.create(function() box.snapshot() c:put(true) end) +--- +... +-- Overwrite data stored in the temporary space while snapshot +-- is in progress to make sure that tuples stored in it are freed +-- immediately. +for i = 1, count do box.space.tmp:delete{i} end +--- +... +_ = collectgarbage('collect') +--- +... +for i = 1, count do box.space.tmp:insert{i, pad} end +--- +... +box.error.injection.set('ERRINJ_SNAP_WRITE_ROW_TIMEOUT', 0) +--- +- ok +... +c:get() +--- +- true +... +box.space.tmp:drop() +--- +... +box.space.test:drop() +--- +... +test_run:cmd("switch default") +--- +- true +... +test_run:cmd("stop server test") +--- +- true +... +test_run:cmd("cleanup server test") +--- +- true +... diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua index f2ed823b..188c65d0 100644 --- a/test/box/errinj.test.lua +++ b/test/box/errinj.test.lua @@ -447,3 +447,47 @@ errinj.set('ERRINJ_WAL_IO', false) for i = 1, 10 do s:replace{i + 10} end s:select() s:drop() + +-- +-- gh-3432: check that deletion of temporary tuples is not delayed +-- if snapshot is in progress. +-- +test_run:cmd("create server test with script='box/lua/cfg_memory.lua'") +test_run:cmd(string.format("start server test with args='%d'", 100 * 1024 * 1024)) +test_run:cmd("switch test") + +fiber = require('fiber') + +-- Create a persistent space. +_ = box.schema.space.create('test') +_ = box.space.test:create_index('pk') +for i = 1, 100 do box.space.test:insert{i} end + +-- Create a temporary space. +count = 500 +pad = string.rep('x', 100 * 1024) +_ = box.schema.space.create('tmp', {temporary = true}) +_ = box.space.tmp:create_index('pk') +for i = 1, count do box.space.tmp:insert{i, pad} end + +-- Start background snapshot. +c = fiber.channel(1) +box.error.injection.set('ERRINJ_SNAP_WRITE_ROW_TIMEOUT', 0.01) +_ = fiber.create(function() box.snapshot() c:put(true) end) + +-- Overwrite data stored in the temporary space while snapshot +-- is in progress to make sure that tuples stored in it are freed +-- immediately. +for i = 1, count do box.space.tmp:delete{i} end +_ = collectgarbage('collect') +for i = 1, count do box.space.tmp:insert{i, pad} end + +box.error.injection.set('ERRINJ_SNAP_WRITE_ROW_TIMEOUT', 0) +c:get() + +box.space.tmp:drop() +box.space.test:drop() + +test_run:cmd("switch default") +test_run:cmd("stop server test") +test_run:cmd("cleanup server test") -- 2.11.0