Exemple #1
0
/*
 * 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);
}
Exemple #2
0
/*
 * compaction --
 *	Periodically do a compaction operation.
 */
WT_THREAD_RET
compact(void *arg)
{
	WT_CONNECTION *conn;
	WT_DECL_RET;
	WT_SESSION *session;
	u_int period;

	(void)(arg);

	/* Compaction isn't supported for all data sources. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		return (WT_THREAD_RET_VALUE);

	/* Open a session. */
	conn = g.wts_conn;
	testutil_check(conn->open_session(conn, NULL, NULL, &session));

	/*
	 * Perform compaction at somewhere under 15 seconds (so we get at
	 * least one done), and then at 23 second intervals.
	 */
	for (period = mmrand(NULL, 1, 15);; period = 23) {
		/* Sleep for short periods so we don't make the run wait. */
		while (period > 0 && !g.workers_finished) {
			--period;
			__wt_sleep(1, 0);
		}
		if (g.workers_finished)
			break;

		/*
		 * Compact can return EBUSY if concurrent with alter or if there
		 * is eviction pressure, or we collide with checkpoints.
		 *
		 * Compact returns ETIMEDOUT if the compaction doesn't finish in
		 * in some number of seconds. We don't configure a timeout and
		 * occasionally exceed the default of 1200 seconds.
		 */
		ret = session->compact(session, g.uri, NULL);
		if (ret != 0 &&
		    ret != EBUSY && ret != ETIMEDOUT && ret != WT_ROLLBACK)
			testutil_die(ret, "session.compact");
	}

	testutil_check(session->close(session, NULL));

	return (WT_THREAD_RET_VALUE);
}
Exemple #3
0
/*
 * wts_salvage --
 *	Salvage testing.
 */
void
wts_salvage(void)
{
	int ret;

	/* Some data-sources don't support salvage. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		return;

	if (g.c_salvage == 0)
		return;

	/*
	 * 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");

	/* Salvage, then verify. */
	wts_open(g.home, 1, &g.wts_conn);
	salvage();
	wts_verify("post-salvage verify");
	wts_close();

	/*
	 * If no records were deleted, dump and compare against Berkeley DB.
	 * (The problem with deleting records is salvage restores deleted
	 * records if a page splits leaving a deleted record on one side of
	 * the split, so we cannot depend on correctness in that case.)
	 */
	if (g.c_delete_pct == 0)
		wts_dump("salvage", SINGLETHREADED);

	/* Corrupt the file randomly, salvage, then verify. */
	if (corrupt()) {
		wts_open(g.home, 1, &g.wts_conn);
		salvage();
		wts_verify("post-corrupt-salvage verify");
		wts_close();
	}
}
Exemple #4
0
/*
 * compaction --
 *	Periodically do a compaction operation.
 */
