diff options
author | Alex Gorrod <alexg@wiredtiger.com> | 2013-05-30 16:12:03 -0700 |
---|---|---|
committer | Alex Gorrod <alexg@wiredtiger.com> | 2013-05-30 16:12:03 -0700 |
commit | 0c0e1fea6bf8ed00c829aa6e6c5ddb4eb17125ce (patch) | |
tree | c302b4f66df2b61c9b8fd7e3254ec01e8ec2d1dc | |
parent | e8f6358f9ab66b74a7e7643e4104c927291bbcd8 (diff) | |
parent | b6a0a9bddca7cc6b614778785d6de7d7f18cd1e3 (diff) | |
download | mongo-0c0e1fea6bf8ed00c829aa6e6c5ddb4eb17125ce.tar.gz |
Merge pull request #556 from wiredtiger/hot-backup
Fix several bugs in hot backup, including race conditions between backup and table drop (and other schema level operations).
@closes #557
31 files changed, 937 insertions, 514 deletions
diff --git a/examples/c/ex_all.c b/examples/c/ex_all.c index edf2437d167..7e5e1568eff 100644 --- a/examples/c/ex_all.c +++ b/examples/c/ex_all.c @@ -44,7 +44,6 @@ #include <wiredtiger.h> int add_collator(WT_CONNECTION *conn); -int add_compressor(WT_CONNECTION *conn); int add_extractor(WT_CONNECTION *conn); int checkpoint_ops(WT_SESSION *session); int connection_ops(WT_CONNECTION *conn); @@ -683,117 +682,6 @@ add_collator(WT_CONNECTION *conn) return (ret); } -/*! [WT_COMPRESSOR compress] */ -/* - * A simple compression example that passes data through unchanged. - */ -static int -my_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, - uint8_t *src, size_t src_len, - uint8_t *dst, size_t dst_len, - size_t *result_lenp, int *compression_failed) -{ - /* Unused parameters */ - (void)compressor; - (void)session; - - *compression_failed = 0; - if (dst_len < src_len) { - *compression_failed = 1; - return (0); - } - memcpy(dst, src, src_len); - *result_lenp = src_len; - return (0); -} -/*! [WT_COMPRESSOR compress] */ - -/*! [WT_COMPRESSOR decompress] */ -/* - * A simple decompression example that passes data through unchanged. - */ -static int -my_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, - uint8_t *src, size_t src_len, - uint8_t *dst, size_t dst_len, - size_t *result_lenp) -{ - /* Unused parameters */ - (void)compressor; - (void)session; - - if (dst_len < src_len) - return (ENOMEM); - - memcpy(dst, src, src_len); - *result_lenp = src_len; - return (0); -} -/*! [WT_COMPRESSOR decompress] */ - -/*! [WT_COMPRESSOR presize] */ -/* - * A simple pre-size example that returns the source length. - */ -static int -my_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, - uint8_t *src, size_t src_len, - size_t *result_lenp) -{ - /* Unused parameters */ - (void)compressor; - (void)session; - (void)src; - - *result_lenp = src_len; - return (0); -} -/*! [WT_COMPRESSOR presize] */ - -static int -my_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session, - size_t page_max, u_int split_pct, size_t extra, - uint8_t *src, uint32_t *offsets, uint32_t slots, - uint8_t *dst, size_t dst_len, int final, - size_t *result_lenp, uint32_t *result_slotsp) -{ - /* Unused parameters */ - (void)compressor; - (void)session; - (void)page_max; - (void)split_pct; - (void)extra; - (void)src; - (void)offsets; - (void)slots; - (void)dst; - (void)dst_len; - (void)final; - (void)result_lenp; - (void)result_slotsp; - - return (0); -} - -int -add_compressor(WT_CONNECTION *conn) -{ - int ret; - - /*! [WT_COMPRESSOR register] */ - static WT_COMPRESSOR my_compressor = { - my_compress, - my_compress_raw, /* NULL, if no raw compression */ - my_decompress, - my_pre_size, /* NULL, if pre-sizing not needed */ - NULL /* NULL, if no termination cleanup */ - }; - ret = conn->add_compressor(conn, "my_compress", &my_compressor, NULL); - /*! [WT_COMPRESSOR register] */ - - return (ret); -} - /*! [WT_EXTRACTOR] */ static int my_extract(WT_EXTRACTOR *extractor, WT_SESSION *session, diff --git a/ext/compressors/bzip2/bzip2_compress.c b/ext/compressors/bzip2/bzip2_compress.c index aa162009abe..c7001549638 100644 --- a/ext/compressors/bzip2/bzip2_compress.c +++ b/ext/compressors/bzip2/bzip2_compress.c @@ -26,6 +26,7 @@ */ #include <bzlib.h> +#include <errno.h> #include <inttypes.h> #include <stdlib.h> #include <string.h> @@ -33,68 +34,105 @@ #include <wiredtiger.h> #include <wiredtiger_ext.h> -static WT_EXTENSION_API *wt_api; - static int bzip2_compress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *, int *); static int bzip2_decompress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *); +static int +bzip2_terminate(WT_COMPRESSOR *, WT_SESSION *); #ifdef WIREDTIGER_TEST_COMPRESS_RAW static int -bzip2_compress_raw(WT_COMPRESSOR *, WT_SESSION *, size_t, u_int, +bzip2_compress_raw(WT_COMPRESSOR *, WT_SESSION *, size_t, int, size_t, uint8_t *, uint32_t *, uint32_t, uint8_t *, size_t, int, size_t *, uint32_t *); #endif -static WT_COMPRESSOR bzip2_compressor = { - bzip2_compress, NULL, bzip2_decompress, NULL, NULL }; +/* Local compressor structure. */ +typedef struct { + WT_COMPRESSOR compressor; /* Must come first */ -/* between 0-4: set the amount of verbosity to stderr */ -static int bz_verbosity = 0; + WT_EXTENSION_API *wt_api; /* Extension API */ -/* between 1-9: set the block size to 100k x this number (compression only) */ -static int bz_blocksize100k = 1; + int bz_verbosity; /* Configuration */ + int bz_blocksize100k; + int bz_workfactor; + int bz_small; +} BZIP_COMPRESSOR; /* - * between 0-250: workFactor: see bzip2 manual. 0 is a reasonable default - * (compression only) + * Bzip gives us a cookie to pass to the underlying allocation functions; we + * we need two handles, package them up. */ -static int bz_workfactor = 0; - -/* if nonzero, decompress using less memory, but slower (decompression only) */ -static int bz_small = 0; +typedef struct { + WT_COMPRESSOR *compressor; + WT_SESSION *session; +} BZIP_OPAQUE; int wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) { + BZIP_COMPRESSOR *bzip_compressor; + (void)config; /* Unused parameters */ - /* Find the extension API */ - wt_api = connection->get_extension_api(connection); + if ((bzip_compressor = calloc(1, sizeof(BZIP_COMPRESSOR))) == NULL) + return (errno); + + bzip_compressor->compressor.compress = bzip2_compress; + bzip_compressor->compressor.compress_raw = NULL; + bzip_compressor->compressor.decompress = bzip2_decompress; + bzip_compressor->compressor.pre_size = NULL; + bzip_compressor->compressor.terminate = bzip2_terminate; + + bzip_compressor->wt_api = connection->get_extension_api(connection); + + /* between 0-4: set the amount of verbosity to stderr */ + bzip_compressor->bz_verbosity = 0; + + /* + * between 1-9: set the block size to 100k x this number (compression + * only) + */ + bzip_compressor->bz_blocksize100k = 1; + + /* + * between 0-250: workFactor: see bzip2 manual. 0 is a reasonable + * default (compression only) + */ + bzip_compressor->bz_workfactor = 0; + + /* + * if nonzero, decompress using less memory, but slower (decompression + * only) + */ + bzip_compressor->bz_small = 0; /* Load the compressor */ #ifdef WIREDTIGER_TEST_COMPRESS_RAW - bzip2_compressor.compress_raw = bzip2_compress_raw; + bzip_compressor->compressor.compress_raw = bzip2_compress_raw; return (connection->add_compressor( - connection, "raw", &bzip2_compressor, NULL)); + connection, "raw", (WT_COMPRESSOR *)bzip_compressor, NULL)); #else return (connection->add_compressor( - connection, "bzip2", &bzip2_compressor, NULL)); + connection, "bzip2", (WT_COMPRESSOR *)bzip_compressor, NULL)); #endif } -/* Bzip2 WT_COMPRESSOR implementation for WT_CONNECTION::add_compressor. */ /* * bzip2_error -- * Output an error message, and return a standard error code. */ static int -bzip2_error(WT_SESSION *session, const char *call, int bzret) +bzip2_error( + WT_COMPRESSOR *compressor, WT_SESSION *session, const char *call, int bzret) { + WT_EXTENSION_API *wt_api; const char *msg; + wt_api = ((BZIP_COMPRESSOR *)compressor)->wt_api; + switch (bzret) { case BZ_MEM_ERROR: msg = "BZ_MEM_ERROR"; @@ -128,21 +166,32 @@ bzip2_error(WT_SESSION *session, const char *call, int bzret) break; } - (void)wt_api->err_printf(wt_api, - session, "bzip2 error: %s: %s: %d", call, msg, bzret); + (void)wt_api->err_printf(wt_api, session, + "bzip2 error: %s: %s: %d", call, msg, bzret); return (WT_ERROR); } static void * bzalloc(void *cookie, int number, int size) { - return (wt_api->scr_alloc(wt_api, cookie, (size_t)(number * size))); + BZIP_OPAQUE *opaque; + WT_EXTENSION_API *wt_api; + + opaque = cookie; + wt_api = ((BZIP_COMPRESSOR *)opaque->compressor)->wt_api; + return (wt_api->scr_alloc( + wt_api, opaque->session, (size_t)(number * size))); } static void bzfree(void *cookie, void *p) { - wt_api->scr_free(wt_api, cookie, p); + BZIP_OPAQUE *opaque; + WT_EXTENSION_API *wt_api; + + opaque = cookie; + wt_api = ((BZIP_COMPRESSOR *)opaque->compressor)->wt_api; + wt_api->scr_free(wt_api, opaque->session, p); } static int @@ -151,19 +200,26 @@ bzip2_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *dst, size_t dst_len, size_t *result_lenp, int *compression_failed) { + BZIP_COMPRESSOR *bzip_compressor; + BZIP_OPAQUE opaque; bz_stream bz; int ret; - (void)compressor; /* Unused */ + bzip_compressor = (BZIP_COMPRESSOR *)compressor; memset(&bz, 0, sizeof(bz)); bz.bzalloc = bzalloc; bz.bzfree = bzfree; - bz.opaque = session; + opaque.compressor = compressor; + opaque.session = session; + bz.opaque = &opaque; if ((ret = BZ2_bzCompressInit(&bz, - bz_blocksize100k, bz_verbosity, bz_workfactor)) != BZ_OK) - return (bzip2_error(session, "BZ2_bzCompressInit", ret)); + bzip_compressor->bz_blocksize100k, + bzip_compressor->bz_verbosity, + bzip_compressor->bz_workfactor)) != BZ_OK) + return (bzip2_error( + compressor, session, "BZ2_bzCompressInit", ret)); bz.next_in = (char *)src; bz.avail_in = (uint32_t)src_len; @@ -176,7 +232,8 @@ bzip2_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, *compression_failed = 1; if ((ret = BZ2_bzCompressEnd(&bz)) != BZ_OK) - return (bzip2_error(session, "BZ2_bzCompressEnd", ret)); + return ( + bzip2_error(compressor, session, "BZ2_bzCompressEnd", ret)); return (0); } @@ -207,7 +264,7 @@ __bzip2_compress_raw_random(void) */ static int bzip2_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session, - size_t page_max, u_int split_pct, size_t extra, + size_t page_max, int split_pct, size_t extra, uint8_t *src, uint32_t *offsets, uint32_t slots, uint8_t *dst, size_t dst_len, int final, size_t *result_lenp, uint32_t *result_slotsp) @@ -215,7 +272,7 @@ bzip2_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session, uint32_t take, twenty_pct; int compression_failed, ret; - (void)page_max; /* Unused */ + (void)page_max; /* Unused parameters */ (void)split_pct; (void)extra; (void)final; @@ -281,18 +338,24 @@ bzip2_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *dst, size_t dst_len, size_t *result_lenp) { + BZIP_COMPRESSOR *bzip_compressor; + BZIP_OPAQUE opaque; bz_stream bz; int ret, tret; - (void)compressor; /* Unused */ + bzip_compressor = (BZIP_COMPRESSOR *)compressor; memset(&bz, 0, sizeof(bz)); bz.bzalloc = bzalloc; bz.bzfree = bzfree; - bz.opaque = session; + opaque.compressor = compressor; + opaque.session = session; + bz.opaque = &opaque; - if ((ret = BZ2_bzDecompressInit(&bz, bz_small, bz_verbosity)) != BZ_OK) - return (bzip2_error(session, "BZ2_bzDecompressInit", ret)); + if ((ret = BZ2_bzDecompressInit(&bz, + bzip_compressor->bz_small, bzip_compressor->bz_verbosity)) != BZ_OK) + return (bzip2_error( + compressor, session, "BZ2_bzDecompressInit", ret)); bz.next_in = (char *)src; bz.avail_in = (uint32_t)src_len; @@ -302,12 +365,21 @@ bzip2_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, *result_lenp = dst_len - bz.avail_out; ret = 0; } else - (void)bzip2_error(session, "BZ2_bzDecompress", ret); + (void)bzip2_error(compressor, session, "BZ2_bzDecompress", ret); if ((tret = BZ2_bzDecompressEnd(&bz)) != BZ_OK) - return (bzip2_error(session, "BZ2_bzDecompressEnd", tret)); + return (bzip2_error( + compressor, session, "BZ2_bzDecompressEnd", tret)); return (ret == 0 ? - 0 : bzip2_error(session, "BZ2_bzDecompressEnd", ret)); + 0 : bzip2_error(compressor, session, "BZ2_bzDecompressEnd", ret)); +} + +static int +bzip2_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session) +{ + (void)session; /* Unused parameters */ + + free(compressor); + return (0); } -/* End Bzip2 WT_COMPRESSOR implementation for WT_CONNECTION::add_compressor. */ diff --git a/ext/compressors/nop/nop_compress.c b/ext/compressors/nop/nop_compress.c index 8a0765e2db8..c017d6a33c1 100644 --- a/ext/compressors/nop/nop_compress.c +++ b/ext/compressors/nop/nop_compress.c @@ -25,46 +25,82 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <errno.h> +#include <stdlib.h> #include <string.h> #include <wiredtiger.h> #include <wiredtiger_ext.h> -static WT_EXTENSION_API *wt_api; - static int nop_compress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *, int *); static int nop_decompress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *); +static int +nop_pre_size(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, size_t *); +static int +nop_terminate(WT_COMPRESSOR *, WT_SESSION *); + +/*! [WT_COMPRESSOR initialization] */ +/* Local compressor structure. */ +typedef struct { + WT_COMPRESSOR compressor; /* Must come first */ -static WT_COMPRESSOR nop_compressor = { - nop_compress, NULL, nop_decompress, NULL, NULL }; + WT_EXTENSION_API *wt_api; /* Extension API */ +} NOP_COMPRESSOR; -/*! [WT_EXTENSION_API initialization] */ +/* + * A simple shared library compression example. + */ int wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) { + NOP_COMPRESSOR *nop_compressor; + (void)config; /* Unused parameters */ - /* Find the extension API */ - wt_api = connection->get_extension_api(connection); + if ((nop_compressor = calloc(1, sizeof(NOP_COMPRESSOR))) == NULL) + return (errno); + + /* + * Allocate a local compressor structure, with a WT_COMPRESSOR structure + * as the first field, allowing us to treat references to either type of + * structure as a reference to the other type. + * + * This could be simplified if only a single database is opened in the + * application, we could use a static WT_COMPRESSOR structure, and a + * static reference to the WT_EXTENSION_API methods, then we don't need + * to allocate memory when the compressor is initialized or free it when + * the compressor is terminated. However, this approach is more general + * purpose and supports multiple databases per application. + */ + nop_compressor->compressor.compress = nop_compress; + nop_compressor->compressor.compress_raw = NULL; + nop_compressor->compressor.decompress = nop_decompress; + nop_compressor->compressor.pre_size = nop_pre_size; + nop_compressor->compressor.terminate = nop_terminate; + + nop_compressor->wt_api = connection->get_extension_api(connection); /* Load the compressor */ return (connection->add_compressor( - connection, "nop", &nop_compressor, NULL)); + connection, "nop", (WT_COMPRESSOR *)nop_compressor, NULL)); } -/*! [WT_EXTENSION_API initialization] */ +/*! [WT_COMPRESSOR initialization] */ -/* Implementation of WT_COMPRESSOR for WT_CONNECTION::add_compressor. */ +/*! [WT_COMPRESSOR compress] */ +/* + * A simple compression example that passes data through unchanged. + */ static int nop_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, size_t *result_lenp, int *compression_failed) { - (void)compressor; /* Unused */ + (void)compressor; /* Unused parameters */ (void)session; *compression_failed = 0; @@ -78,14 +114,19 @@ nop_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, return (0); } +/*! [WT_COMPRESSOR compress] */ +/*! [WT_COMPRESSOR decompress] */ +/* + * A simple compression example that passes data through unchanged. + */ static int nop_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, size_t *result_lenp) { - (void)compressor; /* Unused */ + (void)compressor; /* Unused parameters */ (void)session; (void)src_len; @@ -97,4 +138,37 @@ nop_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, *result_lenp = dst_len; return (0); } -/* End implementation of WT_COMPRESSOR. */ +/*! [WT_COMPRESSOR decompress] */ + +/*! [WT_COMPRESSOR presize] */ +/* + * A simple pre-size example that returns the source length. + */ +static int +nop_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, + uint8_t *src, size_t src_len, + size_t *result_lenp) +{ + /* Unused parameters */ + (void)compressor; + (void)session; + (void)src; + + *result_lenp = src_len; + return (0); +} +/*! [WT_COMPRESSOR presize] */ + +/*! [WT_COMPRESSOR terminate] */ +/* + * A simple termination example that frees the allocated memory. + */ +static int +nop_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session) +{ + (void)session; /* Unused parameters */ + + free(compressor); + return (0); +} +/*! [WT_COMPRESSOR terminate] */ diff --git a/ext/compressors/snappy/snappy_compress.c b/ext/compressors/snappy/snappy_compress.c index 0e8261b6226..efd6fe45bff 100644 --- a/ext/compressors/snappy/snappy_compress.c +++ b/ext/compressors/snappy/snappy_compress.c @@ -26,14 +26,13 @@ */ #include <snappy-c.h> +#include <errno.h> #include <stdlib.h> #include <string.h> #include <wiredtiger.h> #include <wiredtiger_ext.h> -static WT_EXTENSION_API *wt_api; - static int wt_snappy_compress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *, int *); @@ -42,33 +41,51 @@ wt_snappy_decompress(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, uint8_t *, size_t, size_t *); static int wt_snappy_pre_size(WT_COMPRESSOR *, WT_SESSION *, uint8_t *, size_t, size_t *); +static int +wt_snappy_terminate(WT_COMPRESSOR *, WT_SESSION *); -static WT_COMPRESSOR wt_snappy_compressor = { - wt_snappy_compress, NULL, wt_snappy_decompress, wt_snappy_pre_size, NULL }; +/* Local compressor structure. */ +typedef struct { + WT_COMPRESSOR compressor; /* Must come first */ + + WT_EXTENSION_API *wt_api; /* Extension API */ +} SNAPPY_COMPRESSOR; int wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) { + SNAPPY_COMPRESSOR *snappy_compressor; + (void)config; /* Unused parameters */ - /* Find the extension API */ - wt_api = connection->get_extension_api(connection); + if ((snappy_compressor = calloc(1, sizeof(SNAPPY_COMPRESSOR))) == NULL) + return (errno); + + snappy_compressor->compressor.compress = wt_snappy_compress; + snappy_compressor->compressor.compress_raw = NULL; + snappy_compressor->compressor.decompress = wt_snappy_decompress; + snappy_compressor->compressor.pre_size = wt_snappy_pre_size; + snappy_compressor->compressor.terminate = wt_snappy_terminate; + + snappy_compressor->wt_api = connection->get_extension_api(connection); - /* Load the compressor */ return (connection->add_compressor( - connection, "snappy", &wt_snappy_compressor, NULL)); + connection, "snappy", (WT_COMPRESSOR *)snappy_compressor, NULL)); } -/* Snappy WT_COMPRESSOR for WT_CONNECTION::add_compressor. */ /* * wt_snappy_error -- * Output an error message, and return a standard error code. */ static int -wt_snappy_error(WT_SESSION *session, const char *call, snappy_status snret) +wt_snappy_error(WT_COMPRESSOR *compressor, + WT_SESSION *session, const char *call, snappy_status snret) { + WT_EXTENSION_API *wt_api; const char *msg; + wt_api = ((SNAPPY_COMPRESSOR *)compressor)->wt_api; + switch (snret) { case SNAPPY_BUFFER_TOO_SMALL: msg = "SNAPPY_BUFFER_TOO_SMALL"; @@ -96,8 +113,6 @@ wt_snappy_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, size_t snaplen; char *snapbuf; - (void)compressor; /* Unused */ - /* * dst_len was computed in wt_snappy_pre_size, so we know it's big * enough. Skip past the space we'll use to store the final count @@ -125,7 +140,7 @@ wt_snappy_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, *compression_failed = 1; return (0); } - return (wt_snappy_error(session, "snappy_compress", snret)); + return (wt_snappy_error(compressor, session, "snappy_compress", snret)); } static int @@ -134,10 +149,11 @@ wt_snappy_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *dst, size_t dst_len, size_t *result_lenp) { + WT_EXTENSION_API *wt_api; snappy_status snret; size_t snaplen; - (void)compressor; /* Unused */ + wt_api = ((SNAPPY_COMPRESSOR *)compressor)->wt_api; /* retrieve the saved length */ snaplen = *(size_t *)src; @@ -157,7 +173,8 @@ wt_snappy_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, return (0); } - return (wt_snappy_error(session, "snappy_decompress", snret)); + return ( + wt_snappy_error(compressor, session, "snappy_decompress", snret)); } static int @@ -165,7 +182,7 @@ wt_snappy_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, size_t *result_lenp) { - (void)compressor; /* Unused */ + (void)compressor; /* Unused parameters */ (void)session; (void)src; @@ -178,4 +195,12 @@ wt_snappy_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, *result_lenp = snappy_max_compressed_length(src_len) + sizeof(size_t); return (0); } -/* End Snappy WT_COMPRESSOR for WT_CONNECTION::add_compressor. */ + +static int +wt_snappy_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session) +{ + (void)session; /* Unused parameters */ + + free(compressor); + return (0); +} diff --git a/src/btree/bt_handle.c b/src/btree/bt_handle.c index f8f58221c18..4400af93687 100644 --- a/src/btree/bt_handle.c +++ b/src/btree/bt_handle.c @@ -14,7 +14,7 @@ static int __btree_preload(WT_SESSION_IMPL *); static int __btree_tree_open_empty(WT_SESSION_IMPL *, int); static int pse1(WT_SESSION_IMPL *, const char *, uint32_t, uint32_t); -static int pse2(WT_SESSION_IMPL *, const char *, uint32_t, uint32_t, uint32_t); +static int pse2(WT_SESSION_IMPL *, const char *, uint32_t, uint32_t, int); /* * __wt_btree_open -- @@ -562,7 +562,7 @@ __btree_page_sizes(WT_SESSION_IMPL *session) btree->maxleafitem = (uint32_t)cval.val; WT_RET(__wt_config_gets(session, cfg, "split_pct", &cval)); - btree->split_pct = (u_int)cval.val; + btree->split_pct = (int)cval.val; /* * When a page is forced to split, we want at least 50 entries on its @@ -649,8 +649,8 @@ __wt_split_page_size(WT_BTREE *btree, uint32_t maxpagesize) * we don't waste space when we write). */ a = maxpagesize; /* Don't overflow. */ - split_size = - (uint32_t)WT_ALIGN((a * btree->split_pct) / 100, btree->allocsize); + split_size = (uint32_t) + WT_ALIGN((a * (u_int)btree->split_pct) / 100, btree->allocsize); /* * If the result of that calculation is the same as the allocation unit @@ -658,7 +658,7 @@ __wt_split_page_size(WT_BTREE *btree, uint32_t maxpagesize) * unit, use a percentage of the maximum page size). */ if (split_size == btree->allocsize) - split_size = (uint32_t)((a * btree->split_pct) / 100); + split_size = (uint32_t)((a * (u_int)btree->split_pct) / 100); return (split_size); } @@ -674,11 +674,11 @@ pse1(WT_SESSION_IMPL *session, const char *type, uint32_t max, uint32_t ovfl) static int pse2(WT_SESSION_IMPL *session, - const char *type, uint32_t max, uint32_t ovfl, uint32_t pct) + const char *type, uint32_t max, uint32_t ovfl, int pct) { WT_RET_MSG(session, EINVAL, "%s page size (%" PRIu32 "B) too small for the maximum item size " - "(%" PRIu32 "B), because of the split percentage (%" PRIu32 - "%%); a split page must be able to hold at least 2 items", + "(%" PRIu32 "B), because of the split percentage (%d %%); a split " + "page must be able to hold at least 2 items", type, max, ovfl, pct); } diff --git a/src/cursor/cur_backup.c b/src/cursor/cur_backup.c index 5c95d89c9d7..e5f3880753e 100644 --- a/src/cursor/cur_backup.c +++ b/src/cursor/cur_backup.c @@ -7,20 +7,16 @@ #include "wt_internal.h" -static int __backup_all(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, FILE *); -static int __backup_file_create(WT_SESSION_IMPL *, FILE **); +static int __backup_all(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *); +static int __backup_file_create(WT_SESSION_IMPL *, WT_CURSOR_BACKUP *); static int __backup_file_remove(WT_SESSION_IMPL *); static int __backup_list_append( WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *); static int __backup_start( WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *[]); static int __backup_stop(WT_SESSION_IMPL *); -static int __backup_table( - WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *, FILE *); -static int __backup_table_element(WT_SESSION_IMPL *, - WT_CURSOR_BACKUP *, WT_CURSOR *, const char *, const char *, FILE *); static int __backup_uri( - WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *[], FILE *, int *); + WT_SESSION_IMPL *, WT_CURSOR_BACKUP *, const char *[], int *); /* * __curbackup_next -- @@ -36,13 +32,13 @@ __curbackup_next(WT_CURSOR *cursor) cb = (WT_CURSOR_BACKUP *)cursor; CURSOR_API_CALL(cursor, session, next, NULL); - if (cb->list == NULL || cb->list[cb->next] == NULL) { + if (cb->list == NULL || cb->list[cb->next].name == NULL) { F_CLR(cursor, WT_CURSTD_KEY_SET); WT_ERR(WT_NOTFOUND); } - cb->iface.key.data = cb->list[cb->next]; - cb->iface.key.size = WT_STORE_SIZE(strlen(cb->list[cb->next]) + 1); + cb->iface.key.data = cb->list[cb->next].name; + cb->iface.key.size = WT_STORE_SIZE(strlen(cb->list[cb->next].name) + 1); ++cb->next; F_SET(cursor, WT_CURSTD_KEY_RET); @@ -80,22 +76,29 @@ static int __curbackup_close(WT_CURSOR *cursor) { WT_CURSOR_BACKUP *cb; + WT_CURSOR_BACKUP_ENTRY *p; WT_DECL_RET; WT_SESSION_IMPL *session; - char **p; int tret; cb = (WT_CURSOR_BACKUP *)cursor; CURSOR_API_CALL(cursor, session, close, NULL); - /* Free the list of files. */ + /* Release the handles, free the file names, free the list itself. */ if (cb->list != NULL) { - for (p = cb->list; *p != NULL; ++p) - __wt_free(session, *p); + for (p = cb->list; p->name != NULL; ++p) { + if (p->handle != NULL) + WT_WITH_DHANDLE(session, p->handle, + WT_TRET( + __wt_session_release_btree(session))); + __wt_free(session, p->name); + } + __wt_free(session, cb->list); } - ret = __wt_cursor_close(cursor); + WT_TRET(__wt_cursor_close(cursor)); + session->bkp_cursor = NULL; WT_WITH_SCHEMA_LOCK(session, tret = __backup_stop(session)); /* Stop the backup. */ @@ -138,6 +141,7 @@ __wt_curbackup_open(WT_SESSION_IMPL *session, cursor = &cb->iface; *cursor = iface; cursor->session = &session->iface; + session->bkp_cursor = cb; cursor->key_format = "S"; /* Return the file names as the key. */ cursor->value_format = ""; /* No value. */ @@ -168,14 +172,12 @@ static int __backup_start( WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[]) { - FILE *bfp; WT_CONNECTION_IMPL *conn; WT_DECL_RET; int target_list; conn = S2C(session); - bfp = NULL; cb->next = 0; cb->list = NULL; @@ -203,24 +205,28 @@ __backup_start( __wt_spin_unlock(session, &conn->hot_backup_lock); /* Create the hot backup file. */ - WT_ERR(__backup_file_create(session, &bfp)); + WT_ERR(__backup_file_create(session, cb)); /* * If a list of targets was specified, work our way through them. * Else, generate a list of all database objects. */ target_list = 0; - WT_ERR(__backup_uri(session, cb, cfg, bfp, &target_list)); + WT_ERR(__backup_uri(session, cb, cfg, &target_list)); if (!target_list) - WT_ERR(__backup_all(session, cb, bfp)); + WT_ERR(__backup_all(session, cb)); + + /* Add the hot backup and single-threading file to the list. */ + WT_ERR(__backup_list_append(session, cb, WT_METADATA_BACKUP)); + WT_ERR(__backup_list_append(session, cb, WT_SINGLETHREAD)); /* Close the hot backup file. */ - ret = fclose(bfp); - bfp = NULL; + ret = fclose(cb->bfp); + cb->bfp = NULL; WT_ERR_TEST(ret == EOF, __wt_errno()); -err: if (bfp != NULL) - WT_TRET(fclose(bfp) == 0 ? 0 : __wt_errno()); +err: if (cb->bfp != NULL) + WT_TRET(fclose(cb->bfp) == 0 ? 0 : __wt_errno()); if (ret != 0) WT_TRET(__backup_stop(session)); @@ -256,31 +262,50 @@ __backup_stop(WT_SESSION_IMPL *session) * Backup all objects in the database. */ static int -__backup_all(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, FILE *bfp) +__backup_all(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb) { + WT_CONFIG_ITEM cval; WT_CURSOR *cursor; WT_DECL_RET; int cmp; - const char *key, *path, *uri, *value; + const char *key, *uri, *value; cursor = NULL; - path = NULL; /* - * Open a cursor on the metadata file. - * - * Copy object references from the metadata to the hot backup file. - * - * We're copying everything, there's nothing in the metadata file we - * don't want. If that ever changes, we'll need to limit the copy to - * specific object entries. + * Open a cursor on the metadata file and copy all of the entries to + * the hot backup file. */ WT_ERR(__wt_metadata_cursor(session, NULL, &cursor)); while ((ret = cursor->next(cursor)) == 0) { WT_ERR(cursor->get_key(cursor, &key)); WT_ERR(cursor->get_value(cursor, &value)); - WT_ERR_TEST( - (fprintf(bfp, "%s\n%s\n", key, value) < 0), __wt_errno()); + WT_ERR_TEST((fprintf( + cb->bfp, "%s\n%s\n", key, value) < 0), __wt_errno()); + + /* + * While reading the metadata file, check there are no "sources" + * or "types" which can't support hot backup. This checks for + * a data source that's non-standard, which can't be backed up, + * but is also sanity checking: if there's an entry backed by + * anything other than a file or lsm entry, we're confused. + */ + if ((ret = __wt_config_getones( + session, value, "type", &cval)) == 0 && + !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "file") && + !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "lsm")) + WT_ERR_MSG(session, ENOTSUP, + "hot backup is not supported for objects of " + "type %.*s", (int)cval.len, cval.str); + WT_ERR_NOTFOUND_OK(ret); + if ((ret =__wt_config_getones( + session, value, "source", &cval)) == 0 && + !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "file:") && + !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "lsm:")) + WT_ERR_MSG(session, ENOTSUP, + "hot backup is not supported for objects of " + "source %.*s", (int)cval.len, cval.str); + WT_ERR_NOTFOUND_OK(ret); } WT_ERR_NOTFOUND_OK(ret); @@ -294,20 +319,12 @@ __backup_all(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, FILE *bfp) break; if (strcmp(uri, WT_METADATA_URI) == 0) continue; - WT_ERR( - __backup_list_append(session, cb, uri + strlen("file:"))); + WT_ERR(__backup_list_append(session, cb, uri)); } WT_ERR_NOTFOUND_OK(ret); - /* Add the hot backup and single-threading file to the list. */ - WT_ERR(__backup_list_append(session, cb, WT_METADATA_BACKUP)); - WT_ERR(__backup_list_append(session, cb, WT_SINGLETHREAD)); - -err: - if (cursor != NULL) +err: if (cursor != NULL) WT_TRET(cursor->close(cursor)); - if (path != NULL) - __wt_free(session, path); return (ret); } @@ -317,19 +334,16 @@ err: */ static int __backup_uri(WT_SESSION_IMPL *session, - WT_CURSOR_BACKUP *cb, const char *cfg[], FILE *bfp, int *foundp) + WT_CURSOR_BACKUP *cb, const char *cfg[], int *foundp) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; int target_list; - const char *path, *uri, *value; + const char *uri; - *foundp = 0; - - path = NULL; - target_list = 0; + *foundp = target_list = 0; /* * If we find a non-empty target configuration string, we have a job, @@ -352,139 +366,10 @@ __backup_uri(WT_SESSION_IMPL *session, "%s: invalid backup target: URIs may need quoting", uri); - if (WT_PREFIX_MATCH(uri, "file:")) { - /* Copy metadata file information to the backup file. */ - WT_ERR(__wt_metadata_read(session, uri, &value)); - WT_ERR_TEST((fprintf(bfp, - "%s\n%s\n", uri, value) < 0), __wt_errno()); - - WT_ERR(__backup_list_append( - session, cb, uri + strlen("file:"))); - continue; - } - if (WT_PREFIX_MATCH(uri, "table:")) { - WT_ERR(__backup_table(session, cb, uri, bfp)); - continue; - } - - /* - * It doesn't make sense to backup anything other than a file - * or table. - */ - WT_ERR_MSG( - session, EINVAL, "%s: invalid backup target object", uri); + WT_ERR(__wt_schema_worker( + session, uri, NULL, __wt_backup_list_append, cfg, 0)); } WT_ERR_NOTFOUND_OK(ret); - if (!target_list) - return (0); - - /* Add the hot backup and single-threading file to the list. */ - WT_ERR(__backup_list_append(session, cb, WT_METADATA_BACKUP)); - WT_ERR(__backup_list_append(session, cb, WT_SINGLETHREAD)); - -err: if (path != NULL) - __wt_free(session, path); - __wt_scr_free(&tmp); - return (ret); -} - -/* - * __backup_table -- - * Squirrel around in the metadata table until we have enough information - * to back up a table. - */ -static int -__backup_table( - WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *uri, FILE *bfp) -{ - WT_CURSOR *cursor; - WT_DECL_ITEM(tmp); - WT_DECL_RET; - size_t i; - const char *value; - - cursor = NULL; - WT_RET(__wt_scr_alloc(session, 512, &tmp)); - - /* Open a cursor on the metadata file. */ - WT_ERR(__wt_metadata_cursor(session, NULL, &cursor)); - - /* Copy the table's metadata entry to the hot backup file. */ - cursor->set_key(cursor, uri); - WT_ERR(cursor->search(cursor)); - WT_ERR(cursor->get_value(cursor, &value)); - WT_ERR_TEST((fprintf(bfp, "%s\n%s\n", uri, value) < 0), __wt_errno()); - uri += strlen("table:"); - - /* Copy the table's column groups and index entries... */ - WT_ERR( - __backup_table_element(session, cb, cursor, "colgroup:", uri, bfp)); - WT_ERR(__backup_table_element(session, cb, cursor, "index:", uri, bfp)); - - /* Copy the table's file entries... */ - for (i = 0; i < cb->list_next; ++i) { - WT_ERR(__wt_buf_fmt(session, tmp, "file:%s", cb->list[i])); - cursor->set_key(cursor, tmp->data); - WT_ERR(cursor->search(cursor)); - WT_ERR(cursor->get_value(cursor, &value)); - WT_ERR_TEST((fprintf(bfp, - "file:%s\n%s\n", cb->list[i], value) < 0), __wt_errno()); - } - -err: if (cursor != NULL) - WT_TRET(cursor->close(cursor)); - __wt_scr_free(&tmp); - return (ret); -} - -/* - * __backup_table_element -- - * Backup the column groups or indices. - */ -static int -__backup_table_element(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, - WT_CURSOR *cursor, const char *elem, const char *table, FILE *bfp) -{ - WT_CONFIG_ITEM cval; - WT_DECL_RET; - WT_DECL_ITEM(tmp); - int cmp; - const char *key, *value; - - WT_RET(__wt_scr_alloc(session, 512, &tmp)); - - WT_ERR(__wt_buf_fmt(session, tmp, "%s%s", elem, table)); - cursor->set_key(cursor, tmp->data); - if ((ret = cursor->search_near(cursor, &cmp)) == 0 && cmp < 0) - ret = cursor->next(cursor); - for (; ret == 0; ret = cursor->next(cursor)) { - /* Check for a match with the specified table name. */ - WT_ERR(cursor->get_key(cursor, &key)); - if (!WT_PREFIX_MATCH(key, elem) || - !WT_PREFIX_MATCH(key + strlen(elem), table) || - (key[strlen(elem) + strlen(table)] != ':' && - key[strlen(elem) + strlen(table)] != '\0')) - break; - - /* Dump the metadata entry. */ - WT_ERR(cursor->get_value(cursor, &value)); - WT_ERR_TEST( - (fprintf(bfp, "%s\n%s\n", key, value) < 0), __wt_errno()); - - /* Save the source URI, if it is a file. */ - WT_ERR(__wt_config_getones(session, value, "source", &cval)); - if (cval.len > strlen("file:") && - WT_PREFIX_MATCH(cval.str, "file:")) { - WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", - (int)(cval.len - strlen("file:")), - cval.str + strlen("file:"))); - WT_ERR(__backup_list_append( - session, cb, (const char *)tmp->data)); - } else - WT_ERR_MSG(session, EINVAL, - "%s: unknown data source '%.*s'", - (const char *)tmp->data, (int)cval.len, cval.str); - } err: __wt_scr_free(&tmp); return (ret); @@ -495,16 +380,14 @@ err: __wt_scr_free(&tmp); * Create the meta-data backup file. */ static int -__backup_file_create(WT_SESSION_IMPL *session, FILE **fpp) +__backup_file_create(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb) { WT_DECL_RET; const char *path; - *fpp = NULL; - /* Open the hot backup file. */ WT_RET(__wt_filename(session, WT_METADATA_BACKUP, &path)); - WT_ERR_TEST((*fpp = fopen(path, "w")) == NULL, __wt_errno()); + WT_ERR_TEST((cb->bfp = fopen(path, "w")) == NULL, __wt_errno()); err: __wt_free(session, path); return (ret); @@ -521,17 +404,58 @@ __backup_file_remove(WT_SESSION_IMPL *session) } /* + * __wt_backup_list_append -- + * Append a new file name to the list, allocated space as necessary. + * Called via the schema_worker function. + */ +int +__wt_backup_list_append(WT_SESSION_IMPL *session, const char *name) +{ + WT_CURSOR_BACKUP *cb; + const char *value; + + cb = session->bkp_cursor; + + /* Add the metadata entry to the backup file. */ + WT_RET(__wt_metadata_read(session, name, &value)); + WT_RET_TEST( + (fprintf(cb->bfp, "%s\n%s\n", name, value) < 0), __wt_errno()); + + /* Add file type objects to the list of files to be copied. */ + if (WT_PREFIX_MATCH(name, "file:")) + WT_RET(__backup_list_append(session, cb, name)); + + return (0); +} + +/* * __backup_list_append -- * Append a new file name to the list, allocated space as necessary. */ static int __backup_list_append( - WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *name) + WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *uri) { + WT_CURSOR_BACKUP_ENTRY *p; + WT_DATA_HANDLE *old_dhandle; + WT_DECL_RET; + const char *name; + int need_handle; + /* Leave a NULL at the end to mark the end of the list. */ if (cb->list_next + 1 * sizeof(char *) >= cb->list_allocated) WT_RET(__wt_realloc(session, &cb->list_allocated, (cb->list_next + 100) * sizeof(char *), &cb->list)); + p = &cb->list[cb->list_next]; + p[0].name = p[1].name = NULL; + p[0].handle = p[1].handle = NULL; + + need_handle = 0; + name = uri; + if (WT_PREFIX_MATCH(uri, "file:")) { + need_handle = 1; + name += strlen("file:"); + } /* * !!! @@ -542,8 +466,22 @@ __backup_list_append( * that for now, that block manager might not even support physical * copying of files by applications. */ - WT_RET(__wt_strdup(session, name, &cb->list[cb->list_next++])); - cb->list[cb->list_next] = NULL; + WT_RET(__wt_strdup(session, name, &p->name)); + + /* + * If it's a file in the database, get a handle for the underlying + * object (this handle blocks schema level operations, for example + * WT_SESSION.drop or an LSM file discard after level merging). + */ + if (need_handle) { + old_dhandle = session->dhandle; + if ((ret = + __wt_session_get_btree(session, uri, NULL, NULL, 0)) == 0) + p->handle = session->dhandle; + session->dhandle = old_dhandle; + WT_RET(ret); + } + ++cb->list_next; return (0); } diff --git a/src/docs/upgrading.dox b/src/docs/upgrading.dox index d8c069ecb43..622a3c4571a 100644 --- a/src/docs/upgrading.dox +++ b/src/docs/upgrading.dox @@ -11,6 +11,7 @@ configuration string \c internal_page_max changed from 2KB to 4KB. Applications wanting to create files with smaller allocation or internal page sizes will need to set those configuration values explicitly. </dd> + <dt>Shared cache configuration</dt> <dd> In the 1.6.1 release, an explicit shared_cache=(enable=boolean) option was @@ -19,6 +20,13 @@ use shared cache functionality will need to add the enable option to the configuration string. The default value for the option is false. </dd> +<dt>WT_COMPRESSOR::compress_raw signature</dt> +<dd> +In the 1.6.1 release, the \c split_pct argument to the +WT_COMPRESSOR::compress_raw function changed type from \c u_int to \c int, +applications may require modification to avoid compiler warnings. +</dd> + </dl> <hr> @section version_160 Upgrading to Version 1.6.0 diff --git a/src/include/btree.h b/src/include/btree.h index 8e756e518f4..3ef35562446 100644 --- a/src/include/btree.h +++ b/src/include/btree.h @@ -78,7 +78,7 @@ struct __wt_btree { int internal_key_truncate; /* Reconcile: internal key truncate */ int maximum_depth; /* Reconcile: maximum tree depth */ int prefix_compression; /* Reconcile: key prefix compression */ - u_int split_pct; /* Reconcile: split page percent */ + int split_pct; /* Reconcile: split page percent */ WT_COMPRESSOR *compressor; /* Reconcile: page compressor */ WT_RWLOCK *val_ovfl_lock; /* Reconcile: overflow value lock */ diff --git a/src/include/cursor.h b/src/include/cursor.h index 3cc945ca2f3..a86b4b5f702 100644 --- a/src/include/cursor.h +++ b/src/include/cursor.h @@ -52,14 +52,19 @@ 0 /* uint32_t flags */ \ } +struct __wt_cursor_backup_entry { + char *name; /* File name */ + WT_DATA_HANDLE *handle; /* Handle */ +}; struct __wt_cursor_backup { WT_CURSOR iface; size_t next; /* Cursor position */ + FILE *bfp; /* Backup file */ - size_t list_allocated; /* List of files */ + WT_CURSOR_BACKUP_ENTRY *list; /* List of files to be copied. */ + size_t list_allocated; size_t list_next; - char **list; }; struct __wt_cursor_btree { diff --git a/src/include/extern.h b/src/include/extern.h index 244cbc22ad5..0159869302c 100644 --- a/src/include/extern.h +++ b/src/include/extern.h @@ -586,6 +586,7 @@ extern int __wt_curbackup_open(WT_SESSION_IMPL *session, const char *uri, const char *cfg[], WT_CURSOR **cursorp); +extern int __wt_backup_list_append(WT_SESSION_IMPL *session, const char *name); extern int __wt_curbulk_init(WT_CURSOR_BULK *cbulk, int bitmap); extern int __wt_curconfig_open(WT_SESSION_IMPL *session, const char *uri, @@ -730,8 +731,10 @@ extern int __wt_lsm_tree_truncate( WT_SESSION_IMPL *session, const char *cfg[]); extern int __wt_lsm_tree_worker(WT_SESSION_IMPL *session, const char *uri, - int (*func)(WT_SESSION_IMPL *, + int (*file_func)(WT_SESSION_IMPL *, const char *[]), + int (*name_func)(WT_SESSION_IMPL *, + const char *), const char *cfg[], uint32_t open_flags); extern void *__wt_lsm_merge_worker(void *vargs); @@ -1051,8 +1054,10 @@ extern int __wt_schema_get_source( WT_SESSION_IMPL *session, extern int __wt_schema_name_check(WT_SESSION_IMPL *session, const char *uri); extern int __wt_schema_worker(WT_SESSION_IMPL *session, const char *uri, - int (*func)(WT_SESSION_IMPL *, + int (*file_func)(WT_SESSION_IMPL *, const char *[]), + int (*name_func)(WT_SESSION_IMPL *, + const char *), const char *cfg[], uint32_t open_flags); extern int __wt_open_cursor(WT_SESSION_IMPL *session, diff --git a/src/include/misc.h b/src/include/misc.h index 8ded178346d..1d22922ac0c 100644 --- a/src/include/misc.h +++ b/src/include/misc.h @@ -140,13 +140,16 @@ memset(&(s), 0, sizeof(s)) /* Check if a string matches a prefix. */ -#define WT_PREFIX_MATCH(str, pre) \ - (strncmp((str), (pre), strlen(pre)) == 0) +#define WT_PREFIX_MATCH(str, pfx) \ + (strncmp((str), (pfx), strlen(pfx)) == 0) + +#define WT_PREFIX_MATCH_LEN(str, len, pfx) \ + ((len) >= strlen(pfx) && WT_PREFIX_MATCH(str, pfx)) /* Check if a string matches a prefix, and move past it. */ -#define WT_PREFIX_SKIP(str, pre) \ - ((strncmp((str), (pre), strlen(pre)) == 0) ? \ - ((str) += strlen(pre), 1) : 0) +#define WT_PREFIX_SKIP(str, pfx) \ + ((strncmp((str), (pfx), strlen(pfx)) == 0) ? \ + ((str) += strlen(pfx), 1) : 0) /* Check if a string matches a byte string of len bytes. */ #define WT_STRING_MATCH(str, bytes, len) \ diff --git a/src/include/session.h b/src/include/session.h index 8565fa50418..64fbfcd8185 100644 --- a/src/include/session.h +++ b/src/include/session.h @@ -62,6 +62,7 @@ struct __wt_session_impl { WT_CURSOR *cursor; /* Current cursor */ /* Cursors closed with the session */ TAILQ_HEAD(__cursors, __wt_cursor) cursors; + WT_CURSOR_BACKUP *bkp_cursor; /* Cursor for current backup */ WT_BTREE *metafile; /* Metadata file */ void *meta_track; /* Metadata operation tracking */ diff --git a/src/include/wiredtiger.in b/src/include/wiredtiger.in index 2005d208d44..89e9fefe0ec 100644 --- a/src/include/wiredtiger.in +++ b/src/include/wiredtiger.in @@ -1267,7 +1267,7 @@ struct __wt_connection { * The application must first implement the WT_COMPRESSOR interface * and then register the implementation with WiredTiger: * - * @snippet ex_all.c WT_COMPRESSOR register + * @snippet nop_compress.c WT_COMPRESSOR initialization * * @param connection the connection handle * @param name the name of the compression function to be used in calls @@ -1849,7 +1849,7 @@ struct __wt_collator { * Applications register their implementation with WiredTiger by calling * WT_CONNECTION::add_compressor. * - * @snippet ex_all.c WT_COMPRESSOR register + * @snippet nop_compress.c WT_COMPRESSOR initialization */ struct __wt_compressor { /*! @@ -1881,7 +1881,7 @@ struct __wt_compressor { * decrease the length of the data (compression may not have completed) * @returns zero for success, non-zero to indicate an error. * - * @snippet ex_all.c WT_COMPRESSOR compress + * @snippet nop_compress.c WT_COMPRESSOR compress */ int (*compress)(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, @@ -1994,7 +1994,7 @@ struct __wt_compressor { * @returns zero for success, non-zero to indicate an error. */ int (*compress_raw)(WT_COMPRESSOR *compressor, WT_SESSION *session, - size_t page_max, u_int split_pct, size_t extra, + size_t page_max, int split_pct, size_t extra, uint8_t *src, uint32_t *offsets, uint32_t slots, uint8_t *dst, size_t dst_len, int final, @@ -2033,7 +2033,7 @@ struct __wt_compressor { * @param[out] result_lenp the length of the decompressed data * @returns zero for success, non-zero to indicate an error. * - * @snippet ex_all.c WT_COMPRESSOR decompress + * @snippet nop_compress.c WT_COMPRESSOR decompress */ int (*decompress)(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, @@ -2075,7 +2075,7 @@ struct __wt_compressor { * @param[out] result_lenp the required destination buffer size * @returns zero for success, non-zero to indicate an error. * - * @snippet ex_all.c WT_COMPRESSOR presize + * @snippet nop_compress.c WT_COMPRESSOR presize */ int (*pre_size)(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *src, size_t src_len, size_t *result_lenp); @@ -2085,6 +2085,8 @@ struct __wt_compressor { * * The WT_COMPRESSOR::terminate callback is intended to allow cleanup, * the handle will not be subsequently accessed by WiredTiger. + * + * @snippet nop_compress.c WT_COMPRESSOR terminate */ int (*terminate)(WT_COMPRESSOR *compressor, WT_SESSION *session); }; diff --git a/src/include/wiredtiger_ext.h b/src/include/wiredtiger_ext.h index d21449862c2..7d63e559d78 100644 --- a/src/include/wiredtiger_ext.h +++ b/src/include/wiredtiger_ext.h @@ -43,7 +43,7 @@ extern "C" { * The following code is from the sample compression module, where compression * extension functions are configured in the extension's entry point: * - * @snippet nop_compress.c WT_EXTENSION_API initialization + * @snippet nop_compress.c WT_COMPRESSOR initialization */ struct __wt_extension_api { /* !!! To maintain backwards compatibility, this structure is append-only. */ diff --git a/src/include/wt_internal.h b/src/include/wt_internal.h index 71c5c433035..690d8c9fba3 100644 --- a/src/include/wt_internal.h +++ b/src/include/wt_internal.h @@ -99,6 +99,8 @@ struct __wt_connection_stats; typedef struct __wt_connection_stats WT_CONNECTION_STATS; struct __wt_cursor_backup; typedef struct __wt_cursor_backup WT_CURSOR_BACKUP; +struct __wt_cursor_backup_entry; + typedef struct __wt_cursor_backup_entry WT_CURSOR_BACKUP_ENTRY; struct __wt_cursor_btree; typedef struct __wt_cursor_btree WT_CURSOR_BTREE; struct __wt_cursor_bulk; diff --git a/src/lsm/lsm_tree.c b/src/lsm/lsm_tree.c index 1131c2f09f8..9a92cceae3e 100644 --- a/src/lsm/lsm_tree.c +++ b/src/lsm/lsm_tree.c @@ -820,7 +820,8 @@ err: if (locked) int __wt_lsm_tree_worker(WT_SESSION_IMPL *session, const char *uri, - int (*func)(WT_SESSION_IMPL *, const char *[]), + int (*file_func)(WT_SESSION_IMPL *, const char *[]), + int (*name_func)(WT_SESSION_IMPL *, const char *), const char *cfg[], uint32_t open_flags) { WT_DECL_RET; @@ -832,11 +833,11 @@ __wt_lsm_tree_worker(WT_SESSION_IMPL *session, FLD_ISSET(open_flags, WT_DHANDLE_EXCLUSIVE) ? 1 : 0, &lsm_tree)); for (i = 0; i < lsm_tree->nchunks; i++) { chunk = lsm_tree->chunk[i]; - if (func == __wt_checkpoint && + if (file_func == __wt_checkpoint && F_ISSET(chunk, WT_LSM_CHUNK_ONDISK)) continue; - WT_ERR(__wt_schema_worker( - session, chunk->uri, func, cfg, open_flags)); + WT_ERR(__wt_schema_worker(session, chunk->uri, + file_func, name_func, cfg, open_flags)); } err: __wt_lsm_tree_release(session, lsm_tree); return (ret); diff --git a/src/lsm/lsm_worker.c b/src/lsm/lsm_worker.c index ae6353197db..bc892b3309a 100644 --- a/src/lsm/lsm_worker.c +++ b/src/lsm/lsm_worker.c @@ -279,7 +279,7 @@ __wt_lsm_checkpoint_worker(void *arg) F_SET(lsm_tree, WT_LSM_TREE_LOCKED); WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_worker(session, chunk->uri, - __wt_checkpoint, NULL, 0)); + __wt_checkpoint, NULL, NULL, 0)); F_CLR(lsm_tree, WT_LSM_TREE_LOCKED); if (ret != 0) { @@ -445,6 +445,12 @@ __lsm_drop_file(WT_SESSION_IMPL *session, const char *uri) WT_RET(__lsm_discard_handle(session, uri, NULL)); WT_RET(__lsm_discard_handle(session, uri, "WiredTigerCheckpoint")); + /* + * Take the schema lock for the drop operation. Play games with the + * hot backup lock. Since __wt_schema_drop results in the hot backup + * lock being taken when it updates the metadata (which would be too + * late to prevent our drop). + */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_drop(session, uri, drop_cfg)); diff --git a/src/schema/schema_worker.c b/src/schema/schema_worker.c index 8546dff84af..485c751acd7 100644 --- a/src/schema/schema_worker.c +++ b/src/schema/schema_worker.c @@ -15,7 +15,8 @@ int __wt_schema_worker(WT_SESSION_IMPL *session, const char *uri, - int (*func)(WT_SESSION_IMPL *, const char *[]), + int (*file_func)(WT_SESSION_IMPL *, const char *[]), + int (*name_func)(WT_SESSION_IMPL *, const char *), const char *cfg[], uint32_t open_flags) { WT_COLGROUP *colgroup; @@ -30,50 +31,65 @@ __wt_schema_worker(WT_SESSION_IMPL *session, table = NULL; tablename = uri; + if (name_func != NULL) + WT_ERR(name_func(session, uri)); + /* Get the btree handle(s) and call the underlying function. */ if (WT_PREFIX_MATCH(uri, "file:")) { - WT_ERR(__wt_session_get_btree_ckpt( - session, uri, cfg, open_flags)); - ret = func(session, cfg); - WT_TRET(__wt_session_release_btree(session)); + if (file_func != NULL) { + WT_ERR(__wt_session_get_btree_ckpt( + session, uri, cfg, open_flags)); + ret = file_func(session, cfg); + WT_TRET(__wt_session_release_btree(session)); + } } else if (WT_PREFIX_MATCH(uri, "colgroup:")) { WT_ERR(__wt_schema_get_colgroup(session, uri, NULL, &colgroup)); - WT_ERR(__wt_schema_worker( - session, colgroup->source, func, cfg, open_flags)); + WT_ERR(__wt_schema_worker(session, colgroup->source, + file_func, name_func, cfg, open_flags)); } else if (WT_PREFIX_SKIP(tablename, "index:")) { idx = NULL; WT_ERR(__wt_schema_get_index(session, uri, NULL, &idx)); - WT_ERR(__wt_schema_worker( - session, idx->source, func, cfg, open_flags)); + WT_ERR(__wt_schema_worker(session, idx->source, + file_func, name_func, cfg, open_flags)); } else if (WT_PREFIX_MATCH(uri, "lsm:")) { WT_ERR(__wt_lsm_tree_worker( - session, uri, func, cfg, open_flags)); + session, uri, file_func, name_func, cfg, open_flags)); } else if (WT_PREFIX_SKIP(tablename, "table:")) { WT_ERR(__wt_schema_get_table(session, tablename, strlen(tablename), 0, &table)); WT_ASSERT(session, session->dhandle == NULL); + /* + * We could make a recursive call for each colgroup or index + * URI, but since we have already opened the table, we can take + * a short cut and skip straight to the sources. If we have a + * name function, it needs to know about the intermediate URIs. + */ for (i = 0; i < WT_COLGROUPS(table); i++) { colgroup = table->cgroups[i]; - WT_ERR(__wt_schema_worker( - session, colgroup->source, func, cfg, open_flags)); + if (name_func != NULL) + WT_ERR(name_func(session, colgroup->name)); + WT_ERR(__wt_schema_worker(session, colgroup->source, + file_func, name_func, cfg, open_flags)); } WT_ERR(__wt_schema_open_indices(session, table)); for (i = 0; i < table->nindices; i++) { idx = table->indices[i]; - WT_ERR(__wt_schema_worker( - session, idx->source, func, cfg, open_flags)); + if (name_func != NULL) + WT_ERR(name_func(session, idx->name)); + WT_ERR(__wt_schema_worker(session, idx->source, + file_func, name_func, cfg, open_flags)); } } else if ((ret = __wt_schema_get_source(session, uri, &dsrc)) == 0) { wt_session = (WT_SESSION *)session; - if (func == __wt_compact && dsrc->compact != NULL) + if (file_func == __wt_compact && dsrc->compact != NULL) WT_ERR(dsrc->compact( dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg)); - else if (func == __wt_salvage && dsrc->salvage != NULL) + else if (file_func == __wt_salvage && dsrc->salvage != NULL) WT_ERR(dsrc->salvage( dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg)); - else if (func == __wt_verify && dsrc->verify != NULL) + else if (file_func == __wt_verify && dsrc->verify != NULL) WT_ERR(dsrc->verify( dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg)); else diff --git a/src/session/session_api.c b/src/session/session_api.c index 2d037051955..68d04a8f640 100644 --- a/src/session/session_api.c +++ b/src/session/session_api.c @@ -350,7 +350,7 @@ __session_compact_worker( SESSION_API_CALL(session, compact, config, cfg); WT_WITH_SCHEMA_LOCK(session, - ret = __wt_schema_worker(session, uri, __wt_compact, cfg, 0)); + ret = __wt_schema_worker(session, uri, __wt_compact, NULL, cfg, 0)); err: API_END_NOTFOUND_MAP(session, ret); } @@ -466,8 +466,8 @@ __session_salvage(WT_SESSION *wt_session, const char *uri, const char *config) SESSION_API_CALL(session, salvage, config, cfg); WT_WITH_SCHEMA_LOCK(session, - ret = __wt_schema_worker(session, uri, - __wt_salvage, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_SALVAGE)); + ret = __wt_schema_worker(session, uri, __wt_salvage, + NULL, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_SALVAGE)); err: API_END_NOTFOUND_MAP(session, ret); } @@ -588,8 +588,8 @@ __session_upgrade(WT_SESSION *wt_session, const char *uri, const char *config) SESSION_API_CALL(session, upgrade, config, cfg); WT_WITH_SCHEMA_LOCK(session, - ret = __wt_schema_worker(session, uri, - __wt_upgrade, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_UPGRADE)); + ret = __wt_schema_worker(session, uri, __wt_upgrade, + NULL, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_UPGRADE)); err: API_END_NOTFOUND_MAP(session, ret); } @@ -608,8 +608,8 @@ __session_verify(WT_SESSION *wt_session, const char *uri, const char *config) SESSION_API_CALL(session, verify, config, cfg); WT_WITH_SCHEMA_LOCK(session, - ret = __wt_schema_worker(session, uri, - __wt_verify, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_VERIFY)); + ret = __wt_schema_worker(session, uri, __wt_verify, + NULL, cfg, WT_DHANDLE_EXCLUSIVE | WT_BTREE_VERIFY)); err: API_END_NOTFOUND_MAP(session, ret); } diff --git a/src/txn/txn_ckpt.c b/src/txn/txn_ckpt.c index 8d4b56de2f1..d1c14362758 100644 --- a/src/txn/txn_ckpt.c +++ b/src/txn/txn_ckpt.c @@ -95,7 +95,7 @@ __checkpoint_apply(WT_SESSION_IMPL *session, const char *cfg[], WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); if ((ret = __wt_schema_worker( - session, tmp->data, op, cfg, 0)) != 0) + session, tmp->data, op, NULL, cfg, 0)) != 0) WT_ERR_MSG(session, ret, "%s", (const char *)tmp->data); } WT_ERR_NOTFOUND_OK(ret); diff --git a/test/format/Makefile.am b/test/format/Makefile.am index da0c950b080..ed6939ae583 100644 --- a/test/format/Makefile.am +++ b/test/format/Makefile.am @@ -6,7 +6,8 @@ AM_CPPFLAGS = -DWIREDTIGER_TEST_COMPRESS_RAW \ noinst_PROGRAMS = t noinst_SCRIPTS = s_dumpcmp t_SOURCES =\ - config.h format.h bdb.c config.c t.c util.c wts.c wts_bulk.c wts_ops.c + config.h format.h backup.c bdb.c bulk.c config.c ops.c t.c util.c wts.c + t_LDADD = $(top_builddir)/libwiredtiger.la -L$(BERKELEY_DB_PATH)/lib -ldb t_LDFLAGS = -static diff --git a/test/format/backup.c b/test/format/backup.c new file mode 100644 index 00000000000..a7e2d10b909 --- /dev/null +++ b/test/format/backup.c @@ -0,0 +1,157 @@ +/*- + * Public Domain 2008-2013 WiredTiger, Inc. + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "format.h" + +/* + * check_copy -- + * Confirm the hot backup worked. + */ +static void +check_copy(void) +{ + WT_CONNECTION *conn; + WT_SESSION *session; + int ret; + + wts_open(RUNDIR_BACKUP, 0, &conn); + + /* + * Open a session and verify the store; some data-sources don't support + * verify. + * + * XXX + * LSM can deadlock if WT_SESSION methods are called at the wrong time, + * don't do that for now. + */ + if (!DATASOURCE("lsm") && !DATASOURCE("memrata")) { + if ((ret = conn->open_session( + conn, NULL, NULL, &session)) != 0) + die(ret, "connection.open_session"); + + if ((ret = session->verify(session, g.uri, NULL)) != 0) + die(ret, "session.verify: %s", g.uri); + } + + if ((ret = conn->close(conn, NULL)) != 0) + die(ret, "connection.close: %s", RUNDIR_BACKUP); +} + +/* + * hot_copy -- + * Copy a single file into the hot backup directory. + */ +static void +hot_copy(const char *name) +{ + char buf[1024]; + + (void)snprintf( + buf, sizeof(buf), "cp RUNDIR/%s RUNDIR/BACKUP/%s", name, name); + if (system(buf) != 0) + die(errno, "hot backup copy: %s", buf); +} + +/* + * hot_backup -- + * Periodically do a hot backup and verify it. + */ +void * +hot_backup(void *arg) +{ + WT_CONNECTION *conn; + WT_CURSOR *backup_cursor; + WT_SESSION *session; + u_int period; + int ret; + const char *key; + + (void)arg; + + /* If hot backups aren't configured, we're done. */ + if (!g.c_hot_backups) + return (NULL); + + /* Hot backups aren't supported for non-standard data sources. */ + if (DATASOURCE("kvsbdb") || DATASOURCE("memrata")) + return (NULL); + + conn = g.wts_conn; + + /* Open a session. */ + if ((ret = conn->open_session( + conn, NULL, NULL, &session)) != 0) + die(ret, "connection.open_session"); + + /* + * Perform a hot backup at somewhere under 10 seconds (so we get at + * least one done), and then at 45 second intervals. + */ + for (period = MMRAND(1, 10); !g.threads_finished; period = 45) { + + /* Sleep for a short period so we don't make the run wait. */ + if (period > 0) { + --period; + sleep(1); + if (g.threads_finished) + break; + } + + /* Lock out named checkpoints */ + if ((ret = pthread_rwlock_wrlock(&g.backup_lock)) != 0) + die(ret, "pthread_rwlock_wrlock: hot-backup lock"); + + /* Re-create the backup directory. */ + (void)system("cd RUNDIR && rm -rf BACKUP"); + if (mkdir(RUNDIR_BACKUP, 0777) != 0) + die(errno, "mkdir: %s", RUNDIR_BACKUP); + + if ((ret = session->open_cursor(session, + "backup:", NULL, NULL, &backup_cursor)) != 0) + die(ret, "session.open_cursor: backup"); + + while ((ret = backup_cursor->next(backup_cursor)) == 0) { + if ((ret = + backup_cursor->get_key(backup_cursor, &key)) != 0) + die(ret, "cursor.get_key"); + hot_copy(key); + } + + if ((ret = backup_cursor->close(backup_cursor)) != 0) + die(ret, "cursor.close"); + + if ((ret = pthread_rwlock_unlock(&g.backup_lock)) != 0) + die(ret, "pthread_rwlock_unlock: hot-backup lock"); + + check_copy(); + } + + if ((ret = session->close(session, NULL)) != 0) + die(ret, "session.close"); + + return (NULL); +} diff --git a/test/format/wts_bulk.c b/test/format/bulk.c index 71f01483481..14fcd5e0d9e 100644 --- a/test/format/wts_bulk.c +++ b/test/format/bulk.c @@ -43,7 +43,7 @@ wts_load(void) die(ret, "connection.open_session"); if (g.logging != 0) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== bulk load start ==============="); /* @@ -84,7 +84,7 @@ wts_load(void) cursor->set_key(cursor, g.key_cnt); cursor->set_value(cursor, *(uint8_t *)value.data); if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s %" PRIu32 " {0x%02" PRIx8 "}", "bulk V", g.key_cnt, ((uint8_t *)value.data)[0]); @@ -94,7 +94,7 @@ wts_load(void) cursor->set_key(cursor, g.key_cnt); cursor->set_value(cursor, &value); if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s %" PRIu32 " {%.*s}", "bulk V", g.key_cnt, (int)value.size, (char *)value.data); @@ -102,12 +102,12 @@ wts_load(void) case ROW: cursor->set_key(cursor, &key); if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s %" PRIu32 " {%.*s}", "bulk K", g.key_cnt, (int)key.size, (char *)key.data); cursor->set_value(cursor, &value); if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s %" PRIu32 " {%.*s}", "bulk V", g.key_cnt, (int)value.size, (char *)value.data); @@ -128,7 +128,7 @@ wts_load(void) die(ret, "cursor.close"); if (g.logging != 0) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== bulk load stop ==============="); if ((ret = session->close(session, NULL)) != 0) diff --git a/test/format/format.h b/test/format/format.h index 1871e425497..5f09e35d417 100644 --- a/test/format/format.h +++ b/test/format/format.h @@ -32,6 +32,7 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <fcntl.h> #include <inttypes.h> #include <limits.h> #include <pthread.h> @@ -76,6 +77,7 @@ extern WT_EXTENSION_API *wt_api; #define WT_NAME "wt" /* Object name */ #define RUNDIR "RUNDIR" /* Run home */ +#define RUNDIR_BACKUP "RUNDIR/BACKUP" /* Hot-backup directory */ #define RUNDIR_KVS "RUNDIR/KVS" /* Run home for data-source */ #define DATASOURCE(v) (strcmp(v, g.c_data_source) == 0 ? 1 : 0) @@ -87,7 +89,8 @@ typedef struct { void *bdb; /* BDB comparison handle */ void *dbc; /* BDB cursor handle */ - void *wts_conn; /* WT_CONNECTION handle */ + WT_CONNECTION *wts_conn; + WT_EXTENSION_API *wt_api; FILE *rand_log; /* Random number log */ @@ -101,6 +104,9 @@ typedef struct { int replay; /* Replaying a run. */ int track; /* Track progress */ + int threads_finished; /* Operations completed */ + + pthread_rwlock_t backup_lock; /* Hot backup running */ char *uri; /* Object name */ @@ -126,6 +132,7 @@ typedef struct { char *c_data_source; u_int c_delete_pct; u_int c_dictionary; + u_int c_hot_backups; char *c_file_type; u_int c_huffman_key; u_int c_huffman_value; @@ -184,6 +191,7 @@ void config_print(int); void config_setup(void); void config_single(const char *, int); void die(int, const char *, ...); +void *hot_backup(void *); void key_len_setup(void); void key_gen_setup(uint8_t **); void key_gen(uint8_t *, uint32_t *, uint64_t, int); @@ -192,9 +200,10 @@ void track(const char *, uint64_t, TINFO *); void val_gen_setup(uint8_t **); void value_gen(uint8_t *, uint32_t *, uint64_t); void wts_close(void); +void wts_create(void); void wts_dump(const char *, int); void wts_load(void); -void wts_open(void); +void wts_open(const char *, int, WT_CONNECTION **); void wts_ops(void); uint32_t wts_rand(void); void wts_read_scan(void); diff --git a/test/format/wts_ops.c b/test/format/ops.c index 9c9ddb2b88b..b69df945156 100644 --- a/test/format/wts_ops.c +++ b/test/format/ops.c @@ -48,6 +48,7 @@ wts_ops(void) TINFO *tinfo, total; WT_CONNECTION *conn; WT_SESSION *session; + pthread_t backup_tid; int ret, running; uint32_t i; @@ -57,7 +58,7 @@ wts_ops(void) if (g.logging != 0) { if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) die(ret, "connection.open_session"); - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== thread ops start ==============="); } @@ -66,7 +67,16 @@ wts_ops(void) total.id = 1; (void)ops(&total); } else { - /* Create thread structure. */ + /* + * We have to single-thread named checkpoints and hot backups, + * initialize a lock. + */ + if ((ret = pthread_rwlock_init(&g.backup_lock, NULL)) != 0) + die(ret, "pthread_rwlock_init: hot-backup lock"); + + g.threads_finished = 0; + + /* Create thread structure, start worker, hot-backup threads. */ if ((tinfo = calloc((size_t)g.c_threads, sizeof(*tinfo))) == NULL) die(errno, "calloc"); @@ -77,6 +87,9 @@ wts_ops(void) &tinfo[i].tid, NULL, ops, &tinfo[i])) != 0) die(ret, "pthread_create"); } + if ((ret = + pthread_create(&backup_tid, NULL, hot_backup, NULL)) != 0) + die(ret, "pthread_create"); /* Wait for the threads. */ for (;;) { @@ -105,10 +118,17 @@ wts_ops(void) (void)usleep(100000); /* 1/10th of a second */ } free(tinfo); + + /* Wait for the backup thread. */ + g.threads_finished = 1; + (void)pthread_join(backup_tid, NULL); + + if ((ret = pthread_rwlock_destroy(&g.backup_lock)) != 0) + die(ret, "pthread_rwlock_destroy: hot-backup lock"); } if (g.logging != 0) { - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== thread ops stop ==============="); if ((ret = session->close(session, NULL)) != 0) die(ret, "session.close"); @@ -197,29 +217,42 @@ ops(void *arg) if (cnt == ckpt_op) { /* - * LSM trees don't support named checkpoints, else half - * the time we name the checkpoint. + * LSM trees don't support named checkpoints, else 25% + * of the time we name the checkpoint. */ - if (DATASOURCE("lsm") || MMRAND(1, 2) == 1) + if (DATASOURCE("lsm") || MMRAND(1, 4) == 1) ckpt_config = NULL; else { (void)snprintf(buf, sizeof(buf), "name=thread-%d", tinfo->id); ckpt_config = buf; } + + /* Named checkpoints lock out hot backups */ + if (ckpt_config != NULL && + (ret = pthread_rwlock_wrlock(&g.backup_lock)) != 0) + die(ret, + "pthread_rwlock_wrlock: hot-backup lock"); + if ((ret = session->checkpoint(session, ckpt_config)) != 0) die(ret, "session.checkpoint%s%s", ckpt_config == NULL ? "" : ": ", ckpt_config == NULL ? "" : ckpt_config); + if (ckpt_config != NULL && + (ret = pthread_rwlock_unlock(&g.backup_lock)) != 0) + die(ret, + "pthread_rwlock_wrlock: hot-backup lock"); + /* * Pick the next checkpoint operation, try for roughly * five checkpoint operations per thread run. */ ckpt_op += MMRAND(1, thread_ops) / 5; } - /* kvs doesn't support compaction. */ + + /* Data-sources don't support compaction. */ if (cnt == compact_op && !DATASOURCE("kvsbdb") && !DATASOURCE("memrata") && (ret = session->compact(session, g.uri, NULL)) != 0) @@ -383,7 +416,7 @@ read_row(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno) /* Log the operation */ if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64, "read", keyno); /* Retrieve the key/value pair by key. */ @@ -527,18 +560,18 @@ nextprev(WT_CURSOR *cursor, int next, int *notfoundp) if (g.logging == LOG_OPS) switch (g.type) { case FIX: - (void)wt_api->msg_printf(wt_api, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {0x%02x}", which, keyno, ((char *)value.data)[0]); break; case ROW: - (void)wt_api->msg_printf( - wt_api, session, "%-10s{%.*s/%.*s}", which, + (void)g.wt_api->msg_printf( + g.wt_api, session, "%-10s{%.*s/%.*s}", which, (int)key.size, (char *)key.data, (int)value.size, (char *)value.data); break; case VAR: - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {%.*s}", which, keyno, (int)value.size, (char *)value.data); break; @@ -563,7 +596,7 @@ row_update( /* Log the operation */ if (g.logging == LOG_OPS) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s{%.*s}\n%-10s{%.*s}", insert ? "insertK" : "putK", (int)key->size, (char *)key->data, @@ -602,12 +635,12 @@ col_update(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno) /* Log the operation */ if (g.logging == LOG_OPS) { if (g.type == FIX) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}", "update", keyno, ((uint8_t *)value->data)[0]); else - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {%.*s}", "update", keyno, (int)value->size, (char *)value->data); @@ -665,12 +698,12 @@ col_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t *keynop) if (g.logging == LOG_OPS) { if (g.type == FIX) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {0x%02" PRIx8 "}", "insert", keyno, ((uint8_t *)value->data)[0]); else - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "%-10s%" PRIu64 " {%.*s}", "insert", keyno, (int)value->size, (char *)value->data); @@ -699,8 +732,8 @@ row_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno, int *notfoundp) /* Log the operation */ if (g.logging == LOG_OPS) - (void)wt_api->msg_printf( - wt_api, session, "%-10s%" PRIu64, "remove", keyno); + (void)g.wt_api->msg_printf( + g.wt_api, session, "%-10s%" PRIu64, "remove", keyno); cursor->set_key(cursor, key); ret = cursor->remove(cursor); @@ -734,8 +767,8 @@ col_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno, int *notfoundp) /* Log the operation */ if (g.logging == LOG_OPS) - (void)wt_api->msg_printf( - wt_api, session, "%-10s%" PRIu64, "remove", keyno); + (void)g.wt_api->msg_printf( + g.wt_api, session, "%-10s%" PRIu64, "remove", keyno); cursor->set_key(cursor, keyno); ret = cursor->remove(cursor); diff --git a/test/format/t.c b/test/format/t.c index eff9caced48..39f38d9580a 100644 --- a/test/format/t.c +++ b/test/format/t.c @@ -28,7 +28,6 @@ #include "format.h" GLOBAL g; -WT_EXTENSION_API *wt_api; static void onint(int); static void startup(void); @@ -110,7 +109,8 @@ main(int argc, char *argv[]) track("starting up", 0ULL, NULL); if (SINGLETHREADED) bdb_open(); /* Initial file config */ - wts_open(); + wts_open(RUNDIR, 1, &g.wts_conn); + wts_create(); wts_load(); /* Load initial records */ wts_verify("post-bulk verify"); /* Verify */ @@ -165,7 +165,7 @@ main(int argc, char *argv[]) * against the Berkeley DB data set again, if possible). */ if (g.c_delete_pct == 0) { - wts_open(); + wts_open(RUNDIR, 1, &g.wts_conn); wts_salvage(); wts_verify("post-salvage verify"); wts_close(); diff --git a/test/format/wts.c b/test/format/wts.c index f3f60f9cb4f..450325db3a9 100644 --- a/test/format/wts.c +++ b/test/format/wts.c @@ -58,23 +58,25 @@ static WT_EVENT_HANDLER event_handler = { handle_progress }; +/* + * wts_open -- + * Open a connection to a WiredTiger database. + */ void -wts_open(void) +wts_open(const char *home, int set_api, WT_CONNECTION **connp) { WT_CONNECTION *conn; - WT_SESSION *session; - uint32_t maxintlpage, maxintlitem, maxleafpage, maxleafitem; int ret; - char config[2048], *end, *p; + char config[2048]; + + *connp = NULL; /* - * Open configuration. - * * Put configuration file configuration options second to last. Put * command line configuration options at the end. Do this so they * override the standard configuration. */ - snprintf(config, sizeof(config), + (void)snprintf(config, sizeof(config), "create," "sync=false,cache_size=%" PRIu32 "MB," "buffer_alignment=512,error_prefix=\"%s\"," @@ -85,7 +87,7 @@ wts_open(void) g.c_cache, g.progname, g.c_data_extend ? "file_extend=(data=8MB)," : "", - REVERSE_PATH, + g.c_reverse ? REVERSE_PATH : "", access(BZIP_PATH, R_OK) == 0 ? BZIP_PATH : "", access(LZO_PATH, R_OK) == 0 ? LZO_PATH : "", (access(RAW_PATH, R_OK) == 0 && @@ -96,21 +98,48 @@ wts_open(void) g.c_config_open == NULL ? "" : g.c_config_open, g.config_open == NULL ? "" : g.config_open); + /* + * Direct I/O may not work with hot-backups, doing copies through the + * buffer cache after configuring direct I/O in Linux won't work. If + * direct I/O is configured, turn off hot backups. This isn't a great + * place to do this check, but it's only here we have the configuration + * string. + */ + g.c_hot_backups = strstr(config, "direct_io") == NULL; + if ((ret = - wiredtiger_open("RUNDIR", &event_handler, config, &conn)) != 0) - die(ret, "wiredtiger_open"); - g.wts_conn = conn; - /* Load extension functions. */ - wt_api = conn->get_extension_api(conn); + wiredtiger_open(home, &event_handler, config, &conn)) != 0) + die(ret, "wiredtiger_open: %s", home); - if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) - die(ret, "connection.open_session"); + if (set_api) + g.wt_api = conn->get_extension_api(conn); + + *connp = conn; +} + +/* + * wts_create -- + * Create the underlying store. + */ +void +wts_create(void) +{ + WT_CONNECTION *conn; + WT_SESSION *session; + uint32_t maxintlpage, maxintlitem, maxleafpage, maxleafitem; + int ret; + char config[2048], *end, *p; + + conn = g.wts_conn; /* - * Create the object. + * Create the underlying store. * * Make sure at least 2 internal page per thread can fit in cache. */ + if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) + die(ret, "connection.open_session"); + maxintlpage = 1U << g.c_intl_page_max; while (maxintlpage > 512 && 2 * g.c_threads * maxintlpage > g.c_cache << 20) @@ -332,12 +361,12 @@ wts_verify(const char *tag) if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0) die(ret, "connection.open_session"); if (g.logging != 0) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== verify start ==============="); if ((ret = session->verify(session, g.uri, NULL)) != 0) die(ret, "session.verify: %s: %s", g.uri, tag); if (g.logging != 0) - (void)wt_api->msg_printf(wt_api, session, + (void)g.wt_api->msg_printf(g.wt_api, session, "=============== verify stop ==============="); if ((ret = session->close(session, NULL)) != 0) die(ret, "session.close"); diff --git a/test/suite/helper.py b/test/suite/helper.py index fa8e95d249d..31215448987 100644 --- a/test/suite/helper.py +++ b/test/suite/helper.py @@ -165,26 +165,35 @@ def value_populate_complex(i): # config: prefix of the session.create configuration string # rows: entries to insert def complex_populate(self, uri, config, rows): + complex_populate_type(self, uri, config, rows, '') +def complex_populate_lsm(self, uri, config, rows): + complex_populate_type(self, uri, config, rows, 'type=lsm') +def complex_populate_type(self, uri, config, rows, type): self.session.create(uri, config + ',value_format=SiSS,' + 'columns=(record,column2,column3,column4,column5),' + 'colgroups=(cgroup1,cgroup2,cgroup3,cgroup4,cgroup5,cgroup6)') + cgname = 'colgroup:' + uri.split(":")[1] - self.session.create(cgname + ':cgroup1', 'columns=(column2)') - self.session.create(cgname + ':cgroup2', 'columns=(column3)') - self.session.create(cgname + ':cgroup3', 'columns=(column4)') - self.session.create(cgname + ':cgroup4', 'columns=(column2,column3)') - self.session.create(cgname + ':cgroup5', 'columns=(column3,column4)') + self.session.create(cgname + ':cgroup1', 'columns=(column2)' + ',' + type) + self.session.create(cgname + ':cgroup2', 'columns=(column3)' + ',' + type) + self.session.create(cgname + ':cgroup3', 'columns=(column4)' + ',' + type) + self.session.create( + cgname + ':cgroup4', 'columns=(column2,column3)' + ',' + type) + self.session.create( + cgname + ':cgroup5', 'columns=(column3,column4)' + ',' + type) self.session.create( - cgname + ':cgroup6', 'columns=(column2,column4,column5)') + cgname + ':cgroup6', 'columns=(column2,column4,column5)' + ',' + type) indxname = 'index:' + uri.split(":")[1] - self.session.create(indxname + ':indx1', 'columns=(column2)') - self.session.create(indxname + ':indx2', 'columns=(column3)') - self.session.create(indxname + ':indx3', 'columns=(column4)') - self.session.create(indxname + ':indx4', 'columns=(column2,column4)') - self.session.create(indxname + ':indx5', 'columns=(column3,column5)') + self.session.create(indxname + ':indx1', 'columns=(column2)' + ',' + type) + self.session.create(indxname + ':indx2', 'columns=(column3)' + ',' + type) + self.session.create(indxname + ':indx3', 'columns=(column4)' + ',' + type) + self.session.create( + indxname + ':indx4', 'columns=(column2,column4)' + ',' + type) + self.session.create( + indxname + ':indx5', 'columns=(column3,column5)' + ',' + type) self.session.create( - indxname + ':indx6', 'columns=(column3,column5,column4)') + indxname + ':indx6', 'columns=(column3,column5,column4)' + ',' + type) cursor = self.session.open_cursor(uri, None) for i in range(1, rows + 1): cursor.set_key(key_populate(cursor, i)) diff --git a/test/suite/test_backup.py b/test/suite/test_backup01.py index c53cd60f554..0ab7ab2e5ec 100644 --- a/test/suite/test_backup.py +++ b/test/suite/test_backup01.py @@ -31,7 +31,8 @@ import shutil import string from suite_subprocess import suite_subprocess import wiredtiger, wttest -from helper import compare_files, complex_populate, simple_populate +from helper import compare_files,\ + complex_populate, complex_populate_lsm, simple_populate # test_backup.py # Utilities: wt backup @@ -41,17 +42,22 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): pfx = 'test_backup' objs = [ - ( 'file:' + pfx + '.1', simple_populate), - ( 'file:' + pfx + '.2', simple_populate), - ('table:' + pfx + '.3', simple_populate), - ('table:' + pfx + '.4', simple_populate), - ('table:' + pfx + '.5', complex_populate), - ('table:' + pfx + '.6', complex_populate), + ( 'file:' + pfx + '.1', simple_populate, 0), + ( 'file:' + pfx + '.2', simple_populate, 0), + ('table:' + pfx + '.3', simple_populate, 0), + ('table:' + pfx + '.4', simple_populate, 0), + ('table:' + pfx + '.5', complex_populate, 0), + ('table:' + pfx + '.6', complex_populate, 0), + ('table:' + pfx + '.7', complex_populate_lsm, 1), + ('table:' + pfx + '.8', complex_populate_lsm, 1), ] # Populate a set of objects. - def populate(self): + def populate(self, skiplsm): for i in self.objs: + if i[2]: + if skiplsm: + continue i[1](self, i[0], 'key_format=S', 100) # Compare the original and backed-up files using the wt dump command. @@ -75,7 +81,7 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): # Test backup of a database using the wt backup command. def test_backup_database(self): - self.populate() + self.populate(0) os.mkdir(self.dir) self.runWt(['backup', self.dir]) @@ -127,23 +133,16 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): # Test backup of database subsets. def test_backup_table(self): - self.populate() - self.backup_table([0,2,4]) - self.backup_table([1,3,5]) + self.populate(0) + self.backup_table([0,2,4,6]) + self.backup_table([1,3,5,7]) self.backup_table([0,1,2]) self.backup_table([3,4,5]) - - # Test backup of random object types fails. - def test_illegal_objects(self): - for target in ('colgroup:xxx', 'index:xxx'): - msg = '/invalid backup target object/' - config = 'target=("%s")' % target - self.assertRaisesWithMessage(wiredtiger.WiredTigerError, - lambda: self.session.open_cursor('backup:', None, config), msg) + self.backup_table([5,6,7]) # Test cursor reset runs through the list twice. def test_cursor_reset(self): - self.populate() + self.populate(0) cursor = self.session.open_cursor('backup:', None, None) i = 0 while True: @@ -165,7 +164,8 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess): # Test that named checkpoints can't be deleted while backup cursors are # open, but that normal checkpoints continue to work. def test_checkpoint_delete(self): - self.populate() + # You cannot name checkpoints including LSM tables, skip those. + self.populate(1) # Confirm checkpoints are being deleted. self.session.checkpoint("name=one") diff --git a/test/suite/test_backup03.py b/test/suite/test_backup03.py new file mode 100644 index 00000000000..a8b9340cba2 --- /dev/null +++ b/test/suite/test_backup03.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# Public Domain 2008-2013 WiredTiger, Inc. +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +import glob +import os +import shutil +import string +from suite_subprocess import suite_subprocess +import wiredtiger, wttest +from helper import compare_files,\ + complex_populate, complex_populate_lsm, simple_populate + +# test_backup03.py +# Utilities: wt backup +# Test cursor backup with target URIs +class test_backup(wttest.WiredTigerTestCase, suite_subprocess): + dir='backup.dir' # Backup directory name + + pfx = 'test_backup' + objs = [ + ('table:' + pfx + '.1', simple_populate, 100), + ('lsm:' + pfx + '.2', simple_populate, 50000), + ('table:' + pfx + '.3', complex_populate, 100), + ('table:' + pfx + '.4', complex_populate_lsm, 100), + ] + + # Populate a set of objects. + def populate(self): + for i in self.objs: + i[1](self, i[0], 'key_format=S', i[2]) + # Backup needs a checkpoint + self.session.checkpoint(None) + + # Compare the original and backed-up files using the wt dump command. + def compare(self, uri): + self.runWt(['dump', uri], outfilename='orig') + self.runWt(['-h', self.dir, 'dump', uri], outfilename='backup') + compare_files(self, 'orig', 'backup') + + # Check that a URI doesn't exist, both the meta-data and the file names. + def confirmPathDoesNotExist(self, uri): + conn = wiredtiger.wiredtiger_open(self.dir) + session = conn.open_session() + self.assertRaises(wiredtiger.WiredTigerError, + lambda: session.open_cursor(uri, None, None)) + conn.close() + + self.assertEqual( + glob.glob(self.dir + '*' + uri.split(":")[1] + '*'), [], + 'confirmPathDoesNotExist: URI exists, file name matching \"' + + uri.split(":")[1] + '\" found') + + # Backup a set of chosen tables/files using the wt backup command. + def backup_table_cursor(self, l): + # Remove any previous backup directories. + shutil.rmtree(self.dir, True) + os.mkdir(self.dir) + + # Build the target list. + config = 'target=(' + for i in range(0, len(self.objs)): + if i in l: + config += '"' + self.objs[i][0] + '",' + config += ')' + + # Open up the backup cursor, and copy the files. + cursor = self.session.open_cursor('backup:', None, config) + while True: + ret = cursor.next() + if ret != 0: + break + #print 'Copy from: ' + cursor.get_key() + ' to ' + self.dir + shutil.copy(cursor.get_key(), self.dir) + self.assertEqual(ret, wiredtiger.WT_NOTFOUND) + cursor.close() + + # Confirm the objects we backed up exist, with correct contents. + for i in range(0, len(self.objs)): + if i in l: + self.compare(self.objs[i][0]) + + # Confirm the other objects don't exist. + for i in range(0, len(self.objs)): + if i not in l: + self.confirmPathDoesNotExist(self.objs[i][0]) + + # Test backup with targets + def test_targets_groups(self): + self.populate() + self.backup_table_cursor([0,2]) + self.backup_table_cursor([1,3]) + self.backup_table_cursor([0,1,2]) + self.backup_table_cursor([0,1,2,3]) + + def test_target_individual(self): + self.populate() + self.backup_table_cursor([0]) + self.backup_table_cursor([1]) + self.backup_table_cursor([2]) + self.backup_table_cursor([3]) + + +if __name__ == '__main__': + wttest.run() diff --git a/test/suite/test_checkpoint01.py b/test/suite/test_checkpoint01.py index 6e1dd2e6385..aa479cb754a 100644 --- a/test/suite/test_checkpoint01.py +++ b/test/suite/test_checkpoint01.py @@ -26,7 +26,7 @@ # OTHER DEALINGS IN THE SOFTWARE. import wiredtiger, wttest -from helper import key_populate, simple_populate +from helper import key_populate, complex_populate_lsm, simple_populate # test_checkpoint01.py # Checkpoint tests @@ -325,6 +325,17 @@ class test_checkpoint_last_name(wttest.WiredTigerTestCase): self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.checkpoint(conf), msg) + +# Check we can't name checkpoints that include LSM tables. +class test_checkpoint_lsm_name(wttest.WiredTigerTestCase): + def test_checkpoint_lsm_name(self): + complex_populate_lsm(self, + "table:checkpoint", 'type=lsm,key_format=S', 1000) + msg = '/LSM trees do not support named checkpoints/' + self.assertRaisesWithMessage(wiredtiger.WiredTigerError, + lambda: self.session.checkpoint("name=ckpt"), msg) + + class test_checkpoint_empty(wttest.WiredTigerTestCase): scenarios = [ ('file', dict(uri='file:checkpoint')), |