500 lines
14 KiB
C
500 lines
14 KiB
C
/*-
|
|
* Public Domain 2008-2014 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"
|
|
|
|
static int
|
|
handle_message(WT_EVENT_HANDLER *handler,
|
|
WT_SESSION *session, const char *message)
|
|
{
|
|
WT_UNUSED(handler);
|
|
WT_UNUSED(session);
|
|
|
|
if (g.logfp != NULL)
|
|
return (fprintf(g.logfp, "%s\n", message) < 0 ? -1 : 0);
|
|
|
|
return (printf("%s\n", message) < 0 ? -1 : 0);
|
|
}
|
|
|
|
/*
|
|
* __handle_progress_default --
|
|
* Default WT_EVENT_HANDLER->handle_progress implementation: ignore.
|
|
*/
|
|
static int
|
|
handle_progress(WT_EVENT_HANDLER *handler,
|
|
WT_SESSION *session, const char *operation, uint64_t progress)
|
|
{
|
|
WT_UNUSED(handler);
|
|
WT_UNUSED(session);
|
|
|
|
track(operation, progress, NULL);
|
|
return (0);
|
|
}
|
|
|
|
static WT_EVENT_HANDLER event_handler = {
|
|
NULL,
|
|
handle_message,
|
|
handle_progress,
|
|
NULL /* Close handler. */
|
|
};
|
|
|
|
/*
|
|
* wts_open --
|
|
* Open a connection to a WiredTiger database.
|
|
*/
|
|
void
|
|
wts_open(const char *home, int set_api, WT_CONNECTION **connp)
|
|
{
|
|
WT_CONNECTION *conn;
|
|
int ret;
|
|
char config[2048];
|
|
|
|
*connp = NULL;
|
|
|
|
/*
|
|
* Put configuration file configuration options second to last. Put
|
|
* command line configuration options at the end. Do this so they
|
|
* override the standard configuration.
|
|
*/
|
|
(void)snprintf(config, sizeof(config),
|
|
"create,"
|
|
"checkpoint_sync=false,cache_size=%" PRIu32 "MB,"
|
|
"buffer_alignment=512,error_prefix=\"%s\","
|
|
"%s,%s,%s,"
|
|
"extensions="
|
|
"[\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"],"
|
|
"%s,%s",
|
|
g.c_cache,
|
|
g.progname,
|
|
g.c_data_extend ? "file_extend=(data=8MB)," : "",
|
|
g.c_mmap ? "mmap=true" : "mmap=false",
|
|
g.c_statistics ? "statistics=(fast)," : "statistics=(none)",
|
|
g.c_reverse ? REVERSE_PATH : "",
|
|
access(BZIP_PATH, R_OK) == 0 ? BZIP_PATH : "",
|
|
access(LZO_PATH, R_OK) == 0 ? LZO_PATH : "",
|
|
access(SNAPPY_PATH, R_OK) == 0 ? SNAPPY_PATH : "",
|
|
access(ZLIB_PATH, R_OK) == 0 ? ZLIB_PATH : "",
|
|
DATASOURCE("kvsbdb") ? KVS_BDB_PATH : "",
|
|
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.
|
|
*/
|
|
if (strstr(config, "direct_io") != NULL)
|
|
g.c_hot_backups = 0;
|
|
|
|
if ((ret = wiredtiger_open(home, &event_handler, config, &conn)) != 0)
|
|
die(ret, "wiredtiger_open: %s", home);
|
|
|
|
if (set_api)
|
|
g.wt_api = conn->get_extension_api(conn);
|
|
|
|
/*
|
|
* Load the Helium shared library: it would be possible to do this as
|
|
* part of the extensions configured for wiredtiger_open, there's no
|
|
* difference, I am doing it here because it's easier to work with the
|
|
* configuration strings.
|
|
*/
|
|
if (DATASOURCE("helium")) {
|
|
if (g.helium_mount == NULL)
|
|
die(EINVAL, "no Helium mount point specified");
|
|
(void)snprintf(config, sizeof(config),
|
|
"entry=wiredtiger_extension_init,config=["
|
|
"helium_verbose=0,"
|
|
"dev1=[helium_devices=\"he://./%s\","
|
|
"helium_o_volume_truncate=1]]",
|
|
g.helium_mount);
|
|
if ((ret =
|
|
conn->load_extension(conn, HELIUM_PATH, config)) != 0)
|
|
die(ret,
|
|
"WT_CONNECTION.load_extension: %s:%s",
|
|
HELIUM_PATH, config);
|
|
}
|
|
*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[4096], *end, *p;
|
|
|
|
conn = g.wts_conn;
|
|
|
|
/*
|
|
* Ensure that we can service at least one operation per-thread
|
|
* concurrently without filling the cache with pinned pages. We
|
|
* choose a multiplier of three because the max configurations control
|
|
* on disk size and in memory pages are often significantly larger
|
|
* than their disk counterparts.
|
|
*/
|
|
maxintlpage = 1U << g.c_intl_page_max;
|
|
maxleafpage = 1U << g.c_leaf_page_max;
|
|
while (3 * g.c_threads * (maxintlpage + maxleafpage) >
|
|
g.c_cache << 20) {
|
|
if (maxleafpage <= 512 && maxintlpage <= 512)
|
|
break;
|
|
if (maxintlpage > 512)
|
|
maxintlpage >>= 1;
|
|
if (maxleafpage > 512)
|
|
maxleafpage >>= 1;
|
|
}
|
|
maxintlitem = MMRAND(maxintlpage / 50, maxintlpage / 40);
|
|
if (maxintlitem < 40)
|
|
maxintlitem = 40;
|
|
maxleafitem = MMRAND(maxleafpage / 50, maxleafpage / 40);
|
|
if (maxleafitem < 40)
|
|
maxleafitem = 40;
|
|
|
|
p = config;
|
|
end = config + sizeof(config);
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"key_format=%s,"
|
|
"allocation_size=512,%s"
|
|
"internal_page_max=%d,internal_item_max=%d,"
|
|
"leaf_page_max=%d,leaf_item_max=%d",
|
|
(g.type == ROW) ? "u" : "r",
|
|
g.c_firstfit ? "block_allocation=first," : "",
|
|
maxintlpage, maxintlitem, maxleafpage, maxleafitem);
|
|
|
|
switch (g.type) {
|
|
case FIX:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",value_format=%" PRIu32 "t", g.c_bitcnt);
|
|
break;
|
|
case ROW:
|
|
if (g.c_huffman_key)
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",huffman_key=english");
|
|
if (g.c_prefix_compression)
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",prefix_compression_min=%" PRIu32,
|
|
g.c_prefix_compression_min);
|
|
else
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",prefix_compression=false");
|
|
if (g.c_reverse)
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",collator=reverse");
|
|
/* FALLTHROUGH */
|
|
case VAR:
|
|
if (g.c_huffman_value)
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",huffman_value=english");
|
|
if (g.c_dictionary)
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",dictionary=%d", MMRAND(123, 517));
|
|
break;
|
|
}
|
|
|
|
/* Configure checksums. */
|
|
switch (g.c_checksum_flag) {
|
|
case CHECKSUM_OFF:
|
|
p += snprintf(p, (size_t)(end - p), ",checksum=\"off\"");
|
|
break;
|
|
case CHECKSUM_ON:
|
|
p += snprintf(p, (size_t)(end - p), ",checksum=\"on\"");
|
|
break;
|
|
case CHECKSUM_UNCOMPRESSED:
|
|
p += snprintf(
|
|
p, (size_t)(end - p), ",checksum=\"uncompressed\"");
|
|
break;
|
|
}
|
|
|
|
/* Configure compression. */
|
|
switch (g.c_compression_flag) {
|
|
case COMPRESS_NONE:
|
|
break;
|
|
case COMPRESS_BZIP:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",block_compressor=\"bzip2\"");
|
|
break;
|
|
case COMPRESS_BZIP_RAW:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",block_compressor=\"bzip2-raw-test\"");
|
|
break;
|
|
case COMPRESS_LZO:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",block_compressor=\"LZO1B-6\"");
|
|
break;
|
|
case COMPRESS_SNAPPY:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",block_compressor=\"snappy\"");
|
|
break;
|
|
case COMPRESS_ZLIB:
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",block_compressor=\"zlib\"");
|
|
break;
|
|
}
|
|
|
|
/* Configure Btree internal key truncation. */
|
|
p += snprintf(
|
|
p, (size_t)(end - p), ",internal_key_truncate=%s",
|
|
g.c_internal_key_truncation ? "true" : "false");
|
|
|
|
/* Configure Btree page key gap. */
|
|
p += snprintf(p, (size_t)(end - p), ",key_gap=%" PRIu32, g.c_key_gap);
|
|
|
|
/* Configure Btree split page percentage. */
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",split_pct=%" PRIu32, g.c_split_pct);
|
|
|
|
/* Configure LSM and data-sources. */
|
|
if (DATASOURCE("helium"))
|
|
p += snprintf(p, (size_t)(end - p),
|
|
",type=helium,helium_o_compress=%d,helium_o_truncate=1",
|
|
g.c_compression_flag == COMPRESS_NONE ? 0 : 1);
|
|
|
|
if (DATASOURCE("kvsbdb"))
|
|
p += snprintf(p, (size_t)(end - p), ",type=kvsbdb");
|
|
|
|
if (DATASOURCE("lsm")) {
|
|
p += snprintf(p, (size_t)(end - p), ",type=lsm,lsm=(");
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"auto_throttle=%s,", g.c_auto_throttle ? "true" : "false");
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"chunk_size=%" PRIu32 "MB,", g.c_chunk_size);
|
|
/*
|
|
* We can't set bloom_oldest without bloom, and we want to test
|
|
* with Bloom filters on most of the time anyway.
|
|
*/
|
|
if (g.c_bloom_oldest)
|
|
g.c_bloom = 1;
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"bloom=%s,", g.c_bloom ? "true" : "false");
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"bloom_bit_count=%" PRIu32 ",", g.c_bloom_bit_count);
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"bloom_hash_count=%" PRIu32 ",", g.c_bloom_hash_count);
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"bloom_oldest=%s,", g.c_bloom_oldest ? "true" : "false");
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"merge_max=%" PRIu32 ",", g.c_merge_max);
|
|
p += snprintf(p, (size_t)(end - p),
|
|
"merge_threads=%" PRIu32 ",", g.c_merge_threads);
|
|
p += snprintf(p, (size_t)(end - p), ",)");
|
|
}
|
|
|
|
/*
|
|
* Create the underlying store.
|
|
*/
|
|
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
|
|
die(ret, "connection.open_session");
|
|
if ((ret = session->create(session, g.uri, config)) != 0)
|
|
die(ret, "session.create: %s", g.uri);
|
|
if ((ret = session->close(session, NULL)) != 0)
|
|
die(ret, "session.close");
|
|
}
|
|
|
|
void
|
|
wts_close(void)
|
|
{
|
|
WT_CONNECTION *conn;
|
|
int ret;
|
|
|
|
conn = g.wts_conn;
|
|
|
|
if ((ret = conn->close(conn, NULL)) != 0)
|
|
die(ret, "connection.close");
|
|
}
|
|
|
|
void
|
|
wts_dump(const char *tag, int dump_bdb)
|
|
{
|
|
size_t len;
|
|
int ret;
|
|
char *cmd;
|
|
|
|
/* Some data-sources don't support dump through the wt utility. */
|
|
if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
|
|
return;
|
|
|
|
track("dump files and compare", 0ULL, NULL);
|
|
|
|
len = strlen(g.home) + strlen(BERKELEY_DB_PATH) + strlen(g.uri) + 100;
|
|
if ((cmd = malloc(len)) == NULL)
|
|
syserr("malloc");
|
|
(void)snprintf(cmd, len,
|
|
"sh s_dumpcmp -h %s %s %s %s %s %s",
|
|
g.home,
|
|
dump_bdb ? "-b " : "",
|
|
dump_bdb ? BERKELEY_DB_PATH : "",
|
|
g.type == FIX || g.type == VAR ? "-c" : "",
|
|
g.uri == NULL ? "" : "-n",
|
|
g.uri == NULL ? "" : g.uri);
|
|
|
|
if ((ret = system(cmd)) != 0)
|
|
die(ret, "%s: dump comparison failed", tag);
|
|
free(cmd);
|
|
}
|
|
|
|
void
|
|
wts_salvage(void)
|
|
{
|
|
WT_CONNECTION *conn;
|
|
WT_SESSION *session;
|
|
int ret;
|
|
|
|
/* Some data-sources don't support salvage. */
|
|
if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
|
|
return;
|
|
|
|
conn = g.wts_conn;
|
|
track("salvage", 0ULL, NULL);
|
|
|
|
/*
|
|
* Save a copy of the interesting files so we can replay the salvage
|
|
* step as necessary.
|
|
*/
|
|
if ((ret = system(g.home_salvage_copy)) != 0)
|
|
die(ret, "salvage copy step failed");
|
|
|
|
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
|
|
die(ret, "connection.open_session");
|
|
if ((ret = session->salvage(session, g.uri, NULL)) != 0)
|
|
die(ret, "session.salvage: %s", g.uri);
|
|
if ((ret = session->close(session, NULL)) != 0)
|
|
die(ret, "session.close");
|
|
}
|
|
|
|
void
|
|
wts_verify(const char *tag)
|
|
{
|
|
WT_CONNECTION *conn;
|
|
WT_SESSION *session;
|
|
int ret;
|
|
|
|
conn = g.wts_conn;
|
|
track("verify", 0ULL, NULL);
|
|
|
|
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
|
|
die(ret, "connection.open_session");
|
|
if (g.logging != 0)
|
|
(void)g.wt_api->msg_printf(g.wt_api, session,
|
|
"=============== verify start ===============");
|
|
|
|
/* Session operations for LSM can return EBUSY. */
|
|
ret = session->verify(session, g.uri, NULL);
|
|
if (ret != 0 && !(ret == EBUSY && DATASOURCE("lsm")))
|
|
die(ret, "session.verify: %s: %s", g.uri, tag);
|
|
|
|
if (g.logging != 0)
|
|
(void)g.wt_api->msg_printf(g.wt_api, session,
|
|
"=============== verify stop ===============");
|
|
if ((ret = session->close(session, NULL)) != 0)
|
|
die(ret, "session.close");
|
|
}
|
|
|
|
/*
|
|
* wts_stats --
|
|
* Dump the run's statistics.
|
|
*/
|
|
void
|
|
wts_stats(void)
|
|
{
|
|
WT_CONNECTION *conn;
|
|
WT_CURSOR *cursor;
|
|
WT_SESSION *session;
|
|
FILE *fp;
|
|
char *stat_name;
|
|
const char *pval, *desc;
|
|
uint64_t v;
|
|
int ret;
|
|
|
|
/* Ignore statistics if they're not configured. */
|
|
if (g.c_statistics == 0)
|
|
return;
|
|
|
|
/* Some data-sources don't support statistics. */
|
|
if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
|
|
return;
|
|
|
|
conn = g.wts_conn;
|
|
track("stat", 0ULL, NULL);
|
|
|
|
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
|
|
die(ret, "connection.open_session");
|
|
|
|
if ((fp = fopen(g.home_stats, "w")) == NULL)
|
|
die(errno, "fopen: %s", g.home_stats);
|
|
|
|
/* Connection statistics. */
|
|
fprintf(fp, "====== Connection statistics:\n");
|
|
if ((ret = session->open_cursor(session,
|
|
"statistics:", NULL, NULL, &cursor)) != 0)
|
|
die(ret, "session.open_cursor");
|
|
|
|
while ((ret = cursor->next(cursor)) == 0 &&
|
|
(ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0)
|
|
if (fprintf(fp, "%s=%s\n", desc, pval) < 0)
|
|
die(errno, "fprintf");
|
|
|
|
if (ret != WT_NOTFOUND)
|
|
die(ret, "cursor.next");
|
|
if ((ret = cursor->close(cursor)) != 0)
|
|
die(ret, "cursor.close");
|
|
|
|
/* Data source statistics. */
|
|
fprintf(fp, "\n\n====== Data source statistics:\n");
|
|
if ((stat_name =
|
|
malloc(strlen("statistics:") + strlen(g.uri) + 1)) == NULL)
|
|
syserr("malloc");
|
|
sprintf(stat_name, "statistics:%s", g.uri);
|
|
if ((ret = session->open_cursor(
|
|
session, stat_name, NULL, NULL, &cursor)) != 0)
|
|
die(ret, "session.open_cursor");
|
|
free(stat_name);
|
|
|
|
while ((ret = cursor->next(cursor)) == 0 &&
|
|
(ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0)
|
|
if (fprintf(fp, "%s=%s\n", desc, pval) < 0)
|
|
die(errno, "fprintf");
|
|
|
|
if (ret != WT_NOTFOUND)
|
|
die(ret, "cursor.next");
|
|
if ((ret = cursor->close(cursor)) != 0)
|
|
die(ret, "cursor.close");
|
|
|
|
if ((ret = fclose(fp)) != 0)
|
|
die(ret, "fclose");
|
|
|
|
if ((ret = session->close(session, NULL)) != 0)
|
|
die(ret, "session.close");
|
|
}
|