This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Tarantool -- an efficient key/value data store".

The branch master has been updated
       via  3c2e3a59d85e25b495d0b95366e67a6f19ee00da (commit)
      from  105a63debcbf35a03848d1b8e3c60ddca33b71ba (commit)

Summary of changes:
 core/CMakeLists.txt                          |    2 +-
 core/admin.c                                 |   80 +++++---
 core/admin.rl                                |   15 ++-
 core/diagnostics.c                           |   90 +++++++++
 core/diagnostics.h                           |   58 ++++++
 core/fiber.c                                 |   43 +++--
 core/log_io.c                                |  263 +++++++++++++++++++-------
 core/tarantool.c                             |   36 ++--
 include/fiber.h                              |    5 +-
 include/log_io.h                             |    4 +
 include/say.h                                |   17 +-
 include/tarantool.h                          |    2 +-
 test/box/bad_record.xlog                     |    4 +
 test/box/{tarantool_empty.cfg => empty.xlog} |    0
 test/box/just_header.xlog                    |    3 +
 test/box/reconfigure.result                  |   47 +++++
 test/box/reconfigure.test                    |   26 +++
 test/box/show.result                         |   10 +-
 test/box/show.test                           |    3 +
 test/box/snapshot.result                     |   28 +++
 test/box/snapshot.test                       |   32 +++-
 test/box/tarantool.cfg                       |    2 +
 test/box/tarantool_bad1.cfg                  |    2 +
 test/box/tarantool_bad2.cfg                  |    2 +
 test/box/tarantool_bad3.cfg                  |    2 +
 test/box/tarantool_bad4.cfg                  |    2 +
 test/box/tarantool_bad5.cfg                  |    2 +
 test/box/tarantool_good.cfg                  |    2 +
 test/box/unfinished.xlog                     |  Bin 0 -> 86 bytes
 test/box/xlog.result                         |   34 ++++
 test/box/xlog.test                           |  113 +++++++++++
 test/lib/tarantool_silverbox_server.py       |    8 +-
 32 files changed, 786 insertions(+), 151 deletions(-)
 create mode 100644 core/diagnostics.c
 create mode 100644 core/diagnostics.h
 create mode 100644 test/box/bad_record.xlog
 copy test/box/{tarantool_empty.cfg => empty.xlog} (100%)
 create mode 100644 test/box/just_header.xlog
 create mode 100644 test/box/unfinished.xlog
 create mode 100644 test/box/xlog.result
 create mode 100644 test/box/xlog.test

commit 3c2e3a59d85e25b495d0b95366e67a6f19ee00da
Author: Konstantin Osipov <[email protected]>
Date:   Mon Feb 21 17:07:44 2011 +0300

    Fixes and tests for Bug##712447, Bug##695689, Bug#686411
    
    Bug#712447 "Valgrind reports use of not initialized memory
    after 'reload configuration'
    
    Bug#695689 "'save snapshot' does not report error when it occurs"
    
    Bug#686411 "Possible data loss when renaming a snapshot"

diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 55d2c0e..7d0ef61 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -50,7 +50,7 @@ set (recompiled_core_sources
      ${CMAKE_SOURCE_DIR}/core/admin.c
      ${CMAKE_SOURCE_DIR}/core/fiber.c PARENT_SCOPE)
 
-set (common_sources tbuf.c palloc.c util.c
+set (common_sources tbuf.c palloc.c util.c diagnostics.c
     salloc.c pickle.c coro.c stat.c log_io.c
     log_io_remote.c iproto.c)
 
diff --git a/core/admin.c b/core/admin.c
index 000cd85..ab1f5af 100644
--- a/core/admin.c
+++ b/core/admin.c
@@ -186,15 +186,15 @@ case 6:
        }
        goto st0;
 tr12:
-#line 180 "core/admin.rl"
+#line 193 "core/admin.rl"
        {slab_validate(); ok(out);}
        goto st108;
 tr19:
-#line 170 "core/admin.rl"
+#line 183 "core/admin.rl"
        {return 0;}
        goto st108;
 tr28:
-#line 166 "core/admin.rl"
+#line 179 "core/admin.rl"
        {strend = p;}
 #line 137 "core/admin.rl"
        {
@@ -221,12 +221,23 @@ tr43:
                }
        goto st108;
 tr66:
-#line 177 "core/admin.rl"
+#line 190 "core/admin.rl"
        {coredump(60); ok(out);}
        goto st108;
 tr75:
-#line 178 "core/admin.rl"
-       {snapshot(NULL, 0); ok(out);}
+#line 150 "core/admin.rl"
+       {
+                       int ret = snapshot(NULL, 0);
+
+                       if (ret == 0)
+                               ok(out);
+                       else {
+                               tbuf_printf(err, " can't save snapshot, errno 
%d (%s)",
+                                           ret, strerror(ret));
+
+                               fail(out, err);
+                       }
+               }
        goto st108;
 tr92:
 #line 113 "core/admin.rl"
@@ -249,41 +260,41 @@ tr92:
                }
        goto st108;
 tr106:
-#line 172 "core/admin.rl"
+#line 185 "core/admin.rl"
        {start(out); fiber_info(out); end(out);}
        goto st108;
 tr112:
-#line 171 "core/admin.rl"
+#line 184 "core/admin.rl"
        {start(out); mod_info(out); end(out);}
        goto st108;
 tr117:
-#line 175 "core/admin.rl"
+#line 188 "core/admin.rl"
        {start(out); palloc_stat(out); end(out);}
        goto st108;
 tr125:
-#line 174 "core/admin.rl"
+#line 187 "core/admin.rl"
        {start(out); slab_stat(out); end(out);}
        goto st108;
 tr129:
-#line 176 "core/admin.rl"
+#line 189 "core/admin.rl"
        {start(out); stat_print(out);end(out);}
        goto st108;
 st108:
        if ( ++p == pe )
                goto _test_eof108;
 case 108:
-#line 276 "core/admin.c"
+#line 287 "core/admin.c"
        goto st0;
 tr13:
-#line 180 "core/admin.rl"
+#line 193 "core/admin.rl"
        {slab_validate(); ok(out);}
        goto st7;
 tr20:
-#line 170 "core/admin.rl"
+#line 183 "core/admin.rl"
        {return 0;}
        goto st7;
 tr29:
-#line 166 "core/admin.rl"
+#line 179 "core/admin.rl"
        {strend = p;}
 #line 137 "core/admin.rl"
        {
@@ -310,12 +321,23 @@ tr44:
                }
        goto st7;
 tr67:
-#line 177 "core/admin.rl"
+#line 190 "core/admin.rl"
        {coredump(60); ok(out);}
        goto st7;
 tr76:
-#line 178 "core/admin.rl"
-       {snapshot(NULL, 0); ok(out);}
+#line 150 "core/admin.rl"
+       {
+                       int ret = snapshot(NULL, 0);
+
+                       if (ret == 0)
+                               ok(out);
+                       else {
+                               tbuf_printf(err, " can't save snapshot, errno 
%d (%s)",
+                                           ret, strerror(ret));
+
+                               fail(out, err);
+                       }
+               }
        goto st7;
 tr93:
 #line 113 "core/admin.rl"
