From f1862699a5802afe14ba53dd91a7cfabdbcc2e01 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov.dev@gmail.com> Date: Thu, 15 Jun 2017 18:23:33 +0300 Subject: [PATCH] vinyl: test vylog error handling Check that: - Error writing to vylog does not break vinyl permanently. - An index drop/truncate/create record that was not written to vylog due to a write error is buffered and flushed along with the next write. - A buffered index drop/truncate/create record that hasn't been flushed before restart is replayed on WAL recovery. Closes #2490 --- src/box/vy_log.c | 6 + src/errinj.h | 1 + test/vinyl/errinj_vylog.result | 249 +++++++++++++++++++++++++++++++ test/vinyl/errinj_vylog.test.lua | 121 +++++++++++++++ test/vinyl/suite.ini | 2 +- 5 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 test/vinyl/errinj_vylog.result create mode 100644 test/vinyl/errinj_vylog.test.lua diff --git a/src/box/vy_log.c b/src/box/vy_log.c index 3ff9e048d3..147e30af42 100644 --- a/src/box/vy_log.c +++ b/src/box/vy_log.c @@ -49,6 +49,7 @@ #include "coio_task.h" #include "diag.h" #include "errcode.h" +#include "errinj.h" #include "fiber.h" #include "iproto_constants.h" /* IPROTO_INSERT */ #include "key_def.h" @@ -683,6 +684,11 @@ vy_log_flush(void) if (vy_log.tx_size == 0) return 0; /* nothing to do */ + ERROR_INJECT(ERRINJ_VY_LOG_FLUSH, { + diag_set(ClientError, ER_INJECTION, "vinyl log flush"); + return -1; + }); + struct journal_entry *entry = journal_entry_new(vy_log.tx_size); if (entry == NULL) return -1; diff --git a/src/errinj.h b/src/errinj.h index 94a79d9233..75cafd92b1 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -91,6 +91,7 @@ struct errinj { _(ERRINJ_VY_SQUASH_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \ _(ERRINJ_VY_SCHED_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \ _(ERRINJ_VY_GC, ERRINJ_BOOL, {.bparam = false}) \ + _(ERRINJ_VY_LOG_FLUSH, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_RELAY_TIMEOUT, ERRINJ_DOUBLE, {.dparam = 0}) \ _(ERRINJ_RELAY_REPORT_INTERVAL, ERRINJ_DOUBLE, {.dparam = 0}) \ _(ERRINJ_RELAY_FINAL_SLEEP, ERRINJ_BOOL, {.bparam = false}) \ diff --git a/test/vinyl/errinj_vylog.result b/test/vinyl/errinj_vylog.result new file mode 100644 index 0000000000..f9ae3250e7 --- /dev/null +++ b/test/vinyl/errinj_vylog.result @@ -0,0 +1,249 @@ +test_run = require('test_run').new() +--- +... +fiber = require('fiber') +--- +... +-- +-- Check that an error to commit a new run to vylog does not +-- break vinyl permanently. +-- +s = box.schema.space.create('test', {engine = 'vinyl'}) +--- +... +_ = s:create_index('pk') +--- +... +_ = s:insert{1, 'x'} +--- +... +SCHED_TIMEOUT = 0.05 +--- +... +box.error.injection.set('ERRINJ_VY_SCHED_TIMEOUT', SCHED_TIMEOUT) +--- +- ok +... +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); +--- +- ok +... +box.snapshot() +--- +- error: Error injection 'vinyl log flush' +... +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', false); +--- +- ok +... +fiber.sleep(2 * SCHED_TIMEOUT) +--- +... +box.error.injection.set('ERRINJ_VY_SCHED_TIMEOUT', 0) +--- +- ok +... +_ = s:insert{2, 'y'} +--- +... +box.snapshot() +--- +- ok +... +_ = s:insert{3, 'z'} +--- +... +test_run:cmd('restart server default') +s = box.space.test +--- +... +s:select() +--- +- - [1, 'x'] + - [2, 'y'] + - [3, 'z'] +... +s:drop() +--- +... +-- +-- Check that an index drop/truncate/create record we failed to +-- write to vylog is flushed along with the next record. +-- +s1 = box.schema.space.create('test1', {engine = 'vinyl'}) +--- +... +_ = s1:create_index('pk') +--- +... +_ = s1:insert{1, 'a'} +--- +... +s2 = box.schema.space.create('test2', {engine = 'vinyl'}) +--- +... +_ = s2:create_index('pk') +--- +... +_ = s2:insert{2, 'b'} +--- +... +box.snapshot() +--- +- ok +... +_ = s1:insert{3, 'c'} +--- +... +_ = s2:insert{4, 'd'} +--- +... +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); +--- +- ok +... +s1:drop() +--- +... +s2:truncate() +--- +... +_ = s2:insert{5, 'e'} +--- +... +s3 = box.schema.space.create('test3', {engine = 'vinyl'}) +--- +... +_ = s3:create_index('pk') +--- +... +_ = s3:insert{6, 'f'} +--- +... +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', false); +--- +- ok +... +box.snapshot() +--- +- ok +... +_ = s2:insert{7, 'g'} +--- +... +_ = s3:insert{8, 'h'} +--- +... +test_run:cmd('restart server default') +s1 = box.space.test1 +--- +... +s1 == nil +--- +- true +... +s2 = box.space.test2 +--- +... +s2:select() +--- +- - [5, 'e'] + - [7, 'g'] +... +s2:drop() +--- +... +s3 = box.space.test3 +--- +... +s3:select() +--- +- - [6, 'f'] + - [8, 'h'] +... +s3:drop() +--- +... +-- +-- Check that if a buffered index drop/truncate/create record +-- does not make it to the vylog before restart, it will be +-- replayed on recovery. +-- +s1 = box.schema.space.create('test1', {engine = 'vinyl'}) +--- +... +_ = s1:create_index('pk') +--- +... +_ = s1:insert{111, 'aaa'} +--- +... +s2 = box.schema.space.create('test2', {engine = 'vinyl'}) +--- +... +_ = s2:create_index('pk') +--- +... +_ = s2:insert{222, 'bbb'} +--- +... +box.snapshot() +--- +- ok +... +_ = s1:insert{333, 'ccc'} +--- +... +_ = s2:insert{444, 'ddd'} +--- +... +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); +--- +- ok +... +s1:drop() +--- +... +s2:truncate() +--- +... +_ = s2:insert{555, 'eee'} +--- +... +s3 = box.schema.space.create('test3', {engine = 'vinyl'}) +--- +... +_ = s3:create_index('pk') +--- +... +_ = s3:insert{666, 'fff'} +--- +... +test_run:cmd('restart server default') +s1 = box.space.test1 +--- +... +s1 == nil +--- +- true +... +s2 = box.space.test2 +--- +... +s2:select() +--- +- - [555, 'eee'] +... +s2:drop() +--- +... +s3 = box.space.test3 +--- +... +s3:select() +--- +- - [666, 'fff'] +... +s3:drop() +--- +... diff --git a/test/vinyl/errinj_vylog.test.lua b/test/vinyl/errinj_vylog.test.lua new file mode 100644 index 0000000000..ee0073d09c --- /dev/null +++ b/test/vinyl/errinj_vylog.test.lua @@ -0,0 +1,121 @@ +test_run = require('test_run').new() +fiber = require('fiber') + +-- +-- Check that an error to commit a new run to vylog does not +-- break vinyl permanently. +-- +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk') +_ = s:insert{1, 'x'} + +SCHED_TIMEOUT = 0.05 +box.error.injection.set('ERRINJ_VY_SCHED_TIMEOUT', SCHED_TIMEOUT) +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); + +box.snapshot() + +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', false); +fiber.sleep(2 * SCHED_TIMEOUT) +box.error.injection.set('ERRINJ_VY_SCHED_TIMEOUT', 0) + +_ = s:insert{2, 'y'} + +box.snapshot() + +_ = s:insert{3, 'z'} + +test_run:cmd('restart server default') + +s = box.space.test +s:select() +s:drop() + +-- +-- Check that an index drop/truncate/create record we failed to +-- write to vylog is flushed along with the next record. +-- +s1 = box.schema.space.create('test1', {engine = 'vinyl'}) +_ = s1:create_index('pk') +_ = s1:insert{1, 'a'} + +s2 = box.schema.space.create('test2', {engine = 'vinyl'}) +_ = s2:create_index('pk') +_ = s2:insert{2, 'b'} + +box.snapshot() + +_ = s1:insert{3, 'c'} +_ = s2:insert{4, 'd'} + +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); + +s1:drop() +s2:truncate() +_ = s2:insert{5, 'e'} + +s3 = box.schema.space.create('test3', {engine = 'vinyl'}) +_ = s3:create_index('pk') +_ = s3:insert{6, 'f'} + +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', false); + +box.snapshot() + +_ = s2:insert{7, 'g'} +_ = s3:insert{8, 'h'} + +test_run:cmd('restart server default') + +s1 = box.space.test1 +s1 == nil + +s2 = box.space.test2 +s2:select() +s2:drop() + +s3 = box.space.test3 +s3:select() +s3:drop() + +-- +-- Check that if a buffered index drop/truncate/create record +-- does not make it to the vylog before restart, it will be +-- replayed on recovery. +-- + +s1 = box.schema.space.create('test1', {engine = 'vinyl'}) +_ = s1:create_index('pk') +_ = s1:insert{111, 'aaa'} + +s2 = box.schema.space.create('test2', {engine = 'vinyl'}) +_ = s2:create_index('pk') +_ = s2:insert{222, 'bbb'} + +box.snapshot() + +_ = s1:insert{333, 'ccc'} +_ = s2:insert{444, 'ddd'} + +box.error.injection.set('ERRINJ_VY_LOG_FLUSH', true); + +s1:drop() +s2:truncate() +_ = s2:insert{555, 'eee'} + +s3 = box.schema.space.create('test3', {engine = 'vinyl'}) +_ = s3:create_index('pk') +_ = s3:insert{666, 'fff'} + +test_run:cmd('restart server default') + +s1 = box.space.test1 +s1 == nil + +s2 = box.space.test2 +s2:select() +s2:drop() + +s3 = box.space.test3 +s3:select() +s3:drop() diff --git a/test/vinyl/suite.ini b/test/vinyl/suite.ini index 29fbc8d78b..3466a57c4f 100644 --- a/test/vinyl/suite.ini +++ b/test/vinyl/suite.ini @@ -2,7 +2,7 @@ core = tarantool description = vinyl integration tests script = vinyl.lua -release_disabled = errinj.test.lua errinj_gc.test.lua partial_dump.test.lua quota_timeout.test.lua +release_disabled = errinj.test.lua errinj_gc.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua config = suite.cfg lua_libs = suite.lua stress.lua large.lua txn_proxy.lua ../box/lua/utils.lua use_unix_sockets = True -- GitLab