diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index fabeb3fd0065f00147ffc1706e0dd426d5f3c1ca..ad544270b938e4cf85ead82180e1a50481686ca7 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -69,6 +69,7 @@ add_library(box STATIC memtx_engine.c memtx_space.c sysview.c + blackhole.c vinyl.c vy_stmt.c vy_mem.c diff --git a/src/box/blackhole.c b/src/box/blackhole.c new file mode 100644 index 0000000000000000000000000000000000000000..96b9fd147977ef5ba0a89ac5eb94c2a333d700e4 --- /dev/null +++ b/src/box/blackhole.c @@ -0,0 +1,210 @@ +/* + * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "blackhole.h" + +#include <stddef.h> +#include <stdlib.h> +#include <small/rlist.h> + +#include "diag.h" +#include "error.h" +#include "errcode.h" +#include "engine.h" +#include "space.h" +#include "txn.h" +#include "tuple.h" +#include "xrow.h" + +static void +blackhole_space_destroy(struct space *space) +{ + free(space); +} + +static int +blackhole_space_execute_replace(struct space *space, struct txn *txn, + struct request *request, struct tuple **result) +{ + struct txn_stmt *stmt = txn_current_stmt(txn); + stmt->new_tuple = tuple_new(space->format, request->tuple, + request->tuple_end); + if (stmt->new_tuple == NULL) + return -1; + tuple_ref(stmt->new_tuple); + *result = stmt->new_tuple; + return 0; +} + +static int +blackhole_space_execute_delete(struct space *space, struct txn *txn, + struct request *request, struct tuple **result) +{ + (void)space; + (void)txn; + (void)request; + (void)result; + diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "delete()"); + return -1; +} + +static int +blackhole_space_execute_update(struct space *space, struct txn *txn, + struct request *request, struct tuple **result) +{ + (void)space; + (void)txn; + (void)request; + (void)result; + diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "update()"); + return -1; +} + +static int +blackhole_space_execute_upsert(struct space *space, struct txn *txn, + struct request *request) +{ + (void)space; + (void)txn; + (void)request; + diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "upsert()"); + return -1; +} + +static struct index * +blackhole_space_create_index(struct space *space, struct index_def *def) +{ + (void)space; + (void)def; + /* See blackhole_engine_create_space(). */ + unreachable(); + return NULL; +} + +static const struct space_vtab blackhole_space_vtab = { + /* .destroy = */ blackhole_space_destroy, + /* .bsize = */ generic_space_bsize, + /* .apply_initial_join_row = */ generic_space_apply_initial_join_row, + /* .execute_replace = */ blackhole_space_execute_replace, + /* .execute_delete = */ blackhole_space_execute_delete, + /* .execute_update = */ blackhole_space_execute_update, + /* .execute_upsert = */ blackhole_space_execute_upsert, + /* .init_system_space = */ generic_init_system_space, + /* .check_index_def = */ generic_space_check_index_def, + /* .create_index = */ blackhole_space_create_index, + /* .add_primary_key = */ generic_space_add_primary_key, + /* .drop_primary_key = */ generic_space_drop_primary_key, + /* .check_format = */ generic_space_check_format, + /* .build_index = */ generic_space_build_index, + /* .swap_index = */ generic_space_swap_index, + /* .prepare_alter = */ generic_space_prepare_alter, +}; + +static void +blackhole_engine_shutdown(struct engine *engine) +{ + free(engine); +} + +static struct space * +blackhole_engine_create_space(struct engine *engine, struct space_def *def, + struct rlist *key_list) +{ + if (!rlist_empty(key_list)) { + diag_set(ClientError, ER_UNSUPPORTED, "Blackhole", "indexes"); + return NULL; + } + + struct space *space = (struct space *)calloc(1, sizeof(*space)); + if (space == NULL) { + diag_set(OutOfMemory, sizeof(*space), + "malloc", "struct space"); + return NULL; + } + + /* Allocate tuples on runtime arena, but check space format. */ + struct tuple_format *format; + format = tuple_format_new(&tuple_format_runtime->vtab, NULL, 0, 0, + def->fields, def->field_count, def->dict); + if (format == NULL) { + free(space); + return NULL; + } + format->exact_field_count = def->exact_field_count; + tuple_format_ref(format); + + if (space_create(space, engine, &blackhole_space_vtab, + def, key_list, format) != 0) { + tuple_format_unref(format); + free(space); + return NULL; + } + return space; +} + +static const struct engine_vtab blackhole_engine_vtab = { + /* .shutdown = */ blackhole_engine_shutdown, + /* .create_space = */ blackhole_engine_create_space, + /* .join = */ generic_engine_join, + /* .begin = */ generic_engine_begin, + /* .begin_statement = */ generic_engine_begin_statement, + /* .prepare = */ generic_engine_prepare, + /* .commit = */ generic_engine_commit, + /* .rollback_statement = */ generic_engine_rollback_statement, + /* .rollback = */ generic_engine_rollback, + /* .bootstrap = */ generic_engine_bootstrap, + /* .begin_initial_recovery = */ generic_engine_begin_initial_recovery, + /* .begin_final_recovery = */ generic_engine_begin_final_recovery, + /* .end_recovery = */ generic_engine_end_recovery, + /* .begin_checkpoint = */ generic_engine_begin_checkpoint, + /* .wait_checkpoint = */ generic_engine_wait_checkpoint, + /* .commit_checkpoint = */ generic_engine_commit_checkpoint, + /* .abort_checkpoint = */ generic_engine_abort_checkpoint, + /* .collect_garbage = */ generic_engine_collect_garbage, + /* .backup = */ generic_engine_backup, + /* .memory_stat = */ generic_engine_memory_stat, + /* .reset_stat = */ generic_engine_reset_stat, + /* .check_space_def = */ generic_engine_check_space_def, +}; + +struct engine * +blackhole_engine_new(void) +{ + struct engine *engine = calloc(1, sizeof(*engine)); + if (engine == NULL) { + diag_set(OutOfMemory, sizeof(*engine), + "malloc", "struct engine"); + return NULL; + } + + engine->vtab = &blackhole_engine_vtab; + engine->name = "blackhole"; + return engine; +} diff --git a/src/box/blackhole.h b/src/box/blackhole.h new file mode 100644 index 0000000000000000000000000000000000000000..5a78610d978c7b8181293a9e6babd140b7296849 --- /dev/null +++ b/src/box/blackhole.h @@ -0,0 +1,58 @@ +#ifndef TARANTOOL_BOX_BLACKHOLE_H_INCLUDED +#define TARANTOOL_BOX_BLACKHOLE_H_INCLUDED +/* + * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stddef.h> + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +struct engine * +blackhole_engine_new(void); + +#if defined(__cplusplus) +} /* extern "C" */ + +#include "diag.h" + +static inline struct engine * +blackhole_engine_new_xc(void) +{ + struct engine *engine = blackhole_engine_new(); + if (engine == NULL) + diag_raise(); + return engine; +} + +#endif /* defined(__plusplus) */ + +#endif /* TARANTOOL_BOX_BLACKHOLE_H_INCLUDED */ diff --git a/src/box/box.cc b/src/box/box.cc index abb55e9667d41aae159c6c525570b5ba464d6bb7..ecc448919517c82faff2b5167728e19b7412cd94 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -51,6 +51,7 @@ #include "engine.h" #include "memtx_engine.h" #include "sysview.h" +#include "blackhole.h" #include "vinyl.h" #include "space.h" #include "index.h" @@ -1635,6 +1636,9 @@ engine_init() struct sysview_engine *sysview = sysview_engine_new_xc(); engine_register((struct engine *)sysview); + struct engine *blackhole = blackhole_engine_new_xc(); + engine_register(blackhole); + struct vinyl_engine *vinyl; vinyl = vinyl_engine_new_xc(cfg_gets("vinyl_dir"), cfg_geti64("vinyl_memory"), diff --git a/test/box/blackhole.result b/test/box/blackhole.result new file mode 100644 index 0000000000000000000000000000000000000000..a985fcd7236cc21c320f11d0ae0fd4c9262adbcd --- /dev/null +++ b/test/box/blackhole.result @@ -0,0 +1,177 @@ +test_run = require('test_run').new() +--- +... +s = box.schema.space.create('test', {engine = 'blackhole'}) +--- +... +-- Blackhole doesn't support indexes. +s:create_index('pk') +--- +- error: Blackhole does not support indexes +... +-- Blackhole does support space format. +s:format{{'key', 'unsigned'}, {'value', 'string'}} +--- +... +s:format() +--- +- [{'name': 'key', 'type': 'unsigned'}, {'name': 'value', 'type': 'string'}] +... +t = s:insert{1, 'a'} -- ok +--- +... +t, t.key, t.value +--- +- [1, 'a'] +- 1 +- a +... +s:insert{1, 2, 3} -- error +--- +- error: 'Tuple field 2 type does not match one required by operation: expected string' +... +s:replace{'a', 'b', 'c'} -- error +--- +- error: 'Tuple field 1 type does not match one required by operation: expected unsigned' +... +s:format{} +--- +... +s:insert{1, 2, 3} -- ok +--- +- [1, 2, 3] +... +s:replace{'a', 'b', 'c'} -- ok +--- +- ['a', 'b', 'c'] +... +-- Blackhole doesn't support delete/update/upsert operations. +box.internal.delete(s.id, 0, {}) +--- +- error: Blackhole does not support delete() +... +box.internal.update(s.id, 0, {}, {}) +--- +- error: Blackhole does not support update() +... +box.internal.upsert(s.id, {}, {}) +--- +- error: Blackhole does not support upsert() +... +-- Blackhole supports on_replace and before_replace triggers. +s_old = nil +--- +... +s_new = nil +--- +... +f1 = s:on_replace(function(old, new) s_old = old s_new = new end) +--- +... +s:replace{1, 2, 3} +--- +- [1, 2, 3] +... +s_old, s_new +--- +- null +- [1, 2, 3] +... +f2 = s:before_replace(function(old, new) return box.tuple.new{4, 5, 6} end) +--- +... +s:replace{1, 2, 3} +--- +- [4, 5, 6] +... +s_old, s_new +--- +- null +- [4, 5, 6] +... +s:on_replace(nil, f1) +--- +... +s:before_replace(nil, f2) +--- +... +-- Test recovery. +test_run:cmd('restart server default') +s = box.space.test +--- +... +-- Test snapshot. +box.snapshot() +--- +- ok +... +-- Operations done on a blackhole space are written to the WAL +-- and therefore get replicated. Check it with the aid of an +-- on_replace trigger. +box.schema.user.grant('guest', 'replication') +--- +... +test_run:cmd("create server replica with rpl_master=default, script='replication/replica.lua'") +--- +- true +... +test_run:cmd("start server replica") +--- +- true +... +test_run:cmd("switch replica") +--- +- true +... +t = {} +--- +... +_ = box.space.test:on_replace(function(old, new) table.insert(t, new) end) +--- +... +test_run:cmd('switch default') +--- +- true +... +s = box.space.test +--- +... +for i = 1, 5 do s:replace{i} end +--- +... +vclock = test_run:get_vclock('default') +--- +... +test_run:wait_vclock('replica', vclock) +--- +... +test_run:cmd("switch replica") +--- +- true +... +t +--- +- - [1] + - [2] + - [3] + - [4] + - [5] +... +test_run:cmd('switch default') +--- +- true +... +test_run:cmd("stop server replica") +--- +- true +... +test_run:cmd("cleanup server replica") +--- +- true +... +box.schema.user.revoke('guest', 'replication') +--- +... +s:drop() +--- +... diff --git a/test/box/blackhole.test.lua b/test/box/blackhole.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..5dcf9e6181ba1d40a692fcb8d3468eb088aab057 --- /dev/null +++ b/test/box/blackhole.test.lua @@ -0,0 +1,64 @@ +test_run = require('test_run').new() + +s = box.schema.space.create('test', {engine = 'blackhole'}) + +-- Blackhole doesn't support indexes. +s:create_index('pk') + +-- Blackhole does support space format. +s:format{{'key', 'unsigned'}, {'value', 'string'}} +s:format() +t = s:insert{1, 'a'} -- ok +t, t.key, t.value +s:insert{1, 2, 3} -- error +s:replace{'a', 'b', 'c'} -- error +s:format{} +s:insert{1, 2, 3} -- ok +s:replace{'a', 'b', 'c'} -- ok + +-- Blackhole doesn't support delete/update/upsert operations. +box.internal.delete(s.id, 0, {}) +box.internal.update(s.id, 0, {}, {}) +box.internal.upsert(s.id, {}, {}) + +-- Blackhole supports on_replace and before_replace triggers. +s_old = nil +s_new = nil +f1 = s:on_replace(function(old, new) s_old = old s_new = new end) +s:replace{1, 2, 3} +s_old, s_new +f2 = s:before_replace(function(old, new) return box.tuple.new{4, 5, 6} end) +s:replace{1, 2, 3} +s_old, s_new +s:on_replace(nil, f1) +s:before_replace(nil, f2) + +-- Test recovery. +test_run:cmd('restart server default') +s = box.space.test + +-- Test snapshot. +box.snapshot() + +-- Operations done on a blackhole space are written to the WAL +-- and therefore get replicated. Check it with the aid of an +-- on_replace trigger. +box.schema.user.grant('guest', 'replication') +test_run:cmd("create server replica with rpl_master=default, script='replication/replica.lua'") +test_run:cmd("start server replica") +test_run:cmd("switch replica") +t = {} +_ = box.space.test:on_replace(function(old, new) table.insert(t, new) end) +test_run:cmd('switch default') +s = box.space.test +for i = 1, 5 do s:replace{i} end +vclock = test_run:get_vclock('default') +test_run:wait_vclock('replica', vclock) +test_run:cmd("switch replica") +t +test_run:cmd('switch default') +test_run:cmd("stop server replica") +test_run:cmd("cleanup server replica") +box.schema.user.revoke('guest', 'replication') + +s:drop()