@@ -338,30 +360,30 @@ tr93:
                }
        goto st7;
 tr107:
-#line 172 "core/admin.rl"
+#line 185 "core/admin.rl"
        {start(out); fiber_info(out); end(out);}
        goto st7;
 tr113:
-#line 171 "core/admin.rl"
+#line 184 "core/admin.rl"
        {start(out); mod_info(out); end(out);}
        goto st7;
 tr118:
-#line 175 "core/admin.rl"
+#line 188 "core/admin.rl"
        {start(out); palloc_stat(out); end(out);}
        goto st7;
 tr126:
-#line 174 "core/admin.rl"
+#line 187 "core/admin.rl"
        {start(out); slab_stat(out); end(out);}
        goto st7;
 tr130:
-#line 176 "core/admin.rl"
+#line 189 "core/admin.rl"
        {start(out); stat_print(out);end(out);}
        goto st7;
 st7:
        if ( ++p == pe )
                goto _test_eof7;
 case 7:
-#line 365 "core/admin.c"
+#line 387 "core/admin.c"
        if ( (*p) == 10 )
                goto st108;
        goto st0;
@@ -442,28 +464,28 @@ case 15:
        }
        goto tr25;
 tr25:
-#line 166 "core/admin.rl"
+#line 179 "core/admin.rl"
        {strstart = p;}
        goto st16;
 st16:
        if ( ++p == pe )
                goto _test_eof16;
 case 16:
-#line 453 "core/admin.c"
+#line 475 "core/admin.c"
        switch( (*p) ) {
                case 10: goto tr28;
                case 13: goto tr29;
        }
        goto st16;
 tr26:
-#line 166 "core/admin.rl"
+#line 179 "core/admin.rl"
        {strstart = p;}
        goto st17;
 st17:
        if ( ++p == pe )
                goto _test_eof17;
 case 17:
-#line 467 "core/admin.c"
+#line 489 "core/admin.c"
        switch( (*p) ) {
                case 10: goto tr28;
                case 13: goto tr29;
@@ -1427,7 +1449,7 @@ case 107:
        _out: {}
        }
 
-#line 186 "core/admin.rl"
+#line 199 "core/admin.rl"
 
 
        fiber->rbuf->len -= (void *)pe - (void *)fiber->rbuf->data;
diff --git a/core/admin.rl b/core/admin.rl
index 896f203..1be6360 100644
--- a/core/admin.rl
+++ b/core/admin.rl
@@ -147,6 +147,19 @@ admin_dispatch(void)
                                ok(out);
                }
 
+               action save_snapshot {
+                       int ret = snapshot(NULL, 0);
+
+                       if (ret == 0)
+                               ok(out);
+                       else {
+                               tbuf_printf(err, " can't save snapshot, errno 
%d (%s)",
+                                           ret, strerror(ret));
+
+                               fail(out, err);
+                       }
+               }
+
                eol = "\n" | "\r\n";
                show = "sh"("o"("w")?)?;
                info = "in"("f"("o")?)?;
@@ -175,7 +188,7 @@ admin_dispatch(void)
                            show " "+ palloc            %{start(out); 
palloc_stat(out); end(out);}      |
                            show " "+ stat              %{start(out); 
stat_print(out);end(out);}        |
                            save " "+ coredump          %{coredump(60); 
ok(out);}                       |
-                           save " "+ snapshot          %{snapshot(NULL, 0); 
ok(out);}                  |
+                           save " "+ snapshot          %save_snapshot          
                        |
                            exec " "+ string            %mod_exec               
                        |
                            check " "+ slab             %{slab_validate(); 
ok(out);}                    |
                            reload " "+ configuration   %reload_configuration);
diff --git a/core/diagnostics.c b/core/diagnostics.c
new file mode 100644
index 0000000..237c02d
--- /dev/null
+++ b/core/diagnostics.c
@@ -0,0 +1,90 @@
+/*
+ * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR 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 "diagnostics.h"
+#include "fiber.h"
+#include <errno.h>
+#include <string.h>
+
+static struct Error oom_error = { ENOMEM, "Out of memory" };
+
+/**
+ * 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.
+ */
+
+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);
+}
+
+
+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;
+}
+
+
+void diag_clear()
+{
+       error_destroy(fiber->diagnostics);
+}
diff --git a/core/diagnostics.h b/core/diagnostics.h
new file mode 100644
index 0000000..c136c8e
--- /dev/null
+++ b/core/diagnostics.h
@@ -0,0 +1,58 @@
+#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
+ * 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR 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.
+ */
+
+/*
+ * 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.
+ */
+
+struct Error
+{
+       /** 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);
+
+/** Return the last error. Return NULL if no error.
+ */
+struct Error *diag_get_last_error();
+
+/** Clear the last error, if any.
+ */
+void diag_clear();
+
+#endif /* TARANTOOL_CORE_DIAGNOSTICS_H_INCLUDED */
diff --git a/core/fiber.c b/core/fiber.c
index 173bc9f..3d184ad 100644
--- a/core/fiber.c
+++ b/core/fiber.c
@@ -53,6 +53,7 @@
 #include <util.h>
 #include <stat.h>
 #include <pickle.h>
+#include "diagnostics.h"
 
 static struct fiber sched;
 struct fiber *fiber = &sched;
@@ -154,6 +155,18 @@ fiber_sleep(ev_tstamp delay)
        yield();
 }
 
+
+/** Wait for a forked child to complete. */
+
+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);
+}
+
 void
 wait_for(int events)
 {
@@ -305,17 +318,22 @@ fiber_gc(void)
        prelease(ex_pool);
 }
 
-void
-fiber_zombificate(struct fiber *f)
+
+/** Destroy the currently active fiber and prepare it for reuse.
+ */
+
+static void
+fiber_zombificate()
 {
-       f->name = NULL;
-       f->f = NULL;
-       f->data = NULL;
-       unregister_fid(f);
-       f->fid = 0;
-       fiber_alloc(f);
-
-       SLIST_INSERT_HEAD(&zombie_fibers, f, zombie_link);
+       diag_clear();
+       fiber->name = NULL;
+       fiber->f = NULL;
+       fiber->data = NULL;
+       unregister_fid(fiber);
+       fiber->fid = 0;
+       fiber_alloc(fiber);
+
+       SLIST_INSERT_HEAD(&zombie_fibers, fiber, zombie_link);
 }
 
 static void
@@ -330,7 +348,7 @@ fiber_loop(void *data __unused__)
                }
 
                fiber_close();
-               fiber_zombificate(fiber);
+               fiber_zombificate();
                yield();        /* give control back to scheduler */
        }
 }