void *
compact(void *arg)
{
	WT_CONNECTION *conn;
	WT_SESSION *session;
	u_int period;
	int ret;

	(void)(arg);

	/* Compaction isn't supported for all data sources. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		return (NULL);

	/* Open a session. */
	conn = g.wts_conn;
	testutil_check(conn->open_session(conn, NULL, NULL, &session));

	/*
	 * Perform compaction at somewhere under 15 seconds (so we get at
	 * least one done), and then at 23 second intervals.
	 */
	for (period = mmrand(NULL, 1, 15);; period = 23) {
		/* Sleep for short periods so we don't make the run wait. */
		while (period > 0 && !g.workers_finished) {
			--period;
			sleep(1);
		}
		if (g.workers_finished)
			break;

		if ((ret = session->compact(
		    session, g.uri, NULL)) != 0 && ret != WT_ROLLBACK)
			testutil_die(ret, "session.compact");
	}

	testutil_check(session->close(session, NULL));

	return (NULL);
}
Exemple #5
0
static void *
ops(void *arg)
{
	TINFO *tinfo;
	WT_CONNECTION *conn;
	WT_CURSOR *cursor, *cursor_insert;
	WT_SESSION *session;
	WT_ITEM key, value;
	uint64_t keyno, ckpt_op, session_op;
	uint32_t op;
	uint8_t *keybuf, *valbuf;
	u_int np;
	int ckpt_available, dir, insert, intxn, notfound, readonly, ret;
	char *ckpt_config, ckpt_name[64];

	tinfo = arg;

	/* Initialize the per-thread random number generator. */
	__wt_random_init(&tinfo->rnd);

	conn = g.wts_conn;
	keybuf = valbuf = NULL;
	readonly = 0;			/* -Wconditional-uninitialized */

	/* Set up the default key and value buffers. */
	key_gen_setup(&keybuf);
	val_gen_setup(&tinfo->rnd, &valbuf);

	/* Set the first operation where we'll create sessions and cursors. */
	session_op = 0;
	session = NULL;
	cursor = cursor_insert = NULL;

	/* Set the first operation where we'll perform checkpoint operations. */
	ckpt_op = g.c_checkpoints ? mmrand(&tinfo->rnd, 100, 10000) : 0;
	ckpt_available = 0;

	for (intxn = 0; !tinfo->quit; ++tinfo->ops) {
		/*
		 * We can't checkpoint or swap sessions/cursors while in a
		 * transaction, resolve any running transaction.
		 */
		if (intxn &&
		    (tinfo->ops == ckpt_op || tinfo->ops == session_op)) {
			if ((ret = session->commit_transaction(
			    session, NULL)) != 0)
				die(ret, "session.commit_transaction");
			++tinfo->commit;
			intxn = 0;
		}

		/* Open up a new session and cursors. */
		if (tinfo->ops == session_op ||
		    session == NULL || cursor == NULL) {
			if (session != NULL &&
			    (ret = session->close(session, NULL)) != 0)
				die(ret, "session.close");

			if ((ret = conn->open_session(conn, NULL,
			    ops_session_config(&tinfo->rnd), &session)) != 0)
				die(ret, "connection.open_session");

			/*
			 * 10% of the time, perform some read-only operations
			 * from a checkpoint.
			 *
			 * Skip that if we single-threaded and doing checks
			 * against a Berkeley DB database, because that won't
			 * work because the Berkeley DB database records won't
			 * match the checkpoint.  Also skip if we are using
			 * LSM, because it doesn't support reads from
			 * checkpoints.
			 */
			if (!SINGLETHREADED && !DATASOURCE("lsm") &&
			    ckpt_available && mmrand(&tinfo->rnd, 1, 10) == 1) {
				if ((ret = session->open_cursor(session,
				    g.uri, NULL, ckpt_name, &cursor)) != 0)
					die(ret, "session.open_cursor");

				/* Pick the next session/cursor close/open. */
				session_op += 250;

				/* Checkpoints are read-only. */
				readonly = 1;
			} else {
				/*
				 * Open two cursors: one for overwriting and one
				 * for append (if it's a column-store).
				 *
				 * The reason is when testing with existing
				 * records, we don't track if a record was
				 * deleted or not, which means we must use
				 * cursor->insert with overwriting configured.
				 * But, in column-store files where we're
				 * testing with new, appended records, we don't
				 * want to have to specify the record number,
				 * which requires an append configuration.
				 */
				if ((ret = session->open_cursor(session, g.uri,
				    NULL, "overwrite", &cursor)) != 0)
					die(ret, "session.open_cursor");
				if ((g.type == FIX || g.type == VAR) &&
				    (ret = session->open_cursor(session, g.uri,
				    NULL, "append", &cursor_insert)) != 0)
					die(ret, "session.open_cursor");

				/* Pick the next session/cursor close/open. */
				session_op += mmrand(&tinfo->rnd, 100, 5000);

				/* Updates supported. */
				readonly = 0;
			}
		}

		/* Checkpoint the database. */
		if (tinfo->ops == ckpt_op && g.c_checkpoints) {
			/*
			 * LSM and data-sources don't support named checkpoints,
			 * and we can't drop a named checkpoint while there's a
			 * cursor open on it, otherwise 20% of the time name the
			 * checkpoint.
			 */
			if (DATASOURCE("helium") || DATASOURCE("kvsbdb") ||
			    DATASOURCE("lsm") ||
			    readonly || mmrand(&tinfo->rnd, 1, 5) == 1)
				ckpt_config = NULL;
			else {
				(void)snprintf(ckpt_name, sizeof(ckpt_name),
				    "name=thread-%d", tinfo->id);
				ckpt_config = ckpt_name;
			}

			/* Named checkpoints lock out backups */
			if (ckpt_config != NULL &&
			    (ret = pthread_rwlock_wrlock(&g.backup_lock)) != 0)
				die(ret,
				    "pthread_rwlock_wrlock: 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: backup lock");

			/* Rephrase the checkpoint name for cursor open. */
			if (ckpt_config == NULL)
				strcpy(ckpt_name,
				    "checkpoint=WiredTigerCheckpoint");
			else
				(void)snprintf(ckpt_name, sizeof(ckpt_name),
				    "checkpoint=thread-%d", tinfo->id);
			ckpt_available = 1;

			/* Pick the next checkpoint operation. */
			ckpt_op += mmrand(&tinfo->rnd, 5000, 20000);
		}

		/*
		 * If we're not single-threaded and we're not in a transaction,
		 * start a transaction 20% of the time.
		 */
		if (!SINGLETHREADED &&
		    !intxn && mmrand(&tinfo->rnd, 1, 10) >= 8) {
			if ((ret =
			    session->begin_transaction(session, NULL)) != 0)
				die(ret, "session.begin_transaction");
			intxn = 1;
		}

		insert = notfound = 0;

		keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
		key.data = keybuf;
		value.data = valbuf;

		/*
		 * Perform some number of operations: the percentage of deletes,
		 * inserts and writes are specified, reads are the rest.  The
		 * percentages don't have to add up to 100, a high percentage
		 * of deletes will mean fewer inserts and writes.  Modifications
		 * are always followed by a read to confirm it worked.
		 */
		op = readonly ? UINT32_MAX : mmrand(&tinfo->rnd, 1, 100);
		if (op < g.c_delete_pct) {
			++tinfo->remove;
			switch (g.type) {
			case ROW:
				/*
				 * If deleting a non-existent record, the cursor
				 * won't be positioned, and so can't do a next.
				 */
				if (row_remove(cursor, &key, keyno, &notfound))
					goto deadlock;
				break;
			case FIX:
			case VAR:
				if (col_remove(cursor, &key, keyno, &notfound))
					goto deadlock;
				break;
			}
		} else if (op < g.c_delete_pct + g.c_insert_pct) {
			++tinfo->insert;
			switch (g.type) {
			case ROW:
				if (row_insert(
				    tinfo, cursor, &key, &value, keyno))
					goto deadlock;
				insert = 1;
				break;
			case FIX:
			case VAR:
				/*
				 * We can only append so many new records, if
				 * we've reached that limit, update a record
				 * instead of doing an insert.
				 */
				if (g.append_cnt >= g.append_max)
					goto skip_insert;

				/* Insert, then reset the insert cursor. */
				if (col_insert(tinfo,
				    cursor_insert, &key, &value, &keyno))
					goto deadlock;
				if ((ret =
				    cursor_insert->reset(cursor_insert)) != 0)
					die(ret, "cursor.reset");

				insert = 1;
				break;
			}
		} else if (
		    op < g.c_delete_pct + g.c_insert_pct + g.c_write_pct) {
			++tinfo->update;
			switch (g.type) {
			case ROW:
				if (row_update(
				    tinfo, cursor, &key, &value, keyno))
					goto deadlock;
				break;
			case FIX:
			case VAR:
skip_insert:			if (col_update(tinfo,
				    cursor, &key, &value, keyno))
					goto deadlock;
				break;
			}
		} else {
			++tinfo->search;
			if (read_row(cursor, &key, keyno))
				if (intxn)
					goto deadlock;
			continue;
		}

		/*
		 * The cursor is positioned if we did any operation other than
		 * insert, do a small number of next/prev cursor operations in
		 * a random direction.
		 */
		if (!insert) {
			dir = (int)mmrand(&tinfo->rnd, 0, 1);
			for (np = 0; np < mmrand(&tinfo->rnd, 1, 8); ++np) {
				if (notfound)
					break;
				if (nextprev(cursor, dir, &notfound))
					goto deadlock;
			}
		}

		/* Read to confirm the operation. */
		++tinfo->search;
		if (read_row(cursor, &key, keyno))
			goto deadlock;

		/* Reset the cursor: there is no reason to keep pages pinned. */
		if ((ret = cursor->reset(cursor)) != 0)
			die(ret, "cursor.reset");

		/*
		 * If we're in the transaction, commit 40% of the time and
		 * rollback 10% of the time.
		 */
		if (intxn)
			switch (mmrand(&tinfo->rnd, 1, 10)) {
			case 1: case 2: case 3: case 4:		/* 40% */
				if ((ret = session->commit_transaction(
				    session, NULL)) != 0)
					die(ret, "session.commit_transaction");
				++tinfo->commit;
				intxn = 0;
				break;
			case 5:					/* 10% */
				if (0) {
deadlock:				++tinfo->deadlock;
				}
				if ((ret = session->rollback_transaction(
				    session, NULL)) != 0)
					die(ret,
					    "session.rollback_transaction");
				++tinfo->rollback;
				intxn = 0;
				break;
			default:
				break;
			}
	}

	if (session != NULL && (ret = session->close(session, NULL)) != 0)
		die(ret, "session.close");

	free(keybuf);
	free(valbuf);

	tinfo->state = TINFO_COMPLETE;
	return (NULL);
}
Exemple #6
0
/*
 * backup --
 *	Periodically do a backup and verify it.
 */
void *
backup(void *arg)
{
	WT_CONNECTION *conn;
	WT_CURSOR *backup_cursor;
	WT_SESSION *session;
	u_int period;
	int ret;
	const char *key;

	(void)(arg);

	conn = g.wts_conn;

	/* Backups aren't supported for non-standard data sources. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		return (NULL);

	/* Open a session. */
	if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
		die(ret, "connection.open_session");

	/*
	 * Perform a backup at somewhere under 10 seconds (so we get at
	 * least one done), and then at 45 second intervals.
	 */
	for (period = mmrand(NULL, 1, 10);; period = 45) {
		/* Sleep for short periods so we don't make the run wait. */
		while (period > 0 && !g.workers_finished) {
			--period;
			sleep(1);
		}
		if (g.workers_finished)
			break;

		/* Lock out named checkpoints */
		if ((ret = pthread_rwlock_wrlock(&g.backup_lock)) != 0)
			die(ret, "pthread_rwlock_wrlock: backup lock");

		/* Re-create the backup directory. */
		if ((ret = system(g.home_backup_init)) != 0)
			die(ret, "backup directory creation failed");

		/*
		 * open_cursor can return EBUSY if a metadata operation is
		 * currently happening - retry in that case.
		 */
		while ((ret = session->open_cursor(session,
		    "backup:", NULL, NULL, &backup_cursor)) == EBUSY)
			sleep(1);
		if (ret != 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");
			copy_file(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: backup lock");

		check_copy();
	}

	if ((ret = session->close(session, NULL)) != 0)
		die(ret, "session.close");

	return (NULL);
}
Exemple #7
0
void
wts_load(void)
{
	WT_CONNECTION *conn;
	WT_CURSOR *cursor;
	WT_ITEM key, value;
	WT_SESSION *session;
	uint8_t *keybuf, *valbuf;
	int is_bulk, ret;

	conn = g.wts_conn;

	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,
		    "=============== bulk load start ===============");

	/*
	 * Avoid bulk load with KVS (there's no bulk load support for a
	 * data-source); avoid bulk load with a custom collator, because
	 * the order of insertion will not match the collation order.
	 */
	is_bulk = !g.c_reverse &&
	    !DATASOURCE("kvsbdb") && !DATASOURCE("helium");
	if ((ret = session->open_cursor(session, g.uri, NULL,
	    is_bulk ? "bulk" : NULL, &cursor)) != 0)
		die(ret, "session.open_cursor");

	/* Set up the default key buffer. */
	key_gen_setup(&keybuf);
	val_gen_setup(&valbuf);

	for (;;) {
		if (++g.key_cnt > g.c_rows) {
			g.key_cnt = g.rows = g.c_rows;
			break;
		}

		/* Report on progress every 100 inserts. */
		if (g.key_cnt % 100 == 0)
			track("bulk load", g.key_cnt, NULL);

		key_gen(keybuf, &key.size, (uint64_t)g.key_cnt, 0);
		key.data = keybuf;
		value_gen(valbuf, &value.size, (uint64_t)g.key_cnt);
		value.data = valbuf;

		switch (g.type) {
		case FIX:
			if (!is_bulk)
				cursor->set_key(cursor, g.key_cnt);
			cursor->set_value(cursor, *(uint8_t *)value.data);
			if (g.logging == LOG_OPS)
				(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]);
			break;
		case VAR:
			if (!is_bulk)
				cursor->set_key(cursor, g.key_cnt);
			cursor->set_value(cursor, &value);
			if (g.logging == LOG_OPS)
				(void)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu32 " {%.*s}", "bulk V",
				    g.key_cnt,
				    (int)value.size, (char *)value.data);
			break;
		case ROW:
			cursor->set_key(cursor, &key);
			if (g.logging == LOG_OPS)
				(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)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu32 " {%.*s}", "bulk V",
				    g.key_cnt,
				    (int)value.size, (char *)value.data);
			break;
		}

		if ((ret = cursor->insert(cursor)) != 0)
			die(ret, "cursor.insert");

		if (!SINGLETHREADED)
			continue;

		/* Insert the item into BDB. */
		bdb_insert(key.data, key.size, value.data, value.size);
	}

	if ((ret = cursor->close(cursor)) != 0)
		die(ret, "cursor.close");

	if (g.logging != 0)
		(void)g.wt_api->msg_printf(g.wt_api, session,
		    "=============== bulk load stop ===============");

	if ((ret = session->close(session, NULL)) != 0)
		die(ret, "session.close");

	free(keybuf);
	free(valbuf);
}
Exemple #8
0
void
wts_load(void)
{
	WT_CONNECTION *conn;
	WT_CURSOR *cursor;
	WT_DECL_RET;
	WT_ITEM key, value;
	WT_SESSION *session;
	bool is_bulk;

	conn = g.wts_conn;

	testutil_check(conn->open_session(conn, NULL, NULL, &session));

	if (g.logging != 0)
		(void)g.wt_api->msg_printf(g.wt_api, session,
		    "=============== bulk load start ===============");

	/*
	 * No bulk load with data-sources.
	 *
	 * No bulk load with custom collators, the order of insertion will not
	 * match the collation order.
	 */
	is_bulk = true;
	if (DATASOURCE("kvsbdb"))
		is_bulk = false;
	if (g.c_reverse)
		is_bulk = false;

	/*
	 * open_cursor can return EBUSY if concurrent with a metadata
	 * operation, retry in that case.
	 */
	while ((ret = session->open_cursor(session, g.uri, NULL,
	    is_bulk ? "bulk,append" : NULL, &cursor)) == EBUSY)
		__wt_yield();
	testutil_check(ret);

	/* Set up the key/value buffers. */
	key_gen_init(&key);
	val_gen_init(&value);

	for (;;) {
		if (++g.key_cnt > g.c_rows) {
			g.key_cnt = g.rows = g.c_rows;
			break;
		}

		/* Report on progress every 100 inserts. */
		if (g.key_cnt % 1000 == 0)
			track("bulk load", g.key_cnt, NULL);

		key_gen(&key, g.key_cnt);
		val_gen(NULL, &value, g.key_cnt);

		switch (g.type) {
		case FIX:
			if (!is_bulk)
				cursor->set_key(cursor, g.key_cnt);
			cursor->set_value(cursor, *(uint8_t *)value.data);
			if (g.logging == LOG_OPS)
				(void)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu64 " {0x%02" PRIx8 "}",
				    "bulk V",
				    g.key_cnt, ((uint8_t *)value.data)[0]);
			break;
		case VAR:
			if (!is_bulk)
				cursor->set_key(cursor, g.key_cnt);
			cursor->set_value(cursor, &value);
			if (g.logging == LOG_OPS)
				(void)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu64 " {%.*s}", "bulk V",
				    g.key_cnt,
				    (int)value.size, (char *)value.data);
			break;
		case ROW:
			cursor->set_key(cursor, &key);
			if (g.logging == LOG_OPS)
				(void)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu64 " {%.*s}", "bulk K",
				    g.key_cnt, (int)key.size, (char *)key.data);
			cursor->set_value(cursor, &value);
			if (g.logging == LOG_OPS)
				(void)g.wt_api->msg_printf(g.wt_api, session,
				    "%-10s %" PRIu64 " {%.*s}", "bulk V",
				    g.key_cnt,
				    (int)value.size, (char *)value.data);
			break;
		}

		/*
		 * We don't want to size the cache to ensure the initial data
		 * set can load in the in-memory case, guaranteeing the load
		 * succeeds probably means future updates are also guaranteed
		 * to succeed, which isn't what we want. If we run out of space
		 * in the initial load, reset the row counter and continue.
		 *
		 * Decrease inserts, they can't be successful if we're at the
		 * cache limit, and increase the delete percentage to get some
		 * extra space once the run starts.
		 */
		if ((ret = cursor->insert(cursor)) != 0) {
			if (ret != WT_CACHE_FULL)
				testutil_die(ret, "cursor.insert");
			g.rows = --g.key_cnt;
			g.c_rows = (uint32_t)g.key_cnt;

			if (g.c_insert_pct > 5)
				g.c_insert_pct = 5;
			if (g.c_delete_pct < 20)
				g.c_delete_pct += 20;
			break;
		}

#ifdef HAVE_BERKELEY_DB
		if (SINGLETHREADED)
			bdb_insert(key.data, key.size, value.data, value.size);
#endif
	}

	testutil_check(cursor->close(cursor));

	if (g.logging != 0)
		(void)g.wt_api->msg_printf(g.wt_api, session,
		    "=============== bulk load stop ===============");

	testutil_check(session->close(session, NULL));

	key_gen_teardown(&key);
	val_gen_teardown(&value);
}
Exemple #9
0
/*
 * 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);; period = 45) {
		/* Sleep for short periods so we don't make the run wait. */
		while (period > 0 && !g.threads_finished) {
			--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);

		/*
		 * open_cursor can return EBUSY if a metadata operation is
		 * currently happening - retry in that case.
		 */
		while ((ret = session->open_cursor(session,
		    "backup:", NULL, NULL, &backup_cursor)) == EBUSY)
			sleep(1);
		if (ret != 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);
}
Exemple #10
0
static void *
ops(void *arg)
{
	TINFO *tinfo;
	WT_CONNECTION *conn;
	WT_CURSOR *cursor, *cursor_insert;
	WT_SESSION *session;
	WT_ITEM key, value;
	uint64_t cnt, keyno, ckpt_op, session_op, thread_ops;
	uint32_t op;
	uint8_t *keybuf, *valbuf;
	u_int np;
	int dir, insert, intxn, notfound, ret;
	char *ckpt_config, config[64];

	tinfo = arg;

	conn = g.wts_conn;
	keybuf = valbuf = NULL;

	/* Set up the default key and value buffers. */
	key_gen_setup(&keybuf);
	val_gen_setup(&valbuf);

	/*
	 * Each thread does its share of the total operations, and make sure
	 * that it's not 0 (testing runs: threads might be larger than ops).
	 */
	thread_ops = 100 + g.c_ops / g.c_threads;

	/*
	 * Select the first operation where we'll create sessions and cursors,
	 * perform checkpoint operations.
	 */
	ckpt_op = MMRAND(1, thread_ops);
	session_op = 0;

	session = NULL;
	cursor = cursor_insert = NULL;
	for (intxn = 0, cnt = 0; cnt < thread_ops; ++cnt) {
		if (SINGLETHREADED && cnt % 100 == 0)
			track("ops", 0ULL, tinfo);

		/*
		 * We can't checkpoint or swap sessions/cursors while in a
		 * transaction, resolve any running transaction.  Otherwise,
		 * reset the cursor: we may block waiting for a lock and there
		 * is no reason to keep pages pinned.
		 */
		if (cnt == ckpt_op || cnt == session_op) {
			if (intxn) {
				if ((ret = session->commit_transaction(
				    session, NULL)) != 0)
					die(ret, "session.commit_transaction");
				++tinfo->commit;
				intxn = 0;
			}
			else if (cursor != NULL &&
			    (ret = cursor->reset(cursor)) != 0)
				die(ret, "cursor.reset");
		}

		/* Open up a new session and cursors. */
		if (cnt == session_op || session == NULL || cursor == NULL) {
			if (session != NULL &&
			    (ret = session->close(session, NULL)) != 0)
				die(ret, "session.close");

			if ((ret = conn->open_session(
			    conn, NULL, NULL, &session)) != 0)
				die(ret, "connection.open_session");

			/*
			 * Open two cursors: one configured for overwriting and
			 * one configured for append if we're dealing with a
			 * column-store.
			 *
			 * The reason is when testing with existing records, we
			 * don't track if a record was deleted or not, which
			 * means we must use cursor->insert with overwriting
			 * configured.  But, in column-store files where we're
			 * testing with new, appended records, we don't want to
			 * have to specify the record number, which requires an
			 * append configuration.
			 */
			if ((ret = session->open_cursor(session,
			    g.uri, NULL, "overwrite", &cursor)) != 0)
				die(ret, "session.open_cursor");
			if ((g.type == FIX || g.type == VAR) &&
			    (ret = session->open_cursor(session,
			    g.uri, NULL, "append", &cursor_insert)) != 0)
				die(ret, "session.open_cursor");

			/* Pick the next session/cursor close/open. */
			session_op += SINGLETHREADED ?
			    MMRAND(1, thread_ops) : 100 * MMRAND(1, 50);
		}

		/* Checkpoint the database. */
		if (cnt == ckpt_op) {
			/*
			 * LSM and data-sources don't support named checkpoints,
			 * else 25% of the time we name the checkpoint.
			 */
			if (DATASOURCE("lsm") || DATASOURCE("kvsbdb") ||
			    DATASOURCE("memrata") || MMRAND(1, 4) == 1)
				ckpt_config = NULL;
			else {
				(void)snprintf(config, sizeof(config),
				    "name=thread-%d", tinfo->id);
				ckpt_config = config;
			}

			/* 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;
		}

		/*
		 * If we're not single-threaded and we're not in a transaction,
		 * start a transaction 80% of the time.
		 */
		if (!SINGLETHREADED && !intxn && MMRAND(1, 10) >= 8) {
			if ((ret =
			    session->begin_transaction(session, NULL)) != 0)
				die(ret, "session.begin_transaction");
			intxn = 1;
		}

		insert = notfound = 0;

		keyno = MMRAND(1, g.rows);
		key.data = keybuf;
		value.data = valbuf;

		/*
		 * Perform some number of operations: the percentage of deletes,
		 * inserts and writes are specified, reads are the rest.  The
		 * percentages don't have to add up to 100, a high percentage
		 * of deletes will mean fewer inserts and writes.  Modifications
		 * are always followed by a read to confirm it worked.
		 */
		op = (uint32_t)(rng() % 100);
		if (op < g.c_delete_pct) {
			++tinfo->remove;
			switch (g.type) {
			case ROW:
				/*
				 * If deleting a non-existent record, the cursor
				 * won't be positioned, and so can't do a next.
				 */
				if (row_remove(cursor, &key, keyno, &notfound))
					goto deadlock;
				break;
			case FIX:
			case VAR:
				if (col_remove(cursor, &key, keyno, &notfound))
					goto deadlock;
				break;
			}
		} else if (op < g.c_delete_pct + g.c_insert_pct) {
			++tinfo->insert;
			switch (g.type) {
			case ROW:
				if (row_insert(cursor, &key, &value, keyno))
					goto deadlock;
				insert = 1;
				break;
			case FIX:
			case VAR:
				/*
				 * We can only append so many new records, if
				 * we've reached that limit, update a record
				 * instead of doing an insert.
				 */
				if (g.append_cnt >= g.append_max)
					goto skip_insert;

				/*
				 * Reset the standard cursor so it doesn't keep
				 * pages pinned.
				 */
				if ((ret = cursor->reset(cursor)) != 0)
					die(ret, "cursor.reset");

				/* Insert, then reset the insert cursor. */
				if (col_insert(
				    cursor_insert, &key, &value, &keyno))
					goto deadlock;
				if ((ret =
				    cursor_insert->reset(cursor_insert)) != 0)
					die(ret, "cursor.reset");

				insert = 1;
				break;
			}
		} else if (
		    op < g.c_delete_pct + g.c_insert_pct + g.c_write_pct) {
			++tinfo->update;
			switch (g.type) {
			case ROW:
				if (row_update(cursor, &key, &value, keyno))
					goto deadlock;
				break;
			case FIX:
			case VAR:
skip_insert:			if (col_update(cursor, &key, &value, keyno))
					goto deadlock;
				break;
			}
		} else {
			++tinfo->search;
			if (read_row(cursor, &key, keyno))
				goto deadlock;
			continue;
		}

		/*
		 * The cursor is positioned if we did any operation other than
		 * insert, do a small number of next/prev cursor operations in
		 * a random direction.
		 */
		if (!insert) {
			dir = (int)MMRAND(0, 1);
			for (np = 0; np < MMRAND(1, 8); ++np) {
				if (notfound)
					break;
				if (nextprev(cursor, dir, &notfound))
					goto deadlock;
			}
		}

		/* Read the value we modified to confirm the operation. */
		++tinfo->search;
		if (read_row(cursor, &key, keyno))
			goto deadlock;

		/*
		 * If we're in the transaction, commit 40% of the time and
		 * rollback 10% of the time.
		 */
		if (intxn)
			switch (MMRAND(1, 10)) {
			case 1: case 2: case 3: case 4:		/* 40% */
				if ((ret = session->commit_transaction(
				    session, NULL)) != 0)
					die(ret, "session.commit_transaction");
				++tinfo->commit;
				intxn = 0;
				break;
			case 5:					/* 10% */
				if (0) {
deadlock:				++tinfo->deadlock;
				}
				if ((ret = session->rollback_transaction(
				    session, NULL)) != 0)
					die(ret, "session.commit_transaction");
				++tinfo->rollback;
				intxn = 0;
				break;
			default:
				break;
			}
	}

	if (session != NULL && (ret = session->close(session, NULL)) != 0)
		die(ret, "session.close");

	free(keybuf);
	free(valbuf);

	tinfo->state = TINFO_COMPLETE;
	return (NULL);
}
Exemple #11
0
/*
 * backup --
 *	Periodically do a backup and verify it.
 */
void *
backup(void *arg)
{
	WT_CONNECTION *conn;
	WT_CURSOR *backup_cursor;
	WT_DECL_RET;
	WT_SESSION *session;
	u_int incremental, period;
	bool full;
	const char *config, *key;

	(void)(arg);

	conn = g.wts_conn;

	/* Backups aren't supported for non-standard data sources. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		return (NULL);

	/* Open a session. */
	testutil_check(conn->open_session(conn, NULL, NULL, &session));

	/*
	 * Perform a full backup at somewhere under 10 seconds (that way there's
	 * at least one), then at larger intervals, optionally do incremental
	 * backups between full backups.
	 */
	incremental = 0;
	for (period = mmrand(NULL, 1, 10);; period = mmrand(NULL, 20, 45)) {
		/* Sleep for short periods so we don't make the run wait. */
		while (period > 0 && !g.workers_finished) {
			--period;
			sleep(1);
		}

		/*
		 * We can't drop named checkpoints while there's a backup in
		 * progress, serialize backups with named checkpoints. Wait
		 * for the checkpoint to complete, otherwise backups might be
		 * starved out.
		 */
		testutil_check(pthread_rwlock_wrlock(&g.backup_lock));
		if (g.workers_finished) {
			testutil_check(pthread_rwlock_unlock(&g.backup_lock));
			break;
		}

		if (incremental) {
			config = "target=(\"log:\")";
			full = false;
		} else {
			/* Re-create the backup directory. */
			testutil_checkfmt(
			    system(g.home_backup_init),
			    "%s", "backup directory creation failed");

			config = NULL;
			full = true;
		}

		/*
		 * open_cursor can return EBUSY if concurrent with a metadata
		 * operation, retry in that case.
		 */
		while ((ret = session->open_cursor(
		    session, "backup:", NULL, config, &backup_cursor)) == EBUSY)
			__wt_yield();
		if (ret != 0)
			testutil_die(ret, "session.open_cursor: backup");

		while ((ret = backup_cursor->next(backup_cursor)) == 0) {
			testutil_check(
			    backup_cursor->get_key(backup_cursor, &key));
			copy_file(session, key);
		}
		if (ret != WT_NOTFOUND)
			testutil_die(ret, "backup-cursor");

		/* After an incremental backup, truncate the log files. */
		if (incremental)
			testutil_check(session->truncate(
			    session, "log:", backup_cursor, NULL, NULL));

		testutil_check(backup_cursor->close(backup_cursor));
		testutil_check(pthread_rwlock_unlock(&g.backup_lock));

		/*
		 * If automatic log archival isn't configured, optionally do
		 * incremental backups after each full backup. If we're not
		 * doing any more incrementals, verify the backup (we can't
		 * verify intermediate states, once we perform recovery on the
		 * backup database, we can't do any more incremental backups).
		 */
		if (full)
			incremental =
			    g.c_logging_archive ? 1 : mmrand(NULL, 1, 5);
		if (--incremental == 0)
			check_copy();
	}

	if (incremental != 0)
		check_copy();

	testutil_check(session->close(session, NULL));

	return (NULL);
}
Exemple #12
0
/*
 * config_setup --
 *	Initialize configuration for a run.
 */
void
config_setup(void)
{
	CONFIG *cp;

	/* Clear any temporary values. */
	config_clear();

	/*
	 * Choose a data source type and a file type: they're interrelated (LSM
	 * trees are only compatible with row-store) and other items depend on
	 * them.
	 */
	if (!config_find_is_perm("data_source", strlen("data_source")))
		switch (MMRAND(1, 3)) {
		case 1:
			config_single("data_source=file", 0);
			break;
		case 2:
			config_single("data_source=lsm", 0);
			break;
		case 3:
			config_single("data_source=table", 0);
			break;
		}

	if (!config_find_is_perm("file_type", strlen("file_type")))
		switch (DATASOURCE("lsm") ? 3 : MMRAND(1, 3)) {
		case 1:
			config_single("file_type=fix", 0);
			break;
		case 2:
			config_single("file_type=var", 0);
			break;
		case 3:
			config_single("file_type=row", 0);
			break;
		}
	config_map_file_type(g.c_file_type, &g.type);

	/*
	 * If data_source and file_type were both "permanent", we may still
	 * have a mismatch.
	 */
	if (DATASOURCE("lsm") && g.type != ROW) {
		fprintf(stderr,
	    "%s: lsm data_source is only compatible with row file_type\n",
		    g.progname);
		exit(EXIT_FAILURE);
	}

	/*
	 * Build the top-level object name: we're overloading data_source in
	 * our configuration, LSM or KVS devices are "tables", but files are
	 * tested as well.
	 */
	if ((g.uri = malloc(256)) == NULL)
		syserr("malloc");
	strcpy(g.uri, DATASOURCE("file") ? "file:" : "table:");
	if (DATASOURCE("helium"))
		strcat(g.uri, "dev1/");
	strcat(g.uri, WT_NAME);

	/* Default single-threaded 10% of the time. */
	cp = config_find("threads", strlen("threads"));
	if (!(cp->flags & C_PERM))
		*cp->v = MMRAND(1, 100) < 10 ? 1: CONF_RAND(cp);

	/* Fill in random values for the rest of the run. */
	for (cp = c; cp->name != NULL; ++cp) {
		if (cp->flags & (C_IGNORE | C_PERM | C_TEMP))
			continue;

		/*
		 * Boolean flags are 0 or 1, but only set N in 100 where the
		 * variable's min value is N.  Set the flag if we rolled >=
		 * the min, 0 otherwise.
		 */
		if (cp->flags & C_BOOL)
			*cp->v = MMRAND(1, 100) <= cp->min ? 1 : 0;
		else
			*cp->v = CONF_RAND(cp);
	}

	/* Required shared libraries. */
	if (DATASOURCE("helium") && access(HELIUM_PATH, R_OK) != 0)
		die(errno, "Levyx/helium shared library: %s", HELIUM_PATH);
	if (DATASOURCE("kvsbdb") && access(KVS_BDB_PATH, R_OK) != 0)
		die(errno, "kvsbdb shared library: %s", KVS_BDB_PATH);

	/* Some data-sources don't support user-specified collations. */
	if (DATASOURCE("helium") || DATASOURCE("kvsbdb"))
		g.c_reverse = 0;

	config_checksum();
	config_compression();

	/* Clear operations values if the whole run is read-only. */
	if (g.c_ops == 0)
		for (cp = c; cp->name != NULL; ++cp)
			if (cp->flags & C_OPS)
				*cp->v = 0;

	/*
	 * Periodically, set the delete percentage to 0 so salvage gets run,
	 * as long as the delete percentage isn't nailed down.
	 */
	if (!g.replay && g.run_cnt % 10 == 0) {
		cp = config_find("delete_pct", strlen("delete_pct"));
		if (cp->name != NULL &&
		    !(cp->flags & (C_IGNORE | C_PERM | C_TEMP)))
			g.c_delete_pct = 0;
	}

	/*
	 * If this is an LSM run, set the cache size and crank up the insert
	 * percentage.
	 */
	if (DATASOURCE("lsm")) {
		cp = config_find("cache", strlen("cache"));
		if (!(cp->flags & C_PERM))
			g.c_cache = 30 * g.c_chunk_size;

		cp = config_find("insert_pct", strlen("insert_pct"));
		if (cp->name != NULL &&
		    !(cp->flags & (C_IGNORE | C_PERM | C_TEMP)))
			g.c_insert_pct = MMRAND(50, 85);
	}

	/*
	 * Key/value minimum/maximum are related, correct unless specified by
	 * the configuration.
	 */
	cp = config_find("key_min", strlen("key_min"));
	if (!(cp->flags & C_PERM) && g.c_key_min > g.c_key_max)
		g.c_key_min = g.c_key_max;
	cp = config_find("key_max", strlen("key_max"));
	if (!(cp->flags & C_PERM) && g.c_key_max < g.c_key_min)
		g.c_key_max = g.c_key_min;
	if (g.c_key_min > g.c_key_max)
		die(EINVAL, "key_min may not be larger than key_max");

	cp = config_find("value_min", strlen("value_min"));
	if (!(cp->flags & C_PERM) && g.c_value_min > g.c_value_max)
		g.c_value_min = g.c_value_max;
	cp = config_find("value_max", strlen("value_max"));
	if (!(cp->flags & C_PERM) && g.c_value_max < g.c_value_min)
		g.c_value_max = g.c_value_min;
	if (g.c_value_min > g.c_value_max)
		die(EINVAL, "value_min may not be larger than value_max");

	/* Reset the key count. */
	g.key_cnt = 0;
}