diff --git a/cfg/tarantool_box_cfg.c b/cfg/tarantool_box_cfg.c index 7b600d2b79cc4f5ca4d5b7a1bfb9285e8f3591c9..fd6e2d864a5a405cf491488b1367efdc387f8e3b 100644 --- a/cfg/tarantool_box_cfg.c +++ b/cfg/tarantool_box_cfg.c @@ -119,6 +119,13 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { return 0; } +void +swap_tarantool_cfg(struct tarantool_cfg *c1, struct tarantool_cfg *c2) { + struct tarantool_cfg tmpcfg = *c1; + *c1 = *c2; + *c2 = tmpcfg; +} + static int acceptDefault_name__namespace(tarantool_cfg_namespace *c) { c->enabled = -1; diff --git a/cfg/tarantool_box_cfg.h b/cfg/tarantool_box_cfg.h index 41cd800f948b512c21cad710a66946fac9c430f0..9122fb36795d0d33359ecea5f064853624f5edba 100644 --- a/cfg/tarantool_box_cfg.h +++ b/cfg/tarantool_box_cfg.h @@ -201,6 +201,8 @@ void init_tarantool_cfg(tarantool_cfg *c); int fill_default_tarantool_cfg(tarantool_cfg *c); +void swap_tarantool_cfg(struct tarantool_cfg *c1, struct tarantool_cfg *c2); + void parse_cfg_file_tarantool_cfg(tarantool_cfg *c, FILE *fh, int check_rdonly, int *n_accepted, int *n_skipped); void parse_cfg_buffer_tarantool_cfg(tarantool_cfg *c, char *buffer, int check_rdonly, int *n_accepted, int *n_skipped); diff --git a/connector/c/client.c b/connector/c/client.c index d117e3d5e3c3a915bd238961bfdd669c63dcd104..79385a4dd817b904e9c90caf67dad5bcc8d52f44 100644 --- a/connector/c/client.c +++ b/connector/c/client.c @@ -115,7 +115,7 @@ int tnt_execute_raw(struct tnt_connection *tnt, const char *message, if (send(tnt->data_port, message, len, 0) < 0) return -1; - char buf[2048]; + static char buf[2048]; if (recv(tnt->data_port, buf, 2048, 0) < 16) return -1; @@ -126,6 +126,10 @@ int tnt_execute_raw(struct tnt_connection *tnt, const char *message, /* @fixme: we may want to support big-endian some * day. */ tnt_res->errcode = * (uint32_t*) (buf+12); /* see iproto.h */ + if (tnt_res->errcode) + tnt_res->errmsg = (const char *)(buf + 16); + else + tnt_res->errmsg = ""; } return 0; } diff --git a/connector/c/client.h b/connector/c/client.h index f54cf7f77d2b30e0a84eca7abd11c1aa86d9b42a..ee124d868f4a5065f098ac64f934d92df4544e42 100644 --- a/connector/c/client.h +++ b/connector/c/client.h @@ -43,6 +43,7 @@ struct tnt_result { /** Server error or 0. */ uint32_t errcode; + const char *errmsg; }; diff --git a/mod/box/client/perl/MANIFEST b/connector/perl/MANIFEST old mode 100755 new mode 100644 similarity index 100% rename from mod/box/client/perl/MANIFEST rename to connector/perl/MANIFEST diff --git a/mod/box/client/perl/Makefile.PL b/connector/perl/Makefile.PL old mode 100755 new mode 100644 similarity index 100% rename from mod/box/client/perl/Makefile.PL rename to connector/perl/Makefile.PL diff --git a/mod/box/client/perl/lib/MR/IProto.pm b/connector/perl/lib/MR/IProto.pm similarity index 100% rename from mod/box/client/perl/lib/MR/IProto.pm rename to connector/perl/lib/MR/IProto.pm diff --git a/mod/box/client/perl/lib/MR/SilverBox.pm b/connector/perl/lib/MR/SilverBox.pm similarity index 100% rename from mod/box/client/perl/lib/MR/SilverBox.pm rename to connector/perl/lib/MR/SilverBox.pm diff --git a/mod/box/client/perl/lib/MR/Storage/Const.pm b/connector/perl/lib/MR/Storage/Const.pm similarity index 100% rename from mod/box/client/perl/lib/MR/Storage/Const.pm rename to connector/perl/lib/MR/Storage/Const.pm diff --git a/mod/box/t/TBox.pm b/connector/perl/t/TBox.pm similarity index 100% rename from mod/box/t/TBox.pm rename to connector/perl/t/TBox.pm diff --git a/mod/box/t/box.pl b/connector/perl/t/box.pl similarity index 100% rename from mod/box/t/box.pl rename to connector/perl/t/box.pl diff --git a/mod/box/t/box_tree.pl b/connector/perl/t/box_tree.pl similarity index 100% rename from mod/box/t/box_tree.pl rename to connector/perl/t/box_tree.pl diff --git a/mod/box/client/ruby/README b/connector/ruby/README similarity index 100% rename from mod/box/client/ruby/README rename to connector/ruby/README diff --git a/mod/box/client/ruby/box.rb b/connector/ruby/box.rb similarity index 100% rename from mod/box/client/ruby/box.rb rename to connector/ruby/box.rb diff --git a/mod/box/client/ruby/iproto.rb b/connector/ruby/iproto.rb similarity index 100% rename from mod/box/client/ruby/iproto.rb rename to connector/ruby/iproto.rb diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 86293ad18727b05c0fe6b9ca767bbbe8caa41171..8f7a5f95c0742187fff55123f81166a255954039 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -55,9 +55,9 @@ set (recompiled_core_sources ${CMAKE_SOURCE_DIR}/core/replicator.m ${CMAKE_SOURCE_DIR}/core/fiber.m PARENT_SCOPE) -set (common_sources tbuf.m palloc.m util.m diagnostics.m +set (common_sources tbuf.m palloc.m util.m salloc.m pickle.m coro.m stat.m log_io.m - log_io_remote.m iproto.m exceptions.m errcode.c) + log_io_remote.m iproto.m exception.m errcode.c latch.m) if (ENABLE_TRACE) set (common_sources ${common_sources} trace.m) diff --git a/core/admin.m b/core/admin.m index 73fb1654141c479ed998b3001e82aa3092658865..6ffc97035e7caef54fd337bb2d44e6639f721751 100644 --- a/core/admin.m +++ b/core/admin.m @@ -61,7 +61,7 @@ static const char *help = static const char *unknown_command = "unknown command. try typing help." CRLF; -#line 65 "core/admin.c" +#line 65 "core/admin.m" static const int admin_start = 1; static const int admin_first_final = 108; static const int admin_error = 0; @@ -119,12 +119,12 @@ admin_dispatch(void) p = fiber->rbuf->data; -#line 123 "core/admin.c" +#line 123 "core/admin.m" { cs = admin_start; } -#line 128 "core/admin.c" +#line 128 "core/admin.m" { if ( p == pe ) goto _test_eof; @@ -283,7 +283,7 @@ st108: if ( ++p == pe ) goto _test_eof108; case 108: -#line 287 "core/admin.c" +#line 287 "core/admin.m" goto st0; tr13: #line 193 "core/admin.rl" @@ -383,7 +383,7 @@ st7: if ( ++p == pe ) goto _test_eof7; case 7: -#line 387 "core/admin.c" +#line 387 "core/admin.m" if ( (*p) == 10 ) goto st108; goto st0; @@ -471,7 +471,7 @@ st16: if ( ++p == pe ) goto _test_eof16; case 16: -#line 475 "core/admin.c" +#line 475 "core/admin.m" switch( (*p) ) { case 10: goto tr28; case 13: goto tr29; @@ -485,7 +485,7 @@ st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 489 "core/admin.c" +#line 489 "core/admin.m" switch( (*p) ) { case 10: goto tr28; case 13: goto tr29; diff --git a/core/errcode.c b/core/errcode.c index c7bed55d1f597e3833592122ac94df3ab0311215..3d4d534ffef30a69d5bc1305a19cfa7cb1f16795 100644 --- a/core/errcode.c +++ b/core/errcode.c @@ -3,7 +3,7 @@ #define ERRCODE_RECORD_MEMBER(s, f, d) { \ .errstr = #s, \ .errflags = f, \ - .errdesc = #d \ + .errdesc = d \ }, struct errcode_record tnt_error_codes[tnt_error_codes_enum_MAX] = { diff --git a/core/exception.m b/core/exception.m new file mode 100644 index 0000000000000000000000000000000000000000..955c91c86aec3c6629b760d4382f7bf2f6190286 --- /dev/null +++ b/core/exception.m @@ -0,0 +1,92 @@ +/* + * 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 "exception.h" +#include "say.h" + + +@implementation tnt_Exception ++ (id) alloc +{ + static __thread tnt_Exception *e = nil; + + if ([e isKindOf:self]) { + *(Class *) e = self; + } else { + [e free]; + e = [super alloc]; + } + return e; +} +@end + + +@implementation ClientError +- (id) init: (uint32_t)errcode_, ... +{ + va_list ap; + va_start(ap, errcode_); + [self init: errcode_ args: ap]; + va_end(ap); + + return self; +} + + +- (id) init: (uint32_t)errcode_ args: (va_list)ap +{ + [super init]; + errcode = errcode_; + vsnprintf(errmsg, sizeof(errmsg), tnt_errcode_desc(errcode), ap); + return self; +} +@end + + +@implementation LoggedError +- (id) init: (uint32_t) errcode_, ... +{ + va_list ap; + va_start(ap, errcode_); + [super init: errcode_ args: ap]; + + say_error("%s at %s:%d, %s", [self name], file, line, errmsg); + + return self; +} +@end + + +@implementation IllegalParams +- (id) init: (const char*) msg +{ + printf("IllegalParams init\n"); + return [super init: ER_ILLEGAL_PARAMS, msg]; +} +@end + diff --git a/core/exceptions.m b/core/exceptions.m deleted file mode 100644 index 2db8c416a0f0e106823526a8d78b105ca8d25c8b..0000000000000000000000000000000000000000 --- a/core/exceptions.m +++ /dev/null @@ -1,35 +0,0 @@ -#include <exceptions.h> -#include <say.h> - -@implementation tnt_Exception -+ alloc -{ - static __thread tnt_Exception *e = nil; - - if (![e isKindOf:self]) { - [e free]; - e = [super alloc]; - } - - return e; -} - -- init:(const char *)p_file:(unsigned)p_line reason:(const char *)p_reason -{ - [super init]; - - file = p_file; - line = p_line; - - reason = p_reason; - - return self; -} - -- init:(const char *)p_file:(unsigned)p_line -{ - return [self init:p_file:p_line reason:"unknown"]; -} - -@end - diff --git a/core/fiber.m b/core/fiber.m index c0503b138ec7b4377192b248b2de9c71368177b1..02de9b510ced918c2e3cbfc56d11973a4864c6a0 100644 --- a/core/fiber.m +++ b/core/fiber.m @@ -54,9 +54,8 @@ #include <util.h> #include <stat.h> #include <pickle.h> -#include "diagnostics.h" -@implementation tnt_FiberException +@implementation FiberCancelException @end static struct fiber sched; @@ -121,14 +120,94 @@ fiber_call(struct fiber *callee) coro_transfer(&caller->coro.ctx, &callee->coro.ctx); } + +/** Interrupt a synchronous wait of a fiber inside the event loop. + * We do so by keeping an "async" event in every fiber, solely + * for this purpose, and raising this event here. + */ + +void +fiber_wakeup(struct fiber *f) +{ + ev_async_start(&f->async); + ev_async_send(&f->async); +} + +/** Cancel the subject fiber. + * + * Note: this is not guaranteed to succeed, and requires a level + * of cooperation on behalf of the fiber. A fiber may opt to set + * FIBER_CANCELLABLE to false, and never test that it was + * cancelled. Such fiber we won't be ever to cancel, ever, and + * for such fiber this call will lead to an infinite wait. + * However, fiber_testcancel() is embedded to the rest of fiber_* + * API (@sa yield()), which makes most of the fibers that opt in, + * cancellable. + * + * Currently cancellation can only be synchronous: this call + * returns only when the subject fiber has terminated. + * + * The fiber which is cancelled, has tnt_FiberCancelException + * raised in it. For cancellation to work, this exception type + * should be re-raised whenever (if) it is caught. + */ + +void +fiber_cancel(struct fiber *f) +{ + assert(fiber->fid != 0); + assert(!(f->flags & FIBER_CANCEL)); + + f->flags |= FIBER_CANCEL; + + if (f->flags & FIBER_CANCELLABLE) + fiber_wakeup(f); + + assert(f->waiter == NULL); + f->waiter = fiber; + + @try { + yield(); + } + @finally { + f->waiter = NULL; + } +} + + +/** Test if this fiber is in a cancellable state and was indeed + * cancelled, and raise an exception (tnt_FiberCancelException) if + * that's the case. + */ + void -fiber_raise(struct fiber *callee) +fiber_testcancel(void) { - callee->flags |= FIBER_RAISE; + if (!(fiber->flags & FIBER_CANCELLABLE)) + return; - fiber_call(callee); + if (!(fiber->flags & FIBER_CANCEL)) + return; + + tnt_raise(FiberCancelException); } +/** Change the current cancellation state of a fiber. This is not + * a cancellation point. + */ + +void fiber_setcancelstate(bool enable) +{ + if (enable == true) + fiber->flags |= FIBER_CANCELLABLE; + else + fiber->flags &= ~FIBER_CANCELLABLE; +} + +/** + * @note: this is a cancellation point (@sa fiber_testcancel()) + */ + void yield(void) { @@ -141,61 +220,79 @@ yield(void) callee->csw++; coro_transfer(&caller->coro.ctx, &callee->coro.ctx); - if (fiber->flags & FIBER_RAISE) { - fiber->flags &= ~FIBER_RAISE; - - tnt_raise(tnt_FiberException, reason:"fiber_raise"); - } + fiber_testcancel(); } +/** + * @note: this is a cancellation point (@sa fiber_testcancel()) + */ + void fiber_sleep(ev_tstamp delay) { ev_timer_set(&fiber->timer, delay, 0.); ev_timer_start(&fiber->timer); - yield(); + @try { + yield(); + } + @finally { + ev_timer_stop(&fiber->timer); + } } - -/** Wait for a forked child to complete. */ +/** Wait for a forked child to complete. + * @note: this is a cancellation point (@sa fiber_testcancel()). +*/ void wait_for_child(pid_t pid) { ev_child_set(&fiber->cw, pid, 0); ev_child_start(&fiber->cw); - yield(); - ev_child_stop(&fiber->cw); + @try { + yield(); + } + @finally { + ev_child_stop(&fiber->cw); + } } -void -wait_for(int events) + +static void +fiber_io_start(int events) { ev_io *io = &fiber->io; - if (io->fd != fiber->fd || io->events != events) { /* events are not monitored */ - if (ev_is_active(io)) - ev_io_stop(io); - ev_io_set(io, fiber->fd, events); - } + assert (!ev_is_active(io)); - if (!ev_is_active(io)) - ev_io_start(io); + ev_io_set(io, fiber->fd, events); + ev_io_start(io); +} - yield(); +/** @note: this is a cancellation point. + */ + +static void +fiber_io_yield() +{ + assert(ev_is_active(&fiber->io)); + + @try { + yield(); + } + @catch (id o) + { + ev_io_stop(&fiber->io); + @throw; + } } -void -unwait(int events) +static void +fiber_io_stop(int events) { ev_io *io = &fiber->io; - assert(io->fd == fiber->fd); - if (!ev_is_active(io)) - return; - - if ((io->events & events) == 0) - return; + assert(ev_is_active(io) && io->fd == fiber->fd && (io->events & events)); ev_io_stop(io); } @@ -324,7 +421,6 @@ fiber_gc(void) static void fiber_zombificate() { - diag_clear(); fiber_set_name(fiber, "zombie"); fiber->f = NULL; fiber->data = NULL; @@ -338,21 +434,19 @@ fiber_zombificate() static void fiber_loop(void *data __attribute__((unused))) { - while (42) { + for (;;) { assert(fiber != NULL && fiber->f != NULL && fiber->fid != 0); @try { fiber->f(fiber->f_data); } - @catch (tnt_FiberException *e) { - say_info("fiber `%s': exception `tnt_FiberException': `%s'", - fiber->name, e->reason); + @catch (FiberCancelException *e) { + say_info("fiber `%s' has been cancelled", fiber->name); + + if (fiber->waiter != NULL) + fiber_call(fiber->waiter); + say_info("fiber `%s': exiting", fiber->name); } - @catch (tnt_Exception *e) { - say_error("fiber `%s': exception `%s': `%s'", - fiber->name, [e name], e->reason); - panic("fiber `%s': exiting", fiber->name); - } @catch (id e) { say_error("fiber `%s': exception `%s'", fiber->name, [e name]); panic("fiber `%s': exiting", fiber->name); @@ -364,9 +458,10 @@ fiber_loop(void *data __attribute__((unused))) } } - -/** Set fiber name. -* @Param[in] name the new name of the fiber. Truncated to FIBER_NAME_MAXLEN. +/** Set fiber name. + * + * @param[in] name the new name of the fiber. Truncated to + * FIBER_NAME_MAXLEN. */ void @@ -403,9 +498,10 @@ fiber_create(const char *name, int fd, int inbox_size, void (*f) (void *), void fiber_alloc(fiber); ev_init(&fiber->io, (void *)ev_schedule); + ev_async_init(&fiber->async, (void *)ev_schedule); ev_init(&fiber->timer, (void *)ev_schedule); ev_init(&fiber->cw, (void *)ev_schedule); - fiber->io.data = fiber->timer.data = fiber->cw.data = fiber; + fiber->io.data = fiber->async.data = fiber->timer.data = fiber->cw.data = fiber; SLIST_INSERT_HEAD(&fibers, fiber, link); } @@ -416,6 +512,7 @@ fiber_create(const char *name, int fd, int inbox_size, void (*f) (void *), void while (++last_used_fid <= 100) ; /* fids from 0 to 100 are reserved */ fiber->fid = last_used_fid; fiber->flags = 0; + fiber->waiter = NULL; fiber_set_name(fiber, name); palloc_set_name(fiber->pool, fiber->name); register_fid(fiber); @@ -483,13 +580,16 @@ fiber_close(void) if (fiber->fd < 0) return 0; - unwait(-1); + /* We don't know if IO is active if there was an error. */ + if (ev_is_active(&fiber->io)) + fiber_io_stop(-1); + int r = close(fiber->fd); - if (r != -1) { - fiber->io.fd = fiber->fd = -1; - fiber->has_peer = false; - fiber->peer_name[0] = 0; - } + + fiber->fd = -1; + fiber->has_peer = false; + fiber->peer_name[0] = 0; + return r; } @@ -505,13 +605,21 @@ inbox_size(struct fiber *recipient) return ring_size(recipient->inbox); } +/** + * @note: this is a cancellation point (@sa fiber_testcancel()) + */ + void wait_inbox(struct fiber *recipient) { while (ring_size(recipient->inbox) == 0) { recipient->flags |= FIBER_READING_INBOX; - yield(); - recipient->flags &= ~FIBER_READING_INBOX; + @try { + yield(); + } + @finally { + recipient->flags &= ~FIBER_READING_INBOX; + } } } @@ -532,14 +640,23 @@ write_inbox(struct fiber *recipient, struct tbuf *msg) return true; } + +/** + * @note: this is a cancellation point (@sa fiber_testcancel()) + */ + struct msg * read_inbox(void) { struct ring *restrict inbox = fiber->inbox; while (ring_size(inbox) == 0) { fiber->flags |= FIBER_READING_INBOX; - yield(); - fiber->flags &= ~FIBER_READING_INBOX; + @try { + yield(); + } + @finally { + fiber->flags &= ~FIBER_READING_INBOX; + } } struct msg *msg = inbox->ring[inbox->tail]; @@ -549,14 +666,19 @@ read_inbox(void) return msg; } +/** + * @note: this is a cancellation point. + */ + int fiber_bread(struct tbuf *buf, size_t at_least) { ssize_t r; tbuf_ensure(buf, MAX(cfg.readahead, at_least)); + fiber_io_start(EV_READ); for (;;) { - wait_for(EV_READ); + fiber_io_yield(); r = read(fiber->fd, buf->data + buf->len, buf->size - buf->len); if (r > 0) { buf->len += r; @@ -568,19 +690,23 @@ fiber_bread(struct tbuf *buf, size_t at_least) break; } } - unwait(EV_READ); + fiber_io_stop(EV_READ); return r; } void -add_iov_dup(void *buf, size_t len) +add_iov_dup(const void *buf, size_t len) { void *copy = palloc(fiber->pool, len); memcpy(copy, buf, len); add_iov(copy, len); } +/** + * @note: this is a cancellation point. + */ + ssize_t fiber_flush_output(void) { @@ -588,8 +714,9 @@ fiber_flush_output(void) struct iovec *iov = iovec(fiber->iov); size_t iov_cnt = fiber->iov_cnt; + fiber_io_start(EV_WRITE); while (iov_cnt > 0) { - wait_for(EV_WRITE); + fiber_io_yield(); bytes += r = writev(fiber->fd, iov, MIN(iov_cnt, IOV_MAX)); if (r <= 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) @@ -610,7 +737,7 @@ fiber_flush_output(void) } } } - unwait(EV_WRITE); + fiber_io_stop(EV_WRITE); if (r < 0) { size_t rem = 0; @@ -627,16 +754,19 @@ fiber_flush_output(void) return result; } +/** + * @note: this is a cancellation point. + */ + ssize_t fiber_read(void *buf, size_t count) { ssize_t r, done = 0; - if (count == 0) - return 0; - + fiber_io_start(EV_READ); while (count != done) { - wait_for(EV_READ); + + fiber_io_yield(); if ((r = read(fiber->fd, buf + done, count - done)) <= 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) @@ -646,22 +776,25 @@ fiber_read(void *buf, size_t count) } done += r; } + fiber_io_stop(EV_READ); - unwait(EV_READ); return done; } +/** + * @note: this is a cancellation point. + */ + ssize_t fiber_write(const void *buf, size_t count) { int r; unsigned int done = 0; - if (count == 0) - return 0; + fiber_io_start(EV_WRITE); while (count != done) { - wait_for(EV_WRITE); + fiber_io_yield(); if ((r = write(fiber->fd, buf + done, count - done)) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; @@ -670,17 +803,18 @@ fiber_write(const void *buf, size_t count) } done += r; } + fiber_io_stop(EV_WRITE); - unwait(EV_WRITE); return done; } +/** + * @note: this is a cancellation point. + */ + int fiber_connect(struct sockaddr_in *addr) { - int error; - socklen_t error_size = sizeof(error); - fiber->fd = socket(AF_INET, SOCK_STREAM, 0); if (fiber->fd < 0) goto error; @@ -689,25 +823,32 @@ fiber_connect(struct sockaddr_in *addr) goto error; if (connect(fiber->fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { + if (errno != EINPROGRESS) goto error; - } - wait_for(EV_WRITE); - if (getsockopt(fiber->fd, SOL_SOCKET, SO_ERROR, &error, &error_size) < 0) - goto error; + fiber_io_start(EV_WRITE); + fiber_io_yield(); + fiber_io_stop(EV_WRITE); - assert(error_size == sizeof(error)); + int error; + socklen_t error_size = sizeof(error); - if (error != 0) { - errno = error; - goto error; + if (getsockopt(fiber->fd, SOL_SOCKET, SO_ERROR, + &error, &error_size) < 0) + goto error; + + assert(error_size == sizeof(error)); + + if (error != 0) { + errno = error; + goto error; + } } - unwait(EV_WRITE); return fiber->fd; + error: - unwait(EV_WRITE); fiber_close(); return fiber->fd; } @@ -847,7 +988,6 @@ inbox2sock(void *_data __attribute__((unused))) if (fiber_write(out->data, out->len) != out->len) panic("child is dead"); fiber_gc(); - unwait(-1); } } @@ -923,7 +1063,11 @@ spawn_child(const char *name, int inbox_size, struct tbuf *(*handler) (void *, s return c; } else { char child_name[sizeof(fiber->name)]; - + /* + * Move to an own process group, to not receive + * signals from the controlling tty. + */ + setpgid(0, 0); salloc_destroy(); close_all_xcpt(2, socks[0], sayfd); snprintf(child_name, sizeof(child_name), "%s/child", name); @@ -951,8 +1095,9 @@ tcp_server_handler(void *data) if (server->on_bind != NULL) server->on_bind(server->data); - while (1) { - wait_for(EV_READ); + fiber_io_start(EV_READ); + for (;;) { + fiber_io_yield(); while ((fd = accept(fiber->fd, NULL, NULL)) > 0) { if (set_nonblock(fd) == -1) { @@ -981,31 +1126,49 @@ tcp_server_handler(void *data) say_syserror("accept"); continue; } - + exit(EX_OSERR); } -} - -static void -udp_server_handler(void *data) -{ - struct fiber_server *server = fiber->data; - if (fiber_serv_socket(fiber, udp_server, server->port, true, 0.1) != 0) { - say_error("bind port %i fail", server->port); + if (set_nonblock(fiber->fd) == -1) exit(EX_OSERR); + + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(server->port); + sin.sin_addr.s_addr = INADDR_ANY; + + for (;;) { + if (bind(fiber->fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + if (errno == EADDRINUSE) + goto sleep_and_retry; + say_syserror("bind"); + exit(EX_OSERR); + } + + say_info("bound to UDP port %i", server->port); + break; + + sleep_and_retry: + if (!warning_said) { + say_warn("port %i is already in use, " + "will retry binding after 0.1 seconds.", server->port); + warning_said = true; + } + fiber_sleep(0.1); } if (server->on_bind != NULL) server->on_bind(server->data); - while (1) { + fiber_io_start(EV_READ); + for (;;) { #define MAXUDPPACKETLEN 128 char buf[MAXUDPPACKETLEN]; struct sockaddr_in addr; socklen_t addrlen; ssize_t sz; - wait_for(EV_READ); + fiber_io_yield(); for (;;) { addrlen = sizeof(addr); @@ -1026,6 +1189,7 @@ udp_server_handler(void *data) } } } + fiber_io_stop(EV_READ); } struct fiber * @@ -1036,10 +1200,11 @@ fiber_server(fiber_server_type type, int port, void (*handler) (void *data), voi struct fiber_server *server; struct fiber *s; + assert(type == tcp_server); + server_name = palloc(eter_pool, 64); snprintf(server_name, 64, "%i/acceptor", port); - s = fiber_create(server_name, -1, -1, - (type == tcp_server) ? tcp_server_handler : udp_server_handler, data); + s = fiber_create(server_name, -1, -1, tcp_server_handler, data); s->data = server = palloc(eter_pool, sizeof(struct fiber_server)); assert(server != NULL); server->port = port; diff --git a/core/iproto.m b/core/iproto.m index 967c793391c10071dd9f562d1c789e2a682a52c6..3e7a5a9e6304cf8feaa861e5b98807b11e3ac137 100644 --- a/core/iproto.m +++ b/core/iproto.m @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov + * Copyright (C) 2010, 2011 Mail.RU + * Copyright (C) 2010, 2011 Yuriy Vostrikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,6 +24,7 @@ * SUCH DAMAGE. */ #include "iproto.h" +#include "exception.h" #include <stdio.h> #include <string.h> @@ -36,64 +37,78 @@ const uint32_t msg_ping = 0xff00; -static struct tbuf * -iproto_parse(struct tbuf *in) -{ - if (in->len < sizeof(struct iproto_header)) - return NULL; - if (in->len < sizeof(struct iproto_header) + iproto(in)->len) - return NULL; - - return tbuf_split(in, sizeof(struct iproto_header) + iproto(in)->len); -} +static void iproto_reply(iproto_callback callback, struct tbuf *request); void -iproto_interact(void *data) +iproto_interact(iproto_callback *callback) { - uint32_t (*callback) (uint32_t msg, struct tbuf *requst_data) = data; - struct tbuf *request; - struct iproto_header_retcode *reply; - ssize_t r; + struct tbuf *in = fiber->rbuf; + ssize_t to_read = sizeof(struct iproto_header); for (;;) { - if (fiber_bread(fiber->rbuf, sizeof(struct iproto_header)) <= 0) + if (to_read > 0 && fiber_bread(in, to_read) <= 0) break; - while ((request = iproto_parse(fiber->rbuf)) != NULL) { - reply = palloc(fiber->pool, sizeof(*reply)); - reply->msg_code = iproto(request)->msg_code; - reply->sync = iproto(request)->sync; - - if (unlikely(reply->msg_code == msg_ping)) { - reply->len = 0; - add_iov(reply, sizeof(struct iproto_header)); - } else { - add_iov(reply, sizeof(struct iproto_header_retcode)); - /* j is last used iov in loop */ - int j = fiber->iov_cnt; - - /* make requst point to iproto data */ - u32 msg_code = iproto(request)->msg_code; - request->len = iproto(request)->len; - request->data = iproto(request)->data; - u32 err = callback(msg_code, request); - reply->ret_code = tnt_errcode_val(err); - - /* - * retcode is uint32_t and included int struct iproto_header_retcode - * but we has to count it anyway - */ - reply->len = sizeof(uint32_t); - - for (; j < fiber->iov_cnt; j++) - reply->len += iovec(fiber->iov)[j].iov_len; - } - } - r = fiber_flush_output(); - fiber_gc(); - if (r < 0) { - say_warn("io_error: %s", strerror(errno)); + ssize_t request_len = sizeof(struct iproto_header) + iproto(in)->len; + to_read = request_len - in->len; + + if (to_read > 0 && fiber_bread(in, to_read) <= 0) break; + + struct tbuf *request = tbuf_split(in, request_len); + iproto_reply(*callback, request); + + to_read = sizeof(struct iproto_header) - in->len; + + /* + * Flush output and garbage collect before reading + * next header. + */ + if (to_read > 0) { + if (fiber_flush_output() < 0) { + say_warn("io_error: %s", strerror(errno)); + break; + } + fiber_gc(); + /* Must be reset after fiber_gc() */ + in = fiber->rbuf; } } } + +/** Stack a reply to a single request to the fiber's io vector. */ + +static void iproto_reply(iproto_callback callback, struct tbuf *request) +{ + struct iproto_header_retcode *reply; + + reply = palloc(fiber->pool, sizeof(*reply)); + reply->msg_code = iproto(request)->msg_code; + reply->sync = iproto(request)->sync; + + if (unlikely(reply->msg_code == msg_ping)) { + reply->len = 0; + add_iov(reply, sizeof(struct iproto_header)); + return; + } + + reply->len = sizeof(uint32_t); /* ret_code */ + add_iov(reply, sizeof(struct iproto_header_retcode)); + size_t saved_iov_cnt = fiber->iov_cnt; + /* make request point to iproto data */ + request->len = iproto(request)->len; + request->data = iproto(request)->data; + + @try { + callback(reply->msg_code, request); + reply->ret_code = 0; + } + @catch (ClientError *e) { + fiber->iov->len -= (fiber->iov_cnt - saved_iov_cnt) * sizeof(struct iovec); + fiber->iov_cnt = saved_iov_cnt; + reply->ret_code = tnt_errcode_val(e->errcode); + add_iov_dup(e->errmsg, strlen(e->errmsg)+1); + } + for (; saved_iov_cnt < fiber->iov_cnt; saved_iov_cnt++) + reply->len += iovec(fiber->iov)[saved_iov_cnt].iov_len; +} diff --git a/core/diagnostics.h b/core/latch.m similarity index 57% rename from core/diagnostics.h rename to core/latch.m index c136c8ecdfb06f4b75fe7d86d055c53078ef6088..a9b9c17eb0b633f146a5991eb73b5138257eb817 100644 --- a/core/diagnostics.h +++ b/core/latch.m @@ -1,5 +1,3 @@ -#ifndef TARANTOOL_CORE_DIAGNOSTICS_H_INCLUDED -#define TARANTOOL_CORE_DIAGNOSTICS_H_INCLUDED /* * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following @@ -23,36 +21,46 @@ * SUCH DAMAGE. */ -/* - * This is used globally in the program to pass around information - * about execution errors. Each fiber has its own error context, - * setting an error in one doesn't affect another. - */ +#include "latch.h" +#include "fiber.h" -struct Error +void +tnt_latch_init(struct tnt_latch *latch) { - /** Most often contains system errno. */ - int code; - /** Text description of the error. Can be NULL. */ - const char *msg; -}; - -/** - * Set the last error in the current execution context (fiber). - * If another error was already set, it's overwritten. - * - * @param code Error code. - * @todo: think how to distinguish errno and tarantool codes here. - * @param message Optional text message. Can be NULL. - */ -void diag_set_error(int code, const char *msg); + latch->locked = false; + latch->owner = NULL; +} -/** Return the last error. Return NULL if no error. - */ -struct Error *diag_get_last_error(); +void +tnt_latch_destroy(struct tnt_latch *latch) +{ + assert(latch->locked == false); -/** Clear the last error, if any. - */ -void diag_clear(); + latch->owner = NULL; +} + +int +tnt_latch_trylock(struct tnt_latch *latch) +{ + if (latch->locked) { + assert(latch->owner != fiber); + + return -1; + } + + assert(latch->owner == NULL); + + latch->locked = true; + latch->owner = fiber; + + return 0; +} + +void +tnt_latch_unlock(struct tnt_latch *latch) +{ + assert(latch->owner == fiber); -#endif /* TARANTOOL_CORE_DIAGNOSTICS_H_INCLUDED */ + latch->locked = false; + latch->owner = NULL; +} diff --git a/core/log_io.m b/core/log_io.m index 45dbe023d8d2ad12934c7f88d9f68a62c531e8f7..ba48b6847ec80929308d0192d03dee31829cb4d1 100644 --- a/core/log_io.m +++ b/core/log_io.m @@ -43,29 +43,22 @@ #include <say.h> #include <third_party/crc32.h> #include <pickle.h> -#include "diagnostics.h" const u16 snap_tag = -1; const u16 wal_tag = -2; const u64 default_cookie = 0; const u32 default_version = 11; -const u32 snap_marker_v04 = -1U; -const u64 xlog_marker_v04 = -1ULL; -const u64 xlog_eof_marker_v04 = 0; const u32 marker_v11 = 0xba0babed; const u32 eof_marker_v11 = 0x10adab1e; const char *snap_suffix = ".snap"; const char *xlog_suffix = ".xlog"; const char *inprogress_suffix = ".inprogress"; -const char *v04 = "0.04\n"; -const char *v03 = "0.03\n"; const char *v11 = "0.11\n"; const char *snap_mark = "SNAP\n"; const char *xlog_mark = "XLOG\n"; #define ROW_EOF (void *)1 -static struct tbuf *row_reader_v04(FILE *f, struct palloc_pool *pool); static struct tbuf *row_reader_v11(FILE *f, struct palloc_pool *pool); struct log_io_iter { @@ -78,19 +71,15 @@ struct log_io_iter { int io_rate_limit; }; -struct row_v04 { - i64 lsn; /* this used to be tid */ - u16 type; - u32 len; - u8 data[]; -} __packed__; -static inline struct row_v04 *row_v04(const struct tbuf *t) +void +wait_lsn_set(struct wait_lsn *wait_lsn, i64 lsn) { - return (struct row_v04 *)t->data; + assert(wait_lsn->waiter == NULL); + wait_lsn->waiter = fiber; + wait_lsn->lsn = lsn; } - int confirm_lsn(struct recovery_state *r, i64 lsn) { @@ -102,6 +91,14 @@ confirm_lsn(struct recovery_state *r, i64 lsn) " new:%" PRIi64 " diff: %" PRIi64, r->confirmed_lsn, lsn, lsn - r->confirmed_lsn); r->confirmed_lsn = lsn; + /* Alert the waiter, if any. There can be holes in + * confirmed_lsn, in case of disk write failure, + * but wal_writer never confirms LSNs out order. + */ + if (r->wait_lsn.waiter && r->confirmed_lsn >= r->wait_lsn.lsn) { + fiber_call(r->wait_lsn.waiter); + } + return 0; } else { say_warn("lsn double confirmed:%" PRIi64, r->confirmed_lsn); @@ -110,6 +107,23 @@ confirm_lsn(struct recovery_state *r, i64 lsn) return -1; } + +/** Wait until the given LSN makes its way to disk. */ + +void +recovery_wait_lsn(struct recovery_state *r, i64 lsn) +{ + while (lsn < r->confirmed_lsn) { + wait_lsn_set(&r->wait_lsn, lsn); + @try { + yield(); + } @finally { + wait_lsn_clear(&r->wait_lsn); + } + } +} + + i64 next_lsn(struct recovery_state *r, i64 new_lsn) { @@ -122,21 +136,6 @@ next_lsn(struct recovery_state *r, i64 new_lsn) return r->lsn; } -static void -xlog04_class(struct log_io_class *c) -{ - c->suffix = xlog_suffix; - c->filetype = xlog_mark; - c->version = v04; - c->reader = row_reader_v04; - c->marker = xlog_marker_v04; - c->marker_size = sizeof(xlog_marker_v04); - c->eof_marker = xlog_eof_marker_v04; - c->eof_marker_size = sizeof(xlog_eof_marker_v04); - - c->rows_per_file = 50000; /* sane defaults */ - c->fsync_delay = 0; -} static void v11_class(struct log_io_class *c) @@ -153,51 +152,31 @@ v11_class(struct log_io_class *c) c->fsync_delay = 0; } -static struct log_io_class ** -snap_classes(row_reader snap_row_reader, const char *dirname) +static struct log_io_class * +snapshot_class_create(const char *dirname) { - struct log_io_class **c = calloc(3, sizeof(*c)); + struct log_io_class *c = calloc(1, sizeof(*c)); if (c == NULL) panic("calloc"); - c[0] = calloc(1, sizeof(**c)); - c[1] = calloc(1, sizeof(**c)); - if (c[0] == NULL || c[1] == NULL) - panic("calloc"); - - c[0]->suffix = snap_suffix; - c[0]->filetype = snap_mark; - c[0]->version = v03; - c[0]->eof_marker_size = 0; /* no end marker */ - c[0]->marker = snap_marker_v04; - c[0]->marker_size = sizeof(snap_marker_v04); - c[0]->rows_per_file = 0; - c[0]->reader = snap_row_reader; - - v11_class(c[1]); - c[1]->filetype = c[0]->filetype; - c[1]->suffix = c[0]->suffix; + v11_class(c); + c->filetype = snap_mark; + c->suffix = snap_suffix; - c[0]->dirname = c[1]->dirname = dirname ? strdup(dirname) : NULL; + c->dirname = dirname ? strdup(dirname) : NULL; return c; } -static struct log_io_class ** -xlog_classes(const char *dirname) +static struct log_io_class * +xlog_class_create(const char *dirname) { - struct log_io_class **c = calloc(3, sizeof(*c)); + struct log_io_class *c = calloc(1, sizeof(*c)); if (c == NULL) panic("calloc"); - c[0] = calloc(1, sizeof(**c)); - c[1] = calloc(1, sizeof(**c)); - if (c[0] == NULL || c[1] == NULL) - panic("calloc"); + v11_class(c); - xlog04_class(c[0]); - v11_class(c[1]); - - c[0]->dirname = c[1]->dirname = dirname ? strdup(dirname) : NULL; + c->dirname = dirname ? strdup(dirname) : NULL; return c; } @@ -306,10 +285,6 @@ read_rows(struct log_io_iter *i) eof = 1; goto out; } - if (l->class->eof_marker_size == 0 && ftello(l->f) == good_offset) { - eof = 1; - goto out; - } out: l->rows += row_count; @@ -461,70 +436,6 @@ find_including_file(struct log_io_class *class, i64 target_lsn) return *lsn; } -struct tbuf * -convert_to_v11(struct tbuf *orig, u16 tag, const u64 cookie, i64 lsn) -{ - struct tbuf *row = tbuf_alloc(orig->pool); - tbuf_ensure(row, sizeof(struct row_v11)); - row->len = sizeof(struct row_v11); - row_v11(row)->lsn = lsn; - row_v11(row)->tm = 0; - row_v11(row)->len = orig->len + sizeof(tag) + sizeof(cookie); - - tbuf_append(row, &tag, sizeof(tag)); - tbuf_append(row, &cookie, sizeof(cookie)); - tbuf_append(row, orig->data, orig->len); - return row; -} - -static struct tbuf * -row_reader_v04(FILE *f, struct palloc_pool *pool) -{ - const int header_size = offsetof(struct row_v04, data); - struct tbuf *m = tbuf_alloc(pool); - u32 crc, calculated_crc; - - /* - * it's imposible to distinguish between EOF and bad record condition here - * since bad record may have bogus length - * so if record is suspicious simply return NULL to the caller - */ - - if (fread(m->data, header_size, 1, f) != 1) - return ROW_EOF; - m->len = header_size; - - /* filter out rows with definitly wrong length */ - if (row_v04(m)->len > (1 << 20)) { - say_error("record too long(%" PRIi32 "), probably damaged", row_v04(m)->len); - return NULL; - } - - tbuf_ensure(m, header_size + row_v04(m)->len); - if (fread(row_v04(m)->data, row_v04(m)->len, 1, f) != 1) - return ROW_EOF; - - m->len += row_v04(m)->len; - - if (fread(&crc, sizeof(crc), 1, f) != 1) - return ROW_EOF; - - calculated_crc = crc32(m->data, m->len); - if (crc != calculated_crc) { - say_error("crc32 mismatch"); - return NULL; - } - - say_debug("read row v04 success lsn:%" PRIi64, row_v04(m)->lsn); - - /* we're copying row data twice here, it's ok since this is legacy function */ - struct tbuf *data = tbuf_alloc(pool); - tbuf_append(data, &row_v04(m)->type, sizeof(row_v04(m)->type)); - tbuf_append(data, row_v04(m)->data, row_v04(m)->len); - - return convert_to_v11(data, wal_tag, default_cookie, row_v04(m)->lsn); -} - static struct tbuf * row_reader_v11(FILE *f, struct palloc_pool *pool) { @@ -619,7 +530,7 @@ close_log(struct log_io **lptr) panic("can't rename 'inprogress' WAL"); } - if (l->class->eof_marker_size > 0 && l->mode == LOG_WRITE) { + if (l->mode == LOG_WRITE) { if (fwrite(&l->class->eof_marker, l->class->eof_marker_size, 1, l->f) != 1) say_error("can't write eof_marker"); } @@ -657,24 +568,14 @@ flush_log(struct log_io *l) static int write_header(struct log_io *l) { - char buf[27]; - time_t tm; - if (fwrite(l->class->filetype, strlen(l->class->filetype), 1, l->f) != 1) return -1; if (fwrite(l->class->version, strlen(l->class->version), 1, l->f) != 1) return -1; - if (strcmp(l->class->version, v11) == 0) { - if (fwrite("\n", 1, 1, l->f) != 1) - return -1; - } else { - time(&tm); - ctime_r(&tm, buf); - if (fwrite(buf, strlen(buf), 1, l->f) != 1) - return -1; - } + if (fwrite("\n", 1, 1, l->f) != 1) + return -1; return 0; } @@ -704,16 +605,17 @@ format_filename(char *filename, struct log_io_class *class, i64 lsn, int suffix) } static struct log_io * -open_for_read(struct recovery_state *recover, struct log_io_class **class, i64 lsn, int suffix, +open_for_read(struct recovery_state *recover, struct log_io_class *class, i64 lsn, int suffix, const char *filename) { char filetype[32], version[32], buf[256]; struct log_io *l = NULL; char *r; + const char *errmsg; l = calloc(1, sizeof(*l)); if (l == NULL) { - diag_set_error(errno, strerror(errno)); + errmsg = strerror(errno); goto error; } l->mode = LOG_READ; @@ -723,7 +625,7 @@ open_for_read(struct recovery_state *recover, struct log_io_class **class, i64 l /* when filename is not null it is forced open for debug reading */ if (filename == NULL) { assert(lsn != 0); - format_filename(l->filename, *class, lsn, suffix); + format_filename(l->filename, class, lsn, suffix); } else { assert(lsn == 0); strncpy(l->filename, filename, PATH_MAX); @@ -733,61 +635,47 @@ open_for_read(struct recovery_state *recover, struct log_io_class **class, i64 l l->f = fopen(l->filename, "r"); if (l->f == NULL) { - diag_set_error(errno, strerror(errno)); + errmsg = strerror(errno); goto error; } r = fgets(filetype, sizeof(filetype), l->f); if (r == NULL) { - diag_set_error(1, "header reading failed"); + errmsg = "header reading failed"; goto error; } r = fgets(version, sizeof(version), l->f); if (r == NULL) { - diag_set_error(1, "header reading failed"); + errmsg = "header reading failed"; goto error; } - if (strcmp((*class)->filetype, filetype) != 0) { - diag_set_error(1, "unknown filetype"); + if (strcmp(class->filetype, filetype) != 0) { + errmsg = "unknown filetype"; goto error; } - while (*class) { - if (strcmp((*class)->version, version) == 0) - break; - class++; - } - - if (*class == NULL) { - diag_set_error(1, "unknown version"); + if (strcmp(class->version, version) != 0) { + errmsg = "unknown version"; goto error; } - l->class = *class; - - if (strcmp(version, v11) == 0) { - for (;;) { - r = fgets(buf, sizeof(buf), l->f); - if (r == NULL) { - diag_set_error(1, "header reading failed"); - goto error; - } - if (strcmp(r, "\n") == 0 || strcmp(r, "\r\n") == 0) - break; - } - } else { - r = fgets(buf, sizeof(buf), l->f); /* skip line with time */ + l->class = class; + + for (;;) { + r = fgets(buf, sizeof(buf), l->f); if (r == NULL) { - diag_set_error(1, "header reading failed"); + errmsg = "header reading failed"; goto error; } + if (strcmp(r, "\n") == 0 || strcmp(r, "\r\n") == 0) + break; } return l; error: say_error("open_for_read: failed to open `%s': %s", l->filename, - diag_get_last_error()->msg); + errmsg); if (l != NULL) { if (l->f != NULL) fclose(l->f); @@ -797,16 +685,19 @@ open_for_read(struct recovery_state *recover, struct log_io_class **class, i64 l } struct log_io * -open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 lsn, int suffix) +open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 lsn, + int suffix, int *save_errno) { struct log_io *l = NULL; int fd; char *dot; bool exists; + const char *errmsg; l = calloc(1, sizeof(*l)); if (l == NULL) { - diag_set_error(errno, strerror(errno)); + *save_errno = errno; + errmsg = strerror(errno); goto error; } l->mode = LOG_WRITE; @@ -828,7 +719,8 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l exists = access(l->filename, F_OK) == 0; *dot = '.'; if (exists) { - diag_set_error(EEXIST, "exists"); + *save_errno = EEXIST; + errmsg = "exists"; goto error; } } @@ -839,13 +731,15 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l */ fd = open(l->filename, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, 0664); if (fd < 0) { - diag_set_error(errno, strerror(errno)); + *save_errno = errno; + errmsg = strerror(errno); goto error; } l->f = fdopen(fd, "a"); if (l->f == NULL) { - diag_set_error(errno, strerror(errno)); + *save_errno = errno; + errmsg = strerror(errno); goto error; } @@ -854,7 +748,7 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l return l; error: say_error("find_log: failed to open `%s': %s", l->filename, - diag_get_last_error()->msg); + errmsg); if (l != NULL) { if (l->f != NULL) fclose(l->f); @@ -865,23 +759,23 @@ open_for_write(struct recovery_state *recover, struct log_io_class *class, i64 l /* this little hole shouldn't be used too much */ int -read_log(const char *filename, row_reader reader, +read_log(const char *filename, row_handler *xlog_handler, row_handler *snap_handler, void *state) { struct log_io_iter i; struct log_io *l; - struct log_io_class **c; + struct log_io_class *c; struct tbuf *row; row_handler *h; if (strstr(filename, xlog_suffix)) { - c = xlog_classes(NULL); + c = xlog_class_create(NULL); h = xlog_handler; } else if (strstr(filename, snap_suffix)) { - c = snap_classes(reader, NULL); + c = snapshot_class_create(NULL); h = snap_handler; } else { - say_error("don't know what how to read `%s'", filename); + say_error("don't know how to read `%s'", filename); return -1; } @@ -908,30 +802,37 @@ recover_snap(struct recovery_state *r) @try { memset(&i, 0, sizeof(i)); - lsn = greatest_lsn(r->snap_prefered_class); - if (lsn <= 0) - tnt_raise(tnt_Exception, reason:"can't find snapshot"); + lsn = greatest_lsn(r->snap_class); + if (lsn <= 0) { + say_error("can't find snapshot"); + return -1; + } snap = open_for_read(r, r->snap_class, lsn, 0, NULL); - if (snap == NULL) - tnt_raise(tnt_Exception, reason:"can't find/open snapshot"); + if (snap == NULL) { + say_error("can't find/open snapshot"); + return -1; + } iter_open(snap, &i, read_rows); say_info("recover from `%s'", snap->filename); while ((row = iter_inner(&i, (void *)1))) { - if (r->row_handler(r, row) < 0) - tnt_raise(tnt_Exception, reason:"can't apply row"); + if (r->row_handler(r, row) < 0) { + say_error("can't apply row"); + return -1; + } + } + if (i.error != 0) { + say_error("failure reading snapshot"); + return -1; } - if (i.error != 0) - tnt_raise(tnt_Exception, reason:"error during snapshot processing"); r->lsn = r->confirmed_lsn = lsn; return 0; } - @catch (tnt_Exception *e) { - say_error("tnt_Exception: `%s'", e->reason); + @catch (id e) { say_error("failure reading snapshot"); return -1; @@ -974,8 +875,10 @@ recover_wal(struct recovery_state *r, struct log_io *l) } /* after handler(r, row) returned, row may be modified, do not use it */ - if (r->row_handler(r, row) < 0) - tnt_raise(tnt_Exception, reason:"can't apply row"); + if (r->row_handler(r, row) < 0) { + say_error("can't apply row"); + return -1; + } if (r) { next_lsn(r, lsn); @@ -983,16 +886,17 @@ recover_wal(struct recovery_state *r, struct log_io *l) } } - if (i.error != 0) - tnt_raise(tnt_Exception, reason:"error during xlog processing"); + if (i.error != 0) { + say_error("error during xlog processing"); + return -1; + } if (i.eof) return LOG_EOF; return 1; } - @catch (tnt_Exception *e) { - say_error("tnt_Exception: `%s'", e->reason); + @catch (id e) { say_error("failure reading xlog"); return -1; @@ -1023,7 +927,7 @@ recover_remaining_wals(struct recovery_state *r) size_t rows_before; current_lsn = r->confirmed_lsn + 1; - wal_greatest_lsn = greatest_lsn(r->wal_prefered_class); + wal_greatest_lsn = greatest_lsn(r->wal_class); /* if the caller already opened WAL for us, recover from it first */ if (r->current_wal != NULL) @@ -1054,7 +958,7 @@ recover_remaining_wals(struct recovery_state *r) next_wal = open_for_read(r, r->wal_class, current_lsn, -1, NULL); if (next_wal == NULL) { char *filename = - format_filename(NULL, *r->wal_class, current_lsn, -1); + format_filename(NULL, r->wal_class, current_lsn, -1); say_warn("unlink broken %s wal", filename); if (inprogress_log_unlink(filename) != 0) @@ -1123,7 +1027,7 @@ recover(struct recovery_state *r, i64 lsn) if (lsn == 0) { result = recover_snap(r); if (result < 0) { - if (greatest_lsn(r->snap_prefered_class) <= 0) { + if (greatest_lsn(r->snap_class) <= 0) { say_crit("didn't you forget to initialize storage with --init-storage switch?"); _exit(1); } @@ -1143,17 +1047,16 @@ recover(struct recovery_state *r, i64 lsn) */ if (r->current_wal == NULL) { i64 next_lsn = r->confirmed_lsn + 1; - i64 lsn = find_including_file(r->wal_prefered_class, next_lsn); + i64 lsn = find_including_file(r->wal_class, next_lsn); if (lsn <= 0) { say_error("can't find wal containing record with lsn:%" PRIi64, next_lsn); result = -1; goto out; - } else { - r->current_wal = open_for_read(r, r->wal_class, lsn, 0, NULL); - if (r->current_wal == NULL) { - result = -1; - goto out; - } + } + r->current_wal = open_for_read(r, r->wal_class, lsn, 0, NULL); + if (r->current_wal == NULL) { + result = -1; + goto out; } } @@ -1285,9 +1188,12 @@ write_to_disk(void *_state, struct tbuf *t) reply = tbuf_alloc(t->pool); - if (wal == NULL) + if (wal == NULL) { + int unused; /* Open WAL with '.inprogress' suffix. */ - wal = open_for_write(r, r->wal_prefered_class, wal_write_request(t)->lsn, -1); + wal = open_for_write(r, r->wal_class, wal_write_request(t)->lsn, -1, + &unused); + } else if (wal->rows == 1) { /* rename wal after first successfull write to name without inprogress suffix*/ if (inprogress_log_rename(wal->filename) != 0) { @@ -1392,26 +1298,25 @@ wal_write(struct recovery_state *r, u16 tag, u64 cookie, i64 lsn, struct tbuf *r struct recovery_state * recover_init(const char *snap_dirname, const char *wal_dirname, - row_reader snap_row_reader, row_handler row_handler, + row_handler row_handler, int rows_per_file, double fsync_delay, int inbox_size, int flags, void *data) { struct recovery_state *r = p0alloc(eter_pool, sizeof(*r)); if (rows_per_file <= 1) - panic("inacceptable value of 'rows_per_file'"); + panic("unacceptable value of 'rows_per_file'"); r->wal_timer.data = r; r->row_handler = row_handler; r->data = data; - r->snap_class = snap_classes(snap_row_reader, snap_dirname); - r->snap_prefered_class = r->snap_class[1]; + r->snap_class = snapshot_class_create(snap_dirname); - r->wal_class = xlog_classes(wal_dirname); - r->wal_prefered_class = r->wal_class[1]; - r->wal_prefered_class->rows_per_file = rows_per_file; - r->wal_prefered_class->fsync_delay = fsync_delay; + r->wal_class = xlog_class_create(wal_dirname); + r->wal_class->rows_per_file = rows_per_file; + r->wal_class->fsync_delay = fsync_delay; + wait_lsn_clear(&r->wait_lsn); if ((flags & RECOVER_READONLY) == 0) r->wal_writer = spawn_child("wal_writer", inbox_size, write_to_disk, r); @@ -1422,13 +1327,8 @@ recover_init(const char *snap_dirname, const char *wal_dirname, void recovery_setup_panic(struct recovery_state *r, bool on_snap_error, bool on_wal_error) { - struct log_io_class **class; - - for (class = r->wal_class; *class; class++) - (*class)->panic_if_error = on_wal_error; - - for (class = r->snap_class; *class; class++) - (*class)->panic_if_error = on_snap_error; + r->wal_class->panic_if_error = on_wal_error; + r->snap_class->panic_if_error = on_snap_error; } static void @@ -1516,13 +1416,13 @@ snapshot_save(struct recovery_state *r, void (*f) (struct log_io_iter *)) struct log_io *snap; char final_filename[PATH_MAX + 1]; char *dot; + int save_errno; memset(&i, 0, sizeof(i)); - snap = open_for_write(r, r->snap_prefered_class, r->confirmed_lsn, -1); + snap = open_for_write(r, r->snap_class, r->confirmed_lsn, -1, &save_errno); if (snap == NULL) - panic_status(diag_get_last_error()->code, - "can't open snap for writing"); + panic_status(save_errno, "can't open snap for writing"); iter_open(snap, &i, write_rows); diff --git a/core/log_io_remote.m b/core/log_io_remote.m index 7b1a14966221439c3a294788b4a7050ebcfe5ddc..d7508ec2ca9bff3a832c8bdeebad4d38e7a474f5 100644 --- a/core/log_io_remote.m +++ b/core/log_io_remote.m @@ -134,7 +134,10 @@ pull_from_remote(void *state) struct tbuf *row; for (;;) { + fiber_setcancelstate(true); row = remote_read_row(h->r->confirmed_lsn + 1); + fiber_setcancelstate(false); + h->r->recovery_lag = ev_now() - row_v11(row)->tm; h->r->recovery_last_update_tstamp = ev_now(); @@ -183,9 +186,10 @@ recover_follow_remote(struct recovery_state *r, char *ip_addr, int port, struct sockaddr_in *addr; struct remote_state *h; - say_crit("initializing remote hot standby, WAL replicator %s:%i", ip_addr, port); + say_crit("initializing the replica, WAL replicator %s:%i", + ip_addr, port); name = palloc(eter_pool, 64); - snprintf(name, 64, "remote_hot_standby/%s:%i", ip_addr, port); + snprintf(name, 64, "replica/%s:%i", ip_addr, port); h = palloc(eter_pool, sizeof(*h)); h->r = r; diff --git a/core/pickle.m b/core/pickle.m index 07824ec0383e83ecede40208c93f4f95715239cd..2178089bcedae1bb512b1ca35db28fdb206aa0d4 100644 --- a/core/pickle.m +++ b/core/pickle.m @@ -28,11 +28,8 @@ #include <tbuf.h> #include <fiber.h> #include <iproto.h> /* for err codes */ -#include "errcode.h" #include "say.h" - -@implementation tnt_PickleException -@end +#include "exception.h" /* caller must ensure that there is space in target */ u8 * @@ -84,7 +81,7 @@ write_varint32(struct tbuf *b, u32 value) u##bits read_u##bits(struct tbuf *b) \ { \ if (b->len < (bits)/8) \ - tnt_raise(tnt_PickleException, reason:"buffer too short"); \ + tnt_raise(IllegalParams, :"packet too short (expected "#bits" bits)");\ u##bits r = *(u##bits *)b->data; \ b->size -= (bits)/8; \ b->len -= (bits)/8; \ @@ -104,7 +101,7 @@ read_varint32(struct tbuf *buf) int len = buf->len; if (len < 1) { - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected 1 byte)"); } if (!(b[0] & 0x80)) { buf->data += 1; @@ -114,7 +111,7 @@ read_varint32(struct tbuf *buf) } if (len < 2) - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected 2 bytes)"); if (!(b[1] & 0x80)) { buf->data += 2; buf->size -= 2; @@ -122,7 +119,7 @@ read_varint32(struct tbuf *buf) return (b[0] & 0x7f) << 7 | (b[1] & 0x7f); } if (len < 3) - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected 3 bytes)"); if (!(b[2] & 0x80)) { buf->data += 3; buf->size -= 3; @@ -131,7 +128,7 @@ read_varint32(struct tbuf *buf) } if (len < 4) - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected 4 bytes)"); if (!(b[3] & 0x80)) { buf->data += 4; buf->size -= 4; @@ -141,7 +138,7 @@ read_varint32(struct tbuf *buf) } if (len < 5) - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected 5 bytes)"); if (!(b[4] & 0x80)) { buf->data += 5; buf->size -= 5; @@ -150,7 +147,7 @@ read_varint32(struct tbuf *buf) (b[2] & 0x7f) << 14 | (b[3] & 0x7f) << 7 | (b[4] & 0x7f); } - tnt_raise(tnt_PickleException, reason:"impossible happened"); + tnt_raise(IllegalParams, :"incorrect BER format"); return 0; } @@ -170,7 +167,7 @@ read_field(struct tbuf *buf) u32 data_len = read_varint32(buf); if (data_len > buf->len) - tnt_raise(tnt_PickleException, reason:"buffer too short"); + tnt_raise(IllegalParams, :"packet too short (expected a field)"); buf->size -= data_len; buf->len -= data_len; diff --git a/core/replicator.m b/core/replicator.m index c04cef8a27384f86be345e018bff0bbfa368e322..5bd723fdf94788b2e25a915088ae2bf48ca04478 100644 --- a/core/replicator.m +++ b/core/replicator.m @@ -37,6 +37,7 @@ #include <sys/uio.h> #include <netinet/in.h> #include <arpa/inet.h> +#include "fiber.h" /** replicator process context struct */ @@ -269,13 +270,14 @@ acceptor_handler(void *data) msg = tbuf_alloc(fiber->pool); - while (true) { + fiber_io_start(EV_READ); + for (;;) { struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int client_sock = -1; /* wait new connection request */ - wait_for(EV_READ); + fiber_io_yield(); /* accept connection */ client_sock = accept(fiber->fd, &addr, &addrlen); @@ -294,6 +296,7 @@ acceptor_handler(void *data) tbuf_reset(msg); wait_inbox(sender); } + fiber_io_stop(EV_READ); } /** Replication sender fiber. */ diff --git a/core/salloc.m b/core/salloc.m index e9a8e52262bde80cdd6af8d2d4edc3eb5f9b6891..c8195ce865d38327a24015442f436d443cfaad9a 100644 --- a/core/salloc.m +++ b/core/salloc.m @@ -191,7 +191,7 @@ format_slab(struct slab_class *class, struct slab *slab) } static bool -full_formated(struct slab *slab) +fully_formatted(struct slab *slab) { return slab->brk + slab->class->item_size >= (void *)slab + SLAB_SIZE; } @@ -284,7 +284,7 @@ salloc(size_t size) VALGRIND_MAKE_MEM_UNDEFINED(item, sizeof(void *)); } - if (full_formated(slab) && slab->free == NULL) + if (fully_formatted(slab) && slab->free == NULL) TAILQ_REMOVE(&class->free_slabs, slab, class_free_link); slab->used += class->item_size + sizeof(red_zone); @@ -302,7 +302,7 @@ sfree(void *ptr) struct slab_class *class = slab->class; struct slab_item *item = ptr; - if (full_formated(slab) && slab->free == NULL) + if (fully_formatted(slab) && slab->free == NULL) TAILQ_INSERT_TAIL(&class->free_slabs, slab, class_free_link); assert(valid_item(slab, item)); diff --git a/core/say.m b/core/say.m index 8a29c91fa4952fe5d2fa3a9a444c5458bff10fca..c0aac43e6e21fa7b47082e43da9d5793ed28786d 100644 --- a/core/say.m +++ b/core/say.m @@ -85,6 +85,14 @@ say_logger_init(int nonblock) if (pid == 0) { close(pipefd[1]); dup2(pipefd[0], STDIN_FILENO); + /* + * Move to an own process group, to not + * receive signals from the controlling + * tty. This keeps the log open as long as + * the parent is around. When the parent + * dies, we get SIGPIPE and terminate. + */ + setpgid(0, 0); execve(argv[0], argv, envp); } else { close(pipefd[0]); diff --git a/core/tarantool.m b/core/tarantool.m index 52510c2e871c6cf5c8960e410eb8b2096daf9e54..ad80f96cc33417eddd9b1d083dce9ab652541bbf 100644 --- a/core/tarantool.m +++ b/core/tarantool.m @@ -46,6 +46,7 @@ #include <replicator.h> #include <fiber.h> #include <iproto.h> +#include <latch.h> #include <log_io.h> #include <palloc.h> #include <salloc.h> @@ -108,58 +109,72 @@ load_cfg(struct tarantool_cfg *conf, i32 check_rdonly) return 0; } + i32 reload_cfg(struct tbuf *out) { - struct tarantool_cfg new_cfg1, new_cfg2; - i32 ret; - - // Load with checking readonly params - if (dup_tarantool_cfg(&new_cfg1, &cfg) != 0) { - destroy_tarantool_cfg(&new_cfg1); + static struct tnt_latch *latch = NULL; + struct tarantool_cfg new_cfg, aux_cfg; - return -1; + if (latch == NULL) { + latch = palloc(eter_pool, sizeof(*latch)); + tnt_latch_init(latch); } - ret = load_cfg(&new_cfg1, 1); - if (ret == -1) { - tbuf_append(out, cfg_out->data, cfg_out->len); - destroy_tarantool_cfg(&new_cfg1); + if (tnt_latch_trylock(latch) == -1) { + out_warning(0, "Could not reload configuration: it is being reloaded right now"); + tbuf_append(out, cfg_out->data, cfg_out->len); return -1; } - // Load without checking readonly params - if (fill_default_tarantool_cfg(&new_cfg2) != 0) { - destroy_tarantool_cfg(&new_cfg2); - return -1; - } - ret = load_cfg(&new_cfg2, 0); - if (ret == -1) { - tbuf_append(out, cfg_out->data, cfg_out->len); + @try { + init_tarantool_cfg(&new_cfg); + init_tarantool_cfg(&aux_cfg); - destroy_tarantool_cfg(&new_cfg1); + /* + Prepare a copy of the original config file + for confetti, so that it can compare the new + file with the old one when loading the new file. + Load the new file and return an error if it + contains a different value for some read-only + parameter. + */ + if (dup_tarantool_cfg(&aux_cfg, &cfg) != 0 || + load_cfg(&aux_cfg, 1) != 0) + return -1; + /* + Load the new configuration file, but + skip the check for read only parameters. + new_cfg contains only defaults and + new settings. + */ + if (fill_default_tarantool_cfg(&new_cfg) != 0 || + load_cfg(&new_cfg, 0) != 0) + return -1; + + /* Check that no default value has been changed. */ + char *diff = cmp_tarantool_cfg(&aux_cfg, &new_cfg, 1); + if (diff != NULL) { + out_warning(0, "Could not accept read only '%s' option", diff); + return -1; + } - return -1; + /* Now pass the config to the module, to take action. */ + if (mod_reload_config(&cfg, &new_cfg) != 0) + return -1; + /* All OK, activate the config. */ + swap_tarantool_cfg(&cfg, &new_cfg); } - // Compare only readonly params - char *diff = cmp_tarantool_cfg(&new_cfg1, &new_cfg2, 1); - if (diff != NULL) { - destroy_tarantool_cfg(&new_cfg1); - destroy_tarantool_cfg(&new_cfg2); + @finally { + destroy_tarantool_cfg(&aux_cfg); + destroy_tarantool_cfg(&new_cfg); - out_warning(0, "Could not accept read only '%s' option", diff); - tbuf_append(out, cfg_out->data, cfg_out->len); + if (cfg_out->len != 0) + tbuf_append(out, cfg_out->data, cfg_out->len); - return -1; + tnt_latch_unlock(latch); } - destroy_tarantool_cfg(&new_cfg1); - - mod_reload_config(&cfg, &new_cfg2); - - destroy_tarantool_cfg(&cfg); - - cfg = new_cfg2; return 0; } @@ -224,7 +239,7 @@ snapshot(void *ev, int events __attribute__((unused))) static void sig_int(int signal) { - say_info("SIGINT or SIGTERM recieved, terminating"); + say_info("Exiting: %s", strsignal(signal)); if (recovery_state != NULL) { struct child *writer = recovery_state->wal_writer; diff --git a/doc/box-protocol.txt b/doc/box-protocol.txt index a81c1282e6b1a8d95cab795193e4b3d0a03e93ce..8dd73596019859896473b99154c21c8e544c0174 100644 --- a/doc/box-protocol.txt +++ b/doc/box-protocol.txt @@ -167,7 +167,7 @@ ; Even this type awareness is very limited: it's only used when ; the field participates in an index. For example, when a numeric ; 32-bit index is defined on a field, and a non-32-bit value -; is supplied for that field, an ERR_CODE_BAD_PARAMS +; is supplied for that field, an ER_BAD_PARAMS ; is returned. ; @@ -297,7 +297,7 @@ ; it can be deduced from the error code. There are only ; 3 completion status codes in use: ; 0 - success; The only possible error code with this status is - 0, ERR_CODE_OK + 0, ER_OK ; 1 - try again; An indicator of an intermittent error. ; Usually is returned when two clients attempt to change ; the same tuple simultaneously. @@ -308,45 +308,45 @@ ; ; Completion status 0 (success) ; ----------------------------- -; 0x00000000 -- ERR_CODE_OK +; 0x00000000 -- ER_OK ; ; Completion status 1 (try again) ; ------------------------------- -; 0x00000401 -- ERR_CODE_NODE_IS_RO +; 0x00000401 -- ER_TUPLE_IS_RO ; The requested data is blocked from modification ; -; 0x00000601 -- ERR_CODE_NODE_IS_LOCKED +; 0x00000601 -- ER_TUPLE_IS_LOCKED ; The requested data is not available ; -; 0x00000701 -- ERR_CODE_MEMORY_ISSUE +; 0x00000701 -- ER_MEMORY_ISSUE ; An error occurred when allocating memory ; ; Completion status 2 (error) ; --------------------------- ; -; 0x00000102 -- ERR_CODE_NONMASTER -; An attempt was made to change data on a read-only port +; 0x00000102 -- ER_NONMASTER +; An attempt was made to change data on a read-only port ; -; 0x00000202 -- ERR_CODE_ILLEGAL_PARAMS -; Malformed query +; 0x00000202 -- ER_ILLEGAL_PARAMS +; Malformed query ; -; 0x00000a02 -- ERR_CODE_UNSUPPORTED_COMMAND +; 0x00000a02 -- ER_UNSUPPORTED_COMMAND ; The query is not recognized ; -; 0x00001e02 -- ERR_CODE_WRONG_FIELD +; 0x00001e02 -- ER_WRONG_FIELD ; An unknown field was requested ; -; 0x00001f02 -- ERR_CODE_WRONG_NUMBER +; 0x00001f02 -- ER_WRONG_NUMBER ; An out-of-range numeric value was included in the query ; -; 0x00002002 -- ERR_CODE_DUPLICATE +; 0x00002002 -- ER_DUPLICATE ; An attempt was made to create an object with an existing key. ; -; 0x000026002 -- ERR_CODE_WRONG_VERSION -; The protocol version is not supported +; 0x00002602 -- ER_WRONG_VERSION +; The protocol version is not supported ; -; 0x000027002 -- ERR_CODE_UNKNOWN_ERROR -; Unknown error +; 0x00002702 -- ER_WAL_IO +; WAL I/O error ; ; Convenience that define hexadecimal constants for <int32> ; return codes (completion status + code) can be found in diff --git a/doc/developer/CMakeLists.txt b/doc/developer/CMakeLists.txt index 52ba4d5e1f948d8b22cb99ec74c3723b12188998..691e16c82e2c17018469853a29576e56238a4f06 100644 --- a/doc/developer/CMakeLists.txt +++ b/doc/developer/CMakeLists.txt @@ -1,6 +1,8 @@ -add_custom_command(OUTPUT tarantool_user_guide.html - COMMAND ${XSLTPROC} --xinclude -o tarantool_user_guide.html - http://docbook.sourceforge.net/release/xsl-ns/current/html/docbook.xsl - user.xml) +add_custom_target(dev-html ALL + COMMAND ${XSLTPROC} --nonet + --stringparam collect.xref.targets "all" + --xinclude -o tarantool_developer_guide.html + ${CMAKE_SOURCE_DIR}/doc/user/tnt-html.xsl + developer.xml) diff --git a/doc/developer/developer.xml b/doc/developer/developer.xml index 64713736d2bb49ef0e618abfaa14b2e3af7c97b4..f761f76ec6ac104b3419fff4ae4833ba51675849 100644 --- a/doc/developer/developer.xml +++ b/doc/developer/developer.xml @@ -1,8 +1,40 @@ <?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE book [ +<!ENTITY % tnt SYSTEM "../tnt.ent"> +%tnt; +]> <book xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0"> -<title>Tarantool/Box User Guide, version 1.3.5</title> -<xi:include href="./intro.xml"/> +<title>Tarantool/Box Developer Guide</title> +<chapter> +<title>Compiling</title> +<section> +<title>How to fix the compile time error about missing confetti</title> +<para> + An error about missing confetti: +<programlisting> +Generating prscfg.h, prscfg.c... +[ 13%] Generating prscfg.h, prscfg.c +/bin/sh: CONFETTI-NOTFOUND: not found +</programlisting> +This error is caused by cmake, trying to re-create generated files +prscfg.h, prscfg.c in <filename>cfg/</filename> directory. +These files are under revision control and normally need not to be +regenerated. +The fix is to +<programlisting> +<prompt>$ </prompt>git checkout cfg/prscfg.h +<prompt>$ </prompt>git checkout cfg/prscfg.c +<prompt>$ </prompt>touch cfg/prscfg.[hc] +</programlisting> +The other alternative, if you have actually modified +<filename>core_cfg.cfg_tmpl</filename> is to install confetti from +<link xlink:href="http://github.com/mailru/confetti"/> and let cmake use it. + + </para> +</section> +</chapter> <!-- TOC: @@ -45,3 +77,8 @@ Glossary of terms --> </book> + +<!-- +vim: tw=66 syntax=docbk +vim: spell spelllang=en_us +--> diff --git a/doc/sql.txt b/doc/sql.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cbbd9a7de20e3aab1203d77a8356fb59a1169f8 --- /dev/null +++ b/doc/sql.txt @@ -0,0 +1,53 @@ +; +; Tarantool SQL parser is implemented entirely on the client side. +; This BNF provides a reference of the supported subset of +; the standard SQL, to which all clients are strongly encouraged +; to stick. +; +; Convention: UPPERCASE letters are used for terminals and literals. +; Lowercase letters are used for <non-terminals>. SQL is +; case-insensitive, so this convention is present only to imporve +; legibility of the BNF. + +<sql> ::= <insert> | <update> | <delete> | <select> + +<insert> ::= INSERT [INTO] <ident> VALUES <value_list> + +<update> ::= UPDATE <ident> SET <update_list> <simple_where> + +<delete> ::= DELETE FROM <ident> <simple_where> + +; It's only possible to select all fields of a tuple (* for field list) +<select> ::= SELECT * FROM <ident> <where> <opt_limit> + +<simple_where> ::= WHERE <predicate> + +<where> ::= WHERE <disjunction> + +<predicate> ::= <ident> = <constant> + +<disjunction> ::= <predicate> [{OR <predicate>}+] + +; LIMIT is optional +<opt_limit> ::= | LIMIT NUM[, NUM] + +<value_list> ::= (<constant> [{, <constant>}+]) + +<update_list> ::= <ident> = <constant> [{, <ident> = <constant>}+] + +<constant> ::= STR | NUM + +<ident> ::= ID + +; Only integer numbers, optionally signed, are supported +NUM ::= [+-]?[0-9]+ + +; Strings must be single-quoted +STR ::= '.*' + +; Identifiers must be standard SQL, but end with digits. +; These digits are used to infer the namespace or index id. + +ID ::= [a-z_]+[0-9]+ + +; vim: syntax=bnf diff --git a/doc/user/CMakeLists.txt b/doc/user/CMakeLists.txt index b48472a87601fe3fd2fa6b51b193a2ebffc449f5..54df67d597ebf95f273a816211acc5544b0712b7 100644 --- a/doc/user/CMakeLists.txt +++ b/doc/user/CMakeLists.txt @@ -5,6 +5,14 @@ add_custom_target(html ALL tnt-html.xsl user.xml) +add_custom_target(pdf + COMMAND ${XSLTPROC} --nonet + --stringparam collect.xref.targets "all" + --xinclude -o tarantool_user_guide.fo + tnt-fo.xsl + user.xml + COMMAND fop tarantool_user_guide.fo tarantool_user_guide.pdf) + add_custom_target(relink COMMAND ${XSLTPROC} --nonet --stringparam collect.xref.targets "only" @@ -23,6 +31,18 @@ add_custom_target(html-saxon -r org.apache.xml.resolver.tools.CatalogResolver -u -o tarantool_user_guide.html user.xml tnt-html.xsl) +add_custom_target(pdf-saxon + COMMAND java -cp "/usr/share/java/saxon.jar:/usr/share/java/xml-commons-resolver-1.1.jar:/usr/share/java/docbook-xsl-saxon.jar:/usr/share/java/xercesImpl.jar:/etc/xml/resolver:/usr/share/java/xslthl.jar" + -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl + -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl + -Dorg.apache.xerces.xni.parser.XMLParserConfiguration=org.apache.xerces.parsers.XIncludeParserConfiguration + com.icl.saxon.StyleSheet + -x org.apache.xml.resolver.tools.ResolvingXMLReader + -y org.apache.xml.resolver.tools.ResolvingXMLReader + -r org.apache.xml.resolver.tools.CatalogResolver + -u -o tarantool_user_guide.fo user.xml tnt-fo.xsl + COMMAND fop tarantool_user_guide.fo tarantool_user_guide.pdf) + # XMLLINT is not able to validate SCHEMATRON constraints, and # therefore is not a good validation tool for DocBook 5. However, # it can validate the entire document, following xinclude diff --git a/doc/user/configuration-reference.xml b/doc/user/configuration-reference.xml index b86c48c7e7033162ef53581cfc2d9f206b82b9b1..bbb384e2259a2a4362133b7858a023bce55c43d8 100644 --- a/doc/user/configuration-reference.xml +++ b/doc/user/configuration-reference.xml @@ -123,7 +123,11 @@ Tarantool/Box 1.4.0-69-g45551dd </para> </listitem> <listitem> - <para><option>--init-storage</option></para> + <para> + <option xml:id="init-storage-option" + xreflabel="--init-storage"> + --init-storage + </option></para> <para>Initialize the directory, specified in <emphasis>vardir</emphasis> configuration option by creating an empty snapshot file in it. If <filename>vardir</filename> doesn't contain at @@ -140,7 +144,8 @@ Tarantool/Box 1.4.0-69-g45551dd </listitem> <listitem> - <para><option>--cat</option> <userinput>snapshot.file</userinput></para> + <para><option xml:id="cat-option" xreflabel="--cat">--cat</option> + <userinput>snapshot.file</userinput></para> <para>Print the snapshot file, pointed to by the argument, in a human-readable form. For each log record, log sequence number, time of entry, operation type and arguments are @@ -180,7 +185,7 @@ lsn:4 tm:1301572313.691 t:65534 127.0.0.1:52728 UPDATE_FIELDS n:0flags:00000000 </itemizedlist> </section> -<section> +<section xml:id="option-file" xreflabel="option file"> <title>The option file</title> <para> All advanced configuration parameters must be specified in a @@ -225,9 +230,11 @@ lsn:4 tm:1301572313.691 t:65534 127.0.0.1:52728 UPDATE_FIELDS n:0flags:00000000 <filename xlink:href="https://github.com/mailru/tarantool/blob/master/test/box_big/tarantool.cfg">test/box_big/tarantool.cfg</filename>. </para> - <table frame='all'> + <table frame='all' pgwide='1'> <title>Basic parameters</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -310,7 +317,8 @@ lsn:4 tm:1301572313.691 t:65534 127.0.0.1:52728 UPDATE_FIELDS n:0flags:00000000 </row> <row> - <entry>secondary_port</entry> + <entry xml:id="secondary_port" + xreflabel="secondary_port">secondary_port</entry> <entry>integer</entry> <entry>none</entry> <entry>no</entry> @@ -320,7 +328,7 @@ lsn:4 tm:1301572313.691 t:65534 127.0.0.1:52728 UPDATE_FIELDS n:0flags:00000000 </row> <row> - <entry>admin_port</entry> + <entry xml:id="admin_port" xreflabel="admin_port">admin_port</entry> <entry>integer</entry> <entry>none</entry> <entry>no</entry> @@ -341,23 +349,27 @@ lsn:4 tm:1301572313.691 t:65534 127.0.0.1:52728 UPDATE_FIELDS n:0flags:00000000 </row> <row> - <entry>custom_proc_title</entry> + <entry xml:id="custom_proc_title" + xreflabel="custom_proc_title"> + custom_proc_title + </entry> <entry>string</entry> <entry>""</entry> <entry>no</entry> <entry>no</entry> <entry> - <para>Inject the given string into process title + <para>Inject the given string into <olink + targetptr="proctitle">server process title</olink> (what's shown in COMMAND column of <command>ps</command> and <command>top</command> commands). For example, an unmodified Tarantool/Box process group looks like: </para> <programlisting>kostja@shmita:~$ ps -a -o command | grep box -tarantool: box:primary pri:33013 sec:33014 adm:33015</programlisting> +tarantool_box: primary pri:33013 sec:33014 adm:33015</programlisting> <para>After "sessions" custom_proc_title is injected it looks like:</para> <programlisting>kostja@shmita:~$ ps -a -o command | grep box -tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> +tarantool_box: primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </entry> </row> @@ -365,9 +377,11 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </tgroup> </table> - <table frame='all'> + <table frame='all' pgwide='1'> <title>Configuring the storage</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -395,7 +409,7 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> store tuples, <emphasis role="strong">in gigabytes</emphasis>. When the limit is reached, INSERT or UPDATE requests begin failing with error - <olink targetptr="ERR_CODE_MEMORY_ISSUE"/>. + <olink targetptr="ER_MEMORY_ISSUE"/>. While the server does not go beyond the defined limit to allocate tuples, there is additional memory used to store indexes and connection @@ -447,9 +461,11 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </tgroup> </table> - <table frame='all'> + <table frame='all' pgwide='1'> <title>Binary logging and snapshots</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -522,24 +538,11 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> <entry>WAL writer is a separate process whose sole purpose is to write the change log to disk. Every incoming data change is sent to this process and - queued for write. This parameter sets the size - of the queue. However, it has no effect - due to <link - xlink:href="https://bugs.launchpad.net/tarantool/+bug/791498">Bug#791498</link>.</entry> - </row> - - <row> - <entry>readahead</entry> - <entry>integer</entry> - <entry>16384</entry> - <entry>no</entry> - <entry>no</entry> - <entry>WAL writer network read-ahead buffer size, in - bytes. - It's not recommended to change this value until - <link xlink:href="https://bugs.launchpad.net/tarantool/+bug/791498">Bug#791498</link> - is fixed. - </entry> + queued for write. This parameter affects the size of the + user space buffer that WAL writer uses to queue + write requests. By default, up to 128 client + connections can have pending updates waiting on disk. + </entry> </row> <row> @@ -564,9 +567,11 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </tgroup> </table> - <table frame='all'> + <table frame='all' pgwide='1'> <title>Replication</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -581,7 +586,8 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> <tbody> <row> - <entry>replication_port</entry> + <entry xml:id="replication_port" + xreflabel="replication_port">replication_port</entry> <entry>integer</entry> <entry>0</entry> <entry>no</entry> @@ -609,7 +615,10 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </row> <row> - <entry>replication_source_ipaddr</entry> + <entry xml:id="replication_source_ipaddr" + xreflabel="replication_source_ipaddr"> + replication_source_ipaddr + </entry> <entry>string</entry> <entry>0</entry> <entry>no</entry> @@ -621,19 +630,67 @@ tarantool: box:primary@sessions pri:33013 sec:33014 adm:33015</programlisting> </tgroup> </table> -<!-- + <table frame='all' pgwide='1'> + <title>Networking</title> + <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> -# delay between loop iterations -io_collect_interval=0.0, ro + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Default</entry> + <entry>Required?</entry> + <entry>Dynamic?</entry> + <entry>Description</entry> + </row> + </thead> -# size of listen backlog -backlog=1024, ro + <tbody> ---> + <row> + <entry>io_collect_interval</entry> + <entry>float</entry> + <entry>0.0</entry> + <entry>no</entry> + <entry>no</entry> + <entry>If non-zero, a sleep given duration is + injected between iterations of the event loop. Can be + used to reduce CPU load in deployments in which the + number of client connections is large, but requests are + not so frequent (for example, each connection issuing + just a handful of requests per second). </entry> + </row> + + <row> + <entry>readahead</entry> + <entry>integer</entry> + <entry>16384</entry> + <entry>no</entry> + <entry>no</entry> + <entry>The size of read-ahead buffer associated with + a client connection.</entry> + </row> + + <row> + <entry>backlog</entry> + <entry>integer</entry> + <entry>1024</entry> + <entry>no</entry> + <entry>no</entry> + <entry>The size of listen backlog.</entry> + </row> - <table frame='all'> + </tbody> + </tgroup> + </table> + + <table frame='all' pgwide='1'> <title>Logging</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -712,9 +769,11 @@ backlog=1024, ro </tgroup> </table> - <table frame='all'> + <table frame='all' pgwide='1'> <title>Memcached protocol support</title> <tgroup cols='6' colsep='1' rowsep='1'> + <colspec colnum="1" colname="col1" colwidth="2*"/> + <colspec colnum="6" colname="col4" colwidth="6*"/> <thead> <row> @@ -730,19 +789,27 @@ backlog=1024, ro <tbody> <row> - <entry>memcached</entry> + <entry>memcached_port</entry> <entry>integer</entry> <entry>0</entry> <entry>no</entry> <entry>no</entry> - <entry>Turn on Memcached mode. In this mode, Tarantool - does not use secondary_port, and speaks memcached - protocol on the primary port. In particular, - memcached-style flags are supported, and expiration - time can be set on tuples. Unlike Memcached, all data - still goes to the binary log and to the replica, if - latter one is set up, which means that power outage - does not lead to a loss of all data. + <entry> + <anchor xml:id="memcached_port" xreflabel="memcached_port"/> + + Turn on Memcached protocol support on the given + port. All requests on this port are directed to + a dedicated namespace, set in <olink + targetptr="memcached_namespace"/>. + Memcached-style flags are supported and stored + along with the value. The expiration time can also be + set and is persistent, but is ignored, unless <olink + targetptr="memcached_expire"/> is turned on. + Unlike Memcached, all data still goes to the binary + log and to the replica, if latter one is set up, which + means that power outage does not lead to loss of all + data. Thanks to data persistence, cache warm up time + is also very short. </entry> </row> @@ -752,9 +819,34 @@ backlog=1024, ro <entry>23</entry> <entry>no</entry> <entry>no</entry> - <entry>Namespace id to store memcached data in. The - format of tuple is [key, metadata, value], with a hash - index based on the key. </entry> + <entry> + <anchor xml:id="memcached_namespace" + xreflabel="memcached_namespace"/> + Namespace id to store memcached data in. The + format of tuple is [key, metadata, value], with a HASH + index based on the key. Since the namespace format + is defined by Memcached data model, it must not be + previously configured.</entry> + </row> + + <row> + <entry>memcached_expire</entry> + <entry>boolean</entry> + <entry>0</entry> + <entry>no</entry> + <entry>no</entry> + <entry> + <anchor xml:id="memcached_expire" + xreflabel="memcached_expire"/> + Turn on tuple time-to-live support in + <olink targetptr="memcached_namespace"/>. This + effectively turns Tarantool into a "persistent" implementation + of Memcached. Since a caching server has + significantly different purpose than a data server, in + this mode Tarantool turns off replication and does not + bind to <olink targetptr="primary_port"/> or <olink + targetptr="secondary_port"/>. + </entry> </row> <row> diff --git a/doc/user/connectors.xml b/doc/user/connectors.xml new file mode 100644 index 0000000000000000000000000000000000000000..c150d64627043d03e888072b356c830413552ded --- /dev/null +++ b/doc/user/connectors.xml @@ -0,0 +1,265 @@ +<!DOCTYPE book [ +<!ENTITY % tnt SYSTEM "../tnt.ent"> +%tnt; +]> +<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" + xmlns:xlink="http://www.w3.org/1999/xlink" + xml:id="connectors"> + +<title>Connectors</title> +<blockquote><para> + This chapter documents APIs for various programming languages -- + C, Perl, Ruby, PHP and Python. +</para></blockquote> + +<para>All connectors are located in srcdir/connector directory. Apart from the native Tarantool client driver, you can always use a <emphasis role="strong">Memcached</emphasis> driver of your choice, after enabling Memcached protocol in the configuration file.</para> + + <section> + <title>C</title> + <para> + Please see <link + xlink:href="https://github.com/mailru/tarantool/blob/master/connector/c/client.h"><filename>connector/c/client.h</filename></link> in the source tree. + </para> + </section> + + <section> + <title>Perl</title> + <para> + The perl client is located in <link + xlink:href="https://github.com/mailru/tarantool/blob/master/connector/perl/lib/"><filename>connector/perl/lib/</filename></link>. + <orderedlist> + + <listitem> + <simpara><emphasis role="strong">new</emphasis></simpara> +<programlisting language="perl"><![CDATA[ + my $box = MR::SilverBox->new({ + namespaces => [ { + indexes => [ { + index_name => 'primary_id', + keys => [0], + }, { + index_name => 'primary_email', + keys => [1], + }, ], + namespace => 0, + format => 'l& SSLL', + default_index => 'primary_id', + } ], + }, + servers => $server}) +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Insert</emphasis></simpara> +<programlisting language="perl"> + $box->Insert(@tuple); +</programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Select</emphasis></simpara> +<programlisting language="perl"><![CDATA[ + [\%tuple1, \%tuple2, ...] = $box->Select(@id); + \%tuple = $box->Select($id); + [\@tuple1, \@tuple2, ...] = $box->Select(@id, {raw => 1}); + \@tuple = $box->Select($id, {raw => 1}); + $box->Select($email, {use_index => 1}); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Update</emphasis> + accepts parameters in the last argument just like Select:</simpara> +<programlisting language="perl"><![CDATA[ + $key = 1; # key, ID of user by default + $field_num = 2; # posititon in tuple, starts from 0, must be >= 1 + $value = pack('L', 123); # integer values must be packed + $box->Update($key, $field_num, $value); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Delete</emphasis></simpara> +<programlisting language="perl"><![CDATA[ + $box->Delete($key); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">AndXorAdd</emphasis> + transforms the tuple field at position <quote>$field_num</quote> + according to formula <quote>field= ((field & $and_mask) ^ $xor_mask) + $add_value</quote>. + </simpara> +<programlisting language="perl"><![CDATA[ + $box->AndXorAdd($key, $field_num, $and_mask, $xor_mask, $add_value); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Bit</emphasis> + performs a bitwise operation on field at position + <quote>$field_num</quote>. Unused arguments can be omitted. + Note: <quote>set</quote> has a higher precedence than + <quote>bit_set</quote> and <quote>bit_clear</quote>. + </simpara> +<programlisting language="perl"><![CDATA[ + $box->Bit($key, $field_num, bit_clear => $clear_mask, bit_set => $set_mask, set => $set_value); + $box->Bit($key, $field_num, bit_set => $set_mask); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Num</emphasis> + performs a numeric update operation on field at position + <quote>$field_num</quote>. Unused arguments can be omitted. + Note: again, <quote>set</quote> has a higher precedence than + <quote>num_add</quote> and <quote>num_sub</quote>. + </simpara> +<programlisting language="perl"><![CDATA[ + $box->Num($key, $field_num, set => $set_value, num_add => $add_value, num_sub => $sub_value); +]]></programlisting> + </listitem> + + <listitem> + <simpara><emphasis role="strong">Flag</emphasis> + sets or clears flags on a tuple, the calling convention + is the same as for <quote>Bit</quote> and <quote>Num</quote> + operations. + </simpara> +<programlisting language="perl"><![CDATA[ + $box->Flags(bit_set => $set_mask, bit_clear => $clear_mask); +]]></programlisting> + </listitem> + + </orderedlist> + + </para> + </section> + + <section> + <title>PHP</title> + <para> + @tba + </para> + </section> + + <section> + <title>Python</title> + <para> + @tba + </para> + </section> + + <section> + <title>Ruby</title> + <para> + You need <emphasis role="strong">Ruby 1.9</emphasis> or later + to use this connector. Connector sources are located in <link + xlink:href="https://github.com/mailru/tarantool/blob/master/connector/ruby/box.rb"><filename>connector/ruby/box.rb</filename></link>. + </para> + <para> + Assume, for the sake of example, that Tarantool has the + following namespace configuration: +<programlisting language="c"> +primary_port = 33013 +admin_port = 33015 +log_level = 3 +slab_alloc_arena = 0.1 + +namespace[0].enabled = 1 +namespace[0].index[0].type = "NUM" +namespace[0].index[0].key_fields[0].fieldno = 0 + +namespace[0].index[1].type = "STR" +namespace[0].index[1].key_fields[0].fieldno = 1 +</programlisting> + The only defined namespace will be used to store user + account information, such as id, name, email, and other + properties. User ID is used for the primary key, but + it's also possible to find a user by name. + </para> + <para>In Ruby, a helper class is defined to present + Tarantool to the rest of the application as a typical object + container. +<example> +<title>userbox.rb</title> +<programlisting language="ruby"><![CDATA[require 'box' + +class UserBox < Box + def initialize(host) + super(host, :namespace => 0) + end + + def insert(user) + case user + when Hash then super [user[:uid], user[:email], user[:name], user[:apple_count]] + when Array then super user + else fail "don't know what to do with #{user.class}" + end + end + + def update_fields(key, *ops) + mapping = {:uid => 0, :email => 1, :name => 2, :apple_count => 3} + ops.map do |op| + op[0] = mapping[op[0]] if op.is_a? Array + end + + super key, *ops + end + + def unpack_tuple!(data) + tuple = super data + { :uid => tuple[0].unpack(?L)[0], + :email => tuple[1], + :name => tuple[2], + :apple_count => tuple[3].unpack(?L)[0] + } + end +end]]></programlisting></example> + Here's how this helper class can be used: +<programlisting><prompt>kostja@shmita:~$ </prompt><command>irb</command> +<![CDATA[>> # Connect to the server +>> require 'userbox' +=> true +>> b = UserBox.new 'localhost:33013' +=> #<UserBox:0x870fd48 @namespace=1, @end_point=["localhost", 33013], @sock=#<TCPSocket:0x870f85c> +>> # Insert a few users +>> b.insert :uid => 1, :email => 'pupkin@mail.ru', :name => 'Vasya', :apple_count => 1 +=> 1 +>> b.insert :uid => 2, :email => 'masha@mail.ru', :name => 'Masha', :apple_count => 0 +=> 1 +>> b.insert :uid => 3, :email => 'petya@mail.ru', :name => 'Petya', :apple_count => 3 +=> 1 +>> # Perform selects +>> b.select 1 +=> [{:uid=>1, :email=>"pupkin@mail.ru", :name=>"Vasya", :apple_count=>1}] +>> b.select 1,2,3 +=> [{:uid=>1, :email=>"pupkin@mail.ru", :name=>"Vasya", :apple_count=>1}, {:uid=>2, :email=>"masha@mail.ru", :name=>"Masha", :apple_count=>0}, {:uid=>3, :email=>"petya@mail.ru", :name=>"Petya", :apple_count=>3}] +>> # It's possible to retrieve records by email using second index +>> b.select 'pupkin@mail.ru', 'petya@mail.ru', :index => 1 +=> [{:uid=>1, :email=>"pupkin@mail.ru", :name=>"Vasya", :apple_count=>1}, {:uid=>3, :email=>"petya@mail.ru", :name=>"Petya", :apple_count=>3}] +Delete +>> b.delete 2 +=> 1 +>> # Update values +>> b.update_fields 1, [:apple_count, :add, 2 ] +=> 1 +>> b.select 1 +=> [{:uid=>1, :email=>"pupkin@mail.ru", :name=>"Vasya", :apple_count=>3}] +>> # It's possible to do several updates in a single atomic command +>> b.update_fields 3, [:apple_count, :add, 10], [:name, :set, "foobar"] +=> 1 +>> b.select 3 +=> [{:uid=>3, :email=>"petya@mail.ru", :name=>"foobar", :apple_count=>13}] +]]> +</programlisting> + </para> + </section> + +</chapter> + +<!-- +vim: tw=66 syntax=docbk +vim: spell spelllang=en_us +--> diff --git a/doc/user/data-model.xml b/doc/user/data-model.xml index 1e00090e359c9e107551225c55dedf5ec1c5e7e4..81ab0c789c1f0d9b0d1172ea93352f22a8d788aa 100644 --- a/doc/user/data-model.xml +++ b/doc/user/data-model.xml @@ -61,7 +61,7 @@ Found 1 tuple: [1] tarantool> insert into t0 values ('hello') - An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' + An error occurred: ER_ILLEGAL_PARAMS, 'Illegal parameters' tarantool> insert into t0 values (1, 'hello') Insert OK, 1 row affected tarantool> select * from t0 where k0=1 @@ -109,7 +109,7 @@ <para> Since not all NoSQL operations can be expressed in SQL, to gain complete access to data manipulation functionality one must use - a Perl, Python, Ruby or C connector. The client/server protocol + a <olink targetptr="connectors">Perl, Python, Ruby or C connector</olink>. The client/server protocol is open and documented: an annotated BNF can be found in the source tree, file <filename xlink:href="https://github.com/mailru/tarantool/blob/master/doc/box-protocol.txt" xlink:title="A complete BNF of Tarantool client/server protocol">doc/protocol.txt</filename>. diff --git a/doc/user/errcode.xml b/doc/user/errcode.xml index 5d23b526e4a53b29d8bf2a5ffbbe068229daafcc..69cdee6393a856ffe8b6058dc9072b81eb0fe775 100644 --- a/doc/user/errcode.xml +++ b/doc/user/errcode.xml @@ -9,61 +9,61 @@ which is normally more descriptive than error code, is not present in server response. The actual message may contain a file name, a detailed reason or operating system error code. All such messages, however, are logged in the error log. When -using memcached protocol, the error message is sent to the +using <emphasis role="strong">Memcached</emphasis> protocol, the error message is sent to the client along with the code. Below follow only general descriptions of existing codes.</para> <variablelist> <title>List of error codes</title> <varlistentry> - <term xml:id="ERR_CODE_NONMASTER" xreflabel="ERR_CODE_NONMASTER">ERR_CODE_NONMASTER</term> + <term xml:id="ER_NONMASTER" xreflabel="ER_NONMASTER">ER_NONMASTER</term> <listitem><para>Attempt to execute an update over the secondary (read-only) port or on a running replica. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_ILLEGAL_PARAMS" xreflabel="ERR_CODE_ILLEGAL_PARAMS">ERR_CODE_ILLEGAL_PARAMS</term> + <term xml:id="ER_ILLEGAL_PARAMS" xreflabel="ER_ILLEGAL_PARAMS">ER_ILLEGAL_PARAMS</term> <listitem><para>Illegal parameters. Malformed protocol message. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_NODE_IS_RO" xreflabel="ERR_CODE_NODE_IS_RO">ERR_CODE_NODE_IS_RO</term> + <term xml:id="ER_TUPLE_IS_RO" xreflabel="ER_TUPLE_IS_RO">ER_TUPLE_IS_RO</term> <listitem><para>A parallel update of a tuple is in progress. Try again. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_MEMORY_ISSUE" xreflabel="ERR_CODE_MEMORY_ISSUE">ERR_CODE_MEMORY_ISSUE</term> + <term xml:id="ER_MEMORY_ISSUE" xreflabel="ER_MEMORY_ISSUE">ER_MEMORY_ISSUE</term> <listitem><para>Out of memory: <olink targetptr="slab_alloc_arena"/> limit is reached. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_WAL_IO" xreflabel="ERR_CODE_WAL_IO">ERR_CODE_WAL_IO</term> + <term xml:id="ER_WAL_IO" xreflabel="ER_WAL_IO">ER_WAL_IO</term> <listitem><para>Failed to record the change in the write ahead log. Some sort of disk error. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_INDEX_VIOLATION" xreflabel="ERR_CODE_INDEX_VIOLATION">ERR_CODE_INDEX_VIOLATION</term> + <term xml:id="ER_INDEX_VIOLATION" xreflabel="ER_INDEX_VIOLATION">ER_INDEX_VIOLATION</term> <listitem><para>A unique index constraint violation: a tuple with the same key is already present in the index. </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_NO_SUCH_NAMESPACE" xreflabel="ERR_CODE_NO_SUCH_NAMESPACE">ERR_CODE_NO_SUCH_NAMESPACE</term> + <term xml:id="ER_NO_SUCH_NAMESPACE" xreflabel="ER_NO_SUCH_NAMESPACE">ER_NO_SUCH_NAMESPACE</term> <listitem><para>Attempt to access a namespace that is not configured (doesn't exist). </para></listitem> </varlistentry> <varlistentry> - <term xml:id="ERR_CODE_NO_SUCH_INDEX" xreflabel="ERR_CODE_NO_SUCH_INDEX">ERR_CODE_NO_SUCH_INDEX</term> + <term xml:id="ER_NO_SUCH_INDEX" xreflabel="ER_NO_SUCH_INDEX">ER_NO_SUCH_INDEX</term> <listitem><para>No index with the given id exists. </para></listitem> </varlistentry> diff --git a/doc/user/html-highlight.xsl b/doc/user/html-highlight.xsl index 30943c01d88011c2519f83fe8f56c24fee1885d6..02cb8b611b8213911cc5707cdf7f92d3472e0588 100644 --- a/doc/user/html-highlight.xsl +++ b/doc/user/html-highlight.xsl @@ -25,7 +25,7 @@ xmlns:xslthl="http://xslthl.sf.net" xmlns="http://www.w3.org/1999/xhtml" exclude </xsl:template> <xsl:template match="xslthl:comment" mode="xslthl"> - <i class="hl-comment" style="color: blue"><xsl:apply-templates mode="xslthl"/></i> + <i class="hl-comment" style="color: silver"><xsl:apply-templates mode="xslthl"/></i> </xsl:template> <xsl:template match="xslthl:directive" mode="xslthl"> diff --git a/doc/user/language-reference.xml b/doc/user/language-reference.xml index 2115197c7dd927d0983d9dc8c48187f72dc14455..5163f54e41c8a9898efba6480d849b58c55f85aa 100644 --- a/doc/user/language-reference.xml +++ b/doc/user/language-reference.xml @@ -16,96 +16,303 @@ Unlike many other key/value servers, Tarantool uses different TCP ports and client/server protocols for data manipulation and administrative statements. - On start up, the server connects to three TCP ports: + On start up, the server can connect to up to five TCP ports: <itemizedlist> <listitem><para> Read/write data port, to handle INSERTs, UPDATEs, - DELETEs and SELECTs. + DELETEs and SELECTs. This port speaks the native + Tarantool protocol, and provides full access to the server. </para> <para> The default value of the port is <literal>33013</literal>, - as defined in <emphasis>primary_port</emphasis> + as defined in <olink targetptr="primary_port"/> configuration option. </para></listitem> <listitem><para> Read only port, which only accepts SELECTs, port number <literal>33014</literal>, as defined in - <emphasis>secondary_port</emphasis> configuration option. + <olink targetptr="secondary_port"/> configuration option. </para></listitem> <listitem><para> Administrative port, which defaults to <literal>33015</literal>, - and is defined in <emphasis>admin_port</emphasis> + and is defined in <olink targetptr="admin_port"/> configuration option. </para></listitem> + <listitem><para>Replication port (see <olink + targetptr="replication_port"/>), by default set to + <literal>33016</literal>, used to send updates to + replicas. Replication is optional, and if this port is not + set in the option file, the corresponding server process + is not started. + </para></listitem> + <listitem><para>Memcached port. Optional, read-write data port + that speaks Memcached protocol. This port is off by default. + </para></listitem> </itemizedlist> - - The data protocol is binary; a complete description is - provided in doc/box-protocol.txt. - -</para> -<para> - separation of ports is done in part to allow system - administrators to secure the read/write port or the - administrative port. The values of read-only, read-write and - administrative ports can be specified in the configuration file. -</para> -<para> - How command line client understands what port to use. + In absence of authentication, this approach allows system + administrators to restrict access to read/write or + administrative ports. The client, however, has to be aware of + the separation, and <command>tarantool</command> command line + client automatically selects the correct port for you + with help of a simple regular expression. SELECTs, UPDATEs, + INSERTs and DELETEs are sent to the primary port, whereas SHOW, + RELOAD, SAVE and others -- to the administrative port. </para> -<!-- <section> + <title>Data manipulation</title> - <title>Data console</title> - - <para>The default data port is 33014. It can be set in the - configuration file. Data console supports asynchronous I/O. - Four commands are supported. For a complete protocol manual, see - developers guide. + <para>Tarantool protocol was designed with focus on asynchronous + I/O and easy integration with proxies. Each client + request starts with a simple binary header, containing three + fields: request type, length, and a numeric id. + </para> + <para> + Four basic request types are supported: INSERT, UPDATE, DELETE + and SELECT. The mandatory length, present in the header, + allows to simplify client or proxy I/O. + The server response to a request always carries in its header + the same command type and request id. + The id makes it possible to always match request to a + response, even if the latter arrived out of order. + </para> + <para>Request type defines the format of the payload. + INSERTs and DELETEs can only be made by the primary key, so + an index id and key value are always present in these requests. + SELECTs and UPDATEs can use secondary keys. UPDATE only needs to + list the fields that are actually changed. With this one + exception, all commands operate on whole tuple(s). + </para> + <para>Unless implementing a client driver, one needn't + concern him or her self with the complications of the binary + protocol. <olink targetptr="connectors">Language-specific + drivers</olink> provide a friendly way to store domain + language data structures in Tarantool, and the command line + client supports a subset of the standard SQL. + A complete description of both, the binary protocol and + the supported SQL, is maintained in annotated Backus-Naur + form in the source tree: please see + <link xlink:href="https://github.com/mailru/tarantool/blob/master/doc/box-protocol.txt"><filename>doc/box-protocol.txt</filename></link> + and + <link xlink:href="https://github.com/mailru/tarantool/blob/master/doc/sql.txt"><filename>doc/sql.txt</filename></link> respectively. </para> + <section> + <title>Memcached protocol</title> + <para>If full access to Tarantool functionality is not needed, + or there is no readily available connector for the + programming language in use, any existing client driver for + Memcached will make do as a Tarantool connector. + To enable Memcached protocol, turn on + <olink targetptr="memcached_port"/> in the option file. + Since Memcached has no notion of namespaces or secondary + indexes, this port only makes it possible to access one + dedicated namespace (see <olink + targetptr="memcached_namespace"/>) via its primary key. + Unless tuple expiration is enabled with <olink + targetptr="memcached_expire"/>, TTL part of the message is + stored but ignored. + </para> + </section> +</section> +<section> + <title>Writing stored procedures in Lua</title> + <para> + Lua. + </para> </section> ---> <section> <title>Administrative console</title> <para> - The administrative console uses a simple text protocol, so you - can connect to it using any <command>telnet</command> client, - or a tool like <command>rlwrap</command>, if access to - readline features is desired. Additionally, Tarantool features - its own Python-based client, located in directory <filename>test</filename>, <command>tarantool</command>. - Tarantool replies to administrative command in YAML. + The administrative console uses a simple text protocol. + All commands are case-insensitive. + You can connect to the administrative port using any + <command>telnet</command> client, or a tool like + <command>rlwrap</command>, if access to readline features is + desired. Additionally, <command>tarantool</command>, the + SQL-capable command line client, + understands all administrative statements + and automatically directs them to the administrative port. + The server response to an administrative command, even though + is always in plain text, can be quite complex. + It is encoded using YAML markup to simplify automated parsing. </para> + <para>To learn about all supported administrative commands, you + can type <emphasis role="tntadmin">help</emphasis> in the + administrative console. A reference description also follows + below:</para> + + <variablelist> + + <varlistentry> + <term xml:id="save-snapshot" xreflabel="SAVE SNAPSHOT"> + <emphasis role="tntadmin">save snapshot</emphasis> + </term> + <listitem><para> + Take a snapshot of all data and store it in + <filename><olink + targetptr="snap_dir"/>/<latest-lsn>.snap</filename>. + To take a snapshot, Tarantool forks and quickly + <function>munmap(2)</function>s all memory except one that + stores tuples. Since all modern operating systems support + virtual memory copy-on-write, this effectively creates a + consistent snapshot of all tuples in the child process, + which is then written to disk tuple by tuple. Since a + snapshot is written sequentially, you can expect a very + high write performance (averaging to 70MB/second on modern + disks), which means an average database instance gets + saved in a matter of minutes. Note, that as long as there + are any changes to the parent memory through concurrent + updates, there are going to be page splits, and therefore + you need to have some extra free memory to run this + command. 15%-30% of <olink targetptr="slab_alloc_arena"/> + is, on average, sufficient. + </para></listitem> + </varlistentry> + + + <varlistentry> + <term xml:id="reload-configuration" xreflabel="RELOAD CONFIGURATION"> + <emphasis role="tntadmin">reload configuration</emphasis> + </term> + <listitem><para> + Re-read the configuration file. If the file contains + changes to dynamic parameters, update the runtime + settings. If configuration syntax is incorrect, or a + read-only parameter is changed, produce an error and do + nothing. + </para></listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-configuration" xreflabel="SHOW CONFIGURATION"> + <emphasis role="tntadmin">show configuration</emphasis> + </term> + <listitem><para> + Show the current settings. Displays all settings, + including those that have default values and thus are not + necessarily present in the configuration file. + </para></listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-info" xreflabel="SHOW INFO"> + <emphasis role="tntadmin">show info</emphasis> + </term> + <listitem><para> +<programlisting> +tarantool> show info +--- +info: + version: "1.4.0-30-ge500b95" + uptime: 441524 + pid: 16180 + wal_writer_pid: 16182 + lsn: 15481913304 + recovery_lag: 0.000 + recovery_last_update: 1306964594.980 + status: primary +</programlisting> + </para> + <para> + <emphasis role="strong">recovery_lag</emphasis> holds the + difference (in seconds) between the current time on the + machine (wall clock time) and the time stamp of the last + applied record. In replication setup, this difference can + indicate the delay taking place before a change is + applied to a replica. + </para> + <para> + <emphasis role="strong">recovery_last_update</emphasis> is + the wall clock time of the last change recorded in the + write ahead log. + </para> + <para> + <emphasis role="strong">status</emphasis> is + either "primary" or "hot_standby/<hostname>". + </para> + + </listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-stat" xreflabel="SHOW STAT"> + <emphasis role="tntadmin">show stat</emphasis> + </term> + <listitem><para> +<programlisting> +tarantool> show stat +--- +statistics: + INSERT: { rps: 139 , total: 48207694 } + SELECT_LIMIT: { rps: 0 , total: 0 } + SELECT: { rps: 1246 , total: 388322317 } + UPDATE_FIELDS: { rps: 1874 , total: 743350520 } + DELETE: { rps: 147 , total: 48902544 } +</programlisting> + </para></listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-slab" xreflabel="SHOW SLAB"> + <emphasis role="tntadmin">show slab</emphasis> + </term> + <listitem> + <para> + Show the statistics of the slab allocator. The slab + allocator is the main allocator used to store tuples. + This can be used to monitor the total memory use and + memory fragmentation. + </para> + <para> + <emphasis role="strong">items_used</emphasis> contains + the % of <olink targetptr="slab_alloc_arena"/> already + used to store tuples. + </para> + <para> + <emphasis role="strong">arena_used</emphasis> contains + the % of <olink targetptr="slab_alloc_arena"/> that is + already distributed to the slab allocator. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-palloc" xreflabel="SHOW PALLOC"> + <emphasis role="tntadmin">show palloc</emphasis> + </term> + <listitem><para> + A pool allocator is used for temporary memory, when + serving client requests. Every fiber has an own + pool. Shows the current state of pools of all fibers. + </para></listitem> + </varlistentry> + + <varlistentry> + <term xml:id="save-coredump" xreflabel="SAVE COREDUMP"> + <emphasis role="tntadmin">SAVE COREDUMP</emphasis> + </term> + <listitem><para> + Fork and dump a core. Since Tarantool stores all tuples + in memory, it can take some time. Mainly useful for + debugging. + </para></listitem> + </varlistentry> + + <varlistentry> + <term xml:id="show-fiber" xreflabel="SHOW FIBER"> + <emphasis role="tntadmin">show fiber</emphasis> + </term> + <listitem><para> + Show all running fibers, with their stack. + Mainly useful for debugging. + </para></listitem> + </varlistentry> + + </variablelist> - <para><emphasis xml:id="reload-configuration" xreflabel="RELOAD CONFIGURATION" role="tntadmin">reload - configuration</emphasis> Re-read the configuration file. If the - file contains changes to dynamic parameters, update the runtime - settings. If configuration syntax is incorrect, or a read-only parameter is changed, produce an error and do nothing.</para> - <para><emphasis xml:id="show-configuration" xreflabel="SHOW CONFIGURATION" role="tntadmin">show configuration</emphasis> Show the current settings. Displays all - settings, including those that have default values and thus are - not necessarily present in the configuration file.</para> - - <para><emphasis xml:id="save-snapshot" xreflabel="SAVE SNAPSHOT" - role="tntadmin">save snaspshot</emphasis> - Take a snapshot of all data and store it in - <filename><olink targetptr="snap_dir"/>/<latest-lsn>.snap</filename>. - To take a snapshot, Tarantool forks and quickly munmap(2)s all - memory except one that stores tuples. Since all modern operating - systems support virtual memory copy-on-write, this effectively - creates a consistent snapshot of all tuples in the child - process, which is then written to disk tuple by tuple. Since a - snapshot is written - sequentially, you can expect a very high write performance - (averaging to 70MB/second on modern disks), which means an - average database instance gets saved in a matter of minutes. - Note, that as long as there are any changes to the parent - memory through concurrent updates, there are going to be page - splits, and therefore you need to have some extra free memory - to run this command. 15%-30% of <olink - targetptr="slab_alloc_arena"/> is, on average, sufficient. - </para> </section> </chapter> diff --git a/doc/user/namespace.xml b/doc/user/namespace.xml index 1310bea3104cb10ced4f5c416971772a2a37e5ea..781a800174d4db5828e319d9c3fb04ccc3b33159 100644 --- a/doc/user/namespace.xml +++ b/doc/user/namespace.xml @@ -1,5 +1,5 @@ <para xmlns="http://docbook.org/ns/docbook" version="5.0" - xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="namespace"> <bridgehead>Namespace settings explained</bridgehead> @@ -59,11 +59,31 @@ a minimal storage configuration looks like below: <programlisting language="c"> namespace[0].enabled = 1 namespace[0].index[0].type = HASH +namespace[0].index[0].unique = 1 namespace[0].index[0].key_field[0].fieldno = 0 namespace[0].index[0].key_field[0].type = NUM64 </programlisting> -Other namespace properties are set in the same way. -When defining a namespace, please be aware of these restrictions: +The parameters listed above are mandatory. Other namespace +properties are set in the same way. +An alternative syntax, mainly useful when defining large namespaces, exists: +<programlisting language="c"> +namespace[0] = { + enabled = 1, + index = [ + { + type = HASH, + key_field = [ + { + fieldno = 0, + type = NUM64 + } + ] + } + ] +} +</programlisting> +When defining a namespace, +please be aware of these restrictions: <itemizedlist> <listitem><simpara>at least one namespace must be configured,</simpara></listitem> @@ -73,7 +93,7 @@ When defining a namespace, please be aware of these restrictions: <listitem><simpara>"unique" property doesn't have a default, and must be set explicitly, </simpara></listitem> - <listitem><simpara>namespace configuration can not be changed + <listitem><simpara>namespace configuration can not be changed dynamically, currently you need to restart the server even to disable or enable a namespace, </simpara></listitem> diff --git a/doc/user/preface.xml b/doc/user/preface.xml index 34c7bd612fdca285a856eceacdf356713b31edcb..09b6362682ebb1e06a9afe1224ae526ec0396d9c 100644 --- a/doc/user/preface.xml +++ b/doc/user/preface.xml @@ -74,7 +74,8 @@ one of the leading Russian web content providers. At Mail.Ru, the sowtware serves the <quote>hottest</quote> data, such as online users and their sessions, online application - properties, the map between users and their serving shards. + properties, mapping between users and their serving shards, + and so on. </para> <para> @@ -136,11 +137,11 @@ </para> <para> <emphasis role="strong">Caution:</emphasis> To prevent spam, Launchpad - mailing list software silently drops all mail sent by - unregistered users. Registration also - allows you to report bug and create feature requests. + mailing list software silently drops all mail sent from + non-registered email addresses. Launchpad registration also + allows you to report bugs and create feature requests. You can always check whether or not your mail has been - delivered in the public mailing list archive, <link + delivered to the mailing list in the public list archive, <link xlink:href="https://lists.launchpad.net/tarantool-developers"/>. </para> </section> diff --git a/doc/user/proctitle.xml b/doc/user/proctitle.xml new file mode 100644 index 0000000000000000000000000000000000000000..23be03ca579c40af3de1405af5036f46f19011af --- /dev/null +++ b/doc/user/proctitle.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE book [ +<!ENTITY % tnt SYSTEM "../tnt.ent"> +%tnt; +]> +<appendix xmlns="http://docbook.org/ns/docbook" version="5.0" + xmlns:xlink="http://www.w3.org/1999/xlink" + xml:id="proctitle"> + +<title>Server process titles</title> + +<para> + Linux and FreeBSD operating systems allow a running process to + modify its title, which otherwise contains the program name. + Tarantool uses this feature to aid to needs of system + administration, such as figuring out what services are running + on a host, TCP/IP ports in use, et cetera. +</para> +<para> + Tarantool process title follows the following naming scheme: + <command><replaceable>program_name</replaceable>: <replaceable>role</replaceable>[@<olink targetptr="custom_proc_title"/>] [ports in use]</command> +</para> +<para> + <emphasis role="strong">program_name</emphasis> is typically + <command>tarantool_box</command>. The role can be one of the + following: + <itemizedlist> + <listitem><para> + <emphasis role="strong">primary</emphasis> -- the master node, + </para></listitem> + <listitem><para> + <emphasis role="strong">replica/<constant>IP</constant>:<constant>port</constant></emphasis> -- a replication node, + </para></listitem> + <listitem><para> + <emphasis role="strong">wal_writer</emphasis> -- a write + ahead log management process (always pairs up with the main + process, be it primary or replica). + </para></listitem> + <listitem><para> + <emphasis role="strong">replication_server </emphasis>-- + runs only if <olink targetptr="replication_port"/> is set, + accepts connections on this port and creates a + </para></listitem> + <listitem><para> + <emphasis role="strong">replication_relay </emphasis>-- a + process that servers a single replication connection. + </para></listitem> + </itemizedlist> + Possible port names are: <quote>pri</quote> for + <olink targetptr="primary_port"/>, <quote>sec</quote> for <olink + targetptr="secondary_port"/>, <quote>adm</quote> for <olink + targetptr="admin_port"/> and <quote>memcached</quote> for <olink + targetptr="memcached_port"/>. +</para> +<para> + For example: + <itemizedlist> + <listitem><para> + <command>tarantool_box: primary pri:50000 sec:50001 adm:50002</command> + </para></listitem> + <listitem><para> + <command>tarantool_box: primary@infobox pri:15013 sec:15523 adm:10012</command> + </para></listitem> + <listitem><para> + <command>tarantool_box: wal_writer</command> + </para></listitem> + </itemizedlist> +</para> +</appendix> + +<!-- +vim: tw=66 syntax=docbk +vim: spell spelllang=en_us +--> diff --git a/doc/user/replication.xml b/doc/user/replication.xml new file mode 100644 index 0000000000000000000000000000000000000000..ffa5a607dac466754e8a49724191fbe9821d2e6e --- /dev/null +++ b/doc/user/replication.xml @@ -0,0 +1,164 @@ +<!DOCTYPE book [ +<!ENTITY % tnt SYSTEM "../tnt.ent"> +%tnt; +]> +<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" + xmlns:xlink="http://www.w3.org/1999/xlink" + xml:id="replication"> + +<title>Replication</title> +<blockquote><para> + To set up replication, it's necessary to prepare the master, + configure a replica, and establish procedures for recovery from + a degraded state. +</para></blockquote> + +<section> + <title>Setting up the master</title> + <para> + To prepare the master for connections from replica, it's only + necessary to enable <olink targetptr="replication_port"/> in + the configuration file. An example configuration file can be + found in <link + xlink:href="https://github.com/mailru/tarantool/blob/master/test/box_replication/tarantool.cfg"><filename>test/box_replication/tarantool.cfg</filename></link>. A master with enabled replication_port can accept connections + from as many replicas as necessary on that port. Each replica + has its own replication state. + </para> +</section> +<section> + <title>Setting up a replica</title> + <para> + Replica gets all updates from the master by simply shipping + the write ahead log (WAL) and applying it. + Each record in the WAL has a log sequence number + (LSN), operation type (INSERT, UPDATE, DELETE) and a + timestamp. + </para> + <para> + For replication to work correctly, the latest LSN + on the replica must match or fall behind the latest LSN + on the master. If the replica has its own updates, + this leads to it getting out of sync, since + updates from the master having identical LSNs are + not applied. Indeed, if replication is on, Tarantool + does not accept updates, even on its <olink + targetptr="primary_port"/>. + </para> + <para> + Tarantool always requires a valid snapshot file to boot from: + it should be prepared with <olink targetptr="init-storage-option"/> option or copied from the master. + </para> + <para> + To start replication, configure <olink + targetptr="replication_source_port"/> and <olink + targetptr="replication_source_ipaddr"/>. + Other parameters can also be changed, but existing namespaces and + their primary keys on the replica must be identical to ones on the + master. + </para> + <para> + Once connected to the master, the replica requests all changes + that happened after the latest local LSN. It is therefore + necessary to keep WAL files on the master host as long as + there are replicas that haven't applied them yet. An example + configuration can be found in <link + xlink:href="https://github.com/mailru/tarantool/blob/master/test/box_replication/tarantool_slave.cfg"><filename>test/box_replication/tarantool_slave.cfg</filename></link>. + </para> + <para> + In absence of WALs, a replica can be "seeded" at any time + with a snapshot file, manually copied from the master. + </para> + <note><simpara> + Replication parameters are "dynamic", which allows the + replica to become a master and vice versa with help of + <olink targetptr="reload-configuration"/> statement. + </simpara></note> + +</section> +<section> + <title>Recovering from a degraded state</title> + <para> + "Degraded state" is a situation when the master becomes + unavailable -- either due to hardware or network failure, or a + programming bug. There is no reliable way for a replica to detect + that the master is gone for all, since sources of failure and + replication environments vary significantly. + </para> + <para> + A separate monitoring script (or scripts, if decision making + quorum is desirable) is necessary to detect a master failure. + Such script would typically try to update a tuple in an + auxiliary namespace on the master, and raise alarm if a + network or disk error persists longer than is acceptable. + </para> + <para> + When a master failure is detected, the following needs + to be done: + <itemizedlist> + <listitem> + <para> + First and foremost, make sure that the master does not + accepts updates. This is necessary to prevent the + situation when, should the master failure end up being + transient, some updates still go to the master, while + others already end up on the replica. + </para> + <para> + If the master is available, the easiest way to turn + on read-only mode is to turn Tarantool into a replica of + itself. This can be done by setting master's <olink + targetptr="replication_source_ipaddr"/> and <olink + targetptr="replication_source_port"/> to point to self. + </para> + <para> + If the master is not available, best bet is to log into + the machine and kill the server, or change the + machine's network configuration (DNS, IP address). + </para> + <para> + If the machine is not available, it's perhaps prudent + to power it off. + </para> + </listitem> + <listitem> + <para> + Record the replica's LSN, by issuing <olink + targetptr="show-info"/>. This LSN may prove useful if + there are updates on the master that never reached + the replica. + </para> + </listitem> + <listitem> + <para> + Propagate the replica to become a master. This is done + by setting <olink targetptr="replication_source_port"/> + on replica to <constant>0</constant>. + </para> + </listitem> + <listitem> + <para> + Change the application configuration to point to the new + master. This can be done either by changing the + application's internal routing table, or by setting up + old master's IP address on the new master's machine, or + using some other approach. + </para> + </listitem> + <listitem> + <para> + Recover the old master. If there are updates that didn't + make it to the new master, they have to be applied + manually. You can use <olink targetptr="cat-option"/> + option to read server logs. + </para> + </listitem> + </itemizedlist> + </para> +</section> + +</chapter> + +<!-- +vim: tw=66 syntax=docbk +vim: spell spelllang=en_us +--> diff --git a/doc/user/target.db b/doc/user/target.db index ff99ff1b803f520c7e643fc84b1546a807392282..96165a1666494db2645dc659e3f2ff60537752a2 100644 --- a/doc/user/target.db +++ b/doc/user/target.db @@ -1 +1 @@ -<div element="book" href="#tarantool-user-guide" number="" targetptr="tarantool-user-guide"><ttl>Tarantool/Box User Guide, version 1.4.1-31-g8a3bd2c</ttl><xreftext>Tarantool/Box User Guide, version 1.4.1-31-g8a3bd2c</xreftext><div element="chapter" href="#id502411" number="1"><ttl>Preface</ttl><xreftext>Chapter 1, <i>Preface</i></xreftext><div element="section" href="#preface" number="" targetptr="preface"><ttl>Tarantool/Box: an overview</ttl><xreftext>the section called “Tarantool/Box: an overviewâ€</xreftext></div><div element="section" href="#id502454" number=""><ttl>Conventions</ttl><xreftext>the section called “Conventionsâ€</xreftext></div><div element="section" href="#id502528" number=""><ttl>Reporting bugs</ttl><xreftext>the section called “Reporting bugsâ€</xreftext></div></div><div element="chapter" href="#id503666" number="2"><ttl>Getting started</ttl><xreftext>Chapter 2, <i>Getting started</i></xreftext></div><div element="chapter" href="#id504673" number="3"><ttl>Dynamic data model</ttl><xreftext>Chapter 3, <i>Dynamic data model</i></xreftext></div><div element="chapter" href="#language-reference" number="4" targetptr="language-reference"><ttl>Language reference</ttl><xreftext>Chapter 4, <i>Language reference</i></xreftext><div element="section" href="#id502876" number=""><ttl>Administrative console</ttl><xreftext>the section called “Administrative consoleâ€</xreftext><obj element="emphasis" href="#reload-configuration" number="" targetptr="reload-configuration"><ttl>???TITLE???</ttl><xreftext>RELOAD CONFIGURATION</xreftext></obj><obj element="emphasis" href="#show-configuration" number="" targetptr="show-configuration"><ttl>???TITLE???</ttl><xreftext>SHOW CONFIGURATION</xreftext></obj><obj element="emphasis" href="#save-snapshot" number="" targetptr="save-snapshot"><ttl>???TITLE???</ttl><xreftext>SAVE SNAPSHOT</xreftext></obj></div></div><div element="chapter" href="#configuration-reference" number="5" targetptr="configuration-reference"><ttl>Configuration reference</ttl><xreftext>Chapter 5, <i>Configuration reference</i></xreftext><div element="section" href="#id504902" number=""><ttl>Command line options</ttl><xreftext>the section called “Command line optionsâ€</xreftext><obj element="listitem" href="#help-option" number="" targetptr="help-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#version-option" number="" targetptr="version-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#config-option" number="" targetptr="config-option"><ttl>???TITLE???</ttl><xreftext/></obj></div><div element="section" href="#id504287" number=""><ttl>The option file</ttl><xreftext>the section called “The option fileâ€</xreftext><obj element="table" href="#id507149" number="5.1"><ttl>Basic parameters</ttl><xreftext>Table 5.1, “Basic parametersâ€</xreftext></obj><obj element="entry" href="#wal_dir" number="" targetptr="wal_dir"><ttl>???TITLE???</ttl><xreftext>wal_dir</xreftext></obj><obj element="entry" href="#snap_dir" number="" targetptr="snap_dir"><ttl>???TITLE???</ttl><xreftext>snap_dir</xreftext></obj><obj element="entry" href="#primary_port" number="" targetptr="primary_port"><ttl>???TITLE???</ttl><xreftext>primary_port</xreftext></obj><obj element="table" href="#id507511" number="5.2"><ttl>Configuring the storage</ttl><xreftext>Table 5.2, “Configuring the storageâ€</xreftext></obj><obj element="anchor" href="#slab_alloc_arena" number="" targetptr="slab_alloc_arena"><ttl>???TITLE???</ttl><xreftext>slab_alloc_arena</xreftext></obj><obj element="para" href="#namespace" number="" targetptr="namespace"><ttl>???TITLE???</ttl><xreftext>the section called “The option fileâ€</xreftext></obj><obj element="table" href="#id507782" number="5.3"><ttl>Binary logging and snapshots</ttl><xreftext>Table 5.3, “Binary logging and snapshotsâ€</xreftext></obj><obj element="table" href="#id508062" number="5.4"><ttl>Replication</ttl><xreftext>Table 5.4, “Replicationâ€</xreftext></obj><obj element="entry" href="#replication_source_port" number="" targetptr="replication_source_port"><ttl>???TITLE???</ttl><xreftext>replication_source_port</xreftext></obj><obj element="table" href="#id508228" number="5.5"><ttl>Logging</ttl><xreftext>Table 5.5, “Loggingâ€</xreftext></obj><obj element="table" href="#id508414" number="5.6"><ttl>Memcached protocol support</ttl><xreftext>Table 5.6, “Memcached protocol supportâ€</xreftext></obj></div></div><div element="appendix" href="#errcode" number="A" targetptr="errcode"><ttl>List of error codes</ttl><xreftext>Appendix A, <i>List of error codes</i></xreftext><obj element="term" href="#ERR_CODE_NONMASTER" number="" targetptr="ERR_CODE_NONMASTER"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_NONMASTER</xreftext></obj><obj element="term" href="#ERR_CODE_ILLEGAL_PARAMS" number="" targetptr="ERR_CODE_ILLEGAL_PARAMS"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_ILLEGAL_PARAMS</xreftext></obj><obj element="term" href="#ERR_CODE_NODE_IS_RO" number="" targetptr="ERR_CODE_NODE_IS_RO"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_NODE_IS_RO</xreftext></obj><obj element="term" href="#ERR_CODE_MEMORY_ISSUE" number="" targetptr="ERR_CODE_MEMORY_ISSUE"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_MEMORY_ISSUE</xreftext></obj><obj element="term" href="#ERR_CODE_WAL_IO" number="" targetptr="ERR_CODE_WAL_IO"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_WAL_IO</xreftext></obj><obj element="term" href="#ERR_CODE_INDEX_VIOLATION" number="" targetptr="ERR_CODE_INDEX_VIOLATION"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_INDEX_VIOLATION</xreftext></obj><obj element="term" href="#ERR_CODE_NO_SUCH_NAMESPACE" number="" targetptr="ERR_CODE_NO_SUCH_NAMESPACE"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_NO_SUCH_NAMESPACE</xreftext></obj><obj element="term" href="#ERR_CODE_NO_SUCH_INDEX" number="" targetptr="ERR_CODE_NO_SUCH_INDEX"><ttl>???TITLE???</ttl><xreftext>ERR_CODE_NO_SUCH_INDEX</xreftext></obj></div></div> +<div element="book" href="#tarantool-user-guide" number="" targetptr="tarantool-user-guide"><ttl>Tarantool/Box User Guide, version 1.4.1-60-g1b45271</ttl><xreftext>Tarantool/Box User Guide, version 1.4.1-60-g1b45271</xreftext><div element="chapter" href="#id529141" number="1"><ttl>Preface</ttl><xreftext>Chapter 1, <i>Preface</i></xreftext><div element="section" href="#preface" number="" targetptr="preface"><ttl>Tarantool/Box: an overview</ttl><xreftext>the section called “Tarantool/Box: an overviewâ€</xreftext></div><div element="section" href="#id528112" number=""><ttl>Conventions</ttl><xreftext>the section called “Conventionsâ€</xreftext></div><div element="section" href="#id528186" number=""><ttl>Reporting bugs</ttl><xreftext>the section called “Reporting bugsâ€</xreftext></div></div><div element="chapter" href="#id530352" number="2"><ttl>Getting started</ttl><xreftext>Chapter 2, <i>Getting started</i></xreftext></div><div element="chapter" href="#id530367" number="3"><ttl>Dynamic data model</ttl><xreftext>Chapter 3, <i>Dynamic data model</i></xreftext></div><div element="chapter" href="#language-reference" number="4" targetptr="language-reference"><ttl>Language reference</ttl><xreftext>Chapter 4, <i>Language reference</i></xreftext><div element="section" href="#id528516" number=""><ttl>Data manipulation</ttl><xreftext>the section called “Data manipulationâ€</xreftext><div element="section" href="#id528567" number=""><ttl>Memcached protocol</ttl><xreftext>the section called “Memcached protocolâ€</xreftext></div></div><div element="section" href="#id528598" number=""><ttl>Writing stored procedures in Lua</ttl><xreftext>the section called “Writing stored procedures in Luaâ€</xreftext></div><div element="section" href="#id528609" number=""><ttl>Administrative console</ttl><xreftext>the section called “Administrative consoleâ€</xreftext><obj element="term" href="#save-snapshot" number="" targetptr="save-snapshot"><ttl>???TITLE???</ttl><xreftext>SAVE SNAPSHOT</xreftext></obj><obj element="term" href="#reload-configuration" number="" targetptr="reload-configuration"><ttl>???TITLE???</ttl><xreftext>RELOAD CONFIGURATION</xreftext></obj><obj element="term" href="#show-configuration" number="" targetptr="show-configuration"><ttl>???TITLE???</ttl><xreftext>SHOW CONFIGURATION</xreftext></obj><obj element="term" href="#show-info" number="" targetptr="show-info"><ttl>???TITLE???</ttl><xreftext>SHOW INFO</xreftext></obj><obj element="term" href="#show-stat" number="" targetptr="show-stat"><ttl>???TITLE???</ttl><xreftext>SHOW STAT</xreftext></obj><obj element="term" href="#show-slab" number="" targetptr="show-slab"><ttl>???TITLE???</ttl><xreftext>SHOW SLAB</xreftext></obj><obj element="term" href="#show-palloc" number="" targetptr="show-palloc"><ttl>???TITLE???</ttl><xreftext>SHOW PALLOC</xreftext></obj><obj element="term" href="#save-coredump" number="" targetptr="save-coredump"><ttl>???TITLE???</ttl><xreftext>SAVE COREDUMP</xreftext></obj><obj element="term" href="#show-fiber" number="" targetptr="show-fiber"><ttl>???TITLE???</ttl><xreftext>SHOW FIBER</xreftext></obj></div></div><div element="chapter" href="#replication" number="5" targetptr="replication"><ttl>Replication</ttl><xreftext>Chapter 5, <i>Replication</i></xreftext><div element="section" href="#id530956" number=""><ttl>Setting up the master</ttl><xreftext>the section called “Setting up the masterâ€</xreftext></div><div element="section" href="#id530980" number=""><ttl>Setting up a replica</ttl><xreftext>the section called “Setting up a replicaâ€</xreftext></div><div element="section" href="#id531067" number=""><ttl>Recovering from a degraded state</ttl><xreftext>the section called “Recovering from a degraded stateâ€</xreftext></div></div><div element="chapter" href="#configuration-reference" number="6" targetptr="configuration-reference"><ttl>Configuration reference</ttl><xreftext>Chapter 6, <i>Configuration reference</i></xreftext><div element="section" href="#id533750" number=""><ttl>Command line options</ttl><xreftext>the section called “Command line optionsâ€</xreftext><obj element="listitem" href="#help-option" number="" targetptr="help-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#version-option" number="" targetptr="version-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#config-option" number="" targetptr="config-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="option" href="#init-storage-option" number="" targetptr="init-storage-option"><ttl>???TITLE???</ttl><xreftext>--init-storage</xreftext></obj><obj element="option" href="#cat-option" number="" targetptr="cat-option"><ttl>???TITLE???</ttl><xreftext>--cat</xreftext></obj></div><div element="section" href="#option-file" number="" targetptr="option-file"><ttl>The option file</ttl><xreftext>option file</xreftext><obj element="table" href="#id533835" number="6.1"><ttl>Basic parameters</ttl><xreftext>Table 6.1, “Basic parametersâ€</xreftext></obj><obj element="entry" href="#wal_dir" number="" targetptr="wal_dir"><ttl>???TITLE???</ttl><xreftext>wal_dir</xreftext></obj><obj element="entry" href="#snap_dir" number="" targetptr="snap_dir"><ttl>???TITLE???</ttl><xreftext>snap_dir</xreftext></obj><obj element="entry" href="#primary_port" number="" targetptr="primary_port"><ttl>???TITLE???</ttl><xreftext>primary_port</xreftext></obj><obj element="entry" href="#secondary_port" number="" targetptr="secondary_port"><ttl>???TITLE???</ttl><xreftext>secondary_port</xreftext></obj><obj element="entry" href="#admin_port" number="" targetptr="admin_port"><ttl>???TITLE???</ttl><xreftext>admin_port</xreftext></obj><obj element="entry" href="#custom_proc_title" number="" targetptr="custom_proc_title"><ttl>???TITLE???</ttl><xreftext>custom_proc_title</xreftext></obj><obj element="table" href="#id534240" number="6.2"><ttl>Configuring the storage</ttl><xreftext>Table 6.2, “Configuring the storageâ€</xreftext></obj><obj element="anchor" href="#slab_alloc_arena" number="" targetptr="slab_alloc_arena"><ttl>???TITLE???</ttl><xreftext>slab_alloc_arena</xreftext></obj><obj element="para" href="#namespace" number="" targetptr="namespace"><ttl>???TITLE???</ttl><xreftext>the section called “The option fileâ€</xreftext></obj><obj element="table" href="#id534541" number="6.3"><ttl>Binary logging and snapshots</ttl><xreftext>Table 6.3, “Binary logging and snapshotsâ€</xreftext></obj><obj element="table" href="#id534803" number="6.4"><ttl>Replication</ttl><xreftext>Table 6.4, “Replicationâ€</xreftext></obj><obj element="entry" href="#replication_port" number="" targetptr="replication_port"><ttl>???TITLE???</ttl><xreftext>replication_port</xreftext></obj><obj element="entry" href="#replication_source_port" number="" targetptr="replication_source_port"><ttl>???TITLE???</ttl><xreftext>replication_source_port</xreftext></obj><obj element="entry" href="#replication_source_ipaddr" number="" targetptr="replication_source_ipaddr"><ttl>???TITLE???</ttl><xreftext>replication_source_ipaddr</xreftext></obj><obj element="table" href="#id534997" number="6.5"><ttl>Networking</ttl><xreftext>Table 6.5, “Networkingâ€</xreftext></obj><obj element="table" href="#id535153" number="6.6"><ttl>Logging</ttl><xreftext>Table 6.6, “Loggingâ€</xreftext></obj><obj element="table" href="#id535361" number="6.7"><ttl>Memcached protocol support</ttl><xreftext>Table 6.7, “Memcached protocol supportâ€</xreftext></obj><obj element="anchor" href="#memcached_port" number="" targetptr="memcached_port"><ttl>???TITLE???</ttl><xreftext>memcached_port</xreftext></obj><obj element="anchor" href="#memcached_namespace" number="" targetptr="memcached_namespace"><ttl>???TITLE???</ttl><xreftext>memcached_namespace</xreftext></obj><obj element="anchor" href="#memcached_expire" number="" targetptr="memcached_expire"><ttl>???TITLE???</ttl><xreftext>memcached_expire</xreftext></obj></div></div><div element="chapter" href="#connectors" number="7" targetptr="connectors"><ttl>Connectors</ttl><xreftext>Chapter 7, <i>Connectors</i></xreftext><div element="section" href="#id533584" number=""><ttl>C</ttl><xreftext>the section called “Câ€</xreftext></div><div element="section" href="#id530738" number=""><ttl>Perl</ttl><xreftext>the section called “Perlâ€</xreftext></div><div element="section" href="#id532206" number=""><ttl>PHP</ttl><xreftext>the section called “PHPâ€</xreftext></div><div element="section" href="#id532494" number=""><ttl>Python</ttl><xreftext>the section called “Pythonâ€</xreftext></div><div element="section" href="#id532505" number=""><ttl>Ruby</ttl><xreftext>the section called “Rubyâ€</xreftext><obj element="example" href="#id532546" number="7.1"><ttl>userbox.rb</ttl><xreftext>Example 7.1, “userbox.rbâ€</xreftext></obj></div></div><div element="appendix" href="#proctitle" number="A" targetptr="proctitle"><ttl>Server process titles</ttl><xreftext>Appendix A, <i>Server process titles</i></xreftext></div><div element="appendix" href="#errcode" number="B" targetptr="errcode"><ttl>List of error codes</ttl><xreftext>Appendix B, <i>List of error codes</i></xreftext><obj element="term" href="#ER_NONMASTER" number="" targetptr="ER_NONMASTER"><ttl>???TITLE???</ttl><xreftext>ER_NONMASTER</xreftext></obj><obj element="term" href="#ER_ILLEGAL_PARAMS" number="" targetptr="ER_ILLEGAL_PARAMS"><ttl>???TITLE???</ttl><xreftext>ER_ILLEGAL_PARAMS</xreftext></obj><obj element="term" href="#ER_TUPLE_IS_RO" number="" targetptr="ER_TUPLE_IS_RO"><ttl>???TITLE???</ttl><xreftext>ER_TUPLE_IS_RO</xreftext></obj><obj element="term" href="#ER_MEMORY_ISSUE" number="" targetptr="ER_MEMORY_ISSUE"><ttl>???TITLE???</ttl><xreftext>ER_MEMORY_ISSUE</xreftext></obj><obj element="term" href="#ER_WAL_IO" number="" targetptr="ER_WAL_IO"><ttl>???TITLE???</ttl><xreftext>ER_WAL_IO</xreftext></obj><obj element="term" href="#ER_INDEX_VIOLATION" number="" targetptr="ER_INDEX_VIOLATION"><ttl>???TITLE???</ttl><xreftext>ER_INDEX_VIOLATION</xreftext></obj><obj element="term" href="#ER_NO_SUCH_NAMESPACE" number="" targetptr="ER_NO_SUCH_NAMESPACE"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_NAMESPACE</xreftext></obj><obj element="term" href="#ER_NO_SUCH_INDEX" number="" targetptr="ER_NO_SUCH_INDEX"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_INDEX</xreftext></obj></div></div> diff --git a/doc/user/tnt-fo.xsl b/doc/user/tnt-fo.xsl new file mode 100644 index 0000000000000000000000000000000000000000..6952f3bfca3077cdead3a217cddc2168f961b70a --- /dev/null +++ b/doc/user/tnt-fo.xsl @@ -0,0 +1,14 @@ +<?xml version='1.0'?> +<xsl:stylesheet + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" + xmlns:xslthl="http://xslthl.sf.net"> + +<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/fo/docbook.xsl"/> + +<xsl:param name="generate.toc" select="'book toc'"/> +<xsl:param name="fop1.extentions">1</xsl:param> +<xsl:param name="paper.type">A4</xsl:param> +<xsl:param name="highlight.source" select="1"/> +<xsl:param name="highlight.xslthl.config">file:////usr/share/xml/docbook/stylesheet/docbook-xsl-ns/highlighting/xslthl-config.xml</xsl:param> + +</xsl:stylesheet> diff --git a/doc/user/tnt-html.xsl b/doc/user/tnt-html.xsl index dd3ce1cbb9482e2c0e1409b7bcb60d88b56add1e..e1edda21310f5920444787276722d153b1ee8a57 100644 --- a/doc/user/tnt-html.xsl +++ b/doc/user/tnt-html.xsl @@ -4,12 +4,29 @@ xmlns:xslthl="http://xslthl.sf.net"> <xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/html/docbook.xsl"/> -<xsl:import href="html-highlight.xsl"/> +<xsl:import href="html-highlight.xsl"/> <xsl:param name="generate.toc" select="'book toc'"/> <xsl:param name="html.stylesheet" select="'tnt.css'"/> <xsl:param name="highlight.source" select="1"/> <xsl:param name="highlight.xslthl.config">file:////usr/share/xml/docbook/stylesheet/docbook-xsl-ns/highlighting/xslthl-config.xml</xsl:param> +<!-- Add Google Analytics to the generated html--> + +<xsl:template name="user.head.content"> + <script type="text/javascript"> + + var _gaq = _gaq || []; + _gaq.push(['_setAccount', 'UA-22120502-1']); + _gaq.push(['_trackPageview']); + + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); + + </script> +</xsl:template> </xsl:stylesheet> diff --git a/doc/user/tutorial.xml b/doc/user/tutorial.xml index f38b015635e4c815999fab38393f52052cc8527b..201be01f5a66114f60cd95d3e02c35d6d6395067 100644 --- a/doc/user/tutorial.xml +++ b/doc/user/tutorial.xml @@ -41,7 +41,7 @@ <para> After download, unpack the binary package, an own directory will be created: <programlisting> - <prompt>$ </prompt><command>tar</command> <option>zxvf</option> <filename>package-name.tar.gz</filename></programlisting> + <prompt>$ </prompt><command>tar</command> <option>zxvf</option> <filename><replaceable><replaceable>package-name</replaceable></replaceable>.tar.gz</filename></programlisting> The binary download contains just two subdirectories: <filename>bin</filename> and <filename>doc</filename>. The server, by default, looks for its configuration file in @@ -50,7 +50,7 @@ <filename>bin</filename>, thus the server can be started right out of it: <programlisting> - <prompt>$ </prompt><command>cd package-name/bin && ./tarantool_box</command> + <prompt>$ </prompt><command>cd <replaceable>package-name</replaceable>/bin && ./tarantool_box</command> ... 1301424353.416 3459 104/33013/acceptor _ I> I am primary 1301424353.416 3459 1/sched _ I> initialized</programlisting> @@ -61,7 +61,7 @@ Once the server is started, you can connect to it and issue queries using a command line client implemented in Python: <programlisting> - <prompt>$ </prompt><command>cd package-name/bin && ./tarantool</command> + <prompt>$ </prompt><command>cd <replaceable>package-name</replaceable>/bin && ./tarantool</command> <prompt>tarantool> </prompt> <userinput>show info</userinput> <computeroutput> --- @@ -82,7 +82,7 @@ necessary: configuration and build. The easiest way to configure a source directory with CMake is by requesting an in-source build: <programlisting> - <prompt>$ </prompt><command>cd</command> <filename>package-name</filename> && <command>cmake</command> .</programlisting> + <prompt>$ </prompt><command>cd</command> <filename><replaceable>package-name</replaceable></filename> && <command>cmake</command> .</programlisting> Upon successful configuration, CMake prints the status of optional features: <programlisting> diff --git a/doc/user/user.xml b/doc/user/user.xml index 02e677b59e01e1931acb5159065c31f5354aef3c..2ae51a5595c943134b669b9915ee4ff5a08947aa 100644 --- a/doc/user/user.xml +++ b/doc/user/user.xml @@ -12,64 +12,13 @@ <xi:include href="tutorial.xml"/> <xi:include href="data-model.xml"/> <xi:include href="language-reference.xml"/> -<xi:include href="configuration-reference.xml"/> -<!-- <xi:include href="replication.xml"/> -<xi:include href="memcached.xml"/> -<xi:include href="configuration.xml"/> -<xi:include href="logging.xml"/> +<xi:include href="configuration-reference.xml"/> <xi:include href="connectors.xml"/> -<xi:include href="faq.xml"/> ---> +<xi:include href="proctitle.xml"/> <xi:include href="errcode.xml"/> <!-- - TOC: - - Introduction - What it can basically do; - - Installation and Running - Command line options reference - - Data console and administrative console - Data operations - Administrative console operations - Format of response - - Setting up replication - Master-slave replication - - Support of memcached protocol - - Configuration reference - - Log file messages - - Format of the binary log/snapshot - - Tarantool command line shell - - Connectors - C - Ruby - Example - Perl - - Product release cycle - - Frequently Asked Questions - Q. What's on your roadmap? - Q. I found a bug. What to do? - What can possibly go wrong during compile: - as exception - old python - - confetti not found - explain what is going on - gcc compilation error: type 'VERBOSE=1 make' and - check out the exact error. - - Appendixes: - Configuration parameters index - Server error codes - Glossary of terms +<xi:include href="faq.xml"/> --> </book> <!-- diff --git a/include/errcode.h b/include/errcode.h index ba7ad4e1e750e48606e7c040a8391caa2075f947..d1944d81d5395f9910441d9ee85c8a6375367c62 100644 --- a/include/errcode.h +++ b/include/errcode.h @@ -11,94 +11,91 @@ struct errcode_record { uint8_t errflags; }; +enum { TNT_ERRMSG_MAX = 512 }; + /* * To add a new error code to Tarantool, extend this array. Please - * try to reuse empty slots (ERR_CODE_UNUSED*), if there are any - * left. + * try to reuse empty slots (ER_UNUSED*), if there are any left. * * !IMPORTANT! Currently you need to manually update the user - * guide (doc/user/errcode.xml) and the Python command line client - * (test/lib/sql_ast.py) with each added error code. Please don't - * forget to do it! + * guide (doc/user/errcode.xml) with each added error code. + * Please don't forget to do it! */ #define ERROR_CODES(_) \ - /* 0 */_(ERR_CODE_OK, 0, "OK") \ - /* 1 */_(ERR_CODE_NONMASTER, 2, "Non master connection, but it should be") \ - /* 2 */_(ERR_CODE_ILLEGAL_PARAMS, 2, "Illegal parameters") \ - /* 3 */_(ERR_CODE_BAD_UID, 2, "Uid is not from this storage range") \ - /* 4 */_(ERR_CODE_NODE_IS_RO, 1, "Node is marked as read-only") \ - /* 5 */_(ERR_CODE_NODE_IS_NOT_LOCKED, 1, "Node isn't locked") \ - /* 6 */_(ERR_CODE_NODE_IS_LOCKED, 1, "Node is locked") \ - /* 7 */_(ERR_CODE_MEMORY_ISSUE, 1, "Some memory issue") \ - /* 8 */_(ERR_CODE_BAD_INTEGRITY, 2, "Bad graph integrity") \ - /* 9 */_(ERR_CODE_UNUSED9, 0, "Unused9") \ - /* 10 */_(ERR_CODE_UNSUPPORTED_COMMAND, 2, "Unsupported command") \ + /* 0 */_(ER_OK, 0, "OK") \ + /* 1 */_(ER_NONMASTER, 2, "Non master connection, but it should be") \ + /* 2 */_(ER_ILLEGAL_PARAMS, 2, "Illegal parameters, %s") \ + /* 3 */_(ER_BAD_UID, 2, "Uid is not from this storage range") \ + /* 4 */_(ER_TUPLE_IS_RO, 1, "Tuple is marked as read-only") \ + /* 5 */_(ER_TUPLE_IS_NOT_LOCKED, 1, "Tuple isn't locked") \ + /* 6 */_(ER_TUPLE_IS_LOCKED, 1, "Tuple is locked") \ + /* 7 */_(ER_MEMORY_ISSUE, 1, "Failed to allocate %u bytes in %s for %s") \ + /* 8 */_(ER_BAD_INTEGRITY, 2, "Bad graph integrity") \ + /* 9 */_(ER_UNUSED9, 0, "Unused9") \ + /* 10 */_(ER_UNSUPPORTED_COMMAND, 2, "Unsupported command") \ /* silverproxy error codes */ \ - /* 11 */_(ERR_CODE_RESERVED11, 0, "Reserved11") \ - /* 12 */_(ERR_CODE_RESERVED12, 0, "Reserved12") \ - /* 13 */_(ERR_CODE_RESERVED13, 0, "Reserved13") \ - /* 14 */_(ERR_CODE_RESERVED14, 0, "Reserved14") \ - /* 15 */_(ERR_CODE_RESERVED15, 0, "Reserved15") \ - /* 16 */_(ERR_CODE_RESERVED16, 0, "Reserved16") \ - /* 17 */_(ERR_CODE_RESERVED17, 0, "Reserved17") \ - /* 18 */_(ERR_CODE_RESERVED18, 0, "Reserved18") \ - /* 19 */_(ERR_CODE_RESERVED19, 0, "Reserved19") \ - /* 20 */_(ERR_CODE_RESERVED20, 0, "Reserved20") \ - /* 21 */_(ERR_CODE_RESERVED21, 0, "Reserved21") \ - /* 22 */_(ERR_CODE_RESERVED22, 0, "Reserved22") \ - /* 23 */_(ERR_CODE_RESERVED23, 0, "Reserved23") \ + /* 11 */_(ER_RESERVED11, 0, "Reserved11") \ + /* 12 */_(ER_RESERVED12, 0, "Reserved12") \ + /* 13 */_(ER_RESERVED13, 0, "Reserved13") \ + /* 14 */_(ER_RESERVED14, 0, "Reserved14") \ + /* 15 */_(ER_RESERVED15, 0, "Reserved15") \ + /* 16 */_(ER_RESERVED16, 0, "Reserved16") \ + /* 17 */_(ER_RESERVED17, 0, "Reserved17") \ + /* 18 */_(ER_RESERVED18, 0, "Reserved18") \ + /* 19 */_(ER_RESERVED19, 0, "Reserved19") \ + /* 20 */_(ER_RESERVED20, 0, "Reserved20") \ + /* 21 */_(ER_RESERVED21, 0, "Reserved21") \ + /* 22 */_(ER_RESERVED22, 0, "Reserved22") \ + /* 23 */_(ER_RESERVED23, 0, "Reserved23") \ /* end of silverproxy error codes */ \ - /* 24 */_(ERR_CODE_CANNOT_REGISTER, 1, "Can't register new user") \ - /* 25 */_(ERR_CODE_UNUSED25, 0, "Unused25") \ - /* 26 */_(ERR_CODE_CANNOT_INIT_ALERT_ID, 1, "Can't generate alert id") \ - /* 27 */_(ERR_CODE_CANNOT_DEL, 2, "Can't del node") \ - /* 28 */_(ERR_CODE_USER_NOT_REGISTERED, 2, "User isn't registered") \ + /* 24 */_(ER_CANNOT_REGISTER, 1, "Can't register new user") \ + /* 25 */_(ER_UNUSED25, 0, "Unused25") \ + /* 26 */_(ER_CANNOT_INIT_ALERT_ID, 1, "Can't generate alert id") \ + /* 27 */_(ER_CANNOT_DEL, 2, "Can't del node") \ + /* 28 */_(ER_USER_NOT_REGISTERED, 2, "User isn't registered") \ /* silversearch error codes */ \ - /* 29 */_(ERR_CODE_SYNTAX_ERROR, 2, "Syntax error in query") \ - /* 30 */_(ERR_CODE_WRONG_FIELD, 2, "Unknown field") \ - /* 31 */_(ERR_CODE_WRONG_NUMBER, 2, "Number value is out of range") \ - /* 32 */_(ERR_CODE_DUPLICATE, 2, "Insert already existing object") \ - /* 33 */_(ERR_CODE_UNUSED32, 0, "Unused33") \ - /* 34 */_(ERR_CODE_UNSUPPORTED_ORDER, 2, "Can not order result") \ - /* 35 */_(ERR_CODE_MULTIWRITE, 2, "Multiple to update/delete") \ - /* 36 */_(ERR_CODE_NOTHING, 0, "Nothing to do (not an error)") \ - /* 37 */_(ERR_CODE_UPDATE_ID, 2, "Id's update") \ - /* 38 */_(ERR_CODE_WRONG_VERSION, 2, "Unsupported version of protocol") \ + /* 29 */_(ER_SYNTAX_ERROR, 2, "Syntax error in query") \ + /* 30 */_(ER_WRONG_FIELD, 2, "Unknown field") \ + /* 31 */_(ER_WRONG_NUMBER, 2, "Number value is out of range") \ + /* 32 */_(ER_DUPLICATE, 2, "Insert already existing object") \ + /* 33 */_(ER_UNUSED32, 0, "Unused33") \ + /* 34 */_(ER_UNSUPPORTED_ORDER, 2, "Can not order result") \ + /* 35 */_(ER_MULTIWRITE, 2, "Multiple to update/delete") \ + /* 36 */_(ER_NOTHING, 0, "Nothing to do (not an error)") \ + /* 37 */_(ER_UPDATE_ID, 2, "Id's update") \ + /* 38 */_(ER_WRONG_VERSION, 2, "Unsupported version of protocol") \ /* end of silversearch error codes */ \ - /* 39 */_(ERR_CODE_WAL_IO, 2, "Failed to write to disk") \ - /* 40 */_(ERR_CODE_UNUSED40, 0, "Unused40") \ - /* 41 */_(ERR_CODE_UNUSED41, 0, "Unused41") \ - /* 42 */_(ERR_CODE_UNUSED42, 0, "Unused42") \ - /* 43 */_(ERR_CODE_UNUSED43, 0, "Unused43") \ - /* 44 */_(ERR_CODE_UNUSED44, 0, "Unused44") \ - /* 45 */_(ERR_CODE_UNUSED45, 0, "Unused45") \ - /* 46 */_(ERR_CODE_UNUSED46, 0, "Unused46") \ - /* 47 */_(ERR_CODE_UNUSED47, 0, "Unused47") \ - /* 48 */_(ERR_CODE_UNUSED48, 0, "Unused48") \ - /* 49 */_(ERR_CODE_NODE_NOT_FOUND, 2, "Node isn't found") \ - /* 50 */_(ERR_CODE_UNUSED50, 0, "Unused50") \ - /* 51 */_(ERR_CODE_UNUSED51, 0, "Unused51") \ - /* 52 */_(ERR_CODE_UNUSED52, 0, "Unused52") \ - /* 53 */_(ERR_CODE_UNUSED53, 0, "Unused53") \ - /* 54 */_(ERR_CODE_UNUSED54, 0, "Unused54") \ - /* 55 */_(ERR_CODE_NODE_FOUND, 2, "Node is found") \ - /* 56 */_(ERR_CODE_INDEX_VIOLATION, 2, "Constraint violation") \ - /* 57 */_(ERR_CODE_NO_SUCH_NAMESPACE, 2, "There is no such namespace") \ - _(ERR_CODE_NO_SUCH_INDEX, 2, "No index with the given id is defined") + /* 39 */_(ER_WAL_IO, 2, "Failed to write to disk") \ + /* 40 */_(ER_UNUSED40, 0, "Unused40") \ + /* 41 */_(ER_UNUSED41, 0, "Unused41") \ + /* 42 */_(ER_UNUSED42, 0, "Unused42") \ + /* 43 */_(ER_UNUSED43, 0, "Unused43") \ + /* 44 */_(ER_UNUSED44, 0, "Unused44") \ + /* 45 */_(ER_UNUSED45, 0, "Unused45") \ + /* 46 */_(ER_UNUSED46, 0, "Unused46") \ + /* 47 */_(ER_UNUSED47, 0, "Unused47") \ + /* 48 */_(ER_UNUSED48, 0, "Unused48") \ + /* 49 */_(ER_TUPLE_NOT_FOUND, 2, "Tuple doesn't exist") \ + /* 50 */_(ER_UNUSED50, 0, "Unused50") \ + /* 51 */_(ER_UNUSED51, 0, "Unused51") \ + /* 52 */_(ER_NAMESPACE_DISABLED, 2, "Namespace %u is disabled") \ + /* 53 */_(ER_NO_SUCH_INDEX, 2, "No index #%u is defined in namespace %u") \ + /* 54 */_(ER_NO_SUCH_FIELD, 2, "Field %u was not found in the tuple") \ + /* 55 */_(ER_TUPLE_FOUND, 2, "Tuple already exists") \ + /* 56 */_(ER_INDEX_VIOLATION, 2, "Duplicate key exists in a unique index") \ + /* 57 */_(ER_NO_SUCH_NAMESPACE, 2, "Namespace %u does not exists") /* - * !IMPORTANT! Please see an instruction how to add !IMPORTANT! - * !IMPORTANT! new errors at start of the file !IMPORTANT! + * !IMPORTANT! Please follow instructions at start of the file + * when adding new errors. */ ENUM0(tnt_error_codes_enum, ERROR_CODES); extern struct errcode_record tnt_error_codes[]; -/** - * Return a string representation of error name, e.g. - * "ERR_CODE_OK". +/** Return a string representation of error name, e.g. "ER_OK". */ static inline const char *tnt_errcode_str(uint32_t errcode) diff --git a/include/exception.h b/include/exception.h new file mode 100644 index 0000000000000000000000000000000000000000..0cf698ed86cfd69c4c27ba258b7e28eb8eca51ba --- /dev/null +++ b/include/exception.h @@ -0,0 +1,96 @@ +#ifndef TARANTOOL_EXCEPTION_H_INCLUDED +#define TARANTOOL_EXCEPTION_H_INCLUDED +/* + * 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. + */ +#import <objc/Object.h> +#include <stdarg.h> +#include "errcode.h" + +/** The base class for all exceptions. + * + * Note: implements garbage collection (see +alloc + * implementation). + */ +@interface tnt_Exception: Object { + @public + const char *file; + unsigned line; +} + ++ (id) alloc; +@end + + +/** Errors that should make it to the client. + */ +@interface ClientError: tnt_Exception { + @public + uint32_t errcode; + char errmsg[TNT_ERRMSG_MAX]; +} + +- (id) init: (uint32_t)errcode_, ...; +- (id) init: (uint32_t)errcode_ args: (va_list)ap; +@end + + +/** Additionally log this error in the log file. */ +@interface LoggedError: ClientError +- (id) init: (uint32_t)errcode, ...; +@end + + +/** A handy wrapper for ER_ILLEGAL_PARAMS, which is used very + * often. + */ +@interface IllegalParams: LoggedError +- (id) init: (const char *)msg; +@end + + +/** + * A helper macro to add __FILE__ and __LINE__ information to + * raised exceptions. + * + * Usage: + * + * tnt_raise(tnt_Exception); + * tnt_raise(LoggedError, :"invalid argument %d", argno); + */ +#define tnt_raise(...) tnt_raise0(__VA_ARGS__) +#define tnt_raise0(class, ...) do { \ + say_debug("%s at %s:%i", #class, __FILE__, __LINE__); \ + class *exception = [class alloc]; \ + exception->file = __FILE__; \ + exception->line = __LINE__; \ + [exception init __VA_ARGS__]; \ + @throw exception; \ +} while (0) + +#endif /* TARANTOOL_EXCEPTION_H_INCLUDED */ diff --git a/include/exceptions.h b/include/exceptions.h deleted file mode 100644 index f78206c65d14d836156612270f8a18cc0885a249..0000000000000000000000000000000000000000 --- a/include/exceptions.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TARANTOOL_EXCEPTIONS_H -#define TARANTOOL_EXCEPTIONS_H - -#include <objc/Object.h> - -@interface tnt_Exception: Object { - @public - const char *file; - unsigned line; - const char *reason; -} - -+ alloc; - -- init:(const char *)p_file:(unsigned)p_line reason:(const char*)p_reason; -- init:(const char *)p_file:(unsigned)p_line; -@end - -#define tnt_raise(class, message) { \ - say_debug("tnt_raise %s at %s:%i", #class, __FILE__, __LINE__); \ - @throw [[class alloc] init:__FILE__:__LINE__ message]; \ -} - -#endif diff --git a/include/fiber.h b/include/fiber.h index cc84c51b9314eee065a6ebb676b222f86f906f6f..4a11eb4e988b1fe7d433bbc8c2d7dab492dfd44c 100644 --- a/include/fiber.h +++ b/include/fiber.h @@ -40,14 +40,21 @@ #include <util.h> #include "third_party/queue.h" -#include <exceptions.h> +#include "exception.h" #define FIBER_NAME_MAXLEN 32 #define FIBER_READING_INBOX 0x1 -#define FIBER_RAISE 0x2 +/** Can this fiber be cancelled? */ +#define FIBER_CANCELLABLE 0x2 +/** Indicates that a fiber has been cancelled. */ +#define FIBER_CANCEL 0x4 -@interface tnt_FiberException: tnt_Exception +/** This is thrown by fiber_* API calls when the fiber is + * cancelled. + */ + +@interface FiberCancelException: tnt_Exception @end struct msg { @@ -62,6 +69,7 @@ struct ring { struct fiber { ev_io io; + ev_async async; #ifdef ENABLE_BACKTRACE void *last_stack_frame; #endif @@ -96,6 +104,8 @@ struct fiber { char peer_name[32]; u32 flags; + + struct fiber *waiter; }; SLIST_HEAD(, fiber) fibers, zombie_fibers; @@ -118,9 +128,7 @@ extern struct fiber *fiber; void fiber_init(void); struct fiber *fiber_create(const char *name, int fd, int inbox_size, void (*f) (void *), void *); void fiber_set_name(struct fiber *fiber, const char *name); -void wait_for(int events); void wait_for_child(pid_t pid); -void unwait(int events); void yield(void); void fiber_destroy_all(); @@ -149,7 +157,7 @@ inline static void add_iov(const void *buf, size_t len) add_iov_unsafe(buf, len); } -void add_iov_dup(void *buf, size_t len); +void add_iov_dup(const void *buf, size_t len); bool write_inbox(struct fiber *recipient, struct tbuf *msg); int inbox_size(struct fiber *recipient); void wait_inbox(struct fiber *recipient); @@ -162,7 +170,22 @@ ssize_t fiber_flush_output(void); void fiber_cleanup(void); void fiber_gc(void); void fiber_call(struct fiber *callee); -void fiber_raise(struct fiber *callee); +void fiber_wakeup(struct fiber *f); +/** Cancel a fiber. A cancelled fiber will have + * tnt_FiberCancelException raised in it. + * + * A fiber can be cancelled only if it is + * FIBER_CANCELLABLE flag is set. + */ +void fiber_cancel(struct fiber *f); +/** Check if the current fiber has been cancelled. Raises + * tnt_FiberCancelException + */ +void fiber_testcancel(void); +/** Make it possible or not possible to cancel the current + * fiber. + */ +void fiber_setcancelstate(bool enable); int fiber_connect(struct sockaddr_in *addr); void fiber_sleep(ev_tstamp s); void fiber_info(struct tbuf *out); @@ -170,10 +193,12 @@ int set_nonblock(int sock); typedef enum fiber_server_type { tcp_server, - udp_server } fiber_server_type; -struct fiber *fiber_server(fiber_server_type type, int port, void (*handler) (void *), void *, +typedef void (*fiber_server_callback)(void *); + +struct fiber *fiber_server(fiber_server_type type, int port, + fiber_server_callback callback, void *, void (*on_bind) (void *)); /** diff --git a/include/iproto.h b/include/iproto.h index d6f794cc56ae61911d60dc42c344a7e1748cc5e7..34d7fa69f9a1285991a8efed45ca55ee9e34a4b6 100644 --- a/include/iproto.h +++ b/include/iproto.h @@ -44,7 +44,6 @@ struct iproto_header_retcode { uint32_t len; uint32_t sync; uint32_t ret_code; - uint8_t data[]; } __attribute__((packed)); static inline struct iproto_header *iproto(const struct tbuf *t) @@ -52,11 +51,9 @@ static inline struct iproto_header *iproto(const struct tbuf *t) return (struct iproto_header *)t->data; } -struct iproto_interactor; -struct iproto_interactor -*iproto_interactor(uint32_t (*interact) (uint32_t msg, uint8_t *data, size_t len)); +typedef void (*iproto_callback) (uint32_t msg_code, struct tbuf *request); -void iproto_interact(void *); +void iproto_interact(iproto_callback *callback); #endif diff --git a/core/diagnostics.m b/include/latch.h similarity index 50% rename from core/diagnostics.m rename to include/latch.h index 3266ae52a02a94a948749d66f4f810d816ccd2b8..fc723045c57730fa2e2b83cadbcc7427156fbf57 100644 --- a/core/diagnostics.m +++ b/include/latch.h @@ -1,3 +1,5 @@ +#ifndef TARANTOOL_LATCH_H_INCLUDED +#define TARANTOOL_LATCH_H_INCLUDED /* * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following @@ -20,73 +22,45 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include "diagnostics.h" -#include "fiber.h" -#include <errno.h> -#include <string.h> -#include <stdlib.h> +#include <stdbool.h> -static struct Error oom_error = { ENOMEM, "Out of memory" }; +struct fiber; -/** - * Allocate error on heap. Errors are expected to be rare and - * small, thus we don't care much about allocation speed, and - * memory fragmentation should be negligible. +/* + * Internal implementation of a container for a mutex like object + * with similar interface. It's used boolean variable because of + * single threaded nature of tarantool. But it's rather simple to change + * this variable to a mutex object to maintain multi threaded approach. */ +struct tnt_latch { + bool locked; -static struct Error *error_create(int code, const char *msg_arg) -{ - /* Just something large enough. */ - const int MAX_MSGLEN = 200; - - if (msg_arg == NULL) - msg_arg = ""; - - size_t msglen = strlen(msg_arg); - char *msg; - - if (msglen > MAX_MSGLEN) - msglen = MAX_MSGLEN; - - struct Error *error = malloc(sizeof(struct Error) + msglen + 1); - - if (error == NULL) - return &oom_error; - - msg = (char *)(error + 1); - strncpy(msg, msg_arg, msglen); - msg[msglen] = '\0'; - - error->code = code; - error->msg = msg; - - return error; -} - - -static void error_destroy(struct Error *error) -{ - if (error != &oom_error) - free(error); -} - + struct fiber *owner; +}; -void diag_set_error(int code, const char *message) -{ - if (fiber->diagnostics) - diag_clear(); - fiber->diagnostics = error_create(code, message); -} - - -struct Error *diag_get_last_error() -{ - return fiber->diagnostics; -} +/** + * Initialize the given latch. + * + * @param latch Latch to be initialized. + */ +void tnt_latch_init(struct tnt_latch *latch); +/** + * Destroy the given latch. + */ +void tnt_latch_destroy(struct tnt_latch *latch); +/** + * Set the latch to the locked state. If it's already locked + * returns -1 value immediately otherwise returns 0. + * + * @param latch Latch to be locked. + */ +int tnt_latch_trylock(struct tnt_latch *latch); +/** + * Unlock the locked latch. + * + * @param latch Latch to be unlocked. + */ +void tnt_latch_unlock(struct tnt_latch *latch); -void diag_clear() -{ - error_destroy(fiber->diagnostics); - fiber->diagnostics = NULL; -} +#endif /* TARANTOOL_LATCH_H_INCLUDED */ diff --git a/include/log_io.h b/include/log_io.h index 34b0b4ce5f60321b1d0c1e6e6ff0ce21808c0d34..55a1a1655000118164d41d47e22cd75e60a26299 100644 --- a/include/log_io.h +++ b/include/log_io.h @@ -65,6 +65,26 @@ struct log_io_class { const char *dirname; }; + +/** A "condition variable" that allows fibers to wait when a given + * LSN makes it to disk. + */ + +struct wait_lsn { + struct fiber *waiter; + i64 lsn; +}; + +void +wait_lsn_set(struct wait_lsn *wait_lsn, i64 lsn); + +inline static void +wait_lsn_clear(struct wait_lsn *wait_lsn) +{ + wait_lsn->waiter = NULL; + wait_lsn->lsn = 0LL; +} + struct log_io { struct log_io_class *class; FILE *f; @@ -82,7 +102,8 @@ struct recovery_state { i64 lsn, confirmed_lsn; struct log_io *current_wal; /* the WAL we'r currently reading/writing from/to */ - struct log_io_class **snap_class, **wal_class, *snap_prefered_class, *wal_prefered_class; + struct log_io_class *snap_class; + struct log_io_class *wal_class; struct child *wal_writer; /* row_handler will be presented by most recent format of data @@ -94,6 +115,7 @@ struct recovery_state { int snap_io_rate_limit; u64 cookie; + struct wait_lsn wait_lsn; bool finalize; @@ -129,7 +151,7 @@ static inline struct row_v11 *row_v11(const struct tbuf *t) struct tbuf *convert_to_v11(struct tbuf *orig, u16 tag, u64 cookie, i64 lsn); struct recovery_state *recover_init(const char *snap_dirname, const char *xlog_dirname, - row_reader snap_row_reader, row_handler row_handler, + row_handler row_handler, int rows_per_file, double fsync_delay, int inbox_size, int flags, void *data); int recover(struct recovery_state *, i64 lsn); @@ -141,8 +163,9 @@ void recovery_setup_panic(struct recovery_state *r, bool on_snap_error, bool on_ int confirm_lsn(struct recovery_state *r, i64 lsn); int64_t next_lsn(struct recovery_state *r, i64 new_lsn); +void recovery_wait_lsn(struct recovery_state *r, i64 lsn); -int read_log(const char *filename, row_reader reader, +int read_log(const char *filename, row_handler xlog_handler, row_handler snap_handler, void *state); int default_remote_row_handler(struct recovery_state *r, struct tbuf *row); diff --git a/include/pickle.h b/include/pickle.h index 6e2a9ec4a1a84c87429a4c3e0c4765b9bc9bbccf..8a51cde6729449e62e3dcb03da258b8bbf4652cf 100644 --- a/include/pickle.h +++ b/include/pickle.h @@ -28,10 +28,6 @@ #include <stdbool.h> #include <util.h> -#include <exceptions.h> - -@interface tnt_PickleException: tnt_Exception -@end struct tbuf; diff --git a/include/tarantool.h b/include/tarantool.h index fe58812ea1692509ebea0f5d9debb647ef5f1da5..29301ecdb9c8f8ce602ef8086ca4f56a8246a0e5 100644 --- a/include/tarantool.h +++ b/include/tarantool.h @@ -36,7 +36,7 @@ struct tarantool_cfg; extern const char *mod_name; i32 mod_check_config(struct tarantool_cfg *conf); -void mod_reload_config(struct tarantool_cfg *old_conf, struct tarantool_cfg *new_conf); +i32 mod_reload_config(struct tarantool_cfg *old_conf, struct tarantool_cfg *new_conf); int mod_cat(const char *filename); void mod_snapshot(struct log_io_iter *); void mod_info(struct tbuf *out); diff --git a/mod/box/box.h b/mod/box/box.h index b68c6134177e0c6d0b19c474af0010f6ceca24be..8dd66e3c83febe4903bd076168406e095f2ef81d 100644 --- a/mod/box/box.h +++ b/mod/box/box.h @@ -27,18 +27,9 @@ */ #include <mod/box/index.h> -#include <exceptions.h> +#include "exception.h" #include <tbuf.h> -@interface tnt_BoxException: tnt_Exception { - @public - u32 errcode; -} - - -- init:(const char *)p_file:(unsigned)p_line reason:(const char *)p_reason errcode:(u32)p_errcode; -- init:(const char *)p_file:(unsigned)p_line errcode:(u32)p_errcode; -@end extern bool box_updates_allowed; void memcached_handler(void * /* data */); @@ -81,7 +72,6 @@ struct box_txn { struct box_tuple *tuple; struct box_tuple *lock_tuple; - size_t saved_iov_cnt; struct tbuf req; bool in_recover; @@ -139,7 +129,7 @@ enum box_mode { ENUM(messages, MESSAGES); struct box_txn *txn_alloc(u32 flags); -u32 box_process(struct box_txn *txn, u32 op, struct tbuf *request_data); +void box_process(struct box_txn *txn, u32 op, struct tbuf *request_data); void tuple_txn_ref(struct box_txn *txn, struct box_tuple *tuple); void txn_cleanup(struct box_txn *txn); diff --git a/mod/box/box.m b/mod/box/box.m index 6847e81587d564269278e1a2868e2a979153174a..c00618428d685dc64c047ad5b7d4851d4f9827a9 100644 --- a/mod/box/box.m +++ b/mod/box/box.m @@ -31,6 +31,7 @@ #include <errno.h> #include <arpa/inet.h> +#include <cfg/warning.h> #include <errcode.h> #include <fiber.h> #include <iproto.h> @@ -46,9 +47,12 @@ #include <cfg/tarantool_box_cfg.h> #include <mod/box/index.h> +static void box_process_ro(u32 op, struct tbuf *request_data); +static void box_process_rw(u32 op, struct tbuf *request_data); + const char *mod_name = "Box"; -bool box_updates_allowed = false; +iproto_callback rw_callback = box_process_ro; static char *status = "unknown"; static int stat_base; @@ -57,6 +61,8 @@ STRS(messages, MESSAGES); const int MEMCACHED_NAMESPACE = 23; static char *custom_proc_title; +static struct fiber *remote_recover; + /* hooks */ typedef int (*box_hook_t) (struct box_txn * txn); @@ -84,26 +90,6 @@ struct box_snap_row { u8 data[]; } __attribute__((packed)); -@implementation tnt_BoxException -- init:(const char *)p_file:(unsigned)p_line reason:(const char *)p_reason errcode:(u32)p_errcode -{ - [super init:p_file:p_line reason:p_reason]; - - errcode = p_errcode; - - if (errcode != ERR_CODE_NODE_IS_RO) - say_error("tnt_BoxException: %s/`%s' at %s:%i", - tnt_errcode_desc(errcode), reason, file, line); - - return self; -} - -- init:(const char *)p_file:(unsigned)p_line errcode:(u32)p_errcode -{ - return [self init:p_file:p_line reason:"unknown" errcode:p_errcode]; -} - -@end static inline struct box_snap_row * box_snap_row(const struct tbuf *t) @@ -113,20 +99,6 @@ box_snap_row(const struct tbuf *t) static void tuple_add_iov(struct box_txn *txn, struct box_tuple *tuple); -box_hook_t *before_commit_update_hook; - -static void -run_hooks(struct box_txn *txn, box_hook_t * hook) -{ - if (hook != NULL) { - for (int i = 0; hook[i] != NULL; i++) { - int result = (*hook[i]) (txn); - if (result != ERR_CODE_OK) - tnt_raise(tnt_BoxException, - reason:"hook returned error" errcode:result); - } - } -} void * next_field(void *f) @@ -153,7 +125,7 @@ static void lock_tuple(struct box_txn *txn, struct box_tuple *tuple) { if (tuple->flags & WAL_WAIT) - tnt_raise(tnt_BoxException, reason:"tuple is locked" errcode:ERR_CODE_NODE_IS_RO); + tnt_raise(ClientError, :ER_TUPLE_IS_RO); say_debug("lock_tuple(%p)", tuple); txn->lock_tuple = tuple; @@ -212,11 +184,11 @@ tuple_print(struct tbuf *buf, uint8_t cardinality, void *f) static struct box_tuple * tuple_alloc(size_t size) { - struct box_tuple *tuple = salloc(sizeof(struct box_tuple) + size); + size_t total = sizeof(struct box_tuple) + size; + struct box_tuple *tuple = salloc(total); if (tuple == NULL) - tnt_raise(tnt_BoxException, - reason:"can't allocate tuple" errcode:ERR_CODE_MEMORY_ISSUE); + tnt_raise(LoggedError, :ER_MEMORY_ISSUE, total, "slab allocator", "tuple"); tuple->flags = tuple->refs = 0; tuple->flags |= NEW; @@ -260,11 +232,10 @@ prepare_replace(struct box_txn *txn, size_t cardinality, struct tbuf *data) { assert(data != NULL); if (cardinality == 0) - tnt_raise(tnt_BoxException, - reason:"cardinality can't be equal to 0" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"tuple cardinality is 0"); + if (data->len == 0 || data->len != valid_tuple(data, cardinality)) - tnt_raise(tnt_BoxException, - reason:"tuple encoding error" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"incorrect tuple length"); txn->tuple = tuple_alloc(data->len); tuple_txn_ref(txn, txn->tuple); @@ -277,14 +248,12 @@ prepare_replace(struct box_txn *txn, size_t cardinality, struct tbuf *data) tuple_txn_ref(txn, txn->old_tuple); if (txn->flags & BOX_ADD && txn->old_tuple != NULL) - tnt_raise(tnt_BoxException, reason:"tuple found" errcode:ERR_CODE_NODE_FOUND); + tnt_raise(ClientError, :ER_TUPLE_FOUND); if (txn->flags & BOX_REPLACE && txn->old_tuple == NULL) - tnt_raise(tnt_BoxException, - reason:"tuple not found" errcode:ERR_CODE_NODE_NOT_FOUND); + tnt_raise(ClientError, :ER_TUPLE_NOT_FOUND); validate_indexes(txn); - run_hooks(txn, before_commit_update_hook); if (txn->old_tuple != NULL) { #ifndef NDEBUG @@ -360,12 +329,10 @@ static void do_field_arith(u8 op, struct tbuf *field, void *arg, u32 arg_size) { if (field->len != 4) - tnt_raise(tnt_BoxException, - reason:"num op on field with length != 4" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"numeric operation on a field with length != 4"); + if (arg_size != 4) - tnt_raise(tnt_BoxException, - reason:"num op with arg not u32" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"the argument of a numeric operation is not a 4-byte int"); switch (op) { case 1: @@ -404,8 +371,7 @@ do_field_splice(struct tbuf *field, void *args_data, u32 args_data_size) length_field = read_field(&args); list_field = read_field(&args); if (args.len != 0) - tnt_raise(tnt_BoxException, - reason:"do_field_splice: bad args" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field splice: bad arguments"); offset_size = load_varint32(&offset_field); if (offset_size == 0) @@ -414,16 +380,14 @@ do_field_splice(struct tbuf *field, void *args_data, u32 args_data_size) offset = pick_u32(offset_field, &offset_field); if (offset < 0) { if (field->len < -offset) - tnt_raise(tnt_BoxException, - reason:"do_field_splice: noffset is negative" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, + :"field splice: offset is negative"); noffset = offset + field->len; } else noffset = offset; } else - tnt_raise(tnt_BoxException, - reason:"do_field_splice: bad size of offset field" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field splice: wrong size of offset"); + if (noffset > field->len) noffset = field->len; @@ -432,9 +396,8 @@ do_field_splice(struct tbuf *field, void *args_data, u32 args_data_size) nlength = field->len - noffset; else if (length_size == sizeof(length)) { if (offset_size == 0) - tnt_raise(tnt_BoxException, - reason:"do_field_splice: offset field is empty but length is not" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, + :"field splice: offset is empty but length is not"); length = pick_u32(length_field, &length_field); if (length < 0) { @@ -445,21 +408,17 @@ do_field_splice(struct tbuf *field, void *args_data, u32 args_data_size) } else nlength = length; } else - tnt_raise(tnt_BoxException, - reason:"do_field_splice: bad size of length field" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field splice: wrong size of length"); + if (nlength > (field->len - noffset)) nlength = field->len - noffset; list_size = load_varint32(&list_field); if (list_size > 0 && length_size == 0) - tnt_raise(tnt_BoxException, - reason:"do_field_splice: length field is empty but list is not" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, + :"field splice: length field is empty but list is not"); if (list_size > (UINT32_MAX - (field->len - nlength))) - tnt_raise(tnt_BoxException, - reason:"do_field_splice: list_size is too long" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field splice: list_size is too long"); say_debug("do_field_splice: noffset = %i, nlength = %i, list_size = %u", noffset, nlength, list_size); @@ -484,17 +443,17 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data) u32 key_len = read_u32(data); if (key_len != 1) - tnt_raise(tnt_BoxException, - reason:"key must be single valued" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key must be single valued"); + key = read_field(data); op_cnt = read_u32(data); if (op_cnt > 128) - tnt_raise(tnt_BoxException, reason:"too many ops" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"too many operations for update"); if (op_cnt == 0) - tnt_raise(tnt_BoxException, reason:"no ops" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"no operations for update"); if (key == NULL) - tnt_raise(tnt_BoxException, reason:"invalid key" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"invalid key"); txn->old_tuple = txn->index->find(txn->index, key); if (txn->old_tuple == NULL) { @@ -526,17 +485,14 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data) field_no = read_u32(data); if (field_no >= txn->old_tuple->cardinality) - tnt_raise(tnt_BoxException, - reason:"update of field beyond tuple cardinality" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(ClientError, :ER_NO_SUCH_FIELD, field_no); struct tbuf *sptr_field = fields[field_no]; op = read_u8(data); if (op > 5) - tnt_raise(tnt_BoxException, - reason:"op is not 0, 1, 2, 3, 4 or 5" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"op is not 0, 1, 2, 3, 4 or 5"); + arg = read_field(data); arg_size = load_varint32(&arg); @@ -560,8 +516,7 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data) } if (data->len != 0) - tnt_raise(tnt_BoxException, - reason:"can't unpack request" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"can't unpack request"); size_t bsize = 0; for (int i = 0; i < txn->old_tuple->cardinality; i++) @@ -578,11 +533,9 @@ prepare_update_fields(struct box_txn *txn, struct tbuf *data) } validate_indexes(txn); - run_hooks(txn, before_commit_update_hook); if (data->len != 0) - tnt_raise(tnt_BoxException, - reason:"can't unpack request" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"can't unpack request"); out: if (!(txn->flags & BOX_QUIET)) { @@ -617,9 +570,7 @@ process_select(struct box_txn *txn, u32 limit, u32 offset, struct tbuf *data) uint32_t *found; u32 count = read_u32(data); if (count == 0) - tnt_raise(tnt_BoxException, - reason:"tuple count must be greater than zero" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"tuple count must be positive"); found = palloc(fiber->pool, sizeof(*found)); add_iov(found, sizeof(*found)); @@ -667,9 +618,8 @@ process_select(struct box_txn *txn, u32 limit, u32 offset, struct tbuf *data) u32 key_len = read_u32(data); if (key_len != 1) - tnt_raise(tnt_BoxException, - reason:"key must be single valued" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key must be single valued"); + void *key = read_field(data); tuple = txn->index->find(txn->index, key); if (tuple == NULL || tuple->flags & GHOST) @@ -686,8 +636,7 @@ process_select(struct box_txn *txn, u32 limit, u32 offset, struct tbuf *data) } if (data->len != 0) - tnt_raise(tnt_BoxException, - reason:"can't unpack request" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"can't unpack request"); } static void __attribute__((noinline)) @@ -742,47 +691,43 @@ txn_alloc(u32 flags) } /** - * Validate the request and start a transaction associated with that request. - * - * This function does too much: - * - parses the request, - * - takes a "savepoint" of fiber->iov_cnt, - * - performs a "name resolution", i.e. find the namespace object + * Validate the request and start a transaction associated with + * that request: + * + * - parse the request, + * - perform a "name resolution", i.e. find the namespace object * associated with the request. - * @todo: split in smaller blocks. */ static void txn_begin(struct box_txn *txn, u16 op, struct tbuf *data) { - txn->saved_iov_cnt = fiber->iov_cnt; - txn->op = op; txn->req = (struct tbuf){ .data = data->data, .len = data->len }; txn->n = read_u32(data); + if (txn->n < 0 || txn->n > namespace_count - 1) - tnt_raise(tnt_BoxException, - reason:"bad namespace number" errcode:ERR_CODE_NO_SUCH_NAMESPACE); - txn->index = &namespace[txn->n].index[0]; - - if (!namespace[txn->n].enabled) { - say_warn("namespace %i is not enabled", txn->n); - tnt_raise(tnt_BoxException, - reason:"namespace is not enabled" errcode:ERR_CODE_NO_SUCH_NAMESPACE); - } + tnt_raise(ClientError, :ER_NO_SUCH_NAMESPACE, txn->n); txn->namespace = &namespace[txn->n]; + + if (!txn->namespace->enabled) + tnt_raise(ClientError, :ER_NAMESPACE_DISABLED, txn->n); + + txn->index = txn->namespace->index; } void txn_cleanup(struct box_txn *txn) { /* - * txn_cleanup maybe called twice in following scenario: - * several request processed by single iproto loop run - * first one successed, but the last one fails with OOM - * in this case fiber perform fiber_cleanup for every registered callback - * we should not not run cleanup twice. + * txn_cleanup() may get called twice in the following + * scenario: Several requests are processed by a single + * iproto loop iteration. The first few requests are + * handled successfully, but the next one fails with an + * OOM. In this case, fiber performs fiber_cleanup() for + * every registered callback. We should not run + * txn_cleanup() twice. */ if (txn->op == 0) return; @@ -819,9 +764,11 @@ txn_commit(struct box_txn *txn) tbuf_append(t, txn->req.data, txn->req.len); i64 lsn = next_lsn(recovery_state, 0); - if (!wal_write(recovery_state, wal_tag, fiber->cookie, lsn, t)) - tnt_raise(tnt_BoxException, errcode:ERR_CODE_WAL_IO); + bool res = !wal_write(recovery_state, wal_tag, + fiber->cookie, lsn, t); confirm_lsn(recovery_state, lsn); + if (res) + tnt_raise(LoggedError, :ER_WAL_IO); } unlock_tuples(txn); @@ -844,8 +791,6 @@ txn_abort(struct box_txn *txn) if (txn->op == 0) return; - fiber->iov_cnt = txn->saved_iov_cnt; - if (!op_is_select(txn->op)) { say_debug("box_rollback(op:%s)", messages_strs[txn->op]); @@ -859,13 +804,13 @@ txn_abort(struct box_txn *txn) } static void -box_dispach(struct box_txn *txn, struct tbuf *data) +box_dispatch(struct box_txn *txn, struct tbuf *data) { u32 cardinality; void *key; u32 key_len; - say_debug("box_dispach(%i)", txn->op); + say_debug("box_dispatch(%i)", txn->op); switch (txn->op) { case INSERT: @@ -873,23 +818,18 @@ box_dispach(struct box_txn *txn, struct tbuf *data) cardinality = read_u32(data); if (namespace[txn->n].cardinality > 0 && namespace[txn->n].cardinality != cardinality) - tnt_raise(tnt_BoxException, - reason:"tuple cardinality must match namespace cardinality" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"tuple cardinality must match namespace cardinality"); prepare_replace(txn, cardinality, data); break; case DELETE: key_len = read_u32(data); if (key_len != 1) - tnt_raise(tnt_BoxException, - reason:"key must be single valued" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key must be single valued"); key = read_field(data); if (data->len != 0) - tnt_raise(tnt_BoxException, - reason:"can't unpack request" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"can't unpack request"); prepare_delete(txn, key); break; @@ -899,14 +839,12 @@ box_dispach(struct box_txn *txn, struct tbuf *data) u32 offset = read_u32(data); u32 limit = read_u32(data); - if (i > MAX_IDX) - tnt_raise(tnt_BoxException, - reason:"index too big" errcode:ERR_CODE_NO_SUCH_INDEX); + if (i > MAX_IDX || + namespace[txn->n].index[i].key_cardinality == 0) { + + tnt_raise(LoggedError, :ER_NO_SUCH_INDEX, i, txn->n); + } txn->index = &namespace[txn->n].index[i]; - if (txn->index->key_cardinality == 0) - tnt_raise(tnt_BoxException, - reason:"index is invalid" - errcode:ERR_CODE_NO_SUCH_INDEX); process_select(txn, limit, offset, data); break; @@ -918,8 +856,8 @@ box_dispach(struct box_txn *txn, struct tbuf *data) break; default: - say_error("silverbox_dispach: unsupported command = %" PRIi32 "", txn->op); - tnt_raise(tnt_BoxException, errcode:ERR_CODE_ILLEGAL_PARAMS); + say_error("box_dispatch: unsupported command = %" PRIi32 "", txn->op); + tnt_raise(IllegalParams, :"unsupported command code, check the error log"); } } @@ -1013,22 +951,6 @@ box_xlog_sprint(struct tbuf *buf, const struct tbuf *t) return 0; } -struct tbuf * -box_snap_reader(FILE *f, struct palloc_pool *pool) -{ - struct tbuf *row = tbuf_alloc(pool); - const int header_size = sizeof(*box_snap_row(row)); - - tbuf_reserve(row, header_size); - if (fread(row->data, header_size, 1, f) != 1) - return NULL; - - tbuf_reserve(row, box_snap_row(row)->data_size); - if (fread(box_snap_row(row)->data, box_snap_row(row)->data_size, 1, f) != 1) - return NULL; - - return convert_to_v11(row, snap_tag, default_cookie, 0); -} static int snap_print(struct recovery_state *r __attribute__((unused)), struct tbuf *t) @@ -1064,8 +986,6 @@ xlog_print(struct recovery_state *r __attribute__((unused)), struct tbuf *t) static void custom_init(void) { - before_commit_update_hook = calloc(1, sizeof(box_hook_t)); - if (cfg.namespace == NULL) panic("at least one namespace must be configured"); @@ -1199,7 +1119,8 @@ custom_init(void) } } -u32 + +void box_process(struct box_txn *txn, u32 op, struct tbuf *request_data) { ev_tstamp start = ev_now(), stop; @@ -1208,64 +1129,36 @@ box_process(struct box_txn *txn, u32 op, struct tbuf *request_data) @try { txn_begin(txn, op, request_data); - box_dispach(txn, request_data); + box_dispatch(txn, request_data); txn_commit(txn); - - return ERR_CODE_OK; - } - @catch (tnt_PickleException *e) { - txn_abort(txn); - - say_error("tnt_PickleException: `%s' at %s:%i", e->reason, e->file, e->line); - - return ERR_CODE_ILLEGAL_PARAMS; - } - @catch (tnt_BoxException *e) { - txn_abort(txn); - - if (e->errcode != ERR_CODE_NODE_IS_RO) - say_error("tnt_BoxException: %s/`%s' at %s:%i", - tnt_errcode_desc(e->errcode), - e->reason, e->file, e->line); - - return e->errcode; } @catch (id e) { txn_abort(txn); - @throw; } @finally { stop = ev_now(); if (stop - start > cfg.too_long_threshold) - say_warn("too long %s: %.3f sec", messages_strs[txn->op], stop - start); + say_warn("too long %s: %.3f sec", messages_strs[op], stop - start); } } -static u32 +static void box_process_ro(u32 op, struct tbuf *request_data) { - if (!op_is_select(op)) { - say_error("can't process %i command on RO port", op); - - return ERR_CODE_NONMASTER; - } + if (!op_is_select(op)) + tnt_raise(LoggedError, :ER_NONMASTER); return box_process(txn_alloc(0), op, request_data); } -static u32 +static void box_process_rw(u32 op, struct tbuf *request_data) { - if (!op_is_select(op) && !box_updates_allowed) { - say_error("can't process %i command, updates are disallowed", op); - - return ERR_CODE_NONMASTER; - } - return box_process(txn_alloc(0), op, request_data); } + static struct tbuf * convert_snap_row_to_wal(struct tbuf *t) { @@ -1304,8 +1197,12 @@ recover_row(struct recovery_state *r __attribute__((unused)), struct tbuf *t) op = read_u16(t); - if (box_process(txn, op, t) != ERR_CODE_OK) + @try { + box_process(txn, op, t); + } + @catch (id e) { return -1; + } return 0; } @@ -1321,36 +1218,68 @@ title(const char *fmt, ...) va_end(ap); if (cfg.memcached) - set_proc_title("memcached:%s%s pri:%i adm:%i", + set_proc_title("%s%s memcached:%i adm:%i", buf, custom_proc_title, cfg.primary_port, cfg.admin_port); else - set_proc_title("box:%s%s pri:%i sec:%i adm:%i", + set_proc_title("%s%s pri:%i sec:%i adm:%i", buf, custom_proc_title, cfg.primary_port, cfg.secondary_port, cfg.admin_port); } static void -box_bound_to_primary(void *data __attribute__((unused))) +remote_recovery_restart(struct tarantool_cfg *conf) { - recover_finalize(recovery_state); + if (remote_recover) { + say_info("shutting down the replica"); + fiber_call(remote_recover); + } - if (cfg.remote_hot_standby) { - say_info("starting remote hot standby"); - status = palloc(eter_pool, 64); - snprintf(status, 64, "hot_standby/%s:%i%s", cfg.replication_source_ipaddr, - cfg.replication_source_port, custom_proc_title); - recover_follow_remote(recovery_state, cfg.replication_source_ipaddr, cfg.replication_source_port, - default_remote_row_handler); - - title("hot_standby/%s:%i", cfg.replication_source_ipaddr, cfg.replication_source_port); + say_info("starting the replica"); + remote_recover = recover_follow_remote(recovery_state, + conf->replication_source_ipaddr, + conf->replication_source_port, + default_remote_row_handler); + + status = palloc(eter_pool, 64); + snprintf(status, 64, "replica/%s:%i%s", conf->wal_feeder_ipaddr, + conf->wal_feeder_port, custom_proc_title); + title("replica/%s:%i%s", conf->wal_feeder_ipaddr, conf->wal_feeder_port, + custom_proc_title); +} + +static void +box_master_or_slave(struct tarantool_cfg *conf) +{ + if (conf->remote_hot_standby) { + rw_callback = box_process_ro; + + recovery_wait_lsn(recovery_state, recovery_state->lsn); + + remote_recovery_restart(conf); } else { + if (remote_recover) { + say_info("shuting down the replica"); + fiber_cancel(remote_recover); + + remote_recover = NULL; + } + rw_callback = box_process_rw; + say_info("I am primary"); + status = "primary"; - box_updates_allowed = true; title("primary"); } } +static void +box_bound_to_primary(void *data __attribute__((unused))) +{ + recover_finalize(recovery_state); + + box_master_or_slave(&cfg); +} + static void memcached_bound_to_primary(void *data __attribute__((unused))) { @@ -1363,21 +1292,49 @@ memcached_bound_to_primary(void *data __attribute__((unused))) } i32 -mod_check_config(struct tarantool_cfg *conf __attribute__((unused))) +mod_check_config(struct tarantool_cfg *conf) { + if (conf->remote_hot_standby > 0 && conf->local_hot_standby > 0) { + out_warning(0, "Remote and local hot standby modes " + "can't be enabled simultaneously"); + + return -1; + } + return 0; } -void -mod_reload_config(struct tarantool_cfg *old_conf __attribute__((unused)), - struct tarantool_cfg *new_conf __attribute__((unused))) +i32 +mod_reload_config(struct tarantool_cfg *old_conf, struct tarantool_cfg *new_conf) { - return; + if (old_conf->remote_hot_standby != new_conf->remote_hot_standby) { + if (recovery_state->finalize != true) { + out_warning(0, "Could not propagate %s before local recovery finished", + old_conf->remote_hot_standby == true ? "slave to master" : + "master to slave"); + + return -1; + } + } + + if (old_conf->remote_hot_standby != new_conf->remote_hot_standby) { + /* Local recovery must be finalized at this point */ + assert(recovery_state->finalize == true); + + box_master_or_slave(new_conf); + } else if (old_conf->remote_hot_standby > 0 && + (strcmp(old_conf->wal_feeder_ipaddr, new_conf->wal_feeder_ipaddr) != 0 || + old_conf->wal_feeder_port != new_conf->wal_feeder_port)) + remote_recovery_restart(new_conf); + + return 0; } void mod_init(void) { + static iproto_callback ro_callback = box_process_ro; + stat_base = stat_register(messages_strs, messages_MAX); namespace = palloc(eter_pool, sizeof(struct namespace) * namespace_count); @@ -1412,8 +1369,7 @@ mod_init(void) } recovery_state = recover_init(cfg.snap_dir, cfg.wal_dir, - box_snap_reader, recover_row, - cfg.rows_per_wal, cfg.wal_fsync_delay, + recover_row, cfg.rows_per_wal, cfg.wal_fsync_delay, cfg.wal_writer_inbox_size, init_storage ? RECOVER_READONLY : 0, NULL); @@ -1458,7 +1414,7 @@ mod_init(void) recover(recovery_state, 0); stat_cleanup(stat_base, messages_MAX); - title("build_indexes"); + title("building indexes"); build_indexes(); @@ -1467,8 +1423,8 @@ mod_init(void) if (cfg.local_hot_standby) { say_info("starting local hot standby"); recover_follow(recovery_state, cfg.wal_dir_rescan_delay); - status = "hot_standby/local"; - title("hot_standby/local"); + status = "hot_standby"; + title("hot_standby"); } if (cfg.memcached != 0) { @@ -1476,12 +1432,14 @@ mod_init(void) memcached_bound_to_primary); } else { if (cfg.secondary_port != 0) - fiber_server(tcp_server, cfg.secondary_port, iproto_interact, - box_process_ro, NULL); + fiber_server(tcp_server, cfg.secondary_port, + (fiber_server_callback) iproto_interact, + &ro_callback, NULL); if (cfg.primary_port != 0) - fiber_server(tcp_server, cfg.primary_port, iproto_interact, box_process_rw, - box_bound_to_primary); + fiber_server(tcp_server, cfg.primary_port, + (fiber_server_callback) iproto_interact, + &rw_callback, box_bound_to_primary); } say_info("initialized"); @@ -1490,7 +1448,7 @@ mod_init(void) int mod_cat(const char *filename) { - return read_log(filename, box_snap_reader, xlog_print, snap_print, NULL); + return read_log(filename, xlog_print, snap_print, NULL); } void diff --git a/mod/box/index.m b/mod/box/index.m index df74b4d2b2966a5afbbb5005fe6a3444f5b18089..8f23b75e30da9d1f075dfd9cb09e56452c2e4196 100644 --- a/mod/box/index.m +++ b/mod/box/index.m @@ -156,8 +156,8 @@ index_find_hash_by_tuple(struct index *self, struct box_tuple *tuple) { void *key = tuple_field(tuple, self->key_field->fieldno); if (key == NULL) - tnt_raise(tnt_BoxException, - reason:"invalid tuple, can't find key" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(ClientError, :ER_NO_SUCH_FIELD, self->key_field->fieldno); + return self->find(self, key); } @@ -169,8 +169,7 @@ index_find_hash_num(struct index *self, void *key) u32 num = *(u32 *)key; if (key_size != 4) - tnt_raise(tnt_BoxException, - reason:"key is not u32" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u32"); assoc_find(int_ptr_map, self->idx.int_hash, num, ret); #ifdef DEBUG @@ -187,8 +186,7 @@ index_find_hash_num64(struct index *self, void *key) u64 num = *(u64 *)key; if (key_size != 8) - tnt_raise(tnt_BoxException, - reason:"key is not u64" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u64"); assoc_find(int64_ptr_map, self->idx.int64_hash, num, ret); #ifdef DEBUG @@ -303,8 +301,7 @@ index_remove_hash_num(struct index *self, struct box_tuple *tuple) u32 num = *(u32 *)key; if (key_size != 4) - tnt_raise(tnt_BoxException, - reason:"key is not u32" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u32"); assoc_delete(int_ptr_map, self->idx.int_hash, num); #ifdef DEBUG say_debug("index_remove_hash_num(self:%p, key:%i)", self, num); @@ -319,8 +316,7 @@ index_remove_hash_num64(struct index *self, struct box_tuple *tuple) u64 num = *(u64 *)key; if (key_size != 8) - tnt_raise(tnt_BoxException, - reason:"key is not u64" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u64"); assoc_delete(int64_ptr_map, self->idx.int64_hash, num); #ifdef DEBUG say_debug("index_remove_hash_num(self:%p, key:%"PRIu64")", self, num); @@ -353,8 +349,7 @@ index_replace_hash_num(struct index *self, struct box_tuple *old_tuple, struct b u32 num = *(u32 *)key; if (key_size != 4) - tnt_raise(tnt_BoxException, - reason:"key is not u32" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u32"); if (old_tuple != NULL) { void *old_key = tuple_field(old_tuple, self->key_field->fieldno); @@ -378,8 +373,7 @@ index_replace_hash_num64(struct index *self, struct box_tuple *old_tuple, struct u64 num = *(u64 *)key; if (key_size != 8) - tnt_raise(tnt_BoxException, - reason:"key is not u64" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"key is not u64"); if (old_tuple != NULL) { void *old_key = tuple_field(old_tuple, self->key_field->fieldno); @@ -401,9 +395,7 @@ index_replace_hash_str(struct index *self, struct box_tuple *old_tuple, struct b void *key = tuple_field(tuple, self->key_field->fieldno); if (key == NULL) - tnt_raise(tnt_BoxException, - reason:"Supplied tuple misses a field which is part of an index" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(ClientError, :ER_NO_SUCH_FIELD, self->key_field->fieldno); if (old_tuple != NULL) { void *old_key = tuple_field(old_tuple, self->key_field->fieldno); @@ -422,9 +414,7 @@ static void index_replace_tree_str(struct index *self, struct box_tuple *old_tuple, struct box_tuple *tuple) { if (tuple->cardinality < self->field_cmp_order_cnt) - tnt_raise(tnt_BoxException, - reason:"Supplied tuple misses a field which is part of an index" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(ClientError, :ER_NO_SUCH_FIELD, self->field_cmp_order_cnt); struct tree_index_member *member = tuple2tree_index_member(self, tuple, NULL); @@ -463,8 +453,7 @@ validate_indexes(struct box_txn *txn) foreach_index(txn->n, index) { for (u32 f = 0; f < index->key_cardinality; ++f) { if (index->key_field[f].fieldno >= txn->tuple->cardinality) - tnt_raise(tnt_BoxException, - reason:"tuple is too short" errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"tuple must have all indexed fields"); if (index->key_field[f].type == STRING) continue; @@ -473,14 +462,10 @@ validate_indexes(struct box_txn *txn) u32 len = load_varint32(&field); if (index->key_field[f].type == NUM && len != sizeof(u32)) - tnt_raise(tnt_BoxException, - reason:"field must be NUM" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field must be NUM"); if (index->key_field[f].type == NUM64 && len != sizeof(u64)) - tnt_raise(tnt_BoxException, - reason:"field must be NUM64" - errcode:ERR_CODE_ILLEGAL_PARAMS); + tnt_raise(IllegalParams, :"field must be NUM64"); } if (index->type == TREE && index->unique == false) /* Don't check non unique indexes */ @@ -489,9 +474,7 @@ validate_indexes(struct box_txn *txn) struct box_tuple *tuple = index->find_by_tuple(index, txn->tuple); if (tuple != NULL && tuple != txn->old_tuple) - tnt_raise(tnt_BoxException, - reason:"duplicate key in a unique index" - errcode:ERR_CODE_INDEX_VIOLATION); + tnt_raise(ClientError, :ER_INDEX_VIOLATION); } } } diff --git a/mod/box/memcached.m b/mod/box/memcached.m index 2950030bbfb9830d6c0f53c244bfb9ad1c1c4523..649532593997ab14e9c1722a7bcca5b4f5c9b694 100644 --- a/mod/box/memcached.m +++ b/mod/box/memcached.m @@ -1,8 +1,8 @@ #line 1 "mod/box/memcached.rl" /* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov + * Copyright (C) 2010, 2011 Mail.RU + * Copyright (C) 2010, 2011 Yuriy Vostrikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -89,7 +89,7 @@ natoq(const u8 *start, const u8 *end) return num; } -static int +static void store(struct box_txn *txn, void *key, u32 exptime, u32 flags, u32 bytes, u8 *data) { u32 box_flags = BOX_QUIET, cardinality = 4; @@ -121,10 +121,10 @@ store(struct box_txn *txn, void *key, u32 exptime, u32 flags, u32 bytes, u8 *dat int key_len = load_varint32(&key); say_debug("memcached/store key:(%i)'%.*s' exptime:%"PRIu32" flags:%"PRIu32" cas:%"PRIu64, key_len, key_len, (u8 *)key, exptime, flags, cas); - return box_process(txn, INSERT, req); /* FIXME: handle RW/RO */ + box_process(txn, INSERT, req); /* FIXME: handle RW/RO */ } -static int +static void delete(struct box_txn *txn, void *key) { u32 key_len = 1; @@ -134,7 +134,7 @@ delete(struct box_txn *txn, void *key) tbuf_append(req, &key_len, sizeof(key_len)); tbuf_append_field(req, key); - return box_process(txn, DELETE, req); + box_process(txn, DELETE, req); } static struct box_tuple * @@ -250,31 +250,32 @@ memcached_dispatch(struct box_txn *txn) say_debug("memcached_dispatch '%.*s'", MIN((int)(pe - p), 40) , p); -#define STORE ({ \ - stats.cmd_set++; \ - if (bytes > (1<<20)) { \ - add_iov("SERVER_ERROR object too large for cache\r\n", 41); \ - } else { \ - u32 ret_code; \ - if ((ret_code = store(txn, key, exptime, flags, bytes, data)) == 0) { \ - stats.total_items++; \ - add_iov("STORED\r\n", 8); \ - } else { \ - add_iov("SERVER_ERROR ", 13); \ - add_iov(tnt_errcode_desc(ret_code), \ - strlen(tnt_errcode_desc(ret_code))); \ - add_iov("\r\n", 2); \ - } \ - } \ -}) +#define STORE \ +do { \ + stats.cmd_set++; \ + if (bytes > (1<<20)) { \ + add_iov("SERVER_ERROR object too large for cache\r\n", 41); \ + } else { \ + @try { \ + store(txn, key, exptime, flags, bytes, data); \ + stats.total_items++; \ + add_iov("STORED\r\n", 8); \ + } \ + @catch (ClientError *e) { \ + add_iov("SERVER_ERROR ", 13); \ + add_iov(e->errmsg, strlen(e->errmsg)); \ + add_iov("\r\n", 2); \ + } \ + } \ +} while (0) -#line 273 "mod/box/memcached.m" +#line 274 "mod/box/memcached.m" { cs = memcached_start; } -#line 278 "mod/box/memcached.m" +#line 279 "mod/box/memcached.m" { if ( p == pe ) goto _test_eof; @@ -332,7 +333,7 @@ case 5: goto st0; goto tr15; tr15: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -349,7 +350,7 @@ st6: if ( ++p == pe ) goto _test_eof6; case 6: -#line 353 "mod/box/memcached.m" +#line 354 "mod/box/memcached.m" if ( (*p) == 32 ) goto st7; goto st0; @@ -363,49 +364,49 @@ case 7: goto tr17; goto st0; tr17: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 374 "mod/box/memcached.m" +#line 375 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr18; if ( 48 <= (*p) && (*p) <= 57 ) goto st8; goto st0; tr18: -#line 512 "mod/box/memcached.rl" +#line 517 "mod/box/memcached.rl" {flags = natoq(fstart, p);} goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 388 "mod/box/memcached.m" +#line 389 "mod/box/memcached.m" if ( (*p) == 32 ) goto st9; if ( 48 <= (*p) && (*p) <= 57 ) goto tr21; goto st0; tr21: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st10; st10: if ( ++p == pe ) goto _test_eof10; case 10: -#line 402 "mod/box/memcached.m" +#line 403 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr22; if ( 48 <= (*p) && (*p) <= 57 ) goto st10; goto st0; tr22: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -416,21 +417,21 @@ st11: if ( ++p == pe ) goto _test_eof11; case 11: -#line 420 "mod/box/memcached.m" +#line 421 "mod/box/memcached.m" if ( (*p) == 32 ) goto st11; if ( 48 <= (*p) && (*p) <= 57 ) goto tr25; goto st0; tr25: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st12; st12: if ( ++p == pe ) goto _test_eof12; case 12: -#line 434 "mod/box/memcached.m" +#line 435 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr26; case 13: goto tr27; @@ -440,11 +441,11 @@ case 12: goto st12; goto st0; tr26: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -465,13 +466,13 @@ tr26: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/box/memcached.rl" +#line 269 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -482,9 +483,9 @@ tr26: } goto st197; tr30: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -505,13 +506,13 @@ tr30: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/box/memcached.rl" +#line 269 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -522,11 +523,11 @@ tr30: } goto st197; tr39: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -547,13 +548,13 @@ tr39: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 268 "mod/box/memcached.rl" +#line 269 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -564,11 +565,11 @@ tr39: } goto st197; tr58: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -589,13 +590,13 @@ tr58: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 297 "mod/box/memcached.rl" +#line 298 "mod/box/memcached.rl" { struct tbuf *b; void *value; @@ -624,9 +625,9 @@ tr58: } goto st197; tr62: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -647,13 +648,13 @@ tr62: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 297 "mod/box/memcached.rl" +#line 298 "mod/box/memcached.rl" { struct tbuf *b; void *value; @@ -682,11 +683,11 @@ tr62: } goto st197; tr71: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -707,13 +708,13 @@ tr71: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 297 "mod/box/memcached.rl" +#line 298 "mod/box/memcached.rl" { struct tbuf *b; void *value; @@ -742,11 +743,11 @@ tr71: } goto st197; tr91: -#line 514 "mod/box/memcached.rl" +#line 519 "mod/box/memcached.rl" {cas = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -767,13 +768,13 @@ tr91: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 286 "mod/box/memcached.rl" +#line 287 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -786,9 +787,9 @@ tr91: } goto st197; tr95: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -809,13 +810,13 @@ tr95: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 286 "mod/box/memcached.rl" +#line 287 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -828,11 +829,11 @@ tr95: } goto st197; tr105: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -853,13 +854,13 @@ tr105: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 286 "mod/box/memcached.rl" +#line 287 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -872,17 +873,17 @@ tr105: } goto st197; tr118: -#line 515 "mod/box/memcached.rl" +#line 520 "mod/box/memcached.rl" {incr = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 324 "mod/box/memcached.rl" +#line 325 "mod/box/memcached.rl" { struct meta *m; struct tbuf *b; @@ -920,12 +921,16 @@ tr118: bytes = b->len; stats.cmd_set++; - if (store(txn, key, exptime, flags, bytes, data) == 0) { + @try { + store(txn, key, exptime, flags, bytes, data); stats.total_items++; add_iov(b->data, b->len); add_iov("\r\n", 2); - } else { - add_iov("SERVER_ERROR\r\n", 14); + } + @catch (ClientError *e) { + add_iov("SERVER_ERROR ", 13); + add_iov(e->errmsg, strlen(e->errmsg)); + add_iov("\r\n", 2); } } else { add_iov("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); @@ -935,15 +940,15 @@ tr118: } goto st197; tr122: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 324 "mod/box/memcached.rl" +#line 325 "mod/box/memcached.rl" { struct meta *m; struct tbuf *b; @@ -981,12 +986,16 @@ tr122: bytes = b->len; stats.cmd_set++; - if (store(txn, key, exptime, flags, bytes, data) == 0) { + @try { + store(txn, key, exptime, flags, bytes, data); stats.total_items++; add_iov(b->data, b->len); add_iov("\r\n", 2); - } else { - add_iov("SERVER_ERROR\r\n", 14); + } + @catch (ClientError *e) { + add_iov("SERVER_ERROR ", 13); + add_iov(e->errmsg, strlen(e->errmsg)); + add_iov("\r\n", 2); } } else { add_iov("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); @@ -996,17 +1005,17 @@ tr122: } goto st197; tr132: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 324 "mod/box/memcached.rl" +#line 325 "mod/box/memcached.rl" { struct meta *m; struct tbuf *b; @@ -1044,12 +1053,16 @@ tr132: bytes = b->len; stats.cmd_set++; - if (store(txn, key, exptime, flags, bytes, data) == 0) { + @try { + store(txn, key, exptime, flags, bytes, data); stats.total_items++; add_iov(b->data, b->len); add_iov("\r\n", 2); - } else { - add_iov("SERVER_ERROR\r\n", 14); + } + @catch (ClientError *e) { + add_iov("SERVER_ERROR ", 13); + add_iov(e->errmsg, strlen(e->errmsg)); + add_iov("\r\n", 2); } } else { add_iov("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); @@ -1059,107 +1072,107 @@ tr132: } goto st197; tr141: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 375 "mod/box/memcached.rl" +#line 380 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { add_iov("NOT_FOUND\r\n", 11); } else { - u32 ret_code; - if ((ret_code = delete(txn, key)) == 0) + @try { + delete(txn, key); add_iov("DELETED\r\n", 9); - else { + } + @catch (ClientError *e) { add_iov("SERVER_ERROR ", 13); - add_iov(tnt_errcode_desc(ret_code), - strlen(tnt_errcode_desc(ret_code))); + add_iov(e->errmsg, strlen(e->errmsg)); add_iov("\r\n", 2); } } } goto st197; tr146: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) exptime = exptime + ev_now(); } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 375 "mod/box/memcached.rl" +#line 380 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { add_iov("NOT_FOUND\r\n", 11); } else { - u32 ret_code; - if ((ret_code = delete(txn, key)) == 0) + @try { + delete(txn, key); add_iov("DELETED\r\n", 9); - else { + } + @catch (ClientError *e) { add_iov("SERVER_ERROR ", 13); - add_iov(tnt_errcode_desc(ret_code), - strlen(tnt_errcode_desc(ret_code))); + add_iov(e->errmsg, strlen(e->errmsg)); add_iov("\r\n", 2); } } } goto st197; tr157: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 375 "mod/box/memcached.rl" +#line 380 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { add_iov("NOT_FOUND\r\n", 11); } else { - u32 ret_code; - if ((ret_code = delete(txn, key)) == 0) + @try { + delete(txn, key); add_iov("DELETED\r\n", 9); - else { + } + @catch (ClientError *e) { add_iov("SERVER_ERROR ", 13); - add_iov(tnt_errcode_desc(ret_code), - strlen(tnt_errcode_desc(ret_code))); + add_iov(e->errmsg, strlen(e->errmsg)); add_iov("\r\n", 2); } } } goto st197; tr169: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 470 "mod/box/memcached.rl" +#line 475 "mod/box/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1171,17 +1184,17 @@ tr169: } goto st197; tr174: -#line 516 "mod/box/memcached.rl" +#line 521 "mod/box/memcached.rl" {flush_delay = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 470 "mod/box/memcached.rl" +#line 475 "mod/box/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1193,17 +1206,17 @@ tr174: } goto st197; tr185: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 470 "mod/box/memcached.rl" +#line 475 "mod/box/memcached.rl" { if (flush_delay > 0) { struct fiber *f = fiber_create("flush_all", -1, -1, flush_all, (void *)flush_delay); @@ -1215,15 +1228,15 @@ tr185: } goto st197; tr195: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 393 "mod/box/memcached.rl" +#line 398 "mod/box/memcached.rl" { txn->op = SELECT; fiber_register_cleanup((void *)txn_cleanup, txn); @@ -1302,25 +1315,25 @@ tr195: } goto st197; tr213: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 484 "mod/box/memcached.rl" +#line 489 "mod/box/memcached.rl" { return 0; } goto st197; tr233: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1341,13 +1354,13 @@ tr233: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 277 "mod/box/memcached.rl" +#line 278 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1358,9 +1371,9 @@ tr233: } goto st197; tr237: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1381,13 +1394,13 @@ tr237: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 277 "mod/box/memcached.rl" +#line 278 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1398,11 +1411,11 @@ tr237: } goto st197; tr246: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1423,13 +1436,13 @@ tr246: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 277 "mod/box/memcached.rl" +#line 278 "mod/box/memcached.rl" { key = read_field(keys); struct box_tuple *tuple = find(key); @@ -1440,11 +1453,11 @@ tr246: } goto st197; tr263: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1465,22 +1478,22 @@ tr263: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 263 "mod/box/memcached.rl" +#line 264 "mod/box/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr267: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1501,24 +1514,24 @@ tr267: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 263 "mod/box/memcached.rl" +#line 264 "mod/box/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr276: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 518 "mod/box/memcached.rl" +#line 523 "mod/box/memcached.rl" { size_t parsed = p - (u8 *)fiber->rbuf->data; while (fiber->rbuf->len - parsed < bytes + 2) { @@ -1539,28 +1552,28 @@ tr276: goto exit; } } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 263 "mod/box/memcached.rl" +#line 264 "mod/box/memcached.rl" { key = read_field(keys); STORE; } goto st197; tr281: -#line 545 "mod/box/memcached.rl" +#line 550 "mod/box/memcached.rl" { p++; } -#line 539 "mod/box/memcached.rl" +#line 544 "mod/box/memcached.rl" { done = true; stats.bytes_read += p - (u8 *)fiber->rbuf->data; tbuf_peek(fiber->rbuf, p - (u8 *)fiber->rbuf->data); } -#line 480 "mod/box/memcached.rl" +#line 485 "mod/box/memcached.rl" { print_stats(); } @@ -1569,33 +1582,33 @@ st197: if ( ++p == pe ) goto _test_eof197; case 197: -#line 1573 "mod/box/memcached.m" +#line 1586 "mod/box/memcached.m" goto st0; tr27: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st13; tr40: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st13; st13: if ( ++p == pe ) goto _test_eof13; case 13: -#line 1587 "mod/box/memcached.m" +#line 1600 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr30; goto st0; tr28: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st14; st14: if ( ++p == pe ) goto _test_eof14; case 14: -#line 1599 "mod/box/memcached.m" +#line 1612 "mod/box/memcached.m" switch( (*p) ) { case 32: goto st14; case 110: goto st15; @@ -1688,18 +1701,18 @@ case 26: goto tr45; goto st0; tr45: -#line 553 "mod/box/memcached.rl" +#line 558 "mod/box/memcached.rl" {append = true; } goto st27; tr209: -#line 554 "mod/box/memcached.rl" +#line 559 "mod/box/memcached.rl" {append = false;} goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 1703 "mod/box/memcached.m" +#line 1716 "mod/box/memcached.m" switch( (*p) ) { case 13: goto st0; case 32: goto st27; @@ -1708,7 +1721,7 @@ case 27: goto st0; goto tr46; tr46: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1725,7 +1738,7 @@ st28: if ( ++p == pe ) goto _test_eof28; case 28: -#line 1729 "mod/box/memcached.m" +#line 1742 "mod/box/memcached.m" if ( (*p) == 32 ) goto st29; goto st0; @@ -1739,49 +1752,49 @@ case 29: goto tr49; goto st0; tr49: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st30; st30: if ( ++p == pe ) goto _test_eof30; case 30: -#line 1750 "mod/box/memcached.m" +#line 1763 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr50; if ( 48 <= (*p) && (*p) <= 57 ) goto st30; goto st0; tr50: -#line 512 "mod/box/memcached.rl" +#line 517 "mod/box/memcached.rl" {flags = natoq(fstart, p);} goto st31; st31: if ( ++p == pe ) goto _test_eof31; case 31: -#line 1764 "mod/box/memcached.m" +#line 1777 "mod/box/memcached.m" if ( (*p) == 32 ) goto st31; if ( 48 <= (*p) && (*p) <= 57 ) goto tr53; goto st0; tr53: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 1778 "mod/box/memcached.m" +#line 1791 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr54; if ( 48 <= (*p) && (*p) <= 57 ) goto st32; goto st0; tr54: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -1792,21 +1805,21 @@ st33: if ( ++p == pe ) goto _test_eof33; case 33: -#line 1796 "mod/box/memcached.m" +#line 1809 "mod/box/memcached.m" if ( (*p) == 32 ) goto st33; if ( 48 <= (*p) && (*p) <= 57 ) goto tr57; goto st0; tr57: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st34; st34: if ( ++p == pe ) goto _test_eof34; case 34: -#line 1810 "mod/box/memcached.m" +#line 1823 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr58; case 13: goto tr59; @@ -1816,30 +1829,30 @@ case 34: goto st34; goto st0; tr59: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st35; tr72: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st35; st35: if ( ++p == pe ) goto _test_eof35; case 35: -#line 1831 "mod/box/memcached.m" +#line 1844 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr62; goto st0; tr60: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st36; st36: if ( ++p == pe ) goto _test_eof36; case 36: -#line 1843 "mod/box/memcached.m" +#line 1856 "mod/box/memcached.m" switch( (*p) ) { case 32: goto st36; case 110: goto st37; @@ -1929,7 +1942,7 @@ case 47: goto st0; goto tr76; tr76: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1946,7 +1959,7 @@ st48: if ( ++p == pe ) goto _test_eof48; case 48: -#line 1950 "mod/box/memcached.m" +#line 1963 "mod/box/memcached.m" if ( (*p) == 32 ) goto st49; goto st0; @@ -1960,49 +1973,49 @@ case 49: goto tr78; goto st0; tr78: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st50; st50: if ( ++p == pe ) goto _test_eof50; case 50: -#line 1971 "mod/box/memcached.m" +#line 1984 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr79; if ( 48 <= (*p) && (*p) <= 57 ) goto st50; goto st0; tr79: -#line 512 "mod/box/memcached.rl" +#line 517 "mod/box/memcached.rl" {flags = natoq(fstart, p);} goto st51; st51: if ( ++p == pe ) goto _test_eof51; case 51: -#line 1985 "mod/box/memcached.m" +#line 1998 "mod/box/memcached.m" if ( (*p) == 32 ) goto st51; if ( 48 <= (*p) && (*p) <= 57 ) goto tr82; goto st0; tr82: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st52; st52: if ( ++p == pe ) goto _test_eof52; case 52: -#line 1999 "mod/box/memcached.m" +#line 2012 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr83; if ( 48 <= (*p) && (*p) <= 57 ) goto st52; goto st0; tr83: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2013,49 +2026,49 @@ st53: if ( ++p == pe ) goto _test_eof53; case 53: -#line 2017 "mod/box/memcached.m" +#line 2030 "mod/box/memcached.m" if ( (*p) == 32 ) goto st53; if ( 48 <= (*p) && (*p) <= 57 ) goto tr86; goto st0; tr86: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st54; st54: if ( ++p == pe ) goto _test_eof54; case 54: -#line 2031 "mod/box/memcached.m" +#line 2044 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr87; if ( 48 <= (*p) && (*p) <= 57 ) goto st54; goto st0; tr87: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st55; st55: if ( ++p == pe ) goto _test_eof55; case 55: -#line 2045 "mod/box/memcached.m" +#line 2058 "mod/box/memcached.m" if ( (*p) == 32 ) goto st55; if ( 48 <= (*p) && (*p) <= 57 ) goto tr90; goto st0; tr90: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st56; st56: if ( ++p == pe ) goto _test_eof56; case 56: -#line 2059 "mod/box/memcached.m" +#line 2072 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr91; case 13: goto tr92; @@ -2065,30 +2078,30 @@ case 56: goto st56; goto st0; tr106: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st57; tr92: -#line 514 "mod/box/memcached.rl" +#line 519 "mod/box/memcached.rl" {cas = natoq(fstart, p);} goto st57; st57: if ( ++p == pe ) goto _test_eof57; case 57: -#line 2080 "mod/box/memcached.m" +#line 2093 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr95; goto st0; tr93: -#line 514 "mod/box/memcached.rl" +#line 519 "mod/box/memcached.rl" {cas = natoq(fstart, p);} goto st58; st58: if ( ++p == pe ) goto _test_eof58; case 58: -#line 2092 "mod/box/memcached.m" +#line 2105 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -2149,14 +2162,14 @@ case 65: } goto st0; tr107: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st66; st66: if ( ++p == pe ) goto _test_eof66; case 66: -#line 2160 "mod/box/memcached.m" +#line 2173 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -2194,18 +2207,18 @@ case 70: goto tr113; goto st0; tr113: -#line 562 "mod/box/memcached.rl" +#line 567 "mod/box/memcached.rl" {incr_sign = -1;} goto st71; tr202: -#line 561 "mod/box/memcached.rl" +#line 566 "mod/box/memcached.rl" {incr_sign = 1; } goto st71; st71: if ( ++p == pe ) goto _test_eof71; case 71: -#line 2209 "mod/box/memcached.m" +#line 2222 "mod/box/memcached.m" switch( (*p) ) { case 13: goto st0; case 32: goto st71; @@ -2214,7 +2227,7 @@ case 71: goto st0; goto tr114; tr114: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2231,7 +2244,7 @@ st72: if ( ++p == pe ) goto _test_eof72; case 72: -#line 2235 "mod/box/memcached.m" +#line 2248 "mod/box/memcached.m" if ( (*p) == 32 ) goto st73; goto st0; @@ -2245,14 +2258,14 @@ case 73: goto tr117; goto st0; tr117: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st74; st74: if ( ++p == pe ) goto _test_eof74; case 74: -#line 2256 "mod/box/memcached.m" +#line 2269 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr118; case 13: goto tr119; @@ -2262,30 +2275,30 @@ case 74: goto st74; goto st0; tr133: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st75; tr119: -#line 515 "mod/box/memcached.rl" +#line 520 "mod/box/memcached.rl" {incr = natoq(fstart, p);} goto st75; st75: if ( ++p == pe ) goto _test_eof75; case 75: -#line 2277 "mod/box/memcached.m" +#line 2290 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr122; goto st0; tr120: -#line 515 "mod/box/memcached.rl" +#line 520 "mod/box/memcached.rl" {incr = natoq(fstart, p);} goto st76; st76: if ( ++p == pe ) goto _test_eof76; case 76: -#line 2289 "mod/box/memcached.m" +#line 2302 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2346,14 +2359,14 @@ case 83: } goto st0; tr134: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st84; st84: if ( ++p == pe ) goto _test_eof84; case 84: -#line 2357 "mod/box/memcached.m" +#line 2370 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2400,7 +2413,7 @@ case 89: goto st0; goto tr140; tr140: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2417,7 +2430,7 @@ st90: if ( ++p == pe ) goto _test_eof90; case 90: -#line 2421 "mod/box/memcached.m" +#line 2434 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2425,7 +2438,7 @@ case 90: } goto st0; tr147: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2433,14 +2446,14 @@ tr147: } goto st91; tr158: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st91; st91: if ( ++p == pe ) goto _test_eof91; case 91: -#line 2444 "mod/box/memcached.m" +#line 2457 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr141; goto st0; @@ -2458,14 +2471,14 @@ case 92: goto tr144; goto st0; tr144: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st93; st93: if ( ++p == pe ) goto _test_eof93; case 93: -#line 2469 "mod/box/memcached.m" +#line 2482 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr146; case 13: goto tr147; @@ -2475,7 +2488,7 @@ case 93: goto st93; goto st0; tr148: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2486,7 +2499,7 @@ st94: if ( ++p == pe ) goto _test_eof94; case 94: -#line 2490 "mod/box/memcached.m" +#line 2503 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2547,14 +2560,14 @@ case 101: } goto st0; tr159: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st102; st102: if ( ++p == pe ) goto _test_eof102; case 102: -#line 2558 "mod/box/memcached.m" +#line 2571 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2628,18 +2641,18 @@ case 111: } goto st0; tr186: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st112; tr175: -#line 516 "mod/box/memcached.rl" +#line 521 "mod/box/memcached.rl" {flush_delay = natoq(fstart, p);} goto st112; st112: if ( ++p == pe ) goto _test_eof112; case 112: -#line 2643 "mod/box/memcached.m" +#line 2656 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr169; goto st0; @@ -2657,14 +2670,14 @@ case 113: goto tr172; goto st0; tr172: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st114; st114: if ( ++p == pe ) goto _test_eof114; case 114: -#line 2668 "mod/box/memcached.m" +#line 2681 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr174; case 13: goto tr175; @@ -2674,14 +2687,14 @@ case 114: goto st114; goto st0; tr176: -#line 516 "mod/box/memcached.rl" +#line 521 "mod/box/memcached.rl" {flush_delay = natoq(fstart, p);} goto st115; st115: if ( ++p == pe ) goto _test_eof115; case 115: -#line 2685 "mod/box/memcached.m" +#line 2698 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2742,14 +2755,14 @@ case 122: } goto st0; tr187: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st123; st123: if ( ++p == pe ) goto _test_eof123; case 123: -#line 2753 "mod/box/memcached.m" +#line 2766 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2780,18 +2793,18 @@ case 126: } goto st0; tr191: -#line 558 "mod/box/memcached.rl" +#line 563 "mod/box/memcached.rl" {show_cas = false;} goto st127; tr198: -#line 559 "mod/box/memcached.rl" +#line 564 "mod/box/memcached.rl" {show_cas = true;} goto st127; st127: if ( ++p == pe ) goto _test_eof127; case 127: -#line 2795 "mod/box/memcached.m" +#line 2808 "mod/box/memcached.m" switch( (*p) ) { case 13: goto st0; case 32: goto st127; @@ -2800,7 +2813,7 @@ case 127: goto st0; goto tr193; tr193: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2817,7 +2830,7 @@ st128: if ( ++p == pe ) goto _test_eof128; case 128: -#line 2821 "mod/box/memcached.m" +#line 2834 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr195; case 13: goto st129; @@ -3024,7 +3037,7 @@ case 155: goto st0; goto tr222; tr222: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -3041,7 +3054,7 @@ st156: if ( ++p == pe ) goto _test_eof156; case 156: -#line 3045 "mod/box/memcached.m" +#line 3058 "mod/box/memcached.m" if ( (*p) == 32 ) goto st157; goto st0; @@ -3055,49 +3068,49 @@ case 157: goto tr224; goto st0; tr224: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st158; st158: if ( ++p == pe ) goto _test_eof158; case 158: -#line 3066 "mod/box/memcached.m" +#line 3079 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr225; if ( 48 <= (*p) && (*p) <= 57 ) goto st158; goto st0; tr225: -#line 512 "mod/box/memcached.rl" +#line 517 "mod/box/memcached.rl" {flags = natoq(fstart, p);} goto st159; st159: if ( ++p == pe ) goto _test_eof159; case 159: -#line 3080 "mod/box/memcached.m" +#line 3093 "mod/box/memcached.m" if ( (*p) == 32 ) goto st159; if ( 48 <= (*p) && (*p) <= 57 ) goto tr228; goto st0; tr228: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st160; st160: if ( ++p == pe ) goto _test_eof160; case 160: -#line 3094 "mod/box/memcached.m" +#line 3107 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr229; if ( 48 <= (*p) && (*p) <= 57 ) goto st160; goto st0; tr229: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -3108,21 +3121,21 @@ st161: if ( ++p == pe ) goto _test_eof161; case 161: -#line 3112 "mod/box/memcached.m" +#line 3125 "mod/box/memcached.m" if ( (*p) == 32 ) goto st161; if ( 48 <= (*p) && (*p) <= 57 ) goto tr232; goto st0; tr232: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st162; st162: if ( ++p == pe ) goto _test_eof162; case 162: -#line 3126 "mod/box/memcached.m" +#line 3139 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr233; case 13: goto tr234; @@ -3132,30 +3145,30 @@ case 162: goto st162; goto st0; tr234: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st163; tr247: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st163; st163: if ( ++p == pe ) goto _test_eof163; case 163: -#line 3147 "mod/box/memcached.m" +#line 3160 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr237; goto st0; tr235: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st164; st164: if ( ++p == pe ) goto _test_eof164; case 164: -#line 3159 "mod/box/memcached.m" +#line 3172 "mod/box/memcached.m" switch( (*p) ) { case 32: goto st164; case 110: goto st165; @@ -3247,7 +3260,7 @@ case 175: goto st0; goto tr252; tr252: -#line 489 "mod/box/memcached.rl" +#line 494 "mod/box/memcached.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -3264,7 +3277,7 @@ st176: if ( ++p == pe ) goto _test_eof176; case 176: -#line 3268 "mod/box/memcached.m" +#line 3281 "mod/box/memcached.m" if ( (*p) == 32 ) goto st177; goto st0; @@ -3278,49 +3291,49 @@ case 177: goto tr254; goto st0; tr254: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st178; st178: if ( ++p == pe ) goto _test_eof178; case 178: -#line 3289 "mod/box/memcached.m" +#line 3302 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr255; if ( 48 <= (*p) && (*p) <= 57 ) goto st178; goto st0; tr255: -#line 512 "mod/box/memcached.rl" +#line 517 "mod/box/memcached.rl" {flags = natoq(fstart, p);} goto st179; st179: if ( ++p == pe ) goto _test_eof179; case 179: -#line 3303 "mod/box/memcached.m" +#line 3316 "mod/box/memcached.m" if ( (*p) == 32 ) goto st179; if ( 48 <= (*p) && (*p) <= 57 ) goto tr258; goto st0; tr258: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st180; st180: if ( ++p == pe ) goto _test_eof180; case 180: -#line 3317 "mod/box/memcached.m" +#line 3330 "mod/box/memcached.m" if ( (*p) == 32 ) goto tr259; if ( 48 <= (*p) && (*p) <= 57 ) goto st180; goto st0; tr259: -#line 505 "mod/box/memcached.rl" +#line 510 "mod/box/memcached.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -3331,21 +3344,21 @@ st181: if ( ++p == pe ) goto _test_eof181; case 181: -#line 3335 "mod/box/memcached.m" +#line 3348 "mod/box/memcached.m" if ( (*p) == 32 ) goto st181; if ( 48 <= (*p) && (*p) <= 57 ) goto tr262; goto st0; tr262: -#line 488 "mod/box/memcached.rl" +#line 493 "mod/box/memcached.rl" { fstart = p; } goto st182; st182: if ( ++p == pe ) goto _test_eof182; case 182: -#line 3349 "mod/box/memcached.m" +#line 3362 "mod/box/memcached.m" switch( (*p) ) { case 10: goto tr263; case 13: goto tr264; @@ -3355,30 +3368,30 @@ case 182: goto st182; goto st0; tr264: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st183; tr277: -#line 547 "mod/box/memcached.rl" +#line 552 "mod/box/memcached.rl" { noreply = true; } goto st183; st183: if ( ++p == pe ) goto _test_eof183; case 183: -#line 3370 "mod/box/memcached.m" +#line 3383 "mod/box/memcached.m" if ( (*p) == 10 ) goto tr267; goto st0; tr265: -#line 513 "mod/box/memcached.rl" +#line 518 "mod/box/memcached.rl" {bytes = natoq(fstart, p);} goto st184; st184: if ( ++p == pe ) goto _test_eof184; case 184: -#line 3382 "mod/box/memcached.m" +#line 3395 "mod/box/memcached.m" switch( (*p) ) { case 32: goto st184; case 110: goto st185; @@ -3674,7 +3687,7 @@ case 196: _out: {} } -#line 572 "mod/box/memcached.rl" +#line 577 "mod/box/memcached.rl" if (!done) { @@ -3798,8 +3811,13 @@ memcached_expire(void *data __attribute__((unused))) while (keys_to_delete->len > 0) { struct box_txn *txn = txn_alloc(BOX_QUIET); - delete(txn, read_field(keys_to_delete)); - expired_keys++; + @try { + delete(txn, read_field(keys_to_delete)); + expired_keys++; + } + @catch (id e) { + /* The error is already logged. */ + } } stat_collect(stat_base, MEMC_EXPIRED_KEYS, expired_keys); @@ -3816,5 +3834,5 @@ memcached_expire(void *data __attribute__((unused))) * Local Variables: * mode: c * End: - * vim: syntax=c + * vim: syntax=objc */ diff --git a/mod/box/memcached.rl b/mod/box/memcached.rl index 766e2b8d842fb943a8ca9ee52ff8ab030d311c51..a698f056f776e0be2a5ddae3e8657aafa1cd3411 100644 --- a/mod/box/memcached.rl +++ b/mod/box/memcached.rl @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010 Mail.RU - * Copyright (C) 2010 Yuriy Vostrikov + * Copyright (C) 2010, 2011 Mail.RU + * Copyright (C) 2010, 2011 Yuriy Vostrikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -80,7 +80,7 @@ natoq(const u8 *start, const u8 *end) return num; } -static int +static void store(struct box_txn *txn, void *key, u32 exptime, u32 flags, u32 bytes, u8 *data) { u32 box_flags = BOX_QUIET, cardinality = 4; @@ -112,10 +112,10 @@ store(struct box_txn *txn, void *key, u32 exptime, u32 flags, u32 bytes, u8 *dat int key_len = load_varint32(&key); say_debug("memcached/store key:(%i)'%.*s' exptime:%"PRIu32" flags:%"PRIu32" cas:%"PRIu64, key_len, key_len, (u8 *)key, exptime, flags, cas); - return box_process(txn, INSERT, req); /* FIXME: handle RW/RO */ + box_process(txn, INSERT, req); /* FIXME: handle RW/RO */ } -static int +static void delete(struct box_txn *txn, void *key) { u32 key_len = 1; @@ -125,7 +125,7 @@ delete(struct box_txn *txn, void *key) tbuf_append(req, &key_len, sizeof(key_len)); tbuf_append_field(req, key); - return box_process(txn, DELETE, req); + box_process(txn, DELETE, req); } static struct box_tuple * @@ -241,23 +241,24 @@ memcached_dispatch(struct box_txn *txn) say_debug("memcached_dispatch '%.*s'", MIN((int)(pe - p), 40) , p); -#define STORE ({ \ - stats.cmd_set++; \ - if (bytes > (1<<20)) { \ - add_iov("SERVER_ERROR object too large for cache\r\n", 41); \ - } else { \ - u32 ret_code; \ - if ((ret_code = store(txn, key, exptime, flags, bytes, data)) == 0) { \ - stats.total_items++; \ - add_iov("STORED\r\n", 8); \ - } else { \ - add_iov("SERVER_ERROR ", 13); \ - add_iov(tnt_errcode_desc(ret_code), \ - strlen(tnt_errcode_desc(ret_code))); \ - add_iov("\r\n", 2); \ - } \ - } \ -}) +#define STORE \ +do { \ + stats.cmd_set++; \ + if (bytes > (1<<20)) { \ + add_iov("SERVER_ERROR object too large for cache\r\n", 41); \ + } else { \ + @try { \ + store(txn, key, exptime, flags, bytes, data); \ + stats.total_items++; \ + add_iov("STORED\r\n", 8); \ + } \ + @catch (ClientError *e) { \ + add_iov("SERVER_ERROR ", 13); \ + add_iov(e->errmsg, strlen(e->errmsg)); \ + add_iov("\r\n", 2); \ + } \ + } \ +} while (0) %%{ action set { @@ -358,12 +359,16 @@ memcached_dispatch(struct box_txn *txn) bytes = b->len; stats.cmd_set++; - if (store(txn, key, exptime, flags, bytes, data) == 0) { + @try { + store(txn, key, exptime, flags, bytes, data); stats.total_items++; add_iov(b->data, b->len); add_iov("\r\n", 2); - } else { - add_iov("SERVER_ERROR\r\n", 14); + } + @catch (ClientError *e) { + add_iov("SERVER_ERROR ", 13); + add_iov(e->errmsg, strlen(e->errmsg)); + add_iov("\r\n", 2); } } else { add_iov("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); @@ -378,13 +383,13 @@ memcached_dispatch(struct box_txn *txn) if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { add_iov("NOT_FOUND\r\n", 11); } else { - u32 ret_code; - if ((ret_code = delete(txn, key)) == 0) + @try { + delete(txn, key); add_iov("DELETED\r\n", 9); - else { + } + @catch (ClientError *e) { add_iov("SERVER_ERROR ", 13); - add_iov(tnt_errcode_desc(ret_code), - strlen(tnt_errcode_desc(ret_code))); + add_iov(e->errmsg, strlen(e->errmsg)); add_iov("\r\n", 2); } } @@ -692,8 +697,13 @@ memcached_expire(void *data __attribute__((unused))) while (keys_to_delete->len > 0) { struct box_txn *txn = txn_alloc(BOX_QUIET); - delete(txn, read_field(keys_to_delete)); - expired_keys++; + @try { + delete(txn, read_field(keys_to_delete)); + expired_keys++; + } + @catch (id e) { + /* The error is already logged. */ + } } stat_collect(stat_base, MEMC_EXPIRED_KEYS, expired_keys); @@ -710,5 +720,5 @@ memcached_expire(void *data __attribute__((unused))) * Local Variables: * mode: c * End: - * vim: syntax=c + * vim: syntax=objc */ diff --git a/test/box/protocol.c b/test/box/protocol.c index 6bc2e864a436b389966404ea8797607983ff4093..89d039f8c7328fbee56f0c9181d83a4fff70af9f 100644 --- a/test/box/protocol.c +++ b/test/box/protocol.c @@ -1,6 +1,6 @@ #include <connector/c/client.h> -#include <include/errcode.h> #include <stdio.h> +#include "errcode.h" /** Server connection. Reused between tests. */ struct tnt_connection *conn; @@ -32,8 +32,7 @@ void test_bug702397() struct tnt_result tnt_res; int res = tnt_execute_raw(conn, message, sizeof message, &tnt_res); printf("return_code: %s, %s\n", - tnt_errcode_str(tnt_res.errcode >> 8), - tnt_errcode_desc(tnt_res.errcode >> 8)); + tnt_errcode_str(tnt_res.errcode >> 8), tnt_res.errmsg); } @@ -54,8 +53,7 @@ void test_bug702399() struct tnt_result tnt_res; int res = tnt_execute_raw(conn, message, sizeof message, &tnt_res); printf("return_code: %s, %s\n", - tnt_errcode_str(tnt_res.errcode >> 8), - tnt_errcode_desc(tnt_res.errcode >> 8)); + tnt_errcode_str(tnt_res.errcode >> 8), tnt_res.errmsg); } diff --git a/test/box/protocol.result b/test/box/protocol.result index 20c738386346dc379dccfdef1da43804dfe130c8..9e1d518d63cc7f2c73b73a961878f5dd43d82d21 100644 --- a/test/box/protocol.result +++ b/test/box/protocol.result @@ -1,3 +1,3 @@ return_code: 0 -return_code: ERR_CODE_ILLEGAL_PARAMS, "Illegal parameters" -return_code: ERR_CODE_NO_SUCH_INDEX, "No index with the given id is defined" +return_code: ER_ILLEGAL_PARAMS, Illegal parameters, tuple count must be positive +return_code: ER_NO_SUCH_INDEX, No index #1 is defined in namespace 0 diff --git a/test/box/sql.result b/test/box/sql.result index 233a1eafedb5f10720fba8649d5f3847ae603468..9c8e9b3d2bb84519fda5335edbb5fa45ee55fbc4 100644 --- a/test/box/sql.result +++ b/test/box/sql.result @@ -2,7 +2,7 @@ ping ok --- select * from t0 -An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' +An error occurred: ER_ILLEGAL_PARAMS, 'Illegal parameters, key is not u32�' insert into t0 values (1, 'I am a tuple') Insert OK, 1 row affected select * from t0 where k0 = 1 @@ -44,7 +44,7 @@ select * from t0 where k0 = 1 Found 1 tuple: [1, 'I am the newest tuple'] update t0 set k1 = 'Huh', k2 = 'Oh-ho-ho' where k0=1 -An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' +An error occurred: ER_NO_SUCH_FIELD, 'Field 2 was not found in the tuple�' select * from t0 where k0 = 1 Found 1 tuple: [1, 'I am the newest tuple'] @@ -82,11 +82,11 @@ Found 1 tuple: # select * from t1 where k0 = 0 -An error occurred: ERR_CODE_NO_SUCH_NAMESPACE, 'No namespace with specified id exists' +An error occurred: ER_NAMESPACE_DISABLED, 'Namespace 1 is disabled�' select * from t65537 where k0 = 0 -An error occurred: ERR_CODE_NO_SUCH_NAMESPACE, 'No namespace with specified id exists' +An error occurred: ER_NO_SUCH_NAMESPACE, 'Namespace 65537 does not exists�' select * from t4294967295 where k0 = 0 -An error occurred: ERR_CODE_NO_SUCH_NAMESPACE, 'No namespace with specified id exists' +An error occurred: ER_NO_SUCH_NAMESPACE, 'Namespace 4294967295 does not exists�' # # A test case for: http://bugs.launchpad.net/bugs/735140 @@ -102,7 +102,7 @@ select * from t0 where k1='Britney' Found 1 tuple: ['Spears', 'Britney'] insert into t0 values ('Spears') -An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' +An error occurred: ER_ILLEGAL_PARAMS, 'Illegal parameters, tuple must have all indexed fields�' select * from t0 where k0='Spears' Found 1 tuple: ['Spears', 'Britney'] diff --git a/test/box_big/sql.result b/test/box_big/sql.result index 1e57a1e562278586922cd9cb82717b0656f06a4f..a1505ec6579473dfb26239d9a4b75cd20f26c527 100644 --- a/test/box_big/sql.result +++ b/test/box_big/sql.result @@ -58,11 +58,11 @@ Delete OK, 1 row affected # get away with it. # insert into t0 values ('Britney') -An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' +An error occurred: ER_ILLEGAL_PARAMS, 'Illegal parameters, tuple must have all indexed fields�' select * from t0 where k1='Anything' No match insert into t0 values ('Stephanie') -An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters' +An error occurred: ER_ILLEGAL_PARAMS, 'Illegal parameters, tuple must have all indexed fields�' select * from t0 where k1='Anything' No match insert into t0 values ('Spears', 'Britney') diff --git a/test/box_replication/feeder.cfg b/test/box_replication/feeder.cfg new file mode 100644 index 0000000000000000000000000000000000000000..90b488bca00e8be0418ff4dc7d4a91b091169902 --- /dev/null +++ b/test/box_replication/feeder.cfg @@ -0,0 +1,8 @@ +pid_file = "tarantool.pid" + +logger="tee -a tarantool.log" + +wal_feeder_bind_ipaddr = "127.0.0.1" +wal_feeder_bind_port = 33016 + +wal_feeder_dir = "../" diff --git a/test/box_replication/feeder_secondary.cfg b/test/box_replication/feeder_secondary.cfg new file mode 100644 index 0000000000000000000000000000000000000000..83f9c44440203d6146647dcb8dd8b82dcfa4e69d --- /dev/null +++ b/test/box_replication/feeder_secondary.cfg @@ -0,0 +1,8 @@ +pid_file = "tarantool.pid" + +logger="tee -a tarantool.log" + +wal_feeder_bind_ipaddr = "127.0.0.1" +wal_feeder_bind_port = 33026 + +wal_feeder_dir = "../" diff --git a/test/box_replication/feeder_slave.cfg b/test/box_replication/feeder_slave.cfg new file mode 100644 index 0000000000000000000000000000000000000000..39c6e32caa4439c903de2c58d813085d66791768 --- /dev/null +++ b/test/box_replication/feeder_slave.cfg @@ -0,0 +1,8 @@ +pid_file = "tarantool.pid" + +logger="tee -a tarantool.log" + +wal_feeder_bind_ipaddr = "127.0.0.1" +wal_feeder_bind_port = 33116 + +wal_feeder_dir = "../" diff --git a/test/box_replication/tarantool.cfg b/test/box_replication/tarantool.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9f75124040313b3cb13d4cdbdc1771d286e746dd --- /dev/null +++ b/test/box_replication/tarantool.cfg @@ -0,0 +1,13 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33013 +secondary_port = 33014 +admin_port = 33015 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" diff --git a/test/box_replication/tarantool_beholder.cfg b/test/box_replication/tarantool_beholder.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6e74dbd891b52c979fc2ca76e87062f21cc9ab24 --- /dev/null +++ b/test/box_replication/tarantool_beholder.cfg @@ -0,0 +1,13 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33013 +secondary_port = 33024 +admin_port = 33025 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" diff --git a/test/box_replication/tarantool_beholder_to_slave.cfg b/test/box_replication/tarantool_beholder_to_slave.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9b97eba8a467cbdecdfe2286c2f1f3bb4ae18e00 --- /dev/null +++ b/test/box_replication/tarantool_beholder_to_slave.cfg @@ -0,0 +1,17 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33013 +secondary_port = 33024 +admin_port = 33025 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" + +remote_hot_standby = 1 +wal_feeder_ipaddr = "127.0.0.1" +wal_feeder_port = 33116 diff --git a/test/box_replication/tarantool_slave.cfg b/test/box_replication/tarantool_slave.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0630c863552a63ff3d788347eb7b884020251f6f --- /dev/null +++ b/test/box_replication/tarantool_slave.cfg @@ -0,0 +1,17 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33113 +secondary_port = 33114 +admin_port = 33115 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" + +remote_hot_standby = 1 +wal_feeder_ipaddr = "127.0.0.1" +wal_feeder_port = 33016 diff --git a/test/box_replication/tarantool_slave_secondary_feeder_replication.cfg b/test/box_replication/tarantool_slave_secondary_feeder_replication.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cd918ba1226da0b3e0b4114e91f2bc10ebf035dd --- /dev/null +++ b/test/box_replication/tarantool_slave_secondary_feeder_replication.cfg @@ -0,0 +1,17 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33113 +secondary_port = 33114 +admin_port = 33115 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" + +remote_hot_standby = 1 +wal_feeder_ipaddr = "127.0.0.1" +wal_feeder_port = 33026 diff --git a/test/box_replication/tarantool_slave_to_master.cfg b/test/box_replication/tarantool_slave_to_master.cfg new file mode 100644 index 0000000000000000000000000000000000000000..33eaa9799bb24ba56ed5df8a76eaaa153e6dd366 --- /dev/null +++ b/test/box_replication/tarantool_slave_to_master.cfg @@ -0,0 +1,13 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33113 +secondary_port = 33114 +admin_port = 33115 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" diff --git a/test/box_replication/tarantool_to_slave.cfg b/test/box_replication/tarantool_to_slave.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fe58a84e4108d2fc959a332d1a6a74f5e08857f8 --- /dev/null +++ b/test/box_replication/tarantool_to_slave.cfg @@ -0,0 +1,17 @@ +slab_alloc_arena=0.05 +pid_file = "tarantool.pid" + +primary_port = 33013 +secondary_port = 33014 +admin_port = 33015 + +logger="tee -a tarantool.log" +namespace[0].enabled = 1 +namespace[0].index[0].type = "HASH" +namespace[0].index[0].unique = 1 +namespace[0].index[0].key_field[0].fieldno = 0 +namespace[0].index[0].key_field[0].type = "NUM" + +remote_hot_standby = 1 +wal_feeder_ipaddr = "127.0.0.1" +wal_feeder_port = 33116 diff --git a/test/lib/box.py b/test/lib/box.py index edc3f0f90a0612113964ad501a13b4bbc2c433db..a2603ccd6854df89735489c63253e1a638120a3b 100644 --- a/test/lib/box.py +++ b/test/lib/box.py @@ -31,6 +31,9 @@ class Box(TarantoolConnection): res = "" while len(res) < length: buf = self.socket.recv(length - len(res)) + if not buf: + raise RuntimeError("Got EOF from socket, the server has " + "probably crashed") res = res + buf return res diff --git a/test/lib/server.py b/test/lib/server.py index f15cd9c94340d8f4e514149402e7eb595065c653..e7892ff1f39531b61d6da761922ab0403480d467 100644 --- a/test/lib/server.py +++ b/test/lib/server.py @@ -23,6 +23,15 @@ def wait_until_connected(port): except socket.error as e: time.sleep(0.001) +def check_port(port): + """Check if the port we're connecting to is available""" + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("localhost", port)) + except socket.error as e: + return + raise RuntimeError("The server is already running on port {0}".format(port)) + def prepare_gdb(args): """Prepare server startup arguments to run under gdb.""" if "TERM" in os.environ: @@ -33,7 +42,7 @@ def prepare_gdb(args): if term not in ["xterm", "rxvt", "urxvt", "gnome-terminal", "konsole"]: raise RuntimeError("--gdb: unsupported terminal {0}".format(term)) - args = [ term, "-e ", "gdb", "-ex", "break main", "-ex", "run" ] + args + args = [ term, "-e", "gdb", "-ex", "break main", "-ex", "run" ] + args return args def prepare_valgrind(args, valgrind_log): @@ -141,12 +150,8 @@ class Server(object): if self.gdb == True: raise RuntimeError("'--gdb' and '--start-and-exit' can't be defined together") - pid = os.fork() - if pid > 0: - os.wait() - else: - with daemon.DaemonContext(working_directory = self.vardir): - os.execvp(args[0], args) + with daemon.DaemonContext(working_directory = self.vardir): + os.execvp(args[0], args) def prepare_args(self): return [self.binary] @@ -165,6 +170,7 @@ class Server(object): version = self.version() print "Starting {0} {1}.".format(os.path.basename(self.binary), version) + check_port(self.port) args = self.prepare_args() if self.gdb: args = prepare_gdb(args) @@ -178,21 +184,31 @@ class Server(object): wait_until_connected(self.port) # Set is_started flag, to nicely support cleanup during an exception. self.is_started = True + with open(self.pidfile) as f: + self.pid = int(f.read()) def stop(self, silent=True): """Stop server instance. Do nothing if the server is not started, to properly shut down the server in case of an exception during start up.""" - if self.is_started: - if not silent: - print "Stopping the server..." - if self.process == None: - self.kill_old_server() - self.process.terminate() + if not self.is_started: + if not silent: + print "The server is not started." + return + if not silent: + print "Stopping the server..." + + if self.process == None: + self.kill_old_server() + else: + self.kill_server() + + if self.gdb: + self.process.expect(pexpect.EOF, timeout = 1 << 30) + else: self.process.expect(pexpect.EOF) - self.is_started = False - elif not silent: - print "The server is not started." + self.is_started = False + self.pid = None def deploy(self, config=None, binary=None, vardir=None, mem=None, start_and_exit=None, gdb=None, valgrind=None, silent=True, @@ -224,6 +240,14 @@ class Server(object): stderr = subprocess.STDOUT).stdout.read() print output + def kill_server(self): + """Kill a server which was started correctly""" + try: + os.kill(self.pid, signal.SIGTERM) + except OSError as e: + print e + pass + def kill_old_server(self, silent=True): """Kill old server instance if it exists.""" if os.access(self.pidfile, os.F_OK) == False: diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index 45050ad085dd0de6f054a3fd338e591620294d88..39d563f2c95b51959363b370a1f4886c65f320c7 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -24,39 +24,43 @@ DELETE_REQUEST_TYPE = 20 PING_REQUEST_TYPE = 65280 ER = { - 0x00000000: ("ERR_CODE_OK" , "OK") , - 0x00000102: ("ERR_CODE_NONMASTER" , "Non master connection, but it should be"), - 0x00000202: ("ERR_CODE_ILLEGAL_PARAMS" , "Illegal parameters") , - 0x00000302: ("ERR_CODE_BAD_UID" , "Uid not from this storage range") , - 0x00000401: ("ERR_CODE_NODE_IS_RO" , "Node is marked as read-only") , - 0x00000501: ("ERR_CODE_NODE_IS_NOT_LOCKED" , "Node isn't locked") , - 0x00000601: ("ERR_CODE_NODE_IS_LOCKED" , "Node is locked") , - 0x00000701: ("ERR_CODE_MEMORY_ISSUE" , "Some memory issues") , - 0x00000802: ("ERR_CODE_BAD_INTEGRITY" , "Bad graph integrity") , - 0x00000a02: ("ERR_CODE_UNSUPPORTED_COMMAND" , "Unsupported command") , - 0x00001801: ("ERR_CODE_CANNOT_REGISTER" , "Can not register new user") , - 0x00001a01: ("ERR_CODE_CANNOT_INIT_ALERT_ID", "Can not generate alert id") , - 0x00001b02: ("ERR_CODE_CANNOT_DEL" , "Can\'t del node") , - 0x00001c02: ("ERR_CODE_USER_NOT_REGISTERED" , "User isn\'t registered") , - 0x00001d02: ("ERR_CODE_SYNTAX_ERROR" , "Syntax error in query") , - 0x00001e02: ("ERR_CODE_WRONG_FIELD" , "Unknown field") , - 0x00001f02: ("ERR_CODE_WRONG_NUMBER" , "Number value is out of range") , - 0x00002002: ("ERR_CODE_DUPLICATE" , "Insert already existing object") , - 0x00002202: ("ERR_CODE_UNSUPPORTED_ORDER" , "Can not order result") , - 0x00002302: ("ERR_CODE_MULTIWRITE" , "Multiple to update/delete") , - 0x00002400: ("ERR_CODE_NOTHING" , "nothing to do (not an error)") , - 0x00002502: ("ERR_CODE_UPDATE_ID" , "id\'s update") , - 0x00002602: ("ERR_CODE_WRONG_VERSION" , "Unsupported version of protocol") , - 0x00002702: ("ERR_CODE_UNKNOWN_ERROR" , "") , - 0x00003102: ("ERR_CODE_NODE_NOT_FOUND" , "") , - 0x00003702: ("ERR_CODE_NODE_FOUND" , "") , - 0x00003802: ("ERR_CODE_INDEX_VIOLATION" , "") , - 0x00003902: ("ERR_CODE_NO_SUCH_NAMESPACE" , "No namespace with specified id exists") , + 0: "ER_OK" , + 1: "ER_NONMASTER" , + 2: "ER_ILLEGAL_PARAMS" , + 3: "ER_BAD_UID" , + 4: "ER_TUPLE_IS_RO" , + 5: "ER_TUPLE_IS_NOT_LOCKED" , + 6: "ER_TUPLE_IS_LOCKED" , + 7: "ER_MEMORY_ISSUE" , + 8: "ER_BAD_INTEGRITY" , + 10: "ER_UNSUPPORTED_COMMAND" , + 24: "ER_CANNOT_REGISTER" , + 26: "ER_CANNOT_INIT_ALERT_ID", + 27: "ER_CANNOT_DEL" , + 28: "ER_USER_NOT_REGISTERED" , + 29: "ER_SYNTAX_ERROR" , + 30: "ER_WRONG_FIELD" , + 31: "ER_WRONG_NUMBER" , + 32: "ER_DUPLICATE" , + 34: "ER_UNSUPPORTED_ORDER" , + 35: "ER_MULTIWRITE" , + 36: "ER_NOTHING" , + 37: "ER_UPDATE_ID" , + 38: "ER_WRONG_VERSION" , + 39: "ER_WAL_IO" , + 49: "ER_TUPLE_NOT_FOUND" , + 52: "ER_NAMESPACE_DISABLED" , + 53: "ER_NO_SUCH_INDEX" , + 54: "ER_NO_SUCH_FIELD" , + 55: "ER_TUPLE_FOUND" , + 56: "ER_INDEX_VIOLATION" , + 57: "ER_NO_SUCH_NAMESPACE" } -def format_error(return_code): - return "An error occurred: {0}, \'{1}'".format(ER[return_code][0], - ER[return_code][1]) + +def format_error(return_code, response): + return "An error occurred: {0}, \'{1}'".format(ER[return_code >> 8], + response[4:]) def save_varint32(value): @@ -185,9 +189,9 @@ class StatementInsert(StatementPing): def unpack(self, response): (return_code,) = struct.unpack("<L", response[:4]) if return_code: - return format_error(return_code) - (result_code, row_count) = struct.unpack("<LL", response) - return "Insert OK, {0} row affected".format(row_count) + return format_error(return_code, response) + (tuple_count,) = struct.unpack("<L", response[4:8]) + return "Insert OK, {0} row affected".format(tuple_count) class StatementUpdate(StatementPing): @@ -212,9 +216,9 @@ class StatementUpdate(StatementPing): def unpack(self, response): (return_code,) = struct.unpack("<L", response[:4]) if return_code: - return format_error(return_code) - (result_code, row_count) = struct.unpack("<LL", response) - return "Update OK, {0} row affected".format(row_count) + return format_error(return_code, response) + (tuple_count,) = struct.unpack("<L", response[4:8]) + return "Update OK, {0} row affected".format(tuple_count) class StatementDelete(StatementPing): reqeust_type = DELETE_REQUEST_TYPE @@ -235,9 +239,9 @@ class StatementDelete(StatementPing): def unpack(self, response): (return_code,) = struct.unpack("<L", response[:4]) if return_code: - return format_error(return_code) - (result_code, row_count) = struct.unpack("<LL", response) - return "Delete OK, {0} row affected".format(row_count) + return format_error(return_code, response) + (tuple_count,) = struct.unpack("<L", response[4:8]) + return "Delete OK, {0} row affected".format(tuple_count) class StatementSelect(StatementPing): reqeust_type = SELECT_REQUEST_TYPE @@ -274,9 +278,9 @@ class StatementSelect(StatementPing): return buf[:offset] def unpack(self, response): - if len(response) == 4: - (return_code,) = struct.unpack("<L", response[:4]) - return format_error(return_code) + (return_code,) = struct.unpack("<L", response[:4]) + if return_code: + return format_error(return_code, response) (tuple_count,) = struct.unpack("<L", response[4:8]) tuples = [] offset = 8 diff --git a/test/lib/tarantool_box_server.py b/test/lib/tarantool_box_server.py index 04bfa7ffef64cb39e074dfc4ad9f219e036001ac..87b600e1e4b25793b198b2efcfa856d291314962 100644 --- a/test/lib/tarantool_box_server.py +++ b/test/lib/tarantool_box_server.py @@ -34,12 +34,10 @@ class TarantoolBoxServer(TarantoolServer): stdout = subprocess.PIPE, stderr = subprocess.PIPE) - def wait_sync(self, lsn): - synced = 0 - while synced == 0: - synced = 1 + def wait_lsn(self, lsn): + while True: data = self.admin.execute("show info\n", silent=True) info = yaml.load(data)["info"] - if (info["lsn"] != lsn): - synced = 0 - time.sleep(0.1) + if (int(info["lsn"]) >= lsn): + break + time.sleep(0.01) diff --git a/test/tarantool b/test/tarantool index 96202b7237104539d57f380ac90968ec55988c5e..a54adbb03ba09cb40e7633c43c73c5ac4b83ac3b 100755 --- a/test/tarantool +++ b/test/tarantool @@ -1,4 +1,4 @@ -#! /usr/bin/python +#!/usr/bin/env python """A simplistic client for tarantool/box: administrative console and SQL client. diff --git a/test/test-run.py b/test/test-run.py index 71461d81acc490ff3e399f08e260d840eca5cdd5..a511941b205b7d52b8a0de5002cd97991501567d 100755 --- a/test/test-run.py +++ b/test/test-run.py @@ -1,4 +1,4 @@ -#! /usr/bin/python +#!/usr/bin/env python """Tarantool regression test suite front-end.""" __author__ = "Konstantin Osipov <kostja.osipov@gmail.com>" diff --git a/third_party/proctitle.c b/third_party/proctitle.c index f2872b66ec08ac01fe140a7026ddb6746a6ff921..1a580765ad2aa21aaca3c2f379711fc50b09d5f7 100644 --- a/third_party/proctitle.c +++ b/third_party/proctitle.c @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <limits.h> #ifdef HAVE_SYS_PSTAT_H #include <sys/pstat.h> /* for HP-UX */ @@ -216,7 +217,18 @@ init_set_proc_title(int argc, char **argv) */ ps_buffer_fixed_size = 0; #else - snprintf(ps_buffer, ps_buffer_size, "tarantool: "); + { + char basename_buf[PATH_MAX]; + + /* + * At least partially mimic FreeBSD, which for + * ./a.out outputs: + * + * a.out: custom title here (a.out) + */ + snprintf(basename_buf, sizeof basename_buf, "%s", argv[0]); + snprintf(ps_buffer, ps_buffer_size, "%s: ", basename(basename_buf)); + } ps_buffer_fixed_size = strlen(ps_buffer);