@@ -367,7 +385,8 @@ fiber_create(const char *restrict name, int fd, int 
inbox_size, void (*f) (void
                fiber_alloc(fiber);
                ev_init(&fiber->io, (void *)ev_schedule);
                ev_init(&fiber->timer, (void *)ev_schedule);
-               fiber->io.data = fiber->timer.data = fiber;
+               ev_init(&fiber->cw, (void *)ev_schedule);
+               fiber->io.data = fiber->timer.data = fiber->cw.data = fiber;
 
                SLIST_INSERT_HEAD(&fibers, fiber, link);
        }
diff --git a/core/log_io.c b/core/log_io.c
index c84c45e..ea57297 100644
--- a/core/log_io.c
+++ b/core/log_io.c
@@ -46,6 +46,7 @@
 #include <util.h>
 #include <pickle.h>
 #include <tbuf.h>
+#include "diagnostics.h"
 
 const u16 snap_tag = -1;
 const u16 wal_tag = -2;
@@ -58,6 +59,7 @@ 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";
@@ -179,7 +181,7 @@ snap_classes(row_reader snap_row_reader, const char 
*dirname)
        c[1]->filetype = c[0]->filetype;
        c[1]->suffix = c[0]->suffix;
 
-       c[0]->dirname = c[1]->dirname = dirname;
+       c[0]->dirname = c[1]->dirname = dirname ? strdup(dirname) : NULL;
        return c;
 }
 
@@ -198,7 +200,7 @@ xlog_classes(const char *dirname)
        xlog04_class(c[0]);
        v11_class(c[1]);
 
-       c[0]->dirname = c[1]->dirname = dirname;
+       c[0]->dirname = c[1]->dirname = dirname ? strdup(dirname) : NULL;
        return c;
 }
 
@@ -365,14 +367,30 @@ scan_dir(struct log_io_class *class, i64 **ret_lsn)
 
        errno = 0;
        while ((dent = readdir(dh)) != NULL) {
-               size_t len = strlen(dent->d_name) + 1;
-               if (len < suffix_len + 1)
+               char *suffix = strrchr(dent->d_name, '.');
+
+               if (suffix == NULL)
                        continue;
-               if (strcmp(dent->d_name + len - 1 - suffix_len, class->suffix))
+
+               char *sub_suffix = memrchr(dent->d_name, '.', suffix - 
dent->d_name);
+
+               /*
+                * A valid suffix is either .xlog or
+                * .xlog.inprogress, given class->suffix ==
+                * 'xlog'.
+                */
+               bool valid_suffix;
+               valid_suffix = (strcmp(suffix, class->suffix) == 0 ||
+                               (sub_suffix != NULL &&
+                                strcmp(suffix, inprogress_suffix) == 0 &&
+                                strncmp(sub_suffix, class->suffix, suffix_len) 
== 0));
+
+               if (!valid_suffix)
                        continue;
 
                lsn[i] = strtoll(dent->d_name, &parse_suffix, 10);
-               if (strcmp(parse_suffix, class->suffix) != 0) { /* d_name 
doesn't parse entirely, ignore it */
+               if (strncmp(parse_suffix, class->suffix, suffix_len) != 0) {
+                       /* d_name doesn't parse entirely, ignore it */
                        say_warn("can't parse `%s', skipping", dent->d_name);
                        continue;
                }
@@ -548,12 +566,59 @@ row_reader_v11(FILE *f, struct palloc_pool *pool)
        return m;
 }
 
+static int
+inprogress_log_rename(char *filename)
+{
+       char *new_filename;
+       char *suffix = strrchr(filename, '.');
+
+       assert(suffix);
+       assert(strcmp(suffix, inprogress_suffix) == 0);
+
+       /* Create a new filename without '.inprogress' suffix. */
+       new_filename = strndupa(filename, suffix - filename);
+
+       if (rename(filename, new_filename) != 0) {
+               say_syserror("can't rename %s to %s", filename, new_filename);
+
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+inprogress_log_unlink(char *filename)
+{
+#ifndef NDEBUG
+       char *suffix = strrchr(filename, '.');
+       assert(suffix);
+       assert(strcmp(suffix, inprogress_suffix) == 0);
+#endif
+       if (unlink(filename) != 0) {
+               if (errno == ENONET)
+                       return 0;
+
+               say_syserror("can't unlink %s", filename);
+
+               return -1;
+       }
+
+       return 0;
+}
+
 int
 close_log(struct log_io **lptr)
 {
        struct log_io *l = *lptr;
        int r;
 
+       if (l->rows == 1 && l->mode == LOG_WRITE) {
+               /* Rename WAL before finalize. */
+               if (inprogress_log_rename(l->filename) != 0)
+                       panic("can't rename 'inprogress' WAL");
+       }
+
        if (l->class->eof_marker_size > 0 && 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");
@@ -628,13 +693,12 @@ format_filename(char *filename, struct log_io_class 
*class, i64 lsn, int suffix)
                         class->dirname, lsn, class->suffix);
                break;
        case -1:
-               snprintf(filename, PATH_MAX, "%s/%020" PRIi64 "%s.inprogress",
-                        class->dirname, lsn, class->suffix);
+               snprintf(filename, PATH_MAX, "%s/%020" PRIi64 "%s%s",
+                        class->dirname, lsn, class->suffix, inprogress_suffix);
                break;
        default:
-               snprintf(filename, PATH_MAX, "%s/%020" PRIi64 "%s.%i",
-                        class->dirname, lsn, class->suffix, suffix);
-               break;
+               /* not reached */
+               assert(0);
        }
        return filename;
 }
@@ -646,14 +710,15 @@ open_for_read(struct recovery_state *recover, struct 
log_io_class **class, i64 l
        char filetype[32], version[32], buf[256];
        struct log_io *l = NULL;
        char *r;
-       char *error = "unknown error";
 
-       l = malloc(sizeof(*l));
-       if (l == NULL)
+       l = calloc(1, sizeof(*l));
+       if (l == NULL) {
+               diag_set_error(errno, strerror(errno));
                goto error;
-       memset(l, 0, sizeof(*l));
+       }
        l->mode = LOG_READ;
        l->stat.data = recover;
+       l->is_inprogress = suffix == -1 ? true : false;
 
        /* when filename is not null it is forced open for debug reading */
        if (filename == NULL) {
@@ -668,24 +733,24 @@ open_for_read(struct recovery_state *recover, struct 
log_io_class **class, i64 l
 
        l->f = fopen(l->filename, "r");
        if (l->f == NULL) {
-               error = strerror(errno);
+               diag_set_error(errno, strerror(errno));
                goto error;
        }
 
        r = fgets(filetype, sizeof(filetype), l->f);
        if (r == NULL) {
-               error = "header reading failed";
+               diag_set_error(1, "header reading failed");
                goto error;
        }
 
        r = fgets(version, sizeof(version), l->f);
        if (r == NULL) {
-               error = "header reading failed";
+               diag_set_error(1, "header reading failed");
                goto error;
        }
 
        if (strcmp((*class)->filetype, filetype) != 0) {
-               error = "unknown filetype";
+               diag_set_error(1, "unknown filetype");
                goto error;
        }
 
@@ -696,7 +761,7 @@ open_for_read(struct recovery_state *recover, struct 
log_io_class **class, i64 l
        }
 
        if (*class == NULL) {
-               error = "unknown version";
+               diag_set_error(1, "unknown version");
                goto error;
        }
        l->class = *class;
@@ -705,7 +770,7 @@ open_for_read(struct recovery_state *recover, struct 
log_io_class **class, i64 l
                for (;;) {
                        r = fgets(buf, sizeof(buf), l->f);
                        if (r == NULL) {
-                               error = "header reading failed";
+                               diag_set_error(1, "header reading failed");
                                goto error;
                        }
                        if (strcmp(r, "\n") == 0 || strcmp(r, "\r\n") == 0)
@@ -714,14 +779,15 @@ open_for_read(struct recovery_state *recover, struct 
log_io_class **class, i64 l
        } else {
                r = fgets(buf, sizeof(buf), l->f);      /* skip line with time 
*/
                if (r == NULL) {
-                       error = "header reading failed";
+                       diag_set_error(1, "header reading failed");
                        goto error;
                }
        }
 
        return l;
       error:
-       say_error("open_for_read: failed to open `%s': %s", l->filename, error);
+       say_error("open_for_read: failed to open `%s': %s", l->filename,
+                 diag_get_last_error()->msg);
        if (l != NULL) {
                if (l->f != NULL)
                        fclose(l->f);
@@ -735,12 +801,14 @@ open_for_write(struct recovery_state *recover, struct 
log_io_class *class, i64 l
 {
        struct log_io *l = NULL;
        int fd;
-       char *error = "unknown error";
+       char *dot;
+       bool exists;
 
-       l = malloc(sizeof(*l));
-       if (l == NULL)
+       l = calloc(1, sizeof(*l));
+       if (l == NULL) {
+               diag_set_error(errno, strerror(errno));
                goto error;
-       memset(l, 0, sizeof(*l));
+       }
        l->mode = LOG_WRITE;
        l->class = class;
        l->stat.data = recover;
@@ -750,15 +818,34 @@ open_for_write(struct recovery_state *recover, struct 
log_io_class *class, i64 l
        format_filename(l->filename, class, lsn, suffix);
        say_debug("find_log for writing `%s'", l->filename);
 
+       if (suffix == -1) {
+               /*
+                * Check whether a file with this name already exists.
+                * We don't overwrite existing files.
+                */
+               dot = strrchr(l->filename, '.');
+               *dot = '\0';
+               exists = access(l->filename, F_OK) == 0;
+               *dot = '.';
+               if (exists) {
+                       diag_set_error(EEXIST, "exists");
+                       goto error;
+               }
+       }
+
+       /*
+        * Open the <lsn>.<suffix>.inprogress file. If it
+        * exists, open will fail.
+        */
        fd = open(l->filename, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, 0664);
        if (fd < 0) {
-               error = strerror(errno);
+               diag_set_error(errno, strerror(errno));
                goto error;
        }
 
        l->f = fdopen(fd, "a");
        if (l->f == NULL) {
-               error = strerror(errno);
+               diag_set_error(errno, strerror(errno));
                goto error;
        }
 
@@ -766,7 +853,8 @@ open_for_write(struct recovery_state *recover, struct 
log_io_class *class, i64 l
        write_header(l);
        return l;
       error:
-       say_error("find_log: failed to open `%s': %s", l->filename, error);
+       say_error("find_log: failed to open `%s': %s", l->filename,
+                 diag_get_last_error()->msg);
        if (l != NULL) {
                if (l->f != NULL)
                        fclose(l->f);
@@ -938,8 +1026,6 @@ recover_remaining_wals(struct recovery_state *r)
 {
        int result = 0;
        struct log_io *next_wal;
-       char *name;
-       int suffix = 0;
        i64 current_lsn, wal_greatest_lsn;
        size_t rows_before;
 
@@ -965,13 +1051,30 @@ recover_remaining_wals(struct recovery_state *r)
                }
 
                current_lsn = r->confirmed_lsn + 1;     /* TODO: find better 
way looking for next xlog */
-               next_wal = open_for_read(r, r->wal_class, current_lsn, suffix, 
NULL);
+               next_wal = open_for_read(r, r->wal_class, current_lsn, 0, NULL);
+
+               /*
+                * When doing final recovery, and dealing with the
+                * last file, try opening .<suffix>.inprogress.
+                */
+               if (next_wal == NULL && r->finalize && current_lsn == 
wal_greatest_lsn) {
+                       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);
+
+                               say_warn("unlink broken %s wal", filename);
+                               if (inprogress_log_unlink(filename) != 0)
+                                       panic("can't unlink 'inprogres' wal");
+                       }
+               }
+
                if (next_wal == NULL) {
-                       if (suffix++ < 10)
-                               continue;
                        result = 0;
                        break;
                }
+
+
                assert(r->current_wal == NULL);
                r->current_wal = next_wal;
                say_info("recover from `%s'", r->current_wal->filename);
@@ -987,26 +1090,10 @@ recover_remaining_wals(struct recovery_state *r)
                if (r->current_wal->rows > 0 && r->current_wal->rows != 
rows_before)
                        r->current_wal->retry = 0;
 
-               /*
-                * rows == 0 could possible indicate an filename confilct
-                * retry filename with same lsn but with bigger suffix
-                */
+               /* rows == 0 could possible indicate to an empty WAL */
                if (r->current_wal->rows == 0) {
-                       say_error("read zero records from %s, RETRY", 
r->current_wal->filename);
-                       if (suffix++ < 10)
-                               continue;
-
-                       say_error("too many filename conflicters");
-                       result = -1;
+                       say_error("read zero records from %s", 
r->current_wal->filename);
                        break;
-               } else {
-                       name = format_filename(NULL, r->wal_prefered_class,
-                                              current_lsn, suffix + 1);
-                       if (access(name, F_OK) == 0) {
-                               say_error("found conflicter `%s' after 
successful reading", name);
-                               result = -1;
-                               break;
-                       }
                }
 
                if (result == LOG_EOF) {
@@ -1014,15 +1101,14 @@ recover_remaining_wals(struct recovery_state *r)
                                 r->confirmed_lsn);
                        close_log(&r->current_wal);
                }
-               suffix = 0;
        }
 
        /*
-        * it's not a fatal error then last wal is empty
-        * but if we lost some logs it is fatal error
+        * It's not a fatal error when last WAL is empty, but if
+        * we lost some logs it is a fatal error.
         */
        if (wal_greatest_lsn > r->confirmed_lsn + 1) {
-               say_error("not all wals have been successfuly read");
+               say_error("not all WALs have been successfully read");
                result = -1;
        }
 
@@ -1140,6 +1226,8 @@ recover_finalize(struct recovery_state *r)
 {
        int result;
 
+       r->finalize = true;
+
        if (ev_is_active(&r->wal_timer))
                ev_timer_stop(&r->wal_timer);
 
@@ -1150,10 +1238,29 @@ recover_finalize(struct recovery_state *r)
 
        result = recover_remaining_wals(r);
        if (result < 0)
-               panic("unable to scucessfully finalize recovery");
+               panic("unable to successfully finalize recovery");
 
        if (r->current_wal != NULL && result != LOG_EOF) {
                say_warn("wal `%s' wasn't correctly closed", 
r->current_wal->filename);
+
+               if (!r->current_wal->is_inprogress) {
+                       if (r->current_wal->rows == 0)
+                               /* Regular WAL (not inprogress) must contain at 
least one row */
+                               panic("zero rows was successfully read from 
last WAL `%s'",
+                                     r->current_wal->filename);
+               } else if (r->current_wal->rows == 0) {
+                       /* Unlink empty inprogress WAL */
+                       say_warn("unlink broken %s wal", 
r->current_wal->filename);
+                       if (inprogress_log_unlink(r->current_wal->filename) != 
0)
+                               panic("can't unlink 'inprogress' wal");
+               } else if (r->current_wal->rows == 1) {
+                       /* Rename inprogress wal with one row */
+                       say_warn("rename unfinished %s wal", 
r->current_wal->filename);
+                       if (inprogress_log_rename(r->current_wal->filename) != 
0)
+                               panic("can't rename 'inprogress' wal");
+               } else
+                       panic("too many rows in inprogress WAL `%s'", 
r->current_wal->filename);
+
                close_log(&r->current_wal);
        }
 }
@@ -1169,11 +1276,9 @@ write_to_disk(void *_state, struct tbuf *t)
 {
        static struct log_io *wal = NULL, *wal_to_close = NULL;
        static ev_tstamp last_flush = 0;
-       static size_t rows = 0;
        struct tbuf *reply, *header;
        struct recovery_state *r = _state;
        u32 result = 0;
-       int suffix = 0;
 
        /* we're not running inside ev_loop, so update ev_now manually */
        ev_now_update();
@@ -1187,11 +1292,17 @@ write_to_disk(void *_state, struct tbuf *t)
 
        reply = tbuf_alloc(t->pool);
 
-       /* if there is filename conflict, try filename with lager suffix */
-       while (wal == NULL && suffix < 10) {
-               wal = open_for_write(r, r->wal_prefered_class, 
wal_write_request(t)->lsn, suffix);
-               suffix++;
+       if (wal == NULL)
+               /* Open WAL with '.inprogress' suffix. */
+               wal = open_for_write(r, r->wal_prefered_class, 
wal_write_request(t)->lsn, -1);
+       else if (wal->rows == 1) {
+               /* rename wal after first successfull write to name without 
inprogress suffix*/
+               if (inprogress_log_rename(wal->filename) != 0) {
+                       say_error("can't rename inprogress wal");
+                       goto fail;
+               }
        }
+
        if (wal_to_close != NULL) {
                if (close_log(&wal_to_close) != 0)
                        goto fail;
@@ -1242,13 +1353,11 @@ write_to_disk(void *_state, struct tbuf *t)
                last_flush = ev_now();
        }
 
-       rows++;
-       if (wal->class->rows_per_file <= rows ||
+       wal->rows++;
+       if (wal->class->rows_per_file <= wal->rows ||
            (wal_write_request(t)->lsn + 1) % wal->class->rows_per_file == 0) {
                wal_to_close = wal;
                wal = NULL;
-               rows = 0;
-               suffix = 0;
        }
 
        tbuf_append(reply, &result, sizeof(result));
@@ -1296,6 +1405,9 @@ recover_init(const char *snap_dirname, const char 
*wal_dirname,
 {
        struct recovery_state *r = p0alloc(eter_pool, sizeof(*r));
 
+       if (rows_per_file <= 1)
+               panic("inacceptable value of 'rows_per_file'");
+
        r->wal_timer.data = r;
        r->row_handler = row_handler;
        r->data = data;
@@ -1416,13 +1528,19 @@ snapshot_save(struct recovery_state *r, void (*f) 
(struct log_io_iter *))
 
        snap = open_for_write(r, r->snap_prefered_class, r->confirmed_lsn, -1);
        if (snap == NULL)
-               panic("can't open snap for writing");
+               panic_status(diag_get_last_error()->code,
+                            "can't open snap for writing");
 
        iter_open(snap, &i, write_rows);
 
        if (r->snap_io_rate_limit > 0)
                i.io_rate_limit = r->snap_io_rate_limit;
 
+       /*
+        * While saving a snapshot, snapshot name is set to
+        * <lsn>.snap.inprogress. When done, the snapshot is
+        * renamed to <lsn>.snap.
+        */
        strncpy(final_filename, snap->filename, PATH_MAX);
        dot = strrchr(final_filename, '.');
        *dot = 0;
@@ -1433,8 +1551,11 @@ snapshot_save(struct recovery_state *r, void (*f) 
(struct log_io_iter *))
        if (fsync(fileno(snap->f)) < 0)
                panic("fsync");
 
-       if (rename(snap->filename, final_filename) != 0)
-               panic("rename");
+       if (link(snap->filename, final_filename) == -1)
+               panic_status(errno, "can't create hard link to snapshot");
+
+       if (unlink(snap->filename) == -1)
+               say_syserror("can't unlink 'inprogress' snapshot");
 
        close_log(&snap);
 
diff --git a/core/tarantool.c b/core/tarantool.c
index da268a4..6964498 100644
--- a/core/tarantool.c
+++ b/core/tarantool.c
@@ -112,8 +112,9 @@ reload_cfg(struct tbuf *out)
                return -1;
        }
        ret = load_cfg(&new_cfg1, 1);
-       tbuf_append(out, cfg_out->data, cfg_out->len);
        if (ret == -1) {
+               tbuf_append(out, cfg_out->data, cfg_out->len);
+
                destroy_tarantool_cfg(&new_cfg1);
 
                return -1;
@@ -125,8 +126,9 @@ reload_cfg(struct tbuf *out)
                return -1;
        }
        ret = load_cfg(&new_cfg2, 0);
-       tbuf_append(out, cfg_out->data, cfg_out->len);
        if (ret == -1) {
+               tbuf_append(out, cfg_out->data, cfg_out->len);
+
                destroy_tarantool_cfg(&new_cfg1);
 
                return -1;
@@ -138,6 +140,7 @@ reload_cfg(struct tbuf *out)
                destroy_tarantool_cfg(&new_cfg2);
 
                out_warning(0, "Could not accept read only '%s' option", diff);
+               tbuf_append(out, cfg_out->data, cfg_out->len);
 
                return -1;
        }
@@ -167,16 +170,21 @@ tarantool_uptime(void)
 }
 
 #ifdef STORAGE
-void
+int
 snapshot(void *ev __unused__, int events __unused__)
 {
        pid_t p = fork();
        if (p < 0) {
                say_syserror("fork");
-               return;
+               return -1;
+       }
+       if (p > 0) {
+               wait_for_child(p);
+
+               assert(p == fiber->cw.rpid);
+
+               return WEXITSTATUS(fiber->cw.rstatus);
        }
-       if (p > 0)
-               return;
 
        fiber->name = "dumper";
        set_proc_title("dumper (%" PRIu32 ")", getppid());
@@ -186,16 +194,10 @@ snapshot(void *ev __unused__, int events __unused__)
        __gcov_flush();
 #endif
        _exit(EXIT_SUCCESS);
-}
-#endif
 
-static void
-sig_child(int signal __unused__)
-{
-       int child_status;
-       /* TODO: watch child status & destroy corresponding fibers */
-       while (waitpid(-1, &child_status, WNOHANG) > 0) ;
+       return 0;
 }
+#endif
 
 static void
 sig_int(int signal)
@@ -231,12 +233,6 @@ signal_init(void)
        if (sigaction(SIGPIPE, &sa, 0) == -1)
                goto error;
 
-       sa.sa_handler = sig_child;
-       sa.sa_flags = SA_RESTART;
-       sigemptyset(&sa.sa_mask);
-       if (sigaction(SIGCHLD, &sa, NULL) == -1)
-               goto error;
-
        sa.sa_handler = sig_int;
        sa.sa_flags = 0;
        sigemptyset(&sa.sa_mask);
diff --git a/include/fiber.h b/include/fiber.h
index 35601ba..9113d06 100644
--- a/include/fiber.h
+++ b/include/fiber.h
@@ -65,6 +65,7 @@ struct fiber {
        int fd;
 
        ev_timer timer;
+       ev_child cw;
 
        struct tbuf *iov;
        size_t iov_cnt;
@@ -83,6 +84,8 @@ struct fiber {
        void *f_data;
 
        void *data;
+       /** Information about the last error. */
+       void *diagnostics;
 
        u64 cookie;
        bool has_peer;
@@ -109,8 +112,8 @@ 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_zombificate(struct fiber *f);
 void wait_for(int events);
+void wait_for_child(pid_t pid);
 void unwait(int events);
 void yield(void);
 void raise_(int);
diff --git a/include/log_io.h b/include/log_io.h
index 25e7246..64fe367 100644
--- a/include/log_io.h
+++ b/include/log_io.h
@@ -73,6 +73,8 @@ struct log_io {
        size_t rows;
        size_t retry;
        char filename[PATH_MAX + 1];
+
+       bool is_inprogress;
 };
 
 struct recovery_state {
@@ -92,6 +94,8 @@ struct recovery_state {
        int snap_io_rate_limit;
        u64 cookie;
 
+       bool finalize;
+
        /* pointer to user supplied custom data */
        void *data;
 };
diff --git a/include/say.h b/include/say.h
index a0cce23..b89183f 100644
--- a/include/say.h
+++ b/include/say.h
@@ -54,13 +54,14 @@ void _say(int level, const char *filename, int line, const 
char *error,
 
 #define say(level, ...) ({ _say(level, __FILE__, __LINE__, __VA_ARGS__); })
 
-#define panic(...)             ({ say(S_FATAL, NULL, __VA_ARGS__); 
exit(EXIT_FAILURE); })
-#define panic_syserror(...)    ({ say(S_FATAL, strerror(errno), __VA_ARGS__); 
exit(EXIT_FAILURE); })
-#define say_syserror(...)      say(S_ERROR, strerror(errno), __VA_ARGS__)
-#define say_error(...)         say(S_ERROR, NULL, __VA_ARGS__)
-#define say_crit(...)          say(S_CRIT, NULL, __VA_ARGS__)
-#define say_warn(...)          say(S_WARN, NULL, __VA_ARGS__)
-#define say_info(...)          say(S_INFO, NULL, __VA_ARGS__)
-#define say_debug(...)         say(S_DEBUG, NULL, __VA_ARGS__)
+#define panic_status(status, ...)      ({ say(S_FATAL, NULL, __VA_ARGS__); 
exit(status); })
+#define panic(...)                     panic_status(EXIT_FAILURE, __VA_ARGS__)
+#define panic_syserror(...)            ({ say(S_FATAL, strerror(errno), 
__VA_ARGS__); exit(EXIT_FAILURE); })
+#define say_syserror(...)              say(S_ERROR, strerror(errno), 
__VA_ARGS__)
+#define say_error(...)                 say(S_ERROR, NULL, __VA_ARGS__)
+#define say_crit(...)                  say(S_CRIT, NULL, __VA_ARGS__)
+#define say_warn(...)                  say(S_WARN, NULL, __VA_ARGS__)
+#define say_info(...)                  say(S_INFO, NULL, __VA_ARGS__)
+#define say_debug(...)                 say(S_DEBUG, NULL, __VA_ARGS__)
 
 #endif
diff --git a/include/tarantool.h b/include/tarantool.h
index 47f1664..78b80b8 100644
--- a/include/tarantool.h
+++ b/include/tarantool.h
@@ -48,7 +48,7 @@ extern const char *cfg_filename;
 extern bool init_storage, booting;
 extern char *binary_filename;
 i32 reload_cfg(struct tbuf *out);
-void snapshot(void *ev __unused__, int events __unused__);
+int snapshot(void *ev __unused__, int events __unused__);
 const char *tarantool_version(void);
 void tarantool_info(struct tbuf *out);
 double tarantool_uptime(void);
diff --git a/test/box/bad_record.xlog b/test/box/bad_record.xlog
new file mode 100644
index 0000000..28e8cd3
--- /dev/null
+++ b/test/box/bad_record.xlog
@@ -0,0 +1,4 @@
+XLOG
+0.11
+
+bad record
diff --git a/test/box/empty.xlog b/test/box/empty.xlog
new file mode 100644
index 0000000..e69de29
diff --git a/test/box/just_header.xlog b/test/box/just_header.xlog
new file mode 100644
index 0000000..3472cc4
--- /dev/null
+++ b/test/box/just_header.xlog
@@ -0,0 +1,3 @@
+XLOG
+0.11
+
diff --git a/test/box/reconfigure.result b/test/box/reconfigure.result
index 7faaa71..3ddcf40 100644
--- a/test/box/reconfigure.result
+++ b/test/box/reconfigure.result
@@ -3,6 +3,7 @@ reload configuration
 fail:
  - Could not accept read only 'slab_alloc_arena' option
  - Could not accept read only 'pid_file' option
+ - Could not accept read only 'logger' option
  - Could not accept read only 'primary_port' option
  - Could not accept read only 'secondary_port' option
  - Could not accept read only 'admin_port' option
@@ -50,3 +51,49 @@ reload configuration
 fail:
  - can't open config `tarantool.cfg'
 ...
+reload configuration
+---
+ok
+...
+#
+# A test case for http://bugs.launchpad.net/bugs/712447:
+# Valgrind reports use of not initialized memory after 'reload
+# configuration'
+#
+insert into t0 values (1, 'tuple')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+reload configuration
+---
+fail:
+ - can't open config `tarantool.cfg'
+...
+insert into t0 values (2, 'tuple 2')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+reload configuration
+---
+ok
+...
+insert into t0 values (3, 'tuple 3')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+reload configuration
+---
+ok
+...
+delete from t0 where k0 = 1
+Delete OK, 1 row affected
+delete from t0 where k0 = 2
+Delete OK, 1 row affected
+delete from t0 where k0 = 3
+Delete OK, 1 row affected
diff --git a/test/box/reconfigure.test b/test/box/reconfigure.test
index 6a292b1..afa62fe 100644
--- a/test/box/reconfigure.test
+++ b/test/box/reconfigure.test
@@ -44,6 +44,32 @@ exec admin "reload configuration"
 os.unlink(cfg)
 exec admin "reload configuration"
 
+# cleanup
 # restore default
 os.rename(oldcfg, cfg)
+exec admin "reload configuration"
+
+print """#
+# A test case for http://bugs.launchpad.net/bugs/712447:
+# Valgrind reports use of not initialized memory after 'reload
+# configuration'
+#"""
+exec sql "insert into t0 values (1, 'tuple')"
+exec admin "save snapshot"
+os.rename(cfg, oldcfg)
+exec admin "reload configuration"
+exec sql "insert into t0 values (2, 'tuple 2')"
+exec admin "save snapshot"
+os.symlink(abspath("box/tarantool_good.cfg"), cfg)
+exec admin "reload configuration"
+exec sql "insert into t0 values (3, 'tuple 3')"
+exec admin "save snapshot"
+# Cleanup
+os.unlink(cfg)
+os.rename(oldcfg, cfg)
+exec admin "reload configuration"
+exec sql "delete from t0 where k0 = 1"
+exec sql "delete from t0 where k0 = 2"
+exec sql "delete from t0 where k0 = 3"
+
 # vim: syntax=python
diff --git a/test/box/show.result b/test/box/show.result
index bc4af41..d00dfc6 100644
--- a/test/box/show.result
+++ b/test/box/show.result
@@ -35,7 +35,7 @@ configuration:
   slab_alloc_factor: "2"
   work_dir: (null)
   pid_file: "box.pid"
-  logger: (null)
+  logger: "tee --append tarantool.log"
   logger_nonblock: "1"
   io_collect_interval: "0"
   backlog: "1024"
@@ -82,10 +82,14 @@ save coredump
 ---
 ok
 ...
+insert into t0 values (1, 'tuple')
+Insert OK, 1 row affected
 save snapshot
 ---
 ok
 ...
+delete from t0 where k0 = 1
+Delete OK, 1 row affected
 exec module command
 ---
 unimplemented
@@ -94,10 +98,10 @@ show info
 ---
 info:
   version: "1.3.minor-<rev>-<commit>
-  uptime: 0
+  uptime: <uptime>
   pid: <pid>
   wal_writer_pid: <pid>
-  lsn: 1
+  lsn: 3
   recovery_lag: 0.000
   recovery_last_update: 0.000
   status: primary
diff --git a/test/box/show.test b/test/box/show.test
index 2c2dbd1..2e4de22 100644
--- a/test/box/show.test
+++ b/test/box/show.test
@@ -9,10 +9,13 @@ exec admin "help"
 exec admin "show configuration"
 exec admin "show stat"
 exec admin "save coredump"
+exec sql "insert into t0 values (1, 'tuple')"
 exec admin "save snapshot"
+exec sql "delete from t0 where k0 = 1"
 exec admin "exec module command"
 sys.stdout.push_filter("(\d\.\d)\.\d-\d+-\S+", "\\1.minor-<rev>-<commit>")
 sys.stdout.push_filter("pid: \d+", "pid: <pid>")
+sys.stdout.push_filter("uptime: \d+", "uptime: <uptime>")
 exec admin "show info"
 sys.stdout.clear_all_filters()
 sys.stdout.push_filter(".*", "")
diff --git a/test/box/snapshot.result b/test/box/snapshot.result
index 2167ee5..edb9e17 100644
--- a/test/box/snapshot.result
+++ b/test/box/snapshot.result
@@ -1505,3 +1505,31 @@ Found 1 tuple:
 select * from t0 where k0=500
 No match
 # Restore the default server...
+#
+# A test case for: http://bugs.launchpad.net/bugs/686411
+# Check that 'save snapshot' does not overwrite a snapshot
+# file that already exists. Verify also that any other
+# error that happens when saving snapshot is propagated
+# to the caller.
+
+insert into t0 values (1, 'first tuple')
+Insert OK, 1 row affected
+save snapshot
+---
+ok
+...
+save snapshot
+---
+fail: can't save snapshot, errno 17 (File exists)
+...
+insert into t0 values (2, 'second tuple')
+Insert OK, 1 row affected
+# Make 'var' directory read-only.
+save snapshot
+---
+fail: can't save snapshot, errno 13 (Permission denied)
+...
+delete from t0 where k0 = 1
+Delete OK, 1 row affected
+delete from t0 where k0 = 2
+Delete OK, 1 row affected
diff --git a/test/box/snapshot.test b/test/box/snapshot.test
index 3eef198..764699f 100644
--- a/test/box/snapshot.test
+++ b/test/box/snapshot.test
@@ -1,4 +1,5 @@
 # encoding: tarantool
+#
 print """
 # Verify that the server starts from a pre-recorded snapshot.
 # This way we check that the server can read old snapshots (v11)
@@ -15,4 +16,33 @@ server.stop(True)
 os.unlink(snapshot)
 server.start(True)
 
-# vim: syntax=python
+print """#
+# A test case for: http://bugs.launchpad.net/bugs/686411
+# Check that 'save snapshot' does not overwrite a snapshot
+# file that already exists. Verify also that any other
+# error that happens when saving snapshot is propagated
+# to the caller.
+"""
+exec sql "insert into t0 values (1, 'first tuple')"
+exec admin "save snapshot"
+
+# In absence of data modifications, two consecutive
+# 'save snapshot' statements will try to write
+# into the same file, since file name is based
+# on LSN.
+#  Don't allow to overwrite snapshots.
+exec admin "save snapshot"
+#
+# Increment LSN
+exec sql "insert into t0 values (2, 'second tuple')"
+#
+# Check for other errors, e.g. "Permission denied".
+print "# Make 'var' directory read-only."
+os.chmod(vardir, 0555)
+exec admin "save snapshot"
+
+# cleanup
+os.chmod(vardir, 0755)
+exec sql "delete from t0 where k0 = 1"
+exec sql "delete from t0 where k0 = 2"
+# vim: syntax=python spell
diff --git a/test/box/tarantool.cfg b/test/box/tarantool.cfg
index c09c23a..d7cd0fe 100644
--- a/test/box/tarantool.cfg
+++ b/test/box/tarantool.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/tarantool_bad1.cfg b/test/box/tarantool_bad1.cfg
index af4eb41..fb0536b 100644
--- a/test/box/tarantool_bad1.cfg
+++ b/test/box/tarantool_bad1.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.2
 
 pid_file = "/var/run/box.pid"
 
+logger="cat tarantool.log"
+
 primary_port = 33023
 secondary_port = 33024
 admin_port = 33025
diff --git a/test/box/tarantool_bad2.cfg b/test/box/tarantool_bad2.cfg
index fa5f170..0d03bfd 100644
--- a/test/box/tarantool_bad2.cfg
+++ b/test/box/tarantool_bad2.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 #primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/tarantool_bad3.cfg b/test/box/tarantool_bad3.cfg
index edac4b2..8c1d14c 100644
--- a/test/box/tarantool_bad3.cfg
+++ b/test/box/tarantool_bad3.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/tarantool_bad4.cfg b/test/box/tarantool_bad4.cfg
index 99bdb38..67b9c89 100644
--- a/test/box/tarantool_bad4.cfg
+++ b/test/box/tarantool_bad4.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/tarantool_bad5.cfg b/test/box/tarantool_bad5.cfg
index 1ba6dfa..1390839 100644
--- a/test/box/tarantool_bad5.cfg
+++ b/test/box/tarantool_bad5.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/tarantool_good.cfg b/test/box/tarantool_good.cfg
index c09c23a..d7cd0fe 100644
--- a/test/box/tarantool_good.cfg
+++ b/test/box/tarantool_good.cfg
@@ -2,6 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
 
+logger="tee --append tarantool.log"
+
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
diff --git a/test/box/unfinished.xlog b/test/box/unfinished.xlog
new file mode 100644
index 0000000..c905fec
Binary files /dev/null and b/test/box/unfinished.xlog differ
diff --git a/test/box/xlog.result b/test/box/xlog.result
new file mode 100644
index 0000000..ba345f6
--- /dev/null
+++ b/test/box/xlog.result
@@ -0,0 +1,34 @@
+
+# Inprogress xlog must be renamed before second insert.
+
+insert into t0 values (1, 'first tuple')
+Insert OK, 1 row affected
+00000000000000000002.xlog.inprogress exists
+insert into t0 values (2, 'second tuple')
+Insert OK, 1 row affected
+00000000000000000002.xlog.inprogress has been successfully renamed
+
+# Inprogress xlog must be renamed during regular termination.
+
+insert into t0 values (3, 'third tuple')
+Insert OK, 1 row affected
+00000000000000000004.xlog.inprogress exists
+Stopping the server...
+00000000000000000004.xlog.inprogress has been successfully renamed
+
+# An inprogress xlog fle with one record must be renamed during recovery.
+
+00000000000000000005.xlog.inprogress hash been successfully renamed
+
+# Empty (zero size) inprogress xlog must be deleted during recovery.
+
+00000000000000000006.xlog.inprogress has been successfully deleted
+
+# Empty (header only, no records) inprogress xlog must be deleted
+# during recovery.
+
+00000000000000000006.xlog.inprogress has been successfully deleted
+
+# Inprogress xlog with bad record must be deleted during recovery.
+
+00000000000000000006.xlog.inprogress has been successfully deleted
diff --git a/test/box/xlog.test b/test/box/xlog.test
new file mode 100644
index 0000000..5054043
--- /dev/null
+++ b/test/box/xlog.test
@@ -0,0 +1,113 @@
+# encoding: tarantool
+#
+from os.path import abspath
+
+# cleanup vardir
+server.stop(True)
+server.install(True)
+
+print """
+# Inprogress xlog must be renamed before second insert.
+"""
+wal_inprogress = os.path.join(vardir, "00000000000000000002.xlog.inprogress")
+wal = os.path.join(vardir, "00000000000000000002.xlog")
+
+server.start(True)
+exec sql "insert into t0 values (1, 'first tuple')"
+if os.access(wal_inprogress, os.F_OK):
+  print "00000000000000000002.xlog.inprogress exists"
+
+exec sql "insert into t0 values (2, 'second tuple')"
+
+if os.access(wal, os.F_OK) and not os.access(wal_inprogress, os.F_OK):
+  print "00000000000000000002.xlog.inprogress has been successfully renamed"
+server.stop(True)
+
+print """
+# Inprogress xlog must be renamed during regular termination.
+"""
+server.start(True)
+
+wal_inprogress = os.path.join(vardir, "00000000000000000004.xlog.inprogress")
+wal = os.path.join(vardir, "00000000000000000004.xlog")
+
+exec sql "insert into t0 values (3, 'third tuple')"
+
+if os.access(wal_inprogress, os.F_OK):
+  print "00000000000000000004.xlog.inprogress exists"
+
+server.stop(False)
+
+if os.access(wal, os.F_OK) and not os.access(wal_inprogress, os.F_OK):
+  print "00000000000000000004.xlog.inprogress has been successfully renamed"
+
+print """
+# An inprogress xlog fle with one record must be renamed during recovery.
+"""
+
+wal_inprogress = os.path.join(vardir, "00000000000000000005.xlog.inprogress")
+wal = os.path.join(vardir, "00000000000000000005.xlog")
+
+os.symlink(abspath("box/unfinished.xlog"), wal_inprogress)
+
+server.start(True)
+
+if os.access(wal, os.F_OK) and not os.access(wal_inprogress, os.F_OK):
+  print "00000000000000000005.xlog.inprogress hash been successfully renamed"
+server.stop(True)
+
+print """
+# Empty (zero size) inprogress xlog must be deleted during recovery.
+"""
+
+wal_inprogress = os.path.join(vardir, "00000000000000000006.xlog.inprogress")
+wal = os.path.join(vardir, "00000000000000000006.xlog")
+
+os.symlink(abspath("box/empty.xlog"), wal_inprogress)
+server.start(True)
+
+if not os.access(wal_inprogress, os.F_OK) and not os.access(wal, os.F_OK):
+   print "00000000000000000006.xlog.inprogress has been successfully deleted"
+server.stop(True)
+
+print """
+# Empty (header only, no records) inprogress xlog must be deleted
+# during recovery.
+"""
+
+# If the previous test has failed, there is a dangling link
+# and symlink fails.
+try:
+  os.symlink(abspath("box/just_header.xlog"), wal_inprogress)
+except OSError as e:
+  print e
+
+server.start(True)
+
+if not os.access(wal_inprogress, os.F_OK) and not os.access(wal, os.F_OK):
+   print "00000000000000000006.xlog.inprogress has been successfully deleted"
+server.stop(True)
+
+print """
+# Inprogress xlog with bad record must be deleted during recovery.
+"""
+
+# If the previous test has failed, there is a dangling link
+# and symlink fails.
+try:
+  os.symlink(abspath("box/bad_record.xlog"), wal_inprogress)
+except OSError as e:
+  print e
+
+server.start(True)
+
+if not os.access(wal_inprogress, os.F_OK) and not os.access(wal, os.F_OK):
+   print "00000000000000000006.xlog.inprogress has been successfully deleted"
+server.stop(True)
+
+# cleanup
+server.stop(True)
+server.install(True)
+server.start(True)
+
+# vim: syntax=python
diff --git a/test/lib/tarantool_silverbox_server.py 
b/test/lib/tarantool_silverbox_server.py
index 9008c05..996ced8 100644
--- a/test/lib/tarantool_silverbox_server.py
+++ b/test/lib/tarantool_silverbox_server.py
@@ -116,9 +116,11 @@ class TarantoolSilverboxServer:
                           stdout = subprocess.PIPE,
                           stderr = subprocess.PIPE)
 
-    version = subprocess.Popen([self.abspath_to_exe, "--version"],
-                               cwd = self.args.vardir,
-                               stdout = subprocess.PIPE).stdout.read().rstrip()
+    p = subprocess.Popen([self.abspath_to_exe, "--version"],
+                         cwd = self.args.vardir,
+                         stdout = subprocess.PIPE)
+    version = p.stdout.read().rstrip()
+    p.wait()
 
     if not silent:
       print "Starting {0} {1}.".format(os.path.basename(self.abspath_to_exe),

-- 
Tarantool -- an efficient key/value data store

_______________________________________________
Mailing list: https://launchpad.net/~tarantool-developers
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~tarantool-developers
More help   : https://help.launchpad.net/ListHelp

Reply via email to