Import wiredtiger: f066b860b41d0a2c955823653d326536a007a175 from branch mongodb-master (#45229)

Co-authored-by: wt-vendoring-bot <wt-vendoring-bot@mongodb.com>
GitOrigin-RevId: 6e5e65d455b71e97fa56c90e22988d5153e1611f
This commit is contained in:
wt-vendoring-bot[bot] 2025-12-15 08:48:03 +11:00 committed by MongoDB Bot
parent 50f35f8ed3
commit cef978f9a8
68 changed files with 2086 additions and 1835 deletions

View File

@ -8,6 +8,7 @@ get_clang_base_flags(clang_flags C)
list(APPEND clang_flags "-Weverything")
list(APPEND clang_flags "-Wno-declaration-after-statement")
list(APPEND clang_flags "-Wjump-misses-init")
list(APPEND clang_flags "-Wmissing-prototypes")
list(APPEND clang_flags "-Wconditional-uninitialized")
list(APPEND clang_flags "-Wno-pre-c11-compat")
list(APPEND clang_flags "-Wno-c++-compat")

View File

@ -644,10 +644,6 @@ connection_runtime_config = [
if true, background compact aggressively removes compact statistics for a file and
decreases the max amount of time a file can be skipped for.''',
type='boolean'),
Config('crash_point_colgroup', 'false', r'''
if true, force crash in table creation while creating colgroup metadata entry. This is
intended for testing purposes only.''',
type='boolean'),
Config('corruption_abort', 'true', r'''
if true and built in diagnostic mode, dump core in the case of data corruption''',
type='boolean'),

View File

@ -446,6 +446,7 @@ conn_stats = [
EvictStat('eviction_walks_stopped', 'eviction walks gave up because they restarted their walk twice'),
EvictStat('eviction_worker_evict_attempt', 'evict page attempts by eviction worker threads'),
EvictStat('eviction_worker_evict_fail', 'evict page failures by eviction worker threads'),
EvictStat('eviction_worker_lock_wait_time', 'time eviction worker threads spend waiting for locks (usecs)'),
# Note eviction_worker_evict_attempt - eviction_worker_evict_fail = evict page successes by eviction worker threads.
##########################################

View File

@ -109,14 +109,11 @@ stat_config = range_config + [
component_config = throttle_config
transaction_config = [
thread_worker_config = [
Config('ops_per_transaction', '', r'''
Defines how many operations a transaction can perform, the range is defined with a minimum
and a maximum and a random number is chosen between the two using a linear distribution.''',
type='category', subconfig=range_config),
]
thread_count = [
Config('thread_count', 0, r'''
Specifies the number of threads that will be used to perform a certain function.''', min=0)
]
@ -137,10 +134,10 @@ background_compact_thread_config = throttle_config + [
Config('free_space_target_mb', '20', r'''
Minimum amount of space in MB recoverable for compaction to proceed.''')
]
custom_operation_thread_config = thread_count + transaction_config + throttle_config + record_config
read_thread_config = thread_count + throttle_config + transaction_config + record_config
remove_thread_config = thread_count + transaction_config + throttle_config
update_insert_thread_config = thread_count + transaction_config + throttle_config + record_config
custom_operation_thread_config = thread_worker_config + throttle_config + record_config
read_thread_config = thread_worker_config + throttle_config + record_config
remove_thread_config = thread_worker_config + throttle_config
update_insert_thread_config = thread_worker_config + throttle_config + record_config
#
# Configuration that applies to the runtime monitor component, this should be a list of statistics

View File

@ -673,6 +673,29 @@ public:
enum Exec { THROW, NO_THROW };
template <Exec Policy = THROW, typename Func, typename... Args>
decltype(auto)
exec_with_retries(size_t retries, Func &&c_func, Args &&...args)
{
const std::chrono::milliseconds retry_delay(100);
int ret = 0;
do {
retries--;
ret = std::invoke(std::forward<Func>(c_func), std::forward<Args>(args)...);
LOG_SQL_TRACE(trace_sqlite3_call(ret, args...));
if (ret != SQLITE_BUSY && ret != SQLITE_LOCKED)
break; /* Not busy or locked; exit retry loop */
std::string error_msg = trace_sqlite3_call(ret, args...);
log(loc, config, WT_VERBOSE_DEBUG_1, "{}; retries left: {}", error_msg, retries);
std::this_thread::sleep_for(retry_delay);
} while ((ret == SQLITE_BUSY || ret == SQLITE_LOCKED) && retries > 0);
return check_result<Policy>(ret, args...);
}
template <Exec Policy = THROW, typename Func, typename... Args>
decltype(auto)
exec(Func &&c_func, Args &&...args)
@ -680,6 +703,13 @@ public:
auto ret = std::invoke(std::forward<Func>(c_func), std::forward<Args>(args)...);
LOG_SQL_TRACE(trace_sqlite3_call(ret, args...));
return check_result<Policy>(ret, args...);
}
template <Exec Policy = THROW, typename R, typename... Args>
int
check_result(R ret, Args &&...args)
{
if (ret != SQLITE_OK && ret != SQLITE_ROW && ret != SQLITE_DONE) {
std::string error_msg = trace_sqlite3_call(ret, args...);
log(loc, config, WT_VERBOSE_ERROR, "{}", error_msg);
@ -743,6 +773,10 @@ public:
}
};
#define SQL_CALL_CHECK_RETRIES(retries, db, func, ...) \
SQLiteCall(config, db, std::source_location::current(), #func) \
.exec_with_retries(retries, func, __VA_ARGS__)
#define SQL_CALL_CHECK(db, func, ...) \
SQLiteCall(config, db, std::source_location::current(), #func).exec(func, __VA_ARGS__)
@ -766,9 +800,9 @@ class Connection {
/* Common configuration parameters for connections */
constexpr static std::string_view config_statements[] = {
/* Set busy timeout to 10 seconds. */
"PRAGMA busy_timeout = 10000;"
"PRAGMA busy_timeout = 10000;",
/*
* The WAL journaling mode uses a write-ahead log instead of a rollback journal to implement
* transactions. This significantly improves performance.
@ -813,8 +847,17 @@ public:
void
configure(const Container &cfg_statements)
{
/*
* FIXME-WT-16159: Enable multi-process DB access in PALite
*
* Execute each configuration statement with retries on BUSY/LOCKED errors. Try each
* statement for up to 60 seconds. Delays between retries are 100ms.
*/
const size_t max_retries = 600;
for (const auto &stmt : cfg_statements) {
SQL_CALL_CHECK(db, sqlite3_exec, db, stmt.data(), nullptr, nullptr, nullptr);
SQL_CALL_CHECK_RETRIES(
max_retries, db, sqlite3_exec, db, stmt.data(), nullptr, nullptr, nullptr);
}
}

View File

@ -2,5 +2,5 @@
"vendor": "wiredtiger",
"github": "wiredtiger/wiredtiger",
"branch": "mongodb-master",
"commit": "39f3858d49092565e4a4996c22b6a2be1eeddc54"
"commit": "f066b860b41d0a2c955823653d326536a007a175"
}

View File

@ -779,7 +779,6 @@ __wti_page_merge_deltas_with_base_image_leaf(WT_SESSION_IMPL *session, WT_ITEM *
F_SET(dsk, WT_PAGE_EMPTY_V_ALL);
if (!disk_s.any_empty_value)
F_SET(dsk, WT_PAGE_EMPTY_V_NONE);
WT_STAT_CONN_DSRC_INCR(session, cache_read_leaf_delta);
/* Compute final on-disk image size using pointer difference. */
new_image->size = WT_PTRDIFF(disk_s.p_ptr, new_image->mem);

View File

@ -12,6 +12,7 @@
* Define functions that increment histogram statistics for reconstruction of pages with deltas.
*/
WT_STAT_USECS_HIST_INCR_FUNC(internal_reconstruct, perf_hist_internal_reconstruct_latency)
WT_STAT_USECS_HIST_INCR_FUNC(leaf_reconstruct, perf_hist_leaf_reconstruct_latency)
/*
* __evict_force_check --
@ -162,10 +163,14 @@ __page_read_build_full_disk_image(WT_SESSION_IMPL *session, WT_REF *ref, WT_ITEM
incr = 0;
/* Merge deltas directly with the base image to build refs in a single pass. */
if (base_dsk->type == WT_PAGE_ROW_LEAF)
if (base_dsk->type == WT_PAGE_ROW_LEAF) {
time_start = __wt_clock(session);
WT_ERR(__wti_page_merge_deltas_with_base_image_leaf(
session, deltas, delta_size, new_image, base_dsk));
else {
time_stop = __wt_clock(session);
__wt_stat_usecs_hist_incr_leaf_reconstruct(session, WT_CLOCKDIFF_US(time_stop, time_start));
WT_STAT_CONN_DSRC_INCR(session, cache_read_leaf_delta);
} else {
time_start = __wt_clock(session);
WT_ERR(__wti_page_merge_deltas_with_base_image_int(session, ref, deltas, delta_size, &refs,
&refs_entries, &incr, new_image, base_image_addr));
@ -595,6 +600,7 @@ read:
break;
}
WT_ASSERT(session, ref->page != NULL);
/*
* If a page has grown too large, we'll try and forcibly evict it before making it
* available to the caller. There are a variety of cases where that's not possible.
@ -661,6 +667,8 @@ read:
skip_evict:
page = ref->page;
WT_ASSERT(session, page != NULL);
/*
* Keep track of whether a session is reading leaf pages into the cache. This allows for
* the session to decide whether pre-fetch would be helpful. It might not work if a

File diff suppressed because it is too large Load Diff

View File

@ -2205,12 +2205,6 @@ __wti_debug_mode_config(WT_SESSION_IMPL *session, const char *cfg[])
else
FLD_CLR(conn->debug_flags, WT_CONN_DEBUG_CORRUPTION_ABORT);
WT_RET(__wt_config_gets(session, cfg, "debug_mode.crash_point_colgroup", &cval));
if (cval.val)
FLD_SET(conn->debug_flags, WT_CONN_DEBUG_CRASH_POINT_COLGROUP);
else
FLD_CLR(conn->debug_flags, WT_CONN_DEBUG_CRASH_POINT_COLGROUP);
WT_RET(__wt_config_gets(session, cfg, "debug_mode.cursor_copy", &cval));
if (cval.val)
FLD_SET(conn->debug_flags, WT_CONN_DEBUG_CURSOR_COPY);

View File

@ -30,6 +30,8 @@ struct __wt_evict {
uint64_t reentry_hs_eviction_ms; /* Total milliseconds spent inside a nested eviction */
struct timespec stuck_time; /* Stuck time */
wt_shared uint64_t evict_lock_wait_time; /* Time spent waiting for locks during eviction */
/*
* Read information.
*/

View File

@ -341,6 +341,7 @@ __wt_evict_create(WT_SESSION_IMPL *session, const char *cfg[])
evict->evict_current_queue = evict->evict_fill_queue = &evict->evict_queues[0];
evict->evict_other_queue = &evict->evict_queues[1];
evict->evict_urgent_queue = &evict->evict_queues[WTI_EVICT_URGENT_QUEUE];
evict->evict_lock_wait_time = 0;
/*
* We get/set some values in the evict statistics (rather than have two copies), configure them.
@ -443,6 +444,9 @@ __wt_evict_stats_update(WT_SESSION_IMPL *session)
WT_STATP_CONN_SET(session, stats, eviction_maximum_attempts_to_evict_page,
__wt_atomic_load_uint16_relaxed(&evict->evict_max_evict_page_attempts));
WT_STATP_CONN_SET(session, stats, eviction_worker_lock_wait_time,
__wt_atomic_load_uint64_relaxed(&evict->evict_lock_wait_time));
/*
* The number of files with active walks ~= number of hazard pointers in the walk session. Note:
* reading without locking.

View File

@ -2881,7 +2881,16 @@ __evict_get_ref(WT_SESSION_IMPL *session, bool is_server, WT_BTREE **btreep, WT_
__evict_queue_empty(evict->evict_fill_queue, false)))
return (WT_NOTFOUND);
uint64_t lock_wait_start, lock_wait_end;
/* Track time spent waiting for the evict queue lock */
lock_wait_start = __wt_clock(session);
__wt_spin_lock(session, &evict->evict_queue_lock);
lock_wait_end = __wt_clock(session);
/* Only track lock wait time for eviction worker threads */
if (F_ISSET(session, WT_SESSION_INTERNAL))
__wt_atomic_add_uint64_v(
&evict->evict_lock_wait_time, WT_CLOCKDIFF_US(lock_wait_end, lock_wait_start));
/* Check the urgent queue first. */
if (urgent_ok && !__evict_queue_empty(urgent_queue, false))
@ -2914,9 +2923,14 @@ __evict_get_ref(WT_SESSION_IMPL *session, bool is_server, WT_BTREE **btreep, WT_
WT_STAT_CONN_INCR(session, eviction_get_ref_empty2);
return (WT_NOTFOUND);
}
if (!is_server)
if (!is_server) {
lock_wait_start = __wt_clock(session);
__wt_spin_lock(session, &queue->evict_lock);
else if (__wt_spin_trylock(session, &queue->evict_lock) != 0)
lock_wait_end = __wt_clock(session);
if (F_ISSET(session, WT_SESSION_INTERNAL))
__wt_atomic_add_uint64_v(
&evict->evict_lock_wait_time, WT_CLOCKDIFF_US(lock_wait_end, lock_wait_start));
} else if (__wt_spin_trylock(session, &queue->evict_lock) != 0)
continue;
break;
}

View File

@ -155,7 +155,7 @@ WT_CONF_API_DECLARE(WT_CONNECTION, debug_info, 1, 8);
WT_CONF_API_DECLARE(WT_CONNECTION, load_extension, 1, 4);
WT_CONF_API_DECLARE(WT_CONNECTION, open_session, 3, 9);
WT_CONF_API_DECLARE(WT_CONNECTION, query_timestamp, 1, 1);
WT_CONF_API_DECLARE(WT_CONNECTION, reconfigure, 20, 127);
WT_CONF_API_DECLARE(WT_CONNECTION, reconfigure, 20, 126);
WT_CONF_API_DECLARE(WT_CONNECTION, rollback_to_stable, 1, 2);
WT_CONF_API_DECLARE(WT_CONNECTION, set_timestamp, 1, 4);
WT_CONF_API_DECLARE(WT_CURSOR, bound, 1, 3);
@ -186,10 +186,10 @@ WT_CONF_API_DECLARE(object, meta, 7, 71);
WT_CONF_API_DECLARE(table, meta, 2, 13);
WT_CONF_API_DECLARE(tier, meta, 7, 72);
WT_CONF_API_DECLARE(tiered, meta, 7, 74);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open, 25, 198);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_all, 25, 199);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_basecfg, 25, 193);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_usercfg, 25, 192);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open, 25, 197);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_all, 25, 198);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_basecfg, 25, 192);
WT_CONF_API_DECLARE(GLOBAL, wiredtiger_open_usercfg, 25, 191);
#define WT_CONF_API_ELEMENTS 57

View File

@ -26,43 +26,43 @@
#define WT_CONF_ID_Disaggregated 20ULL
#define WT_CONF_ID_Dump_version 131ULL
#define WT_CONF_ID_Encryption 22ULL
#define WT_CONF_ID_Eviction 242ULL
#define WT_CONF_ID_File_manager 261ULL
#define WT_CONF_ID_Eviction 241ULL
#define WT_CONF_ID_File_manager 260ULL
#define WT_CONF_ID_Flush_tier 182ULL
#define WT_CONF_ID_Hash 330ULL
#define WT_CONF_ID_Heuristic_controls 266ULL
#define WT_CONF_ID_History_store 270ULL
#define WT_CONF_ID_Hash 329ULL
#define WT_CONF_ID_Heuristic_controls 265ULL
#define WT_CONF_ID_History_store 269ULL
#define WT_CONF_ID_Import 99ULL
#define WT_CONF_ID_Incremental 139ULL
#define WT_CONF_ID_Io_capacity 272ULL
#define WT_CONF_ID_Io_capacity 271ULL
#define WT_CONF_ID_Live_restore 64ULL
#define WT_CONF_ID_Log 40ULL
#define WT_CONF_ID_Lsm 105ULL
#define WT_CONF_ID_Merge_custom 115ULL
#define WT_CONF_ID_Operation_tracking 282ULL
#define WT_CONF_ID_Page_delta 284ULL
#define WT_CONF_ID_Prefetch 310ULL
#define WT_CONF_ID_Prefetch1 312ULL
#define WT_CONF_ID_Rollback_to_stable 290ULL
#define WT_CONF_ID_Operation_tracking 281ULL
#define WT_CONF_ID_Page_delta 283ULL
#define WT_CONF_ID_Prefetch 309ULL
#define WT_CONF_ID_Prefetch1 311ULL
#define WT_CONF_ID_Rollback_to_stable 289ULL
#define WT_CONF_ID_Roundup_timestamps 173ULL
#define WT_CONF_ID_Shared_cache 292ULL
#define WT_CONF_ID_Statistics_log 296ULL
#define WT_CONF_ID_Shared_cache 291ULL
#define WT_CONF_ID_Statistics_log 295ULL
#define WT_CONF_ID_Tiered_storage 51ULL
#define WT_CONF_ID_Transaction_sync 350ULL
#define WT_CONF_ID_Transaction_sync 349ULL
#define WT_CONF_ID_access_pattern_hint 12ULL
#define WT_CONF_ID_action 88ULL
#define WT_CONF_ID_allocation_size 13ULL
#define WT_CONF_ID_app_eviction_min_cache_fill_ratio 248ULL
#define WT_CONF_ID_app_eviction_min_cache_fill_ratio 247ULL
#define WT_CONF_ID_app_metadata 0ULL
#define WT_CONF_ID_append 85ULL
#define WT_CONF_ID_archive 276ULL
#define WT_CONF_ID_archive 275ULL
#define WT_CONF_ID_auth_token 52ULL
#define WT_CONF_ID_auto_throttle 106ULL
#define WT_CONF_ID_available 342ULL
#define WT_CONF_ID_available 341ULL
#define WT_CONF_ID_background 92ULL
#define WT_CONF_ID_background_compact 220ULL
#define WT_CONF_ID_backup 186ULL
#define WT_CONF_ID_backup_restore_target 313ULL
#define WT_CONF_ID_backup_restore_target 312ULL
#define WT_CONF_ID_bitmap 65ULL
#define WT_CONF_ID_blkcache_eviction_aggression 197ULL
#define WT_CONF_ID_block_allocation 14ULL
@ -76,12 +76,12 @@
#define WT_CONF_ID_bound 89ULL
#define WT_CONF_ID_bucket 53ULL
#define WT_CONF_ID_bucket_prefix 54ULL
#define WT_CONF_ID_buckets 331ULL
#define WT_CONF_ID_buffer_alignment 314ULL
#define WT_CONF_ID_builtin_extension_config 315ULL
#define WT_CONF_ID_buckets 330ULL
#define WT_CONF_ID_buffer_alignment 313ULL
#define WT_CONF_ID_builtin_extension_config 314ULL
#define WT_CONF_ID_bulk 127ULL
#define WT_CONF_ID_cache 187ULL
#define WT_CONF_ID_cache_cursors 306ULL
#define WT_CONF_ID_cache_cursors 305ULL
#define WT_CONF_ID_cache_directory 55ULL
#define WT_CONF_ID_cache_max_wait_ms 205ULL
#define WT_CONF_ID_cache_on_checkpoint 195ULL
@ -90,57 +90,56 @@
#define WT_CONF_ID_cache_resident 17ULL
#define WT_CONF_ID_cache_size 207ULL
#define WT_CONF_ID_cache_stuck_timeout_ms 208ULL
#define WT_CONF_ID_cache_tolerance_for_app_eviction 249ULL
#define WT_CONF_ID_capacity 317ULL
#define WT_CONF_ID_cache_tolerance_for_app_eviction 248ULL
#define WT_CONF_ID_capacity 316ULL
#define WT_CONF_ID_checkpoint 60ULL
#define WT_CONF_ID_checkpoint_backup_info 61ULL
#define WT_CONF_ID_checkpoint_cleanup 179ULL
#define WT_CONF_ID_checkpoint_cleanup_obsolete_tw_pages_dirty_max 267ULL
#define WT_CONF_ID_checkpoint_cleanup_obsolete_tw_pages_dirty_max 266ULL
#define WT_CONF_ID_checkpoint_crash_point 180ULL
#define WT_CONF_ID_checkpoint_fail_before_turtle_update 307ULL
#define WT_CONF_ID_checkpoint_fail_before_turtle_update 306ULL
#define WT_CONF_ID_checkpoint_lsn 62ULL
#define WT_CONF_ID_checkpoint_meta 74ULL
#define WT_CONF_ID_checkpoint_read_timestamp 130ULL
#define WT_CONF_ID_checkpoint_retention 223ULL
#define WT_CONF_ID_checkpoint_sync 316ULL
#define WT_CONF_ID_checkpoint_retention 222ULL
#define WT_CONF_ID_checkpoint_sync 315ULL
#define WT_CONF_ID_checkpoint_use_history 128ULL
#define WT_CONF_ID_checkpoint_wait 121ULL
#define WT_CONF_ID_checksum 18ULL
#define WT_CONF_ID_chunk 293ULL
#define WT_CONF_ID_chunk_cache 274ULL
#define WT_CONF_ID_chunk_cache_evict_trigger 318ULL
#define WT_CONF_ID_chunk 292ULL
#define WT_CONF_ID_chunk_cache 273ULL
#define WT_CONF_ID_chunk_cache_evict_trigger 317ULL
#define WT_CONF_ID_chunk_count_limit 112ULL
#define WT_CONF_ID_chunk_max 113ULL
#define WT_CONF_ID_chunk_size 114ULL
#define WT_CONF_ID_claim_prepared_id 167ULL
#define WT_CONF_ID_close_handle_minimum 262ULL
#define WT_CONF_ID_close_idle_time 263ULL
#define WT_CONF_ID_close_scan_interval 264ULL
#define WT_CONF_ID_close_handle_minimum 261ULL
#define WT_CONF_ID_close_idle_time 262ULL
#define WT_CONF_ID_close_scan_interval 263ULL
#define WT_CONF_ID_colgroups 81ULL
#define WT_CONF_ID_collator 6ULL
#define WT_CONF_ID_columns 7ULL
#define WT_CONF_ID_commit_timestamp 2ULL
#define WT_CONF_ID_compare_timestamp 100ULL
#define WT_CONF_ID_compile_configuration_count 323ULL
#define WT_CONF_ID_compressor 335ULL
#define WT_CONF_ID_config 302ULL
#define WT_CONF_ID_config_base 324ULL
#define WT_CONF_ID_configuration 224ULL
#define WT_CONF_ID_compile_configuration_count 322ULL
#define WT_CONF_ID_compressor 334ULL
#define WT_CONF_ID_config 301ULL
#define WT_CONF_ID_config_base 323ULL
#define WT_CONF_ID_configuration 223ULL
#define WT_CONF_ID_consolidate 140ULL
#define WT_CONF_ID_corruption_abort 222ULL
#define WT_CONF_ID_crash_point_colgroup 221ULL
#define WT_CONF_ID_create 325ULL
#define WT_CONF_ID_corruption_abort 221ULL
#define WT_CONF_ID_create 324ULL
#define WT_CONF_ID_cross_key 136ULL
#define WT_CONF_ID_cursor_copy 225ULL
#define WT_CONF_ID_cursor_reposition 226ULL
#define WT_CONF_ID_cursor_copy 224ULL
#define WT_CONF_ID_cursor_reposition 225ULL
#define WT_CONF_ID_cursors 188ULL
#define WT_CONF_ID_default 343ULL
#define WT_CONF_ID_delta_pct 285ULL
#define WT_CONF_ID_dhandle_buckets 332ULL
#define WT_CONF_ID_default 342ULL
#define WT_CONF_ID_delta_pct 284ULL
#define WT_CONF_ID_dhandle_buckets 331ULL
#define WT_CONF_ID_dictionary 19ULL
#define WT_CONF_ID_direct_io 326ULL
#define WT_CONF_ID_disagg_address_cookie_optional_field 228ULL
#define WT_CONF_ID_disagg_address_cookie_upgrade 227ULL
#define WT_CONF_ID_direct_io 325ULL
#define WT_CONF_ID_disagg_address_cookie_optional_field 227ULL
#define WT_CONF_ID_disagg_address_cookie_upgrade 226ULL
#define WT_CONF_ID_do_not_clear_txn_id 155ULL
#define WT_CONF_ID_drop 181ULL
#define WT_CONF_ID_dryrun 93ULL
@ -154,70 +153,70 @@
#define WT_CONF_ID_dump_pages 162ULL
#define WT_CONF_ID_dump_tree_shape 163ULL
#define WT_CONF_ID_durable_timestamp 3ULL
#define WT_CONF_ID_early_load 303ULL
#define WT_CONF_ID_early_load 302ULL
#define WT_CONF_ID_enabled 41ULL
#define WT_CONF_ID_entry 304ULL
#define WT_CONF_ID_error_prefix 241ULL
#define WT_CONF_ID_evict_sample_inmem 245ULL
#define WT_CONF_ID_evict_use_softptr 246ULL
#define WT_CONF_ID_eviction 229ULL
#define WT_CONF_ID_eviction_checkpoint_target 253ULL
#define WT_CONF_ID_eviction_checkpoint_ts_ordering 240ULL
#define WT_CONF_ID_eviction_dirty_target 254ULL
#define WT_CONF_ID_eviction_dirty_trigger 255ULL
#define WT_CONF_ID_eviction_obsolete_tw_pages_dirty_max 268ULL
#define WT_CONF_ID_eviction_target 256ULL
#define WT_CONF_ID_eviction_trigger 257ULL
#define WT_CONF_ID_eviction_updates_target 258ULL
#define WT_CONF_ID_eviction_updates_trigger 259ULL
#define WT_CONF_ID_entry 303ULL
#define WT_CONF_ID_error_prefix 240ULL
#define WT_CONF_ID_evict_sample_inmem 244ULL
#define WT_CONF_ID_evict_use_softptr 245ULL
#define WT_CONF_ID_eviction 228ULL
#define WT_CONF_ID_eviction_checkpoint_target 252ULL
#define WT_CONF_ID_eviction_checkpoint_ts_ordering 239ULL
#define WT_CONF_ID_eviction_dirty_target 253ULL
#define WT_CONF_ID_eviction_dirty_trigger 254ULL
#define WT_CONF_ID_eviction_obsolete_tw_pages_dirty_max 267ULL
#define WT_CONF_ID_eviction_target 255ULL
#define WT_CONF_ID_eviction_trigger 256ULL
#define WT_CONF_ID_eviction_updates_target 257ULL
#define WT_CONF_ID_eviction_updates_trigger 258ULL
#define WT_CONF_ID_exclude 94ULL
#define WT_CONF_ID_exclusive 98ULL
#define WT_CONF_ID_exclusive_refreshed 91ULL
#define WT_CONF_ID_extensions 328ULL
#define WT_CONF_ID_extra_diagnostics 260ULL
#define WT_CONF_ID_extensions 327ULL
#define WT_CONF_ID_extra_diagnostics 259ULL
#define WT_CONF_ID_extractor 70ULL
#define WT_CONF_ID_file 141ULL
#define WT_CONF_ID_file_extend 329ULL
#define WT_CONF_ID_file_max 271ULL
#define WT_CONF_ID_file_extend 328ULL
#define WT_CONF_ID_file_max 270ULL
#define WT_CONF_ID_file_metadata 101ULL
#define WT_CONF_ID_file_wait_ms 214ULL
#define WT_CONF_ID_final_flush 184ULL
#define WT_CONF_ID_flatten_leaf_page_delta 286ULL
#define WT_CONF_ID_flatten_leaf_page_delta 285ULL
#define WT_CONF_ID_flush_time 72ULL
#define WT_CONF_ID_flush_timestamp 73ULL
#define WT_CONF_ID_flushed_data_cache_insertion 320ULL
#define WT_CONF_ID_flushed_data_cache_insertion 319ULL
#define WT_CONF_ID_force 122ULL
#define WT_CONF_ID_force_stop 142ULL
#define WT_CONF_ID_force_write_wait 336ULL
#define WT_CONF_ID_force_write_wait 335ULL
#define WT_CONF_ID_format 25ULL
#define WT_CONF_ID_free_space_target 95ULL
#define WT_CONF_ID_full_target 198ULL
#define WT_CONF_ID_generation_drain_timeout_ms 265ULL
#define WT_CONF_ID_generation_drain_timeout_ms 264ULL
#define WT_CONF_ID_get 154ULL
#define WT_CONF_ID_granularity 143ULL
#define WT_CONF_ID_handles 189ULL
#define WT_CONF_ID_hashsize 200ULL
#define WT_CONF_ID_hazard_max 333ULL
#define WT_CONF_ID_hazard_max 332ULL
#define WT_CONF_ID_huffman_key 26ULL
#define WT_CONF_ID_huffman_value 27ULL
#define WT_CONF_ID_id 63ULL
#define WT_CONF_ID_ignore_cache_size 309ULL
#define WT_CONF_ID_ignore_cache_size 308ULL
#define WT_CONF_ID_ignore_in_memory_cache_size 28ULL
#define WT_CONF_ID_ignore_prepare 168ULL
#define WT_CONF_ID_immutable 71ULL
#define WT_CONF_ID_in_memory 29ULL
#define WT_CONF_ID_inclusive 90ULL
#define WT_CONF_ID_incremental_app_eviction 250ULL
#define WT_CONF_ID_incremental_app_eviction 249ULL
#define WT_CONF_ID_ingest 79ULL
#define WT_CONF_ID_internal_item_max 30ULL
#define WT_CONF_ID_internal_key_max 31ULL
#define WT_CONF_ID_internal_key_truncate 32ULL
#define WT_CONF_ID_internal_page_delta 287ULL
#define WT_CONF_ID_internal_page_delta 286ULL
#define WT_CONF_ID_internal_page_max 33ULL
#define WT_CONF_ID_interval 349ULL
#define WT_CONF_ID_interval 348ULL
#define WT_CONF_ID_isolation 169ULL
#define WT_CONF_ID_json 297ULL
#define WT_CONF_ID_json_output 275ULL
#define WT_CONF_ID_json 296ULL
#define WT_CONF_ID_json_output 274ULL
#define WT_CONF_ID_key_format 34ULL
#define WT_CONF_ID_key_gap 35ULL
#define WT_CONF_ID_keyid 24ULL
@ -225,19 +224,19 @@
#define WT_CONF_ID_last_materialized_lsn 75ULL
#define WT_CONF_ID_leaf_item_max 36ULL
#define WT_CONF_ID_leaf_key_max 37ULL
#define WT_CONF_ID_leaf_page_delta 288ULL
#define WT_CONF_ID_leaf_page_delta 287ULL
#define WT_CONF_ID_leaf_page_max 38ULL
#define WT_CONF_ID_leaf_value_max 39ULL
#define WT_CONF_ID_leak_memory 185ULL
#define WT_CONF_ID_legacy_page_visit_strategy 247ULL
#define WT_CONF_ID_legacy_page_visit_strategy 246ULL
#define WT_CONF_ID_local_files_action 76ULL
#define WT_CONF_ID_local_retention 56ULL
#define WT_CONF_ID_lock_wait 123ULL
#define WT_CONF_ID_log 190ULL
#define WT_CONF_ID_log_retention 230ULL
#define WT_CONF_ID_log_retention 229ULL
#define WT_CONF_ID_log_size 210ULL
#define WT_CONF_ID_lose_all_my_data 77ULL
#define WT_CONF_ID_max_consecutive_delta 289ULL
#define WT_CONF_ID_max_consecutive_delta 288ULL
#define WT_CONF_ID_max_percent_overhead 201ULL
#define WT_CONF_ID_memory_page_image_max 42ULL
#define WT_CONF_ID_memory_page_max 43ULL
@ -246,9 +245,9 @@
#define WT_CONF_ID_metadata 191ULL
#define WT_CONF_ID_metadata_file 102ULL
#define WT_CONF_ID_method 213ULL
#define WT_CONF_ID_mmap 338ULL
#define WT_CONF_ID_mmap_all 339ULL
#define WT_CONF_ID_multiprocess 340ULL
#define WT_CONF_ID_mmap 337ULL
#define WT_CONF_ID_mmap_all 338ULL
#define WT_CONF_ID_multiprocess 339ULL
#define WT_CONF_ID_name 23ULL
#define WT_CONF_ID_nbits 66ULL
#define WT_CONF_ID_next_random 146ULL
@ -257,25 +256,25 @@
#define WT_CONF_ID_no_timestamp 170ULL
#define WT_CONF_ID_nvram_path 202ULL
#define WT_CONF_ID_object_target_size 57ULL
#define WT_CONF_ID_obsolete_tw_btree_max 269ULL
#define WT_CONF_ID_obsolete_tw_btree_max 268ULL
#define WT_CONF_ID_oldest 83ULL
#define WT_CONF_ID_oldest_timestamp 311ULL
#define WT_CONF_ID_on_close 298ULL
#define WT_CONF_ID_oldest_timestamp 310ULL
#define WT_CONF_ID_on_close 297ULL
#define WT_CONF_ID_operation_timeout_ms 171ULL
#define WT_CONF_ID_os_cache_dirty_max 44ULL
#define WT_CONF_ID_os_cache_dirty_pct 277ULL
#define WT_CONF_ID_os_cache_dirty_pct 276ULL
#define WT_CONF_ID_os_cache_max 45ULL
#define WT_CONF_ID_overwrite 86ULL
#define WT_CONF_ID_page_history 231ULL
#define WT_CONF_ID_page_history 230ULL
#define WT_CONF_ID_page_log 21ULL
#define WT_CONF_ID_panic_corrupt 103ULL
#define WT_CONF_ID_path 283ULL
#define WT_CONF_ID_path 282ULL
#define WT_CONF_ID_percent_file_in_dram 203ULL
#define WT_CONF_ID_pinned 216ULL
#define WT_CONF_ID_prealloc 278ULL
#define WT_CONF_ID_prealloc_init_count 279ULL
#define WT_CONF_ID_precise_checkpoint 341ULL
#define WT_CONF_ID_prefer_scrub_eviction 251ULL
#define WT_CONF_ID_prealloc 277ULL
#define WT_CONF_ID_prealloc_init_count 278ULL
#define WT_CONF_ID_precise_checkpoint 340ULL
#define WT_CONF_ID_prefer_scrub_eviction 250ULL
#define WT_CONF_ID_prefix 116ULL
#define WT_CONF_ID_prefix_compression 46ULL
#define WT_CONF_ID_prefix_compression_min 47ULL
@ -283,47 +282,47 @@
#define WT_CONF_ID_prepare_timestamp 176ULL
#define WT_CONF_ID_prepared 174ULL
#define WT_CONF_ID_prepared_id 177ULL
#define WT_CONF_ID_preserve_prepared 344ULL
#define WT_CONF_ID_preserve_prepared 343ULL
#define WT_CONF_ID_priority 172ULL
#define WT_CONF_ID_quota 294ULL
#define WT_CONF_ID_quota 293ULL
#define WT_CONF_ID_raw 149ULL
#define WT_CONF_ID_raw_key_value 135ULL
#define WT_CONF_ID_read 175ULL
#define WT_CONF_ID_read_corrupt 164ULL
#define WT_CONF_ID_read_once 150ULL
#define WT_CONF_ID_read_size 334ULL
#define WT_CONF_ID_read_size 333ULL
#define WT_CONF_ID_read_timestamp 4ULL
#define WT_CONF_ID_readonly 67ULL
#define WT_CONF_ID_realloc_exact 232ULL
#define WT_CONF_ID_realloc_malloc 233ULL
#define WT_CONF_ID_recover 337ULL
#define WT_CONF_ID_realloc_exact 231ULL
#define WT_CONF_ID_realloc_malloc 232ULL
#define WT_CONF_ID_recover 336ULL
#define WT_CONF_ID_release 218ULL
#define WT_CONF_ID_release_evict 137ULL
#define WT_CONF_ID_release_evict_page 308ULL
#define WT_CONF_ID_remove 280ULL
#define WT_CONF_ID_release_evict_page 307ULL
#define WT_CONF_ID_remove 279ULL
#define WT_CONF_ID_remove_files 124ULL
#define WT_CONF_ID_remove_shared 125ULL
#define WT_CONF_ID_repair 104ULL
#define WT_CONF_ID_require_max 321ULL
#define WT_CONF_ID_require_min 322ULL
#define WT_CONF_ID_reserve 295ULL
#define WT_CONF_ID_require_max 320ULL
#define WT_CONF_ID_require_min 321ULL
#define WT_CONF_ID_reserve 294ULL
#define WT_CONF_ID_role 78ULL
#define WT_CONF_ID_rollback_error 234ULL
#define WT_CONF_ID_rollback_error 233ULL
#define WT_CONF_ID_rollback_timestamp 178ULL
#define WT_CONF_ID_run_once 96ULL
#define WT_CONF_ID_salvage 345ULL
#define WT_CONF_ID_secretkey 327ULL
#define WT_CONF_ID_session_max 346ULL
#define WT_CONF_ID_session_scratch_max 347ULL
#define WT_CONF_ID_session_table_cache 348ULL
#define WT_CONF_ID_salvage 344ULL
#define WT_CONF_ID_secretkey 326ULL
#define WT_CONF_ID_session_max 345ULL
#define WT_CONF_ID_session_scratch_max 346ULL
#define WT_CONF_ID_session_table_cache 347ULL
#define WT_CONF_ID_sessions 192ULL
#define WT_CONF_ID_shared 58ULL
#define WT_CONF_ID_size 199ULL
#define WT_CONF_ID_skip_sort_check 151ULL
#define WT_CONF_ID_skip_update_obsolete_check 252ULL
#define WT_CONF_ID_slow_checkpoint 235ULL
#define WT_CONF_ID_skip_update_obsolete_check 251ULL
#define WT_CONF_ID_slow_checkpoint 234ULL
#define WT_CONF_ID_source 8ULL
#define WT_CONF_ID_sources 299ULL
#define WT_CONF_ID_sources 298ULL
#define WT_CONF_ID_split_deepen_min_child 48ULL
#define WT_CONF_ID_split_deepen_per_child 49ULL
#define WT_CONF_ID_split_pct 50ULL
@ -333,45 +332,45 @@
#define WT_CONF_ID_start_generation 117ULL
#define WT_CONF_ID_start_timestamp 133ULL
#define WT_CONF_ID_statistics 152ULL
#define WT_CONF_ID_storage_path 319ULL
#define WT_CONF_ID_stress_skiplist 236ULL
#define WT_CONF_ID_storage_path 318ULL
#define WT_CONF_ID_stress_skiplist 235ULL
#define WT_CONF_ID_strict 166ULL
#define WT_CONF_ID_suffix 118ULL
#define WT_CONF_ID_sync 126ULL
#define WT_CONF_ID_system_ram 204ULL
#define WT_CONF_ID_table_logging 237ULL
#define WT_CONF_ID_table_logging 236ULL
#define WT_CONF_ID_target 153ULL
#define WT_CONF_ID_terminate 305ULL
#define WT_CONF_ID_terminate 304ULL
#define WT_CONF_ID_this_id 145ULL
#define WT_CONF_ID_threads 291ULL
#define WT_CONF_ID_threads_max 243ULL
#define WT_CONF_ID_threads_min 244ULL
#define WT_CONF_ID_tiered_flush_error_continue 238ULL
#define WT_CONF_ID_threads 290ULL
#define WT_CONF_ID_threads_max 242ULL
#define WT_CONF_ID_threads_min 243ULL
#define WT_CONF_ID_tiered_flush_error_continue 237ULL
#define WT_CONF_ID_tiered_object 68ULL
#define WT_CONF_ID_tiers 84ULL
#define WT_CONF_ID_timeout 97ULL
#define WT_CONF_ID_timestamp 300ULL
#define WT_CONF_ID_timestamp 299ULL
#define WT_CONF_ID_timestamp_order 134ULL
#define WT_CONF_ID_timing_stress_for_test 301ULL
#define WT_CONF_ID_total 273ULL
#define WT_CONF_ID_timing_stress_for_test 300ULL
#define WT_CONF_ID_total 272ULL
#define WT_CONF_ID_txn 193ULL
#define WT_CONF_ID_type 9ULL
#define WT_CONF_ID_update_restore_evict 239ULL
#define WT_CONF_ID_use_environment 351ULL
#define WT_CONF_ID_use_environment_priv 352ULL
#define WT_CONF_ID_update_restore_evict 238ULL
#define WT_CONF_ID_use_environment 350ULL
#define WT_CONF_ID_use_environment_priv 351ULL
#define WT_CONF_ID_use_timestamp 183ULL
#define WT_CONF_ID_value_format 59ULL
#define WT_CONF_ID_verbose 10ULL
#define WT_CONF_ID_verify_metadata 353ULL
#define WT_CONF_ID_verify_metadata 352ULL
#define WT_CONF_ID_version 69ULL
#define WT_CONF_ID_visible_only 132ULL
#define WT_CONF_ID_wait 211ULL
#define WT_CONF_ID_write_through 354ULL
#define WT_CONF_ID_write_through 353ULL
#define WT_CONF_ID_write_timestamp 5ULL
#define WT_CONF_ID_write_timestamp_usage 11ULL
#define WT_CONF_ID_zero_fill 281ULL
#define WT_CONF_ID_zero_fill 280ULL
#define WT_CONF_ID_COUNT 355
#define WT_CONF_ID_COUNT 354
/*
* API configuration keys: END
*/
@ -438,7 +437,6 @@ static const struct {
uint64_t checkpoint_retention;
uint64_t configuration;
uint64_t corruption_abort;
uint64_t crash_point_colgroup;
uint64_t cursor_copy;
uint64_t cursor_reposition;
uint64_t disagg_address_cookie_optional_field;
@ -849,7 +847,6 @@ static const struct {
WT_CONF_ID_Debug_mode | (WT_CONF_ID_checkpoint_retention << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_configuration << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_corruption_abort << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_crash_point_colgroup << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_cursor_copy << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_cursor_reposition << 16),
WT_CONF_ID_Debug_mode | (WT_CONF_ID_disagg_address_cookie_optional_field << 16),

View File

@ -889,18 +889,17 @@ struct __wt_connection_impl {
#define WT_CONN_DEBUG_CKPT_RETAIN 0x0001u
#define WT_CONN_DEBUG_CONFIGURATION 0x0002u
#define WT_CONN_DEBUG_CORRUPTION_ABORT 0x0004u
#define WT_CONN_DEBUG_CRASH_POINT_COLGROUP 0x0008u
#define WT_CONN_DEBUG_CURSOR_COPY 0x0010u
#define WT_CONN_DEBUG_CURSOR_REPOSITION 0x0020u
#define WT_CONN_DEBUG_EVICTION_CKPT_TS_ORDERING 0x0040u
#define WT_CONN_DEBUG_EVICT_AGGRESSIVE_MODE 0x0080u
#define WT_CONN_DEBUG_REALLOC_EXACT 0x0100u
#define WT_CONN_DEBUG_REALLOC_MALLOC 0x0200u
#define WT_CONN_DEBUG_SLOW_CKPT 0x0400u
#define WT_CONN_DEBUG_STRESS_SKIPLIST 0x0800u
#define WT_CONN_DEBUG_TABLE_LOGGING 0x1000u
#define WT_CONN_DEBUG_TIERED_FLUSH_ERROR_CONTINUE 0x2000u
#define WT_CONN_DEBUG_UPDATE_RESTORE_EVICT 0x4000u
#define WT_CONN_DEBUG_CURSOR_COPY 0x0008u
#define WT_CONN_DEBUG_CURSOR_REPOSITION 0x0010u
#define WT_CONN_DEBUG_EVICTION_CKPT_TS_ORDERING 0x0020u
#define WT_CONN_DEBUG_EVICT_AGGRESSIVE_MODE 0x0040u
#define WT_CONN_DEBUG_REALLOC_EXACT 0x0080u
#define WT_CONN_DEBUG_REALLOC_MALLOC 0x0100u
#define WT_CONN_DEBUG_SLOW_CKPT 0x0200u
#define WT_CONN_DEBUG_STRESS_SKIPLIST 0x0400u
#define WT_CONN_DEBUG_TABLE_LOGGING 0x0800u
#define WT_CONN_DEBUG_TIERED_FLUSH_ERROR_CONTINUE 0x1000u
#define WT_CONN_DEBUG_UPDATE_RESTORE_EVICT 0x2000u
/* AUTOMATIC FLAG VALUE GENERATION STOP 16 */
uint16_t debug_flags;

View File

@ -691,8 +691,6 @@ extern int __wt_import_repair(WT_SESSION_IMPL *session, const char *uri, char **
WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_inmem_unsupported_op(WT_SESSION_IMPL *session, const char *tag)
WT_GCC_FUNC_DECL_ATTRIBUTE((cold)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_is_simple_table(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *colconf, bool *is_simplep)
WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_json_alloc_unpack(WT_SESSION_IMPL *session, const void *buffer, size_t size,
const char *fmt, WT_JSON *json, bool iskey, va_list ap)
WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));

View File

@ -742,6 +742,7 @@ struct __wt_connection_stats {
int64_t cache_read_restored_tombstone_bytes;
int64_t cache_hs_insert_full_update;
int64_t cache_hs_insert_reverse_modify;
int64_t eviction_worker_lock_wait_time;
int64_t eviction_reentry_hs_eviction_milliseconds;
int64_t cache_bytes_internal;
int64_t cache_bytes_internal_ingest;

File diff suppressed because it is too large Load Diff

View File

@ -71,13 +71,6 @@ __rec_delete_hs_upd_save(WT_SESSION_IMPL *session, WTI_RECONCILE *r, WT_INSERT *
delete_hs_upd->tombstone = tombstone;
++r->delete_hs_upd_next;
/* Clear the durable flag to allow them being included in a delta. */
if (F_ISSET(upd, WT_UPDATE_DURABLE))
F_CLR(upd, WT_UPDATE_DURABLE);
if (tombstone != NULL && F_ISSET(tombstone, WT_UPDATE_DURABLE))
F_CLR(tombstone, WT_UPDATE_DURABLE);
return (0);
}
@ -1624,6 +1617,36 @@ __wti_rec_upd_select(WT_SESSION_IMPL *session, WTI_RECONCILE *r, WT_INSERT *ins,
__wti_rec_time_window_clear_obsolete(session, upd_select, NULL, r);
if (WT_DELTA_LEAF_ENABLED(session)) {
if (WT_TIME_WINDOW_HAS_START_PREPARE(&upd_select->tw)) {
WT_UPDATE *first_committed_upd = upd_select->upd->next;
for (; first_committed_upd != NULL; first_committed_upd = first_committed_upd->next) {
uint64_t next_txnid =
__wt_atomic_load_uint64_v_relaxed(&first_committed_upd->txnid);
if (next_txnid == WT_TXN_ABORTED)
continue;
if (next_txnid == upd_select->tw.start_txn)
continue;
break;
}
/*
* Clear the durable flags on the first committed update to ensure it can be included in
* the next delta if the prepared update is rolled back.
*/
if (first_committed_upd != NULL)
F_CLR(first_committed_upd, WT_UPDATE_DURABLE | WT_UPDATE_DELETE_DURABLE);
} else if (WT_TIME_WINDOW_HAS_STOP_PREPARE(&upd_select->tw))
/*
* When only writing a prepared tombstone, ensure the durable flags on the on-page value
* are cleared. Otherwise, if the prepared tombstone is rolled back, the on-page value
* may be missed in the next delta.
*/
F_CLR(upd_select->upd, WT_UPDATE_DURABLE | WT_UPDATE_DELETE_DURABLE);
}
WT_ASSERT(
session, upd_select->tw.stop_txn != WT_TXN_MAX || upd_select->tw.stop_ts == WT_TS_MAX);

View File

@ -2105,8 +2105,11 @@ __rec_set_updates_durable(WT_SESSION_IMPL *session, WT_MULTI *multi)
/* The on page value is also a prepared update from the same transaction. */
if (WT_TIME_WINDOW_HAS_START_PREPARE(&supd->tw))
F_SET(supd->onpage_upd, WT_UPDATE_PREPARE_DURABLE);
else
F_SET(supd->onpage_upd, WT_UPDATE_DURABLE);
/*
* Never mark the on-page value as durable to ensure it can be included in a
* future write if the prepared tombstone is rolled back.
*/
} else {
F_SET(supd->onpage_tombstone, WT_UPDATE_DURABLE);
F_SET(supd->onpage_upd, WT_UPDATE_DURABLE);

View File

@ -630,15 +630,6 @@ __create_colgroup(WT_SESSION_IMPL *session, const char *name, bool exclusive, co
WT_ERR(__wt_config_collapse(session, cfg, &cgconf));
/* FIXME-WT-12021 Replace this with a proper failpoint once the framework is available. */
if (FLD_ISSET(S2C(session)->debug_flags, WT_CONN_DEBUG_CRASH_POINT_COLGROUP)) {
__wt_verbose_warning(session, WT_VERB_DEFAULT,
"Simulating a crash before inserting column group metadata entry '%s'", name);
/* Wait for the file metadata entry to be persisted. */
__wt_sleep(2, 0);
__wt_abort(session);
}
if (!exists) {
WT_ERR(__wt_metadata_insert(session, name, cgconf));
WT_ERR(__wti_schema_open_colgroups(session, table));

View File

@ -176,7 +176,6 @@ __drop_table(
WT_SESSION_IMPL *session, const char *uri, bool force, const char *cfg[], bool check_visibility)
{
WT_COLGROUP *colgroup;
WT_DECL_ITEM(file_uri_buf);
WT_DECL_RET;
WT_INDEX *idx;
WT_TABLE *table;
@ -187,11 +186,10 @@ __drop_table(
WT_ASSERT(session, FLD_ISSET(session->lock_flags, WT_SESSION_LOCKED_TABLE_WRITE));
name = uri;
WT_PREFIX_SKIP_REQUIRED(session, name, "table:");
table = NULL;
tracked = false;
WT_ERR(__wt_scr_alloc(session, 0, &file_uri_buf));
WT_PREFIX_SKIP_REQUIRED(session, name, "table:");
/*
* Open the table so we can drop its column groups and indexes.
@ -216,17 +214,6 @@ __drop_table(
WT_ERR(ENOTSUP);
}
WT_ERR(__wt_buf_fmt(session, file_uri_buf, "file:%s.wt", name));
/*
* In a crash, it is possible for the file metadata entry to exist even though the colgroup was
* not created completely. In such a scenario, drop the file to keep the metadata consistent.
*
* FIXME-WT-16146: Add capability for cleaning up incomplete complex and tiered tables.
*/
if (!table->cg_complete && table->is_simple)
WT_ERR(__wt_schema_drop(session, file_uri_buf->data, cfg, check_visibility));
/* Drop the column groups. */
for (i = 0; i < WT_COLGROUPS(table); i++) {
if ((colgroup = table->cgroups[i]) == NULL)
@ -266,7 +253,6 @@ __drop_table(
WT_ERR(__wt_metadata_remove(session, uri));
err:
__wt_scr_free(session, &file_uri_buf);
if (!tracked)
WT_TRET(__wt_schema_release_table(session, &table));
return (ret);
@ -476,13 +462,11 @@ __schema_drop(WT_SESSION_IMPL *session, const char *uri, const char *cfg[], bool
if (ret == WT_NOTFOUND || ret == ENOENT)
ret = force ? 0 : ENOENT;
/*
* FIXME-WT-16215: During recovery (including partial backup restore), the meta tracking has not
* been initialized yet. We don't need to use meta tracking as recovery must end with a
* checkpoint to syncs all files.
*/
bool need_sync = !F_ISSET(S2C(session), WT_CONN_BACKUP_PARTIAL_RESTORE | WT_CONN_RECOVERING);
WT_TRET(__wt_meta_track_off(session, need_sync, ret != 0));
if (F_ISSET(S2C(session), WT_CONN_BACKUP_PARTIAL_RESTORE))
WT_TRET(__wt_meta_track_off(session, false, ret != 0));
else
WT_TRET(__wt_meta_track_off(session, true, ret != 0));
return (ret);
}

View File

@ -469,7 +469,14 @@ __schema_open_table(WT_SESSION_IMPL *session)
/* Point to some items in the copy to save re-parsing. */
WT_RET(__wt_config_gets(session, table_cfg, "columns", &table->colconf));
WT_RET(__wt_is_simple_table(session, &table->colconf, &table->is_simple));
/*
* Count the number of columns: tables are "simple" if the columns are not named.
*/
__wt_config_subinit(session, &cparser, &table->colconf);
table->is_simple = true;
while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0)
table->is_simple = false;
WT_RET_NOTFOUND_OK(ret);
/* Check that the columns match the key and value formats. */
if (!table->is_simple)

View File

@ -185,26 +185,3 @@ err:
__wt_scr_free(session, &tmp);
return (ret);
}
/*
* __wt_is_simple_table --
* Check whether the given table is simple.
*/
int
__wt_is_simple_table(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *colconf, bool *is_simplep)
{
WT_CONFIG cparser;
WT_CONFIG_ITEM ckey, cval;
WT_DECL_RET;
__wt_config_subinit(session, &cparser, colconf);
*is_simplep = true;
/* Count the number of columns: tables are "simple" if the columns are not named. */
while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) {
*is_simplep = false;
break;
}
WT_RET_NOTFOUND_OK(ret);
return (0);
}

View File

@ -2118,6 +2118,7 @@ static const char *const __stats_connection_desc[] = {
"cache: size of tombstones restored when reading a page",
"cache: the number of times full update inserted to history store",
"cache: the number of times reverse modify inserted to history store",
"cache: time eviction worker threads spend waiting for locks (usecs)",
"cache: total milliseconds spent inside reentrant history store evictions in a reconciliation",
"cache: tracked bytes belonging to internal pages in the cache",
"cache: tracked bytes belonging to internal pages in the cache from the ingest btrees",
@ -3129,6 +3130,7 @@ __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats)
stats->cache_read_restored_tombstone_bytes = 0;
stats->cache_hs_insert_full_update = 0;
stats->cache_hs_insert_reverse_modify = 0;
stats->eviction_worker_lock_wait_time = 0;
/* not clearing eviction_reentry_hs_eviction_milliseconds */
/* not clearing cache_bytes_internal */
/* not clearing cache_bytes_internal_ingest */
@ -4210,6 +4212,7 @@ __wt_stat_connection_aggregate(WT_CONNECTION_STATS **from, WT_CONNECTION_STATS *
WT_STAT_CONN_READ(from, cache_read_restored_tombstone_bytes);
to->cache_hs_insert_full_update += WT_STAT_CONN_READ(from, cache_hs_insert_full_update);
to->cache_hs_insert_reverse_modify += WT_STAT_CONN_READ(from, cache_hs_insert_reverse_modify);
to->eviction_worker_lock_wait_time += WT_STAT_CONN_READ(from, eviction_worker_lock_wait_time);
to->eviction_reentry_hs_eviction_milliseconds +=
WT_STAT_CONN_READ(from, eviction_reentry_hs_eviction_milliseconds);
to->cache_bytes_internal += WT_STAT_CONN_READ(from, cache_bytes_internal);

View File

@ -799,13 +799,12 @@ __recovery_close_cursors(WT_RECOVERY *r)
}
/*
* __recovery_metadata_scan_prefix --
* Scan the files matching the prefix referenced from the metadata and call the worker function
* for each entry.
* __recovery_file_scan_prefix --
* Scan the files matching the prefix referenced from the metadata and gather information about
* them for recovery.
*/
static int
__recovery_metadata_scan_prefix(WT_RECOVERY *r, const char *prefix, const char *ignore_suffix,
int (*recovery_meta_worker_func)(WT_RECOVERY *, const char *, const char *))
__recovery_file_scan_prefix(WT_RECOVERY *r, const char *prefix, const char *ignore_suffix)
{
WT_CURSOR *c;
WT_DECL_RET;
@ -832,87 +831,25 @@ __recovery_metadata_scan_prefix(WT_RECOVERY *r, const char *prefix, const char *
if (ignore_suffix != NULL && WT_SUFFIX_MATCH(uri, ignore_suffix))
continue;
WT_RET(c->get_value(c, &config));
WT_RET(recovery_meta_worker_func(r, uri, config));
WT_RET(__recovery_setup_file(r, uri, config));
}
WT_RET_NOTFOUND_OK(ret);
return (0);
}
/*
* __metadata_clean_incomplete_table --
* For each table metadata entry, check that the table was fully created. If not, clean up the
* incomplete table.
*/
static int
__metadata_clean_incomplete_table(WT_RECOVERY *r, const char *uri, const char *config)
{
WT_DECL_RET;
char *cg_meta_value;
const char *drop_cfg[] = {WT_CONFIG_BASE(r->session, WT_SESSION_drop), "force=true", NULL};
const char *metadata_cfg[] = {config, NULL};
const char *name;
WT_CONFIG_ITEM cval;
WT_ITEM *colgroup;
cg_meta_value = NULL;
WT_ERR(__wt_scr_alloc(r->session, 0, &colgroup));
/*
* FIXME-WT-16146: Add capability for cleaning up incomplete complex tables and skip checking
* tiered shared tables.
*/
bool is_simple;
WT_ERR(__wt_config_gets(r->session, metadata_cfg, "columns", &cval));
WT_ERR(__wt_is_simple_table(r->session, &cval, &is_simple));
if (!is_simple || ((ret = __wt_config_gets(r->session, metadata_cfg, "shared", &cval)) == 0))
goto done;
WT_ERR_NOTFOUND_OK(ret, false);
/* Check whether the colgroup exists. */
name = uri;
WT_PREFIX_SKIP_REQUIRED(r->session, name, "table:");
WT_ERR(__wt_buf_fmt(r->session, colgroup, "colgroup:%s", name));
WT_ERR_NOTFOUND_OK(__wt_metadata_search(r->session, colgroup->data, &cg_meta_value), true);
if (ret == 0)
goto done;
__wt_verbose_level_multi(r->session, WT_VERB_RECOVERY_ALL, WT_VERBOSE_WARNING, "%s %s",
"removing incomplete table", uri);
WT_WITH_SCHEMA_LOCK(r->session,
WT_WITH_TABLE_WRITE_LOCK(
r->session, ret = __wt_schema_drop(r->session, uri, drop_cfg, false)));
WT_ERR(ret);
err:
done:
__wt_free(r->session, cg_meta_value);
__wt_scr_free(r->session, &colgroup);
return (ret);
}
/*
* __recovery_file_scan --
* Scan the files referenced from the metadata to clean up incomplete tables and gather
* information about them for recovery.
* Scan the files referenced from the metadata and gather information about them for recovery.
*/
static int
__recovery_file_scan(WT_RECOVERY *r)
{
__wt_verbose_level_multi(r->session, WT_VERB_RECOVERY_ALL, WT_VERBOSE_INFO, "%s",
"scanning metadata to remove all incomplete tables");
/* Scan through all table entries in the metadata and clean up incomplete tables. */
__recovery_metadata_scan_prefix(r, "table:", NULL, __metadata_clean_incomplete_table);
__wt_verbose_level_multi(r->session, WT_VERB_RECOVERY_ALL, WT_VERBOSE_INFO, "%s",
"scanning metadata to find the largest file ID");
/*
* Scan through all files and tiered entries in the metadata and gather information about each
* entry for recovery.
*/
WT_RET(__recovery_metadata_scan_prefix(r, "file:", ".wtobj", __recovery_setup_file));
WT_RET(__recovery_metadata_scan_prefix(r, "tiered:", NULL, __recovery_setup_file));
/* Scan through all files and tiered entries in the metadata. */
WT_RET(__recovery_file_scan_prefix(r, "file:", ".wtobj"));
WT_RET(__recovery_file_scan_prefix(r, "tiered:", NULL));
/*
* Set the connection level file id tracker, as such upon creation of a new file we'll begin
@ -1142,8 +1079,7 @@ __wt_txn_recover(WT_SESSION_IMPL *session, const char *cfg[], bool disagg)
r.backup_only = false;
WT_ERR(ret);
/* Scan the metadata to find the live files and their IDs, and clean up any incomplete tables.
*/
/* Scan the metadata to find the live files and their IDs. */
WT_ERR(__recovery_file_scan(&r));
/*

View File

@ -92,8 +92,7 @@ TEST_CASE("Block manager: addr invalid", "[block_api_misc]")
// Build Mock session, this will automatically create a mock connection.
std::shared_ptr<mock_session> session = mock_session::build_test_mock_session();
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
// A session and block manager needs to be initialized otherwise the addr_invalid functionality
// will crash if it attempts to check various session flags.
auto path = std::filesystem::current_path();
@ -152,8 +151,7 @@ TEST_CASE("Block manager: addr string", "[block_api_misc]")
// Build Mock session, this will automatically create a mock connection.
std::shared_ptr<mock_session> session = mock_session::build_test_mock_session();
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
auto path = std::filesystem::current_path();
std::string file_path(path.string() + "/test.wt");
setup_bm(session, &bm, file_path, ALLOCATION_SIZE, BLOCK_ALLOCATION, OS_CACHE_MAX,
@ -184,8 +182,7 @@ TEST_CASE("Block manager: addr string", "[block_api_misc]")
TEST_CASE("Block manager: block header", "[block_api_misc]")
{
// Declare a block manager and set it up so that we can use its legal API methods.
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
__wti_bm_method_set(&bm, false);
SECTION("Test block header size is correct")
@ -197,8 +194,7 @@ TEST_CASE("Block manager: block header", "[block_api_misc]")
TEST_CASE("Block manager: is mapped", "[block_api_misc]")
{
// Declare a block manager and set it up so that we can use its legal API methods.
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
__wti_bm_method_set(&bm, false);
SECTION("Test block manager is mapped")
@ -220,8 +216,7 @@ TEST_CASE("Block manager: size and stat", "[block_api_misc]")
// Build Mock session, this will automatically create a mock connection.
std::shared_ptr<mock_session> session = mock_session::build_test_mock_session();
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
auto path = std::filesystem::current_path();
std::string file_path(path.string() + "/test.wt");
setup_bm(session, &bm, file_path, ALLOCATION_SIZE, BLOCK_ALLOCATION, OS_CACHE_MAX,

View File

@ -38,7 +38,7 @@ struct addr_cookie {
* performing a bm->read and a file read and making sure that the read() matches the original write
* buffer.
*/
void
static void
validate_block_contents(WT_BM *bm, const std::shared_ptr<mock_session> &session, WT_ITEM *write_buf,
addr_cookie cookie, wt_off_t offset, uint32_t size)
{
@ -85,7 +85,7 @@ validate_block_contents(WT_BM *bm, const std::shared_ptr<mock_session> &session,
/*
* Validate that the bm->write() performed correctly.
*/
void
static void
validate_write_block(WT_BM *bm, const std::shared_ptr<mock_session> &session, WT_ITEM *write_buf,
addr_cookie cookie, const std::string &expected_str, bool data_checksum)
{
@ -119,7 +119,7 @@ validate_write_block(WT_BM *bm, const std::shared_ptr<mock_session> &session, WT
}
// Test that all previous write performed are still present in the block and file.
void
static void
test_validate_cookies(WT_BM *bm, const std::shared_ptr<mock_session> &session,
const std::vector<addr_cookie> &cookies, const std::vector<std::string> &expected_strings)
{
@ -147,8 +147,7 @@ TEST_CASE("Block manager: file operation read, write and write_size functions",
// Build Mock session, this will automatically create a mock connection.
std::shared_ptr<mock_session> session = mock_session::build_test_mock_session();
WT_BM bm;
WT_CLEAR(bm);
WT_BM bm = {};
size_t allocation_size = std::stoi(ALLOCATION_SIZE);
auto path = std::filesystem::current_path();
std::string file_path(path.string() + "/test.wt");

View File

@ -17,7 +17,7 @@
#include "wt_internal.h"
void
static void
unpack_addr_cookie_and_check(const uint8_t *packed, uint32_t block_allocsize, wt_off_t pack_offset,
uint32_t pack_size, uint32_t pack_checksum)
{

View File

@ -63,7 +63,7 @@ struct SizeListWrapper {
std::vector<WT_SIZE *> _raw_list;
};
std::unique_ptr<ExtentWrapper>
static std::unique_ptr<ExtentWrapper>
create_new_ext()
{
/*
@ -77,7 +77,7 @@ create_new_ext()
return std::make_unique<ExtentWrapper>(raw);
}
std::unique_ptr<SizeWrapper>
static std::unique_ptr<SizeWrapper>
create_new_sz()
{
auto raw = (WT_SIZE *)malloc(sizeof(WT_SIZE));
@ -95,7 +95,7 @@ create_new_sz()
* ...
* L9: X
*/
void
static void
create_default_test_extent_list(ExtentListWrapper &wrapper)
{
auto &head = wrapper._list;
@ -118,7 +118,7 @@ create_default_test_extent_list(ExtentListWrapper &wrapper)
}
// As above, but for a size list.
void
static void
create_default_test_size_list(SizeListWrapper &wrapper)
{
auto &head = wrapper._list;

View File

@ -133,7 +133,7 @@ setup_bm(std::shared_ptr<mock_session> &session, WT_BM *bm, const std::string &f
* block manager instead
* .
*/
WT_CLEAR(*bm);
memset(reinterpret_cast<void *>(bm), 0, sizeof(WT_BM));
__wti_bm_method_set(bm, false);
// Create the underlying file in the filesystem.

View File

@ -13,7 +13,7 @@
#include "../../utils.h"
#include "../../wrappers/connection_wrapper.h"
bool
static bool
validate_cursor_bounds_restore(WT_CURSOR *cursor, uint64_t original_cursor_flags)
{
return cursor->flags == original_cursor_flags;

View File

@ -15,7 +15,7 @@
using namespace utils;
// Wrapper for the calling the C implementation of fs_exists.
bool
static bool
file_exists(live_restore_test_env &env, const std::string &file_name)
{
WT_SESSION *session = (WT_SESSION *)env.session;
@ -33,7 +33,7 @@ file_exists(live_restore_test_env &env, const std::string &file_name)
* source directories, might have a stop file, and live restore might be in the process of
* migrating. Then return the result of an fs_exist call.
*/
bool
static bool
test_file_exists(live_restore_test_env *env, HasDest has_dest, HasSource has_source,
IsMigrating is_migrating, HasStop stop_file_exists)
{

View File

@ -30,7 +30,7 @@ open_file(live_restore_test_env &env, std::string file_name, WT_FS_OPEN_FILE_TYP
return lr_fh;
}
void
static void
validate_lr_fh(WTI_LIVE_RESTORE_FILE_HANDLE *lr_fh, live_restore_test_env &env,
std::string &file_name, bool is_directory = false)
{

View File

@ -15,7 +15,7 @@
using namespace utils;
bool
static bool
stop_file_exists(std::string file_name)
{
return (testutil_exists(nullptr, (file_name + WTI_LIVE_RESTORE_STOP_FILE_SUFFIX).c_str()));

View File

@ -68,8 +68,7 @@ TEST_CASE("Test various bitmap filling bit ranges",
std::shared_ptr<mock_session> mock_session = mock_session::build_test_mock_session();
WT_SESSION_IMPL *session = mock_session->get_wt_session_impl();
WTI_LIVE_RESTORE_FILE_HANDLE lr_fh;
WT_CLEAR(lr_fh);
WTI_LIVE_RESTORE_FILE_HANDLE lr_fh = {};
// We need to have a non NULL pointer here for the encoding to take place.
lr_fh.source = reinterpret_cast<WT_FILE_HANDLE *>(0xab);

View File

@ -36,7 +36,7 @@ uint16_t DIAGNOSTIC_FLAGS[] = {WT_DIAGNOSTIC_ALL, WT_DIAGNOSTIC_CHECKPOINT_VALID
WT_DIAGNOSTIC_HS_VALIDATE, WT_DIAGNOSTIC_KEY_OUT_OF_ORDER, WT_DIAGNOSTIC_LOG_VALIDATE,
WT_DIAGNOSTIC_PREPARED, WT_DIAGNOSTIC_SLOW_OPERATION, WT_DIAGNOSTIC_TXN_VISIBILITY};
int
static int
check_assertion_fired(WT_SESSION_IMPL *session)
{
WT_DECL_RET;
@ -56,7 +56,7 @@ check_assertion_fired(WT_SESSION_IMPL *session)
* Wrapper to call WT_RET_ASSERT. This returns different values depending on whether WT_RET_ASSERT
* fires or not.
*/
int
static int
call_wt_ret(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pass)
{
WT_RET_ASSERT(session, category, assert_should_pass, ASSERT_RET, "WT_RET raised assert");
@ -68,7 +68,7 @@ call_wt_ret(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pass
* Wrapper to call WT_ERR_ASSERT. This returns different values depending on whether WT_ERR_ASSERT
* fires or not.
*/
int
static int
call_wt_err(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pass)
{
WT_DECL_RET;
@ -88,7 +88,7 @@ err:
* Wrapper to call WT_RET_PANIC_ASSERT. This returns different values depending on whether
* WT_RET_PANIC_ASSERT fires or not.
*/
int
static int
call_wt_panic(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pass)
{
WT_RET_PANIC_ASSERT(
@ -101,7 +101,7 @@ call_wt_panic(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pa
* Wrapper to call WT_ASSERT_OPTIONAL. This returns different values depending on whether
* WT_ASSERT_OPTIONAL fires or not.
*/
int
static int
call_wt_optional(WT_SESSION_IMPL *session, uint16_t category, bool assert_should_pass)
{
WT_ASSERT_OPTIONAL(session, category, assert_should_pass, "WT_OPTIONAL raised assert");
@ -110,7 +110,7 @@ call_wt_optional(WT_SESSION_IMPL *session, uint16_t category, bool assert_should
}
/* Assert that all diagnostic assert categories are off. */
void
static void
all_diag_asserts_off(WT_SESSION_IMPL *session)
{
for (uint16_t flag : DIAGNOSTIC_FLAGS) {
@ -119,7 +119,7 @@ all_diag_asserts_off(WT_SESSION_IMPL *session)
}
/* Assert that all diagnostic assert categories are on. */
void
static void
all_diag_asserts_on(WT_SESSION_IMPL *session)
{
for (uint16_t flag : DIAGNOSTIC_FLAGS) {
@ -128,7 +128,7 @@ all_diag_asserts_on(WT_SESSION_IMPL *session)
}
/* Assert that all expected asserts (passed in via the "category" arg) fire. */
int
static int
configured_asserts_abort(WT_SESSION_IMPL *session, uint16_t category)
{
int ret = 0;
@ -145,7 +145,7 @@ configured_asserts_abort(WT_SESSION_IMPL *session, uint16_t category)
}
/* Assert that the expected asserts don't fire (those not passed in via the "category" arg). */
int
static int
configured_asserts_off(WT_SESSION_IMPL *session, u_int16_t category)
{
int ret = 0;

View File

@ -11,7 +11,7 @@
#include "wiredtiger.h"
#include "../wrappers/mock_session.h"
int
static int
mock_plh_open_handle(
WT_PAGE_LOG *page_log, WT_SESSION *session, uint64_t table_id, WT_PAGE_LOG_HANDLE **plh)
{
@ -24,14 +24,14 @@ mock_plh_open_handle(
return (0);
}
int
static int
mock_terminate(WT_PAGE_LOG *page_log, WT_SESSION *session)
{
__wt_free((WT_SESSION_IMPL *)session, page_log);
return (0);
}
int
static int
mock_plh_close(WT_PAGE_LOG_HANDLE *plh, WT_SESSION *session)
{
WT_UNUSED(plh);
@ -39,7 +39,7 @@ mock_plh_close(WT_PAGE_LOG_HANDLE *plh, WT_SESSION *session)
return (0);
}
void
static void
setup_page_log_queue(WT_SESSION_IMPL *session)
{
WT_CONNECTION_IMPL *conn_impl = S2C(session);

View File

@ -18,7 +18,7 @@
using namespace utils;
int
static int
api_call_with_error(
WT_SESSION_IMPL *session_impl, int err, int sub_level_err, const char *err_msg_content)
{
@ -32,13 +32,13 @@ err:
API_END_RET(session_impl, ret);
}
int
static int
api_call_with_no_error(WT_SESSION_IMPL *session_impl)
{
return (api_call_with_error(session_impl, 0, WT_NONE, NULL));
}
int
static int
txn_api_call_with_error(
WT_SESSION_IMPL *session_impl, int err, int sub_level_err, const char *err_msg_content)
{
@ -54,7 +54,7 @@ err:
return (ret);
}
int
static int
txn_api_call_with_no_error(WT_SESSION_IMPL *session_impl)
{
return (txn_api_call_with_error(session_impl, 0, WT_NONE, NULL));

View File

@ -16,14 +16,14 @@
* Tests the macros for storing verbose information about the last error of the session.
*/
int
static int
test_wt_ret_sub(
WT_SESSION_IMPL *session_impl, int err, int sub_level_err, const char *err_msg_content)
{
WT_RET_SUB(session_impl, err, sub_level_err, "%s", err_msg_content);
}
int
static int
test_wt_err_sub(
WT_SESSION_IMPL *session_impl, int err, int sub_level_err, const char *err_msg_content)
{
@ -33,13 +33,13 @@ err:
return (ret);
}
int
static int
test_wt_ret_msg(WT_SESSION_IMPL *session_impl, int err, const char *err_msg_content)
{
WT_RET_MSG(session_impl, err, "%s", err_msg_content);
}
int
static int
test_wt_err_msg(WT_SESSION_IMPL *session_impl, int err, const char *err_msg_content)
{
WT_DECL_RET;

View File

@ -49,7 +49,7 @@ mock_connection::setup_chunk_cache(
WT_SESSION_IMPL *session, uint64_t capacity, size_t chunk_size, WT_CHUNKCACHE *&chunkcache)
{
chunkcache = &_connection_impl->chunkcache;
memset(chunkcache, 0, sizeof(WT_CHUNKCACHE));
memset(reinterpret_cast<void *>(chunkcache), 0, sizeof(WT_CHUNKCACHE));
chunkcache->capacity = capacity;
chunkcache->chunk_size = chunk_size;
WT_RET(

View File

@ -0,0 +1,87 @@
/*-
* Public Domain 2014-present MongoDB, Inc.
* 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.
*/
#pragma once
#include "src/storage/scoped_cursor.h"
#include "transaction.h"
extern "C" {
#include "wiredtiger.h"
}
namespace test_harness {
namespace crud {
inline bool
insert(scoped_cursor &cursor, transaction &txn, const std::string &key, const std::string &value)
{
cursor->set_key(cursor.get(), key.c_str());
cursor->set_value(cursor.get(), value.c_str());
int ret = cursor->insert(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback();
return (false);
} else
testutil_die(ret, "unhandled error while trying to insert a key");
}
return (true);
}
inline bool
update(scoped_cursor &cursor, transaction &txn, const std::string &key, const std::string &value)
{
cursor->set_key(cursor.get(), key.c_str());
cursor->set_value(cursor.get(), value.c_str());
int ret = cursor->update(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback();
return (false);
} else
testutil_die(ret, "unhandled error while trying to update a key");
}
return (true);
}
inline bool
remove(scoped_cursor &cursor, transaction &txn, const std::string &key)
{
cursor->set_key(cursor.get(), key.c_str());
int ret = cursor->remove(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback();
return (false);
} else
testutil_die(ret, "unhandled error while trying to remove a key");
}
return (true);
}
}; // namespace crud
} // namespace test_harness

View File

@ -41,11 +41,9 @@ database::build_collection_name(const uint64_t id)
}
void
database::add_collection(uint64_t key_count)
database::add_collection(scoped_session &session, uint64_t key_count)
{
std::lock_guard<std::mutex> lg(_mtx);
if (_session.get() == nullptr)
_session = connection_manager::instance().create_session();
if (_collection_create_config.empty())
testutil_die(EINVAL, "database: no collection create config specified!");
uint64_t next_id = _next_collection_id++;
@ -54,7 +52,7 @@ database::add_collection(uint64_t key_count)
_collections.emplace(std::piecewise_construct, std::forward_as_tuple(next_id),
std::forward_as_tuple(next_id, key_count, collection_name));
testutil_check(
_session->create(_session.get(), collection_name.c_str(), _collection_create_config.c_str()));
session->create(session.get(), collection_name.c_str(), _collection_create_config.c_str()));
_operation_tracker->save_schema_operation(
tracking_operation::CREATE_COLLECTION, next_id, _tsm->get_next_ts());
}

View File

@ -48,7 +48,7 @@ public:
/*
* Add a new collection, this will create the underlying collection in the database.
*/
void add_collection(uint64_t key_count = 0);
void add_collection(scoped_session &session, uint64_t key_count = 0);
/* Get a collection using the id of the collection. */
collection &get_collection(uint64_t id);
@ -72,7 +72,6 @@ public:
private:
std::string _collection_create_config = "";
scoped_session _session;
timestamp_manager *_tsm = nullptr;
operation_tracker *_operation_tracker = nullptr;
uint64_t _next_collection_id = 0;

View File

@ -57,15 +57,15 @@ populate_worker(thread_worker *tc)
scoped_cursor cursor = tc->session.open_scoped_cursor(coll.name);
uint64_t j = 0;
while (j < tc->key_count) {
tc->txn.begin();
tc->begin();
auto key = tc->pad_string(std::to_string(j), tc->key_size);
auto value = random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (tc->insert(cursor, coll.id, key, value)) {
if (tc->txn.commit()) {
if (tc->commit()) {
++j;
}
} else {
tc->txn.rollback();
tc->rollback();
}
}
}
@ -96,12 +96,13 @@ database_operation::populate(
LOG_INFO, "Populate: creating " + std::to_string(collection_count) + " collections.");
/* Create n collections as per the configuration. */
scoped_session session = connection_manager::instance().create_session();
for (int64_t i = 0; i < collection_count; ++i)
/*
* The database model will call into the API and create the collection, with its own
* session.
*/
database.add_collection(key_count);
database.add_collection(session, key_count);
logger::log_msg(
LOG_INFO, "Populate: " + std::to_string(collection_count) + " collections created.");
@ -204,21 +205,21 @@ database_operation::insert_operation(thread_worker *tc)
while (tc->running()) {
uint64_t start_key = ccv[counter].coll.get_key_count();
uint64_t added_count = 0;
tc->txn.begin();
tc->begin();
/* Collection cursor. */
auto &cc = ccv[counter];
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
/* Insert a key value pair, rolling back the transaction if required. */
auto key = tc->pad_string(std::to_string(start_key + added_count), tc->key_size);
auto value = random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (!tc->insert(cc.cursor, cc.coll.id, key, value)) {
added_count = 0;
tc->txn.rollback();
tc->rollback();
} else {
added_count++;
if (tc->txn.can_commit()) {
if (tc->txn.commit()) {
if (tc->can_commit()) {
if (tc->commit()) {
/*
* We need to inform the database model that we've added these keys as some
* other thread may rely on the key_count data. Only do so if we
@ -242,7 +243,7 @@ database_operation::insert_operation(thread_worker *tc)
testutil_assert(counter < tc_collection_count);
}
/* Make sure the last transaction is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
void
@ -262,29 +263,29 @@ database_operation::read_operation(thread_worker *tc)
/* Do a second lookup now that we know it exists. */
auto &cursor = cursors[coll.id];
tc->txn.begin();
while (tc->txn.active() && tc->running()) {
tc->begin();
while (tc->active() && tc->running()) {
auto ret = cursor->next(cursor.get());
if (ret != 0) {
if (ret == WT_NOTFOUND) {
testutil_check(cursor->reset(cursor.get()));
} else if (ret == WT_ROLLBACK) {
tc->txn.rollback();
tc->rollback();
tc->sleep();
continue;
} else
testutil_die(ret, "Unexpected error returned from cursor->next()");
}
tc->txn.add_op();
if (tc->txn.get_op_count() >= tc->txn.get_target_op_count())
tc->txn.rollback();
tc->add_op();
if (tc->get_op_count() >= tc->get_target_op_count())
tc->rollback();
tc->sleep();
}
/* Reset our cursor to avoid pinning content. */
testutil_check(cursor->reset(cursor.get()));
}
/* Make sure the last transaction is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
void
@ -325,7 +326,7 @@ database_operation::remove_operation(thread_worker *tc)
}
/* Start a transaction if possible. */
tc->txn.try_begin();
tc->try_begin();
/* Get the cursor associated with the collection. */
scoped_cursor &rnd_cursor = rnd_cursors[coll.id];
@ -341,9 +342,9 @@ database_operation::remove_operation(thread_worker *tc)
* one.
*/
if (ret == WT_NOTFOUND) {
testutil_ignore_ret_bool(tc->txn.commit());
testutil_ignore_ret_bool(tc->commit());
} else if (ret == WT_ROLLBACK) {
tc->txn.rollback();
tc->rollback();
} else {
testutil_die(ret, "Unexpected error returned from cursor->next()");
}
@ -354,7 +355,7 @@ database_operation::remove_operation(thread_worker *tc)
const char *key_str;
testutil_check(rnd_cursor->get_key(rnd_cursor.get(), &key_str));
if (!tc->remove(cursor, coll.id, key_str)) {
tc->txn.rollback();
tc->rollback();
}
/* Reset our cursors to avoid pinning content. */
@ -362,12 +363,12 @@ database_operation::remove_operation(thread_worker *tc)
testutil_check(rnd_cursor->reset(rnd_cursor.get()));
/* Commit the current transaction if we're able to. */
if (tc->txn.can_commit())
testutil_ignore_ret_bool(tc->txn.commit());
if (tc->can_commit())
testutil_ignore_ret_bool(tc->commit());
}
/* Make sure the last operation is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
void
@ -402,7 +403,7 @@ database_operation::update_operation(thread_worker *tc)
}
/* Start a transaction if possible. */
tc->txn.try_begin();
tc->try_begin();
/* Get the cursor associated with the collection. */
scoped_cursor &cursor = cursors[coll.id];
@ -414,19 +415,19 @@ database_operation::update_operation(thread_worker *tc)
auto key = tc->pad_string(std::to_string(key_id), tc->key_size);
auto value = random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (!tc->update(cursor, coll.id, key, value)) {
tc->txn.rollback();
tc->rollback();
}
/* Reset our cursor to avoid pinning content. */
testutil_check(cursor->reset(cursor.get()));
/* Commit the current transaction if we're able to. */
if (tc->txn.can_commit())
testutil_ignore_ret_bool(tc->txn.commit());
if (tc->can_commit())
testutil_ignore_ret_bool(tc->commit());
}
/* Make sure the last operation is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
void

View File

@ -33,6 +33,7 @@
#include "src/common/constants.h"
#include "src/common/logger.h"
#include "src/common/random_generator.h"
#include "crud.h"
#include "transaction.h"
namespace test_harness {
@ -77,13 +78,19 @@ thread_worker::thread_worker(uint64_t id, thread_type type, configuration *confi
key_size(config->get_optional_int(KEY_SIZE, 1)),
value_size(config->get_optional_int(VALUE_SIZE, 1)),
thread_count(config->get_int(THREAD_COUNT)), type(type), id(id), db(dbase),
session(std::move(created_session)), tsm(timestamp_manager),
txn(transaction(config, timestamp_manager, session.get())), op_tracker(op_tracker),
session(std::move(created_session)), tsm(timestamp_manager), op_tracker(op_tracker),
_sleep_time_ms(std::chrono::milliseconds(config->get_throttle_ms())), _barrier(barrier_ptr)
{
if (op_tracker->enabled())
op_track_cursor = session.open_scoped_cursor(op_tracker->get_operation_table_name());
/* Use optional here as our populate threads don't define this configuration. */
configuration *ops_config = config->get_optional_subconfig(OPS_PER_TRANSACTION);
if (ops_config != nullptr) {
_min_op_count = ops_config->get_optional_int(MIN, 1);
_max_op_count = ops_config->get_optional_int(MAX, 1);
delete ops_config;
}
testutil_assert(key_size > 0 && value_size > 0);
}
@ -111,32 +118,23 @@ thread_worker::update(
testutil_assert(cursor.get() != nullptr);
wt_timestamp_t ts = tsm->get_next_ts();
ret = txn.set_commit_timestamp(ts);
ret = set_commit_timestamp(ts);
testutil_assert(ret == 0 || ret == EINVAL);
if (ret != 0) {
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
return (false);
}
cursor->set_key(cursor.get(), key.c_str());
cursor->set_value(cursor.get(), value.c_str());
ret = cursor->update(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback(true);
return (false);
} else
testutil_die(ret, "unhandled error while trying to update a key");
}
if (crud::update(cursor, _txn, key, value) == false)
return (false);
ret = op_tracker->save_operation(
session.get(), tracking_operation::INSERT, collection_id, key, value, ts, op_track_cursor);
if (ret == 0)
txn.add_op();
add_op();
else if (ret == WT_ROLLBACK)
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
else
testutil_die(ret, "unhandled error while trying to save an update to the tracking table");
return (ret == 0);
@ -152,32 +150,23 @@ thread_worker::insert(
testutil_assert(cursor.get() != nullptr);
wt_timestamp_t ts = tsm->get_next_ts();
ret = txn.set_commit_timestamp(ts);
ret = set_commit_timestamp(ts);
testutil_assert(ret == 0 || ret == EINVAL);
if (ret != 0) {
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
return (false);
}
cursor->set_key(cursor.get(), key.c_str());
cursor->set_value(cursor.get(), value.c_str());
ret = cursor->insert(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback(true);
return (false);
} else
testutil_die(ret, "unhandled error while trying to insert a key");
}
if (crud::insert(cursor, _txn, key, value) == false)
return (false);
ret = op_tracker->save_operation(
session.get(), tracking_operation::INSERT, collection_id, key, value, ts, op_track_cursor);
if (ret == 0)
txn.add_op();
add_op();
else if (ret == WT_ROLLBACK)
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
else
testutil_die(ret, "unhandled error while trying to save an insert to the tracking table");
return (ret == 0);
@ -191,30 +180,23 @@ thread_worker::remove(scoped_cursor &cursor, uint64_t collection_id, const std::
testutil_assert(cursor.get() != nullptr);
wt_timestamp_t ts = tsm->get_next_ts();
ret = txn.set_commit_timestamp(ts);
ret = set_commit_timestamp(ts);
testutil_assert(ret == 0 || ret == EINVAL);
if (ret != 0) {
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
return (false);
}
cursor->set_key(cursor.get(), key.c_str());
ret = cursor->remove(cursor.get());
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback(true);
return (false);
} else
testutil_die(ret, "unhandled error while trying to remove a key");
}
if (crud::remove(cursor, _txn, key) == false)
return (false);
ret = op_tracker->save_operation(
session.get(), tracking_operation::DELETE_KEY, collection_id, key, "", ts, op_track_cursor);
if (ret == 0)
txn.add_op();
add_op();
else if (ret == WT_ROLLBACK)
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
else
testutil_die(ret, "unhandled error while trying to save a remove to the tracking table");
return (ret == 0);
@ -233,10 +215,10 @@ thread_worker::truncate(uint64_t collection_id, std::optional<std::string> start
int ret = 0;
wt_timestamp_t ts = tsm->get_next_ts();
ret = txn.set_commit_timestamp(ts);
ret = set_commit_timestamp(ts);
testutil_assert(ret == 0 || ret == EINVAL);
if (ret != 0) {
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
return (false);
}
@ -256,7 +238,7 @@ thread_worker::truncate(uint64_t collection_id, std::optional<std::string> start
if (ret != 0) {
if (ret == WT_ROLLBACK) {
txn.set_needs_rollback(true);
_txn.set_needs_rollback();
return (false);
} else
testutil_die(ret, "unhandled error while trying to truncate a key range");
@ -316,4 +298,98 @@ thread_worker::get_assigned_collection_count() const
uint64_t collection_count = db.get_collection_count();
return collection_count / thread_count + (collection_count % thread_count > id);
}
/*
* Returns true if a transaction can be committed as determined by the op count and the state of the
* transaction.
*/
bool
thread_worker::can_commit()
{
return (!_txn.needs_rollback() && _txn.active() && get_op_count() >= get_target_op_count());
};
/* Get the current number of operations executed. */
int64_t
thread_worker::get_op_count() const
{
return _op_count;
}
/* Get the number of operations this transaction needs before it can commit */
int64_t
thread_worker::get_target_op_count() const
{
return _target_op_count;
}
bool
thread_worker::active() const
{
return _txn.active();
}
void
thread_worker::add_op()
{
_op_count++;
}
void
thread_worker::begin(const std::string &config)
{
/* This randomizes the number of operations to be executed in one transaction. */
_target_op_count = WT_MAX(
1, random_generator::instance().generate_integer<int64_t>(_min_op_count, _max_op_count));
_op_count = 0;
_txn.begin(session, config);
}
/* Begin a transaction if we are not currently in one. */
void
thread_worker::try_begin(const std::string &config)
{
if (!active())
begin(config);
}
/*
* Commit a transaction and return true if the commit was successful.
*/
bool
thread_worker::commit(const std::string &config)
{
_op_count = 0;
return _txn.commit(session, config);
}
/* Rollback a transaction, failure will abort the test. */
void
thread_worker::rollback(const std::string &config)
{
_op_count = 0;
_txn.rollback(session, config);
}
/* Attempt to rollback the transaction given the requirements are met. */
void
thread_worker::try_rollback(const std::string &config)
{
if (active())
rollback(config);
}
/*
* FIXME: WT-9198 We're concurrently doing a transaction that contains a bunch of operations while
* moving the stable timestamp. Eat the occasional EINVAL from the transaction's first commit
* timestamp being earlier than the stable timestamp.
*/
int
thread_worker::set_commit_timestamp(wt_timestamp_t ts)
{
if (!tsm->enabled())
return (0);
const std::string config = COMMIT_TS + "=" + timestamp_manager::decimal_to_hex(ts);
return session->timestamp_transaction(session.get(), config.c_str());
}
} // namespace test_harness

View File

@ -108,6 +108,33 @@ public:
/* Get the number of collections assigned to the thread worker */
uint64_t get_assigned_collection_count() const;
/* Add an operation to the current work item. */
void add_op();
/* Get the current number of operations executed. */
int64_t get_op_count() const;
/* Get the number of operations this work item needs before it can commit */
int64_t get_target_op_count() const;
/*
* Returns true if our transaction can be committed as determined by the op count and the state
* of the transaction.
*/
bool can_commit();
/* Returns whether there is an active transaction. */
bool active() const;
/* Begins a transaction. */
void begin(const std::string &config = "");
/* Begin a transaction if we are not currently in one. */
void try_begin(const std::string &config = "");
/* Commit a transaction and return true if the commit was successful. */
bool commit(const std::string &config = "");
/* Rollback a transaction, failure will abort the test. */
void rollback(const std::string &config = "");
/* Attempt to rollback the transaction given the requirements are met. */
void try_rollback(const std::string &config = "");
/* Set a commit timestamp. */
int set_commit_timestamp(wt_timestamp_t ts);
public:
const int64_t collection_count;
const int64_t free_space_target_mb;
@ -122,12 +149,25 @@ public:
scoped_cursor op_track_cursor;
scoped_cursor stat_cursor;
timestamp_manager *tsm;
transaction txn;
operation_tracker *op_tracker;
private:
std::shared_ptr<barrier> _barrier = nullptr;
bool _running = true;
std::chrono::milliseconds _sleep_time_ms;
transaction _txn;
/*
* _min_op_count and _max_op_count are the minimum and maximum number of operations within one
* transaction.
*/
int64_t _max_op_count = 1;
int64_t _min_op_count = 0;
/*
* op_count is the current number of operations that have been executed in the current
* transaction.
*/
int64_t _op_count = 0;
int64_t _target_op_count = 0;
};
} // namespace test_harness

View File

@ -30,23 +30,12 @@
#include "src/common/constants.h"
#include "src/common/logger.h"
#include "src/common/random_generator.h"
namespace test_harness {
transaction::transaction(
configuration *config, timestamp_manager *timestamp_manager, WT_SESSION *session)
: _timestamp_manager(timestamp_manager), _session(session)
{
/* Use optional here as our populate threads don't define this configuration. */
configuration *transaction_config = config->get_optional_subconfig(OPS_PER_TRANSACTION);
if (transaction_config != nullptr) {
_min_op_count = transaction_config->get_optional_int(MIN, 1);
_max_op_count = transaction_config->get_optional_int(MAX, 1);
delete transaction_config;
}
#include "src/storage/scoped_session.h"
extern "C" {
#include "test_util.h"
}
namespace test_harness {
bool
transaction::active() const
{
@ -54,43 +43,26 @@ transaction::active() const
}
void
transaction::add_op()
{
_op_count++;
}
void
transaction::begin(const std::string &config)
transaction::begin(scoped_session &session, const std::string &config)
{
testutil_assert(!_in_txn);
testutil_check(
_session->begin_transaction(_session, config.empty() ? nullptr : config.c_str()));
/* This randomizes the number of operations to be executed in one transaction. */
_target_op_count =
random_generator::instance().generate_integer<int64_t>(_min_op_count, _max_op_count);
_op_count = 0;
session->begin_transaction(session.get(), config.empty() ? nullptr : config.c_str()));
_in_txn = true;
_needs_rollback = false;
}
void
transaction::try_begin(const std::string &config)
{
if (!_in_txn)
begin(config);
}
/*
* It's possible to receive rollback in commit, when this happens the API will rollback the
* transaction internally.
*/
bool
transaction::commit(const std::string &config)
transaction::commit(scoped_session &session, const std::string &config)
{
int ret = 0;
testutil_assert(_in_txn && !_needs_rollback);
ret = _session->commit_transaction(_session, config.empty() ? nullptr : config.c_str());
ret = session->commit_transaction(session.get(), config.empty() ? nullptr : config.c_str());
/*
* FIXME-WT-9198 Now we are accepting the error code EINVAL because of possible invalid
* timestamps as we know it can happen due to the nature of the framework. The framework may set
@ -103,65 +75,29 @@ transaction::commit(const std::string &config)
if (ret != 0)
logger::log_msg(LOG_WARN,
"Failed to commit transaction in commit, received error code: " + std::to_string(ret));
_op_count = 0;
_in_txn = false;
return (ret == 0);
}
void
transaction::rollback(const std::string &config)
transaction::rollback(scoped_session &session, const std::string &config)
{
testutil_assert(_in_txn);
testutil_check(
_session->rollback_transaction(_session, config.empty() ? nullptr : config.c_str()));
session->rollback_transaction(session.get(), config.empty() ? nullptr : config.c_str()));
_needs_rollback = false;
_op_count = 0;
_in_txn = false;
}
void
transaction::try_rollback(const std::string &config)
transaction::set_needs_rollback()
{
if (_in_txn)
rollback(config);
}
int64_t
transaction::get_op_count() const
{
return _op_count;
}
int64_t
transaction::get_target_op_count() const
{
return _target_op_count;
}
/*
* FIXME: WT-9198 We're concurrently doing a transaction that contains a bunch of operations while
* moving the stable timestamp. Eat the occasional EINVAL from the transaction's first commit
* timestamp being earlier than the stable timestamp.
*/
int
transaction::set_commit_timestamp(wt_timestamp_t ts)
{
/* We don't want to set zero timestamps on transactions if we're not using timestamps. */
if (!_timestamp_manager->enabled())
return 0;
const std::string config = COMMIT_TS + "=" + timestamp_manager::decimal_to_hex(ts);
return _session->timestamp_transaction(_session, config.c_str());
}
void
transaction::set_needs_rollback(bool rollback)
{
_needs_rollback = rollback;
_needs_rollback = true;
}
bool
transaction::can_commit()
transaction::needs_rollback()
{
return (!_needs_rollback && _in_txn && _op_count >= _target_op_count);
return _needs_rollback;
}
} // namespace test_harness

View File

@ -30,9 +30,7 @@
#include <string>
#include "src/main/configuration.h"
#include "src/component/timestamp_manager.h"
#include "src/storage/scoped_session.h"
extern "C" {
#include "wiredtiger.h"
}
@ -41,55 +39,22 @@ namespace test_harness {
class transaction {
public:
transaction(configuration *config, timestamp_manager *timestamp_manager, WT_SESSION *session);
bool active() const;
void add_op();
void begin(const std::string &config = "");
/* Begin a transaction if we are not currently in one. */
void try_begin(const std::string &config = "");
void begin(scoped_session &session, const std::string &config = "");
/*
* Commit a transaction and return true if the commit was successful.
*/
bool commit(const std::string &config = "");
bool commit(scoped_session &session, const std::string &config = "");
/* Rollback a transaction, failure will abort the test. */
void rollback(const std::string &config = "");
/* Attempt to rollback the transaction given the requirements are met. */
void try_rollback(const std::string &config = "");
/* Set a commit timestamp. */
int set_commit_timestamp(wt_timestamp_t ts);
void rollback(scoped_session &session, const std::string &config = "");
/* Set that the transaction needs to be rolled back. */
void set_needs_rollback(bool rollback);
/*
* Returns true if a transaction can be committed as determined by the op count and the state of
* the transaction.
*/
bool can_commit();
/* Get the current number of operations executed. */
int64_t get_op_count() const;
/* Get the number of operations this transaction needs before it can commit */
int64_t get_target_op_count() const;
void set_needs_rollback();
/* Return whether the transaction needs to be rolled back.*/
bool needs_rollback();
private:
bool _in_txn = false;
bool _needs_rollback = false;
/*
* _min_op_count and _max_op_count are the minimum and maximum number of operations within one
* transaction. is the current maximum number of operations that can be executed in the current
* transaction.
*/
int64_t _max_op_count = INT64_MAX;
int64_t _min_op_count = 0;
/*
* op_count is the current number of operations that have been executed in the current
* transaction.
*/
int64_t _op_count = 0;
int64_t _target_op_count = 0;
timestamp_manager *_timestamp_manager = nullptr;
WT_SESSION *_session = nullptr;
};
} // namespace test_harness

View File

@ -138,7 +138,7 @@ public:
const uint64_t MAX_RETRIES = 100;
while (tw->running() && keys_truncated < n_keys_to_truncate && retries < MAX_RETRIES) {
/* Start a transaction if possible. */
tw->txn.try_begin();
tw->try_begin();
/* Choose a random key to delete. */
int ret = rnd_cursor->next(rnd_cursor.get());
@ -150,9 +150,9 @@ public:
* starting a new one.
*/
if (ret == WT_NOTFOUND)
testutil_ignore_ret_bool(tw->txn.commit());
testutil_ignore_ret_bool(tw->commit());
else if (ret == WT_ROLLBACK)
tw->txn.rollback();
tw->rollback();
else
testutil_die(ret, "Unexpected error returned from cursor->next()");
@ -173,7 +173,7 @@ public:
* If we generate an invalid range or our truncate fails rollback the transaction.
*/
if (end_key == first_key || !tw->truncate(coll.id, first_key, end_key, "")) {
tw->txn.rollback();
tw->rollback();
if (end_key == first_key)
logger::log_msg(
LOG_TRACE, log_prefix + "truncate failed because of an invalid range");
@ -183,7 +183,7 @@ public:
continue;
}
if (tw->txn.commit()) {
if (tw->commit()) {
logger::log_msg(LOG_TRACE,
log_prefix + " committed truncation of " + std::to_string(truncate_range) +
" records.");
@ -211,7 +211,7 @@ public:
}
/* Make sure the last operation is rolled back now the work is finished. */
tw->txn.try_rollback();
tw->try_rollback();
}
void
@ -257,22 +257,22 @@ public:
uint64_t start_key = ccv[counter].coll.get_key_count();
uint64_t added_count = 0;
tw->txn.begin();
tw->begin();
/* Collection cursor. */
auto &cc = ccv[counter];
while (tw->txn.active() && tw->running()) {
while (tw->active() && tw->running()) {
/* Insert a key value pair, rolling back the transaction if required. */
auto key = tw->pad_string(std::to_string(start_key + added_count), tw->key_size);
auto value =
random_generator::instance().generate_pseudo_random_string(tw->value_size);
if (!tw->insert(cc.cursor, cc.coll.id, key, value)) {
added_count = 0;
tw->txn.rollback();
tw->rollback();
} else {
added_count++;
if (tw->txn.can_commit()) {
if (tw->txn.commit())
if (tw->can_commit()) {
if (tw->commit())
/*
* We need to inform the database model that we've added these keys as
* some other thread may rely on the key_count data. Only do so if we
@ -295,7 +295,7 @@ public:
testutil_assert(counter < tw_collection_count);
}
/* Make sure the last transaction is rolled back now the work is finished. */
tw->txn.try_rollback();
tw->try_rollback();
}
void

View File

@ -120,7 +120,7 @@ public:
collection &coll = tc->db.get_collection(tc->id);
scoped_cursor cursor = tc->session.open_scoped_cursor(coll.name);
for (uint64_t count = 0; count < tc->key_count; ++count) {
tc->txn.begin();
tc->begin();
/*
* Generate the prefix key, and append a random generated key string based on the key
* size configuration.
@ -128,9 +128,9 @@ public:
prefix_key = random_generator::instance().generate_random_string(tc->key_size);
testutil_assert(cursor.get() != nullptr);
if (perform_unique_index_insertions(tc, cursor, coll, prefix_key)) {
tc->txn.commit();
tc->commit();
} else {
tc->txn.rollback();
tc->rollback();
++rollback_retries;
if (count > 0)
--count;
@ -169,12 +169,13 @@ public:
", key size: " + std::to_string(key_size));
/* Create n collections as per the configuration. */
scoped_session session = connection_manager::instance().create_session();
for (uint64_t i = 0; i < collection_count; ++i)
/*
* The database model will call into the API and create the collection, with its own
* session.
*/
database.add_collection();
database.add_collection(session);
/* Spawn a populate thread for each collection in the database. */
for (uint64_t i = 0; i < collection_count; ++i) {
@ -199,7 +200,6 @@ public:
* traverse through each collection using a cursor to collect the prefix and push it into a
* 2D vector.
*/
scoped_session session = connection_manager::instance().create_session();
const char *key_tmp;
int ret = 0;
for (uint64_t i = 0; i < database.get_collection_count(); i++) {
@ -243,7 +243,7 @@ public:
/* Do a second lookup now that we know it exists. */
auto &cursor = cursors[coll.id];
tc->txn.begin();
tc->begin();
/*
* Grab a random existing prefix and perform unique index insertion. We expect it to
* fail to insert, because it should already exist.
@ -258,7 +258,7 @@ public:
".");
testutil_assert(!perform_unique_index_insertions(tc, cursor, coll, prefix_key));
testutil_check(cursor->reset(cursor.get()));
tc->txn.rollback();
tc->rollback();
}
}
@ -273,7 +273,7 @@ public:
* Each read thread will count the number of keys in each collection, and will double check
* if the size of the table hasn't changed.
*/
tc->txn.begin();
tc->begin();
while (tc->running()) {
for (int i = 0; i < tc->db.get_collection_count(); i++) {
collection &coll = tc->db.get_collection(i);
@ -298,6 +298,6 @@ public:
}
key_count = 0;
}
tc->txn.rollback();
tc->rollback();
}
};

View File

@ -61,8 +61,9 @@ public:
logger::log_msg(
LOG_INFO, "Populate: " + std::to_string(collection_count) + " creating collections.");
scoped_session session = connection_manager::instance().create_session();
for (uint64_t i = 0; i < collection_count; ++i)
database.add_collection();
database.add_collection(session);
logger::log_msg(LOG_INFO, "Populate: finished.");
}
@ -107,9 +108,9 @@ public:
while (tc->running()) {
auto &cc = ccv[counter];
tc->txn.begin();
tc->begin();
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
/* Generate a random key/value pair. */
std::string key = random_generator::instance().generate_random_string(tc->key_size);
@ -118,15 +119,15 @@ public:
/* Insert a key value pair. */
if (tc->insert(cc.cursor, cc.coll.id, key, value)) {
if (tc->txn.can_commit()) {
if (tc->can_commit()) {
/* We are not checking the result of commit as it is not necessary. */
if (tc->txn.commit())
if (tc->commit())
rollback_retries = 0;
else
++rollback_retries;
}
} else {
tc->txn.rollback();
tc->rollback();
++rollback_retries;
}
testutil_assert(rollback_retries < MAX_ROLLBACKS);
@ -136,7 +137,7 @@ public:
}
/* Rollback any transaction that could not commit before the end of the test. */
tc->txn.try_rollback();
tc->try_rollback();
/* Reset our cursor to avoid pinning content. */
testutil_check(cc.cursor->reset(cc.cursor.get()));
@ -178,10 +179,10 @@ public:
* The oldest timestamp might move ahead and the reading timestamp might become invalid.
* To tackle this issue, we round the timestamp to the oldest timestamp value.
*/
tc->txn.begin(
tc->begin(
"roundup_timestamps=(read=true),read_timestamp=" + tc->tsm->decimal_to_hex(ts));
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
/*
* Generate a random prefix. For this, we start by generating a random size and then
* its value.
@ -211,15 +212,15 @@ public:
validate_prefix_search_near(
ret, exact_prefix, key_prefix_str, cursor_default, generated_prefix);
tc->txn.add_op();
if (tc->txn.get_op_count() >= tc->txn.get_target_op_count())
tc->txn.rollback();
tc->add_op();
if (tc->get_op_count() >= tc->get_target_op_count())
tc->rollback();
tc->sleep();
}
testutil_check(cursor_prefix->reset(cursor_prefix.get()));
}
/* Roll back the last transaction if still active now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
private:

View File

@ -71,7 +71,7 @@ class bounded_cursor_prefix_stat : public test {
for (uint64_t j = 0; j < ALPHABET.size(); ++j) {
for (uint64_t k = 0; k < ALPHABET.size(); ++k) {
for (uint64_t count = 0; count < tc->key_count; ++count) {
tc->txn.begin();
tc->begin();
/*
* Generate the prefix key, and append a random generated key string based
* on the key size configuration.
@ -86,14 +86,14 @@ class bounded_cursor_prefix_stat : public test {
if (!tc->insert(cursor, coll.id, prefix_key, value)) {
testutil_assert(rollback_retries < MAX_ROLLBACKS);
/* We failed to insert, rollback our transaction and retry. */
tc->txn.rollback();
tc->rollback();
++rollback_retries;
if (count > 0)
--count;
} else {
/* Commit txn at commit timestamp 100. */
testutil_assert(
tc->txn.commit("commit_timestamp=" + tc->tsm->decimal_to_hex(100)));
tc->commit("commit_timestamp=" + tc->tsm->decimal_to_hex(100)));
rollback_retries = 0;
}
}
@ -131,12 +131,13 @@ public:
" number of collections: " + std::to_string(collection_count));
/* Create n collections as per the configuration. */
scoped_session session = connection_manager::instance().create_session();
for (uint64_t i = 0; i < collection_count; ++i)
/*
* The database model will call into the API and create the collection, with its own
* session.
*/
database.add_collection();
database.add_collection(session);
/* Spawn 26 threads to populate the database. */
for (uint64_t i = 0; i < ALPHABET.size(); ++i) {
@ -158,7 +159,6 @@ public:
/* Force evict all the populated keys in all of the collections. */
int cmpp;
scoped_session session = connection_manager::instance().create_session();
for (uint64_t count = 0; count < collection_count; ++count) {
collection &coll = database.get_collection(count);
scoped_cursor evict_cursor =
@ -200,7 +200,7 @@ public:
* bounded search near, we expect the search to early exit out of its prefix key range and
* return WT_NOTFOUND.
*/
tc->txn.begin("read_timestamp=" + tc->tsm->decimal_to_hex(10));
tc->begin("read_timestamp=" + tc->tsm->decimal_to_hex(10));
cursor->set_key(cursor.get(), srch_key.c_str());
bound_set prefix_bounds = bound_set(srch_key);
prefix_bounds.apply(cursor);
@ -220,7 +220,7 @@ public:
*/
if (srch_key == "z" || srch_key == "zz" || srch_key == "zzz")
++z_key_searches;
tc->txn.rollback();
tc->rollback();
}
void

View File

@ -383,9 +383,9 @@ public:
collection &coll = tc->db.get_random_collection();
scoped_cursor cursor = tc->session.open_scoped_cursor(coll.name);
tc->txn.try_begin();
tc->try_begin();
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
/* Generate a random key. */
auto key = random_generator::instance().generate_random_string(tc->key_size);
@ -393,15 +393,15 @@ public:
random_generator::instance().generate_pseudo_random_string(tc->value_size);
/* Insert a key/value pair. */
if (tc->insert(cursor, coll.id, key, value)) {
if (tc->txn.can_commit()) {
if (tc->can_commit()) {
/* We are not checking the result of commit as it is not necessary. */
if (tc->txn.commit())
if (tc->commit())
rollback_retries = 0;
else
++rollback_retries;
}
} else {
tc->txn.rollback();
tc->rollback();
++rollback_retries;
}
testutil_assert(rollback_retries < MAX_ROLLBACKS);
@ -414,7 +414,7 @@ public:
testutil_check(cursor->reset(cursor.get()));
}
/* Rollback any transaction that could not commit before the end of the test. */
tc->txn.try_rollback();
tc->try_rollback();
}
void
@ -431,9 +431,9 @@ public:
scoped_cursor cursor = tc->session.open_scoped_cursor(coll.name);
scoped_cursor rnd_cursor =
tc->session.open_scoped_cursor(coll.name, "next_random=true");
tc->txn.try_begin();
tc->try_begin();
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
int ret = rnd_cursor->next(rnd_cursor.get());
/* It is possible not to find anything if the collection is empty. */
@ -443,7 +443,7 @@ public:
* If we cannot find any record, finish the current transaction as we might be
* able to see new records after starting a new one.
*/
testutil_ignore_ret_bool(tc->txn.commit());
testutil_ignore_ret_bool(tc->commit());
continue;
} else if (ret == WT_ROLLBACK)
break;
@ -455,15 +455,15 @@ public:
auto value =
random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (tc->update(cursor, coll.id, key, value)) {
if (tc->txn.can_commit()) {
if (tc->can_commit()) {
/* We are not checking the result of commit as it is not necessary. */
if (tc->txn.commit())
if (tc->commit())
rollback_retries = 0;
else
++rollback_retries;
}
} else {
tc->txn.rollback();
tc->rollback();
++rollback_retries;
}
testutil_assert(rollback_retries < MAX_ROLLBACKS);
@ -478,7 +478,7 @@ public:
}
/* Rollback any transaction that could not commit before the end of the test. */
tc->txn.try_rollback();
tc->try_rollback();
}
void
@ -511,9 +511,9 @@ public:
* The oldest timestamp might move ahead and the reading timestamp might become invalid.
* To tackle this issue, we round the timestamp to the oldest timestamp value.
*/
tc->txn.try_begin(
tc->try_begin(
"roundup_timestamps=(read=true),read_timestamp=" + tc->tsm->decimal_to_hex(ts));
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
/* Generate a random string. */
auto key_size = random_generator::instance().generate_integer(
static_cast<int64_t>(1), tc->key_size);
@ -552,16 +552,16 @@ public:
validate_bound_search(ret, bounded_cursor, range_key_copy, bound_pair);
}
tc->txn.add_op();
if (tc->txn.can_commit())
tc->txn.commit();
tc->add_op();
if (tc->can_commit())
tc->commit();
tc->sleep();
}
bounded_cursor->reset(bounded_cursor.get());
normal_cursor->reset(normal_cursor.get());
}
/* Roll back the last transaction if still active now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
/*
@ -745,9 +745,9 @@ public:
* The oldest timestamp might move ahead and the reading timestamp might become invalid.
* To tackle this issue, we round the timestamp to the oldest timestamp value.
*/
tc->txn.begin(
tc->begin(
"roundup_timestamps=(read=true),read_timestamp=" + tc->tsm->decimal_to_hex(ts));
while (tc->txn.active() && tc->running()) {
while (tc->active() && tc->running()) {
int ret = cursor_traversal(bounded_cursor, normal_cursor, bound_pair.get_lower(),
bound_pair.get_upper(), true);
if (ret != 0)
@ -766,14 +766,14 @@ public:
bound_pair.get_upper(), false);
testutil_assert(ret == 0 || ret == WT_ROLLBACK);
}
tc->txn.add_op();
if (tc->txn.get_op_count() >= tc->txn.get_target_op_count())
tc->txn.rollback();
tc->add_op();
if (tc->get_op_count() >= tc->get_target_op_count())
tc->rollback();
tc->sleep();
}
normal_cursor->reset(normal_cursor.get());
}
/* Roll back the last transaction if still active now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
};

View File

@ -96,13 +96,13 @@ public:
while (tc->running() &&
std::chrono::system_clock::now() - burst_start <
std::chrono::seconds(_burst_duration)) {
tc->txn.try_begin();
tc->try_begin();
auto key = tc->pad_string(std::to_string(start_key + added_count), tc->key_size);
/* A return value of true implies the insert was successful. */
auto value =
random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (!tc->insert(cc.write_cursor, cc.coll.id, key, value)) {
tc->txn.rollback();
tc->rollback();
added_count = 0;
continue;
}
@ -114,7 +114,7 @@ public:
if (ret == WT_NOTFOUND) {
testutil_check(cc.read_cursor->reset(cc.read_cursor.get()));
} else if (ret == WT_ROLLBACK) {
tc->txn.rollback();
tc->rollback();
added_count = 0;
continue;
} else {
@ -122,17 +122,17 @@ public:
}
}
if (tc->txn.can_commit()) {
if (tc->txn.commit()) {
if (tc->can_commit()) {
if (tc->commit()) {
cc.coll.increase_key_count(added_count);
start_key = cc.coll.get_key_count();
}
added_count = 0;
}
}
/* Close out our current txn. */
if (tc->txn.active()) {
if (tc->txn.commit()) {
/* Close out our current */
if (tc->active()) {
if (tc->commit()) {
logger::log_msg(LOG_TRACE,
"Committed an insertion of " + std::to_string(added_count) + " keys.");
cc.coll.increase_key_count(added_count);
@ -148,7 +148,7 @@ public:
tc->sleep();
}
/* Make sure the last transaction is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
private:

View File

@ -113,19 +113,19 @@ public:
const std::string value = std::to_string(new_cache_size);
/* Save the change of cache size in the tracking table. */
tc->txn.begin();
tc->begin();
int ret = tc->op_tracker->save_operation(tc->session.get(), tracking_operation::CUSTOM,
collection_id, key, value, tc->tsm->get_next_ts(), tc->op_track_cursor);
if (ret == 0)
testutil_assert(tc->txn.commit());
testutil_assert(tc->commit());
else {
/* Due to the cache pressure, it is possible to fail when saving the operation. */
testutil_assert(ret == WT_ROLLBACK);
logger::log_msg(LOG_WARN,
"The cache size reconfiguration could not be saved in the tracking table, ret: " +
std::to_string(ret));
tc->txn.rollback();
tc->rollback();
}
increase_cache = !increase_cache;
}
@ -149,21 +149,21 @@ public:
((WT_CONNECTION_IMPL *)connection_manager::instance().get_connection())->cache_size;
const std::string value = std::to_string(cache_size);
tc->txn.try_begin();
tc->try_begin();
if (!tc->insert(cursor, coll.id, key, value)) {
tc->txn.rollback();
} else if (tc->txn.can_commit()) {
tc->rollback();
} else if (tc->can_commit()) {
/*
* The transaction can fit in the current cache size and is ready to be committed.
* This means the tracking table will contain a new record to represent this
* transaction which will be used during the validation stage.
*/
testutil_assert(tc->txn.commit());
testutil_assert(tc->commit());
}
}
/* Make sure the last transaction is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
void

View File

@ -46,14 +46,10 @@ extern "C" {
using namespace test_harness;
/* Declarations to avoid the error raised by -Werror=missing-prototypes. */
void insert_op(WT_CURSOR *cursor, int key_size, int value_size);
void read_op(WT_CURSOR *cursor, int key_size);
bool do_inserts = false;
bool do_reads = false;
void
static void
insert_op(WT_CURSOR *cursor, int key_size, int value_size)
{
logger::log_msg(LOG_INFO, "called insert_op");
@ -69,7 +65,7 @@ insert_op(WT_CURSOR *cursor, int key_size, int value_size)
}
}
void
static void
read_op(WT_CURSOR *cursor, int key_size)
{
logger::log_msg(LOG_INFO, "called read_op");

View File

@ -69,14 +69,14 @@ public:
tc->sleep();
/* Start a transaction if possible. */
tc->txn.try_begin();
tc->try_begin();
auto ret = cursor->next(cursor.get());
if (ret != 0) {
if (ret == WT_NOTFOUND)
testutil_check(cursor->reset(cursor.get()));
else if (ret == WT_ROLLBACK)
tc->txn.rollback();
tc->rollback();
else
testutil_die(ret, "Unexpected error returned from cursor->next()");
continue;
@ -93,19 +93,19 @@ public:
std::string value =
random_generator::instance().generate_pseudo_random_string(tc->value_size);
if (tc->update(cursor, coll.id, key_tmp, value)) {
if (tc->txn.can_commit()) {
if (tc->txn.commit())
if (tc->can_commit()) {
if (tc->commit())
rollback_retries = 0;
else
++rollback_retries;
}
} else {
tc->txn.rollback();
tc->rollback();
++rollback_retries;
}
testutil_assert(rollback_retries < MAX_ROLLBACKS);
}
/* Ensure our last transaction is resolved. */
tc->txn.try_rollback();
tc->try_rollback();
}
};

View File

@ -70,10 +70,10 @@ public:
while (tc->running()) {
testutil_check(write_cursor->reset(write_cursor.get()));
tc->txn.begin();
tc->begin();
int ret = write_cursor->next(write_cursor.get());
if (ret != 0) {
tc->txn.rollback();
tc->rollback();
continue;
}
const char *key;
@ -87,10 +87,10 @@ public:
std::string end_key = tc->pad_string(std::to_string(end_key_id), tc->key_size);
/* If we generate an invalid range or our truncate fails rollback the transaction. */
if (min_key_id == end_key_id || !tc->truncate(coll.id, key_str, end_key, "")) {
tc->txn.rollback();
tc->rollback();
continue;
}
if (tc->txn.commit())
if (tc->commit())
logger::log_msg(LOG_TRACE,
"thread {" + std::to_string(tc->id) + "} committed truncation of " +
std::to_string(end_key_id - min_key_id) + " records.");
@ -111,7 +111,7 @@ public:
tc->sync();
}
/* Make sure the last transaction is rolled back now the work is finished. */
tc->txn.try_rollback();
tc->try_rollback();
}
};
} // namespace test_harness

View File

@ -53,13 +53,7 @@ extern "C" {
#include "test_util.h"
}
/* Declarations to avoid the error raised by -Werror=missing-prototypes. */
const std::string parse_configuration_from_file(const std::string &filename);
void print_help();
int64_t run_test(const std::string &test_name, const std::string &config,
const std::string &wt_open_config, const std::string &home);
const std::string
static const std::string
parse_configuration_from_file(const std::string &filename)
{
std::string cfg, line, error;
@ -82,7 +76,7 @@ parse_configuration_from_file(const std::string &filename)
return (cfg);
}
void
static void
print_help()
{
std::cout << "NAME" << std::endl;
@ -127,7 +121,7 @@ print_help()
* - test_name: specifies which test to run.
* - config: defines the configuration used for the test.
*/
int64_t
static int64_t
run_test(const std::string &test_name, const std::string &config, const std::string &wt_open_config,
const std::string &home)
{

View File

@ -122,21 +122,7 @@ static const int value_size = 50000;
static const char *SOURCE_PATH = "WT_LIVE_RESTORE_SOURCE";
static const char *HOME_PATH = DEFAULT_DIR;
/* Declarations to avoid the error raised by -Werror=missing-prototypes. */
void create_collection(scoped_session &session, bool subdirectory = false);
void read(scoped_session &session);
void trigger_fs_truncate(scoped_session &session);
void write(scoped_session &session, bool fresh_start);
std::string key_to_str(uint64_t);
std::string generate_key();
std::string generate_value();
void insert(scoped_cursor &cursor, std::string &coll);
void update(scoped_cursor &cursor, std::string &coll);
void remove(scoped_session &session, scoped_cursor &cursor, std::string &coll);
void configure_database(scoped_session &session);
void reopen_conn(scoped_session &session, const std::string &conn_config, const std::string &home);
void
static void
read(scoped_session &session)
{
auto cursor = session.open_scoped_cursor(db.get_random_collection(), "next_random=true");
@ -149,7 +135,7 @@ read(scoped_session &session)
// Truncate from a random key to the end of the file and then call compact. This should
// result in the backing file on disk being fs_truncated.
void
static void
trigger_fs_truncate(scoped_session &session)
{
// Truncate from a random key all the way to the end of the collection and then call compact
@ -168,19 +154,19 @@ trigger_fs_truncate(scoped_session &session)
testutil_check(session->compact(session.get(), coll_name.c_str(), nullptr));
}
std::string
static std::string
generate_key()
{
return random_generator::instance().generate_random_string(key_size);
}
std::string
static std::string
generate_value()
{
return random_generator::instance().generate_pseudo_random_string(value_size);
}
void
static void
insert(scoped_cursor &cursor, std::string &coll)
{
std::string key = generate_key();
@ -190,7 +176,7 @@ insert(scoped_cursor &cursor, std::string &coll)
testutil_check(cursor->insert(cursor.get()));
}
void
static void
update(scoped_cursor &cursor, std::string &coll)
{
std::string key = generate_key();
@ -200,6 +186,9 @@ update(scoped_cursor &cursor, std::string &coll)
testutil_check(cursor->update(cursor.get()));
}
/* TODO: Remove declaration and make function static when used. See write() below. */
void remove(scoped_session &session, scoped_cursor &cursor, std::string &coll);
void
remove(scoped_session &session, scoped_cursor &cursor, std::string &coll)
{
@ -217,7 +206,7 @@ remove(scoped_session &session, scoped_cursor &cursor, std::string &coll)
testutil_check(cursor->remove(cursor.get()));
}
void
static void
write(scoped_session &session, bool fresh_start)
{
/* Force insertions for a duration on a fresh start. */
@ -245,13 +234,13 @@ write(scoped_session &session, bool fresh_start)
testutil_assert(false);
}
void
static void
create_collection(scoped_session &session, bool subdirectory)
{
db.add_new_collection(session, subdirectory);
}
void
static void
reopen_conn(scoped_session &session, const std::string &conn_config, const std::string &home)
{
session.close_session();
@ -324,7 +313,7 @@ get_stat(WT_CURSOR *cursor, int stat_field, int64_t *valuep)
}
/* Setup the initial set of collections in the source path. */
void
static void
configure_database(scoped_session &session)
{
scoped_cursor cursor = session.open_scoped_cursor("metadata:", "");

View File

@ -147,23 +147,26 @@ disagg_is_mode_switch(void)
void
disagg_switch_roles(void)
{
char disagg_cfg[64];
/* Perform step-up or step-down. */
g.disagg_leader = !g.disagg_leader;
/*
* FIXME-WT-15763: WT does not yet support graceful step-downs. Simply reconfiguring WT to step
* down may cause issues, so we reopen the connection when switching to follower mode.
*/
if (g.disagg_leader)
if (!g.disagg_leader) {
/*
* Stepping down: [leader -> follower]. As part of reopening WT, we will reconfigure the
* database as a follower based on the value of g.disagg_leader.
*/
track("[role change] leader -> follower", 0ULL);
wts_reopen();
/* Perform step-up or step-down. */
g.disagg_leader = !g.disagg_leader;
testutil_snprintf(disagg_cfg, sizeof(disagg_cfg), "disaggregated=(role=\"%s\")",
g.disagg_leader ? "leader" : "follower");
testutil_check(g.wts_conn->reconfigure(g.wts_conn, disagg_cfg));
if (!g.disagg_leader)
follower_read_latest_checkpoint();
} else {
/* Stepping up: [follower -> leader] */
track("[role change] follower -> leader", 0ULL);
testutil_check(g.wts_conn->reconfigure(g.wts_conn, "disaggregated=(role=leader)"));
}
/* After every switch, verify the contents of each table */
wts_verify_mirrors(g.wts_conn, NULL, NULL);

View File

@ -763,7 +763,7 @@ wts_open(const char *home, WT_CONNECTION **connp, bool verify_metadata)
{
WT_CONNECTION *conn;
size_t max;
char config[1024], *p;
char config[1024], disagg_ext_cfg[1024], *p;
const char *enc, *s;
*connp = NULL;
@ -771,6 +771,7 @@ wts_open(const char *home, WT_CONNECTION **connp, bool verify_metadata)
p = config;
max = sizeof(config);
config[0] = '\0';
disagg_ext_cfg[0] = '\0';
/* Configuration settings that are not persistent between open calls. */
enc = encryptor_at_open();
@ -789,6 +790,9 @@ wts_open(const char *home, WT_CONNECTION **connp, bool verify_metadata)
/* Optional debug mode. */
configure_debug_mode(&p, max);
/* Optional disaggregated storage. */
configure_disagg_storage(home, &p, max, disagg_ext_cfg, sizeof(disagg_ext_cfg));
/* Optional live restore. */
configure_live_restore(&p, max);
@ -871,6 +875,12 @@ wts_reopen(void)
if (GV(PRECISE_CHECKPOINT)) {
memset(&sap, 0, sizeof(sap));
wt_wrap_open_session(g.wts_conn, &sap, NULL, NULL, &session);
/*
* Update the oldest/stable timestamps. We may not advance them all the way to the last
* committed timestamp, and that's okay we might lose some data, but the goal is to ensure
* that when we read the data back and later perform verification and mirrored-table
* matching, we don't encounter table mismatches or verification issues.
*/
timestamp_once(session, false, false);
wt_wrap_close_session(session);
}

View File

@ -184,7 +184,7 @@ class suite_subprocess:
# Run a method as a subprocess using the run.py machinery.
# Return the process exit status and the WiredTiger home
# directory used by the subprocess.
def run_subprocess_function(self, directory, funcname, silent=False):
def run_subprocess_function(self, directory, funcname):
testparts = funcname.split('.')
if len(testparts) != 3:
raise ValueError('bad function name "' + funcname +
@ -211,7 +211,7 @@ class suite_subprocess:
with open("subprocess.out", "w") as wtout:
returncode = subprocess.call(
procargs, stdout=wtout, stderr=wterr)
if returncode != 0 and not silent:
if returncode != 0:
# This is not necessarily an error, the primary reason to
# run in a subprocess is that it may crash.
self.show_outputs(procargs,

View File

@ -0,0 +1,280 @@
#!/usr/bin/env python
#
# Public Domain 2014-present MongoDB, Inc.
# 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.
import wiredtiger, wttest
from helper_disagg import disagg_test_class, gen_disagg_storages
from prepare_util import test_prepare_preserve_prepare_base
from wtscenario import make_scenarios
# Test building deltas with prepared rollback. Ensure the old value is included
# in the delta after the prepared update is rolled back.
@disagg_test_class
class test_layered69(test_prepare_preserve_prepare_base):
conn_config = test_prepare_preserve_prepare_base.conn_config + ',disaggregated=(role="leader"),'
uri = "table:test_layered68"
evict = [
('none', dict(evict=False)),
('evict', dict(evict=True)),
]
disagg_storages = gen_disagg_storages('test_layered69', disagg_only = True)
scenarios = make_scenarios(disagg_storages, evict)
def test_rollback_prepared_update(self):
# Setup: Initialize stable timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(20)}')
create_params = 'key_format=i,value_format=S,type=layered'
self.session.create(self.uri, create_params)
# Insert a value and commit for keys 1-19
cursor = self.session.open_cursor(self.uri)
self.session.begin_transaction()
for i in range(1, 20):
cursor.set_key(i)
cursor.set_value('commit_value')
cursor.insert()
self.session.commit_transaction(f'commit_timestamp={self.timestamp_str(21)}')
# Make the updates stable
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(21)}')
# Verify checkpoint writes no prepared to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
if self.evict:
# Force the page to be evicted
evict_cursor = self.session.open_cursor(self.uri, None, "debug=(release_evict)")
self.session.begin_transaction()
for i in range(1, 19):
evict_cursor.set_key(i)
self.assertEqual(evict_cursor.search(), 0)
evict_cursor.reset()
self.session.rollback_transaction()
evict_cursor.close()
# Update key 19 with a prepared update prepared_id=1
session_prepare = self.conn.open_session()
cursor_prepare = session_prepare.open_cursor(self.uri)
session_prepare.begin_transaction()
cursor_prepare.set_key(19)
cursor_prepare.set_value('prepared_value_20_3')
cursor_prepare.insert()
session_prepare.prepare_transaction(f'prepare_timestamp={self.timestamp_str(35)},prepared_id={self.prepared_id_str(1)}')
# Rollback the prepared transaction
session_prepare.rollback_transaction(f'rollback_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes an empty delta to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
# Make stable timestamp equal to prepare timestamp - this should allow checkpoint to reconcile prepared update
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(35)}')
# Verify checkpoint writes a delta with prepared time window to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: True,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Make stable timestamp equal to rollback timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes a delta with the committed update to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Verify the key
self.assertEqual(cursor[19], 'commit_value')
def test_rollback_prepared_reinsert(self):
# Setup: Initialize timestamps with stable < prepare timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(20)}')
self.conn.set_timestamp(f'oldest_timestamp={self.timestamp_str(10)}')
create_params = 'key_format=i,value_format=S,type=layered'
self.session.create(self.uri, create_params)
# Insert a value and commit for keys 1-19
cursor = self.session.open_cursor(self.uri)
self.session.begin_transaction()
for i in range(1, 20):
cursor.set_key(i)
cursor.set_value('commit_value')
cursor.insert()
self.session.commit_transaction(f'commit_timestamp={self.timestamp_str(21)}')
# Delete key 19
self.session.begin_transaction()
cursor.set_key(19)
cursor.remove()
self.session.commit_transaction(f'commit_timestamp={self.timestamp_str(22)}')
# Make the delete globally visible
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(30)},oldest_timestamp={self.timestamp_str(22)}')
# Verify checkpoint writes no prepared to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
if self.evict:
# Force the page to be evicted
evict_cursor = self.session.open_cursor(self.uri, None, "debug=(release_evict)")
self.session.begin_transaction()
for i in range(1, 19):
evict_cursor.set_key(i)
self.assertEqual(evict_cursor.search(), 0)
evict_cursor.reset()
self.session.rollback_transaction()
evict_cursor.close()
# Update key 19 with a prepared update prepared_id=1
session_prepare = self.conn.open_session()
cursor_prepare = session_prepare.open_cursor(self.uri)
session_prepare.begin_transaction()
cursor_prepare.set_key(19)
cursor_prepare.set_value('prepared_value_20_3')
cursor_prepare.insert()
session_prepare.prepare_transaction(f'prepare_timestamp={self.timestamp_str(35)},prepared_id={self.prepared_id_str(1)}')
# Rollback the prepared transaction
session_prepare.rollback_transaction(f'rollback_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes an empty delta to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
# Make stable timestamp equal to prepare timestamp - this should allow checkpoint to reconcile prepared update
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(35)}')
# Verify checkpoint writes a delta with prepared time window to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: True,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Make stable timestamp equal to rollback timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes a delta with the committed update to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Verify the key
cursor.set_key(19)
self.assertEqual(cursor.search(), wiredtiger.WT_NOTFOUND)
def test_rollback_prepared_remove(self):
# Setup: Initialize stable timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(20)}')
create_params = 'key_format=i,value_format=S,type=layered'
self.session.create(self.uri, create_params)
# Insert a value and commit for keys 1-19
cursor = self.session.open_cursor(self.uri)
self.session.begin_transaction()
for i in range(1, 20):
cursor.set_key(i)
cursor.set_value('commit_value')
cursor.insert()
self.session.commit_transaction(f'commit_timestamp={self.timestamp_str(21)}')
# Make the updates stable
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(21)}')
# Verify checkpoint writes no prepared to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
if self.evict:
# Force the page to be evicted
evict_cursor = self.session.open_cursor(self.uri, None, "debug=(release_evict)")
self.session.begin_transaction()
for i in range(1, 19):
evict_cursor.set_key(i)
self.assertEqual(evict_cursor.search(), 0)
evict_cursor.reset()
self.session.rollback_transaction()
evict_cursor.close()
# Delete key 19 with a prepared update prepared_id=1
session_prepare = self.conn.open_session()
cursor_prepare = session_prepare.open_cursor(self.uri)
session_prepare.begin_transaction()
cursor_prepare.set_key(19)
cursor_prepare.remove()
session_prepare.prepare_transaction(f'prepare_timestamp={self.timestamp_str(35)},prepared_id={self.prepared_id_str(1)}')
# Rollback the prepared transaction
session_prepare.rollback_transaction(f'rollback_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes an empty delta to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: False,
}, self.uri)
# Make stable timestamp equal to prepare timestamp - this should allow checkpoint to reconcile prepared update
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(35)}')
# Verify checkpoint writes a delta with prepared time window to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: True,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Make stable timestamp equal to rollback timestamp
self.conn.set_timestamp(f'stable_timestamp={self.timestamp_str(45)}')
# Verify checkpoint writes a delta with the committed update to disk
self.checkpoint_and_verify_stats({
wiredtiger.stat.dsrc.rec_time_window_prepared: False,
wiredtiger.stat.dsrc.rec_page_delta_leaf: True,
}, self.uri)
# Verify the key
self.assertEqual(cursor[19], 'commit_value')

View File

@ -69,7 +69,6 @@ class test_metadata_cursor02(wttest.WiredTigerTestCase):
# Invalidate the table by dropping part of it
if self.drop == 'colgroup':
self.session.drop('colgroup:' + name[-2:])
self.ignoreStdoutPatternIfExists('removing incomplete table')
else:
self.session.drop('file:' + name[-2:] + '.wt')

View File

@ -1,84 +0,0 @@
#!/usr/bin/env python
#
# Public Domain 2014-present MongoDB, Inc.
# 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.
import wttest, wiredtiger
from suite_subprocess import suite_subprocess
# test_schema09.py
# Test that incomplete tables are properly cleaned up during recovery.
class test_schema09(wttest.WiredTigerTestCase, suite_subprocess):
conn_config = 'log=(enabled=true)'
basename = 'test_schema09_fail'
tablename = 'table:' + basename
def create_table(self, fail=False):
self.pr('create table')
self.session.create(self.tablename, 'key_format=5s,value_format=HQ,exclusive=true')
def subprocess_func(self):
self.conn.reconfigure("debug_mode=(crash_point_colgroup=true)")
self.create_table() # Expected to fail
def check_metadata_entry(self, exists):
expect_search = 0 if exists else wiredtiger.WT_NOTFOUND
meta_cursor = self.session.open_cursor('metadata:')
meta_cursor.set_key("file:" + self.basename + ".wt")
self.assertEqual(meta_cursor.search(), expect_search)
meta_cursor.set_key("table:" + self.basename)
self.assertEqual(meta_cursor.search(), expect_search)
meta_cursor.set_key("colgroup:" + self.basename)
self.assertEqual(meta_cursor.search(), expect_search)
meta_cursor.close()
def test_schema(self):
self.close_conn()
subdir = 'SUBPROCESS'
[ignore_result, new_home_dir] = self.run_subprocess_function(subdir,
'test_schema09.test_schema09.subprocess_func', silent=True)
with self.expectedStdoutPattern('removing incomplete table'):
self.conn = self.setUpConnectionOpen(new_home_dir)
self.session = self.setUpSessionOpen(self.conn)
self.conn.reconfigure("debug_mode=(crash_point_colgroup=false)")
self.check_metadata_entry(False)
# Test that we can't open a cursor on the table.
self.assertRaises(
wiredtiger.WiredTigerError, lambda: self.session.open_cursor(self.tablename, None))
# Test that we can't drop the table.
self.assertRaises(
wiredtiger.WiredTigerError, lambda: self.session.drop(self.tablename, None))
# Test that we can create the table.
self.create_table()
self.check_metadata_entry(True)