summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorApollon Oikonomopoulos <apoikos@debian.org>2016-12-14 22:01:42 +0200
committerApollon Oikonomopoulos <apoikos@debian.org>2016-12-14 22:01:42 +0200
commit410c92045948d55a068574531ecebaedb04cb32c (patch)
tree1b17f50d66e201dc77d13dad558e4dbf84f09c7c
parent489bca8af1f21d4a2f30e0b8c0dc936f787e05f4 (diff)
New upstream version 3.2.11upstream/3.2.11
-rw-r--r--SConstruct6
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml5
-rw-r--r--etc/evergreen.yml4
-rw-r--r--etc/longevity.yml232
-rw-r--r--etc/perf.yml247
-rw-r--r--etc/system_perf.yml101
-rw-r--r--jstests/aggregation/bugs/lookup_unwind_getmore.js31
-rw-r--r--jstests/aggregation/bugs/lookup_unwind_killcursor.js127
-rw-r--r--jstests/core/apply_ops1.js140
-rw-r--r--jstests/multiVersion/initialize_from_old_node.js1
-rw-r--r--jstests/multiVersion/large_document_sync.js62
-rw-r--r--jstests/noPassthrough/index_no_retry.js66
-rw-r--r--jstests/noPassthrough/index_retry.js67
-rw-r--r--jstests/noPassthrough/minvalid.js7
-rw-r--r--jstests/noPassthrough/minvalid2.js10
-rw-r--r--jstests/noPassthrough/wt_malformed_creation_string.js63
-rw-r--r--jstests/noPassthrough/wt_nojournal_skip_recovery.js19
-rw-r--r--jstests/noPassthroughWithMongod/index_no_retry.js94
-rw-r--r--jstests/noPassthroughWithMongod/index_retry.js95
-rw-r--r--jstests/replsets/apply_batch_only_goes_forward.js17
-rw-r--r--jstests/replsets/apply_ops_wc.js4
-rw-r--r--jstests/replsets/chaining_removal.js2
-rw-r--r--jstests/replsets/clean_shutdown_oplog_state.js26
-rw-r--r--jstests/replsets/double_rollback.js161
-rw-r--r--jstests/replsets/election_timing.js12
-rw-r--r--jstests/replsets/oplog_replay_on_startup.js427
-rw-r--r--jstests/replsets/oplog_truncated_on_recovery.js45
-rw-r--r--jstests/replsets/rollback_index.js2
-rw-r--r--jstests/replsets/slave_delay_clean_shutdown.js61
-rw-r--r--jstests/replsets/slavedelay1.js4
-rw-r--r--jstests/replsets/stepdown_killop.js1
-rw-r--r--jstests/sharding/addshard2.js310
-rw-r--r--jstests/sharding/authCommands.js2
-rw-r--r--jstests/sharding/auto_rebalance.js10
-rw-r--r--jstests/sharding/autosplit.js (renamed from jstests/sharding/auto1.js)2
-rw-r--r--jstests/sharding/autosplit_heuristics.js7
-rw-r--r--jstests/sharding/autosplit_with_balancer.js (renamed from jstests/sharding/auto2.js)2
-rw-r--r--jstests/sharding/bouncing_count.js2
-rw-r--r--jstests/sharding/conf_server_write_concern.js4
-rw-r--r--jstests/sharding/csrs_upgrade/csrs_upgrade_mongod_using_movechunk.js2
-rw-r--r--jstests/sharding/cursor1.js3
-rw-r--r--jstests/sharding/disable_autosplit.js3
-rw-r--r--jstests/sharding/findandmodify2.js8
-rw-r--r--jstests/sharding/forget_mr_temp_ns.js2
-rw-r--r--jstests/sharding/in_memory_sort_limit.js2
-rw-r--r--jstests/sharding/merge_chunks_basic.js40
-rw-r--r--jstests/sharding/merge_chunks_test.js136
-rw-r--r--jstests/sharding/movechunk_with_default_paranoia.js2
-rw-r--r--jstests/sharding/movechunk_with_moveParanoia.js7
-rw-r--r--jstests/sharding/movechunk_with_noMoveParanoia.js7
-rw-r--r--jstests/sharding/mrShardedOutput.js2
-rw-r--r--jstests/sharding/mrShardedOutputAuth.js2
-rw-r--r--jstests/sharding/sharding_balance4.js8
-rw-r--r--jstests/sharding/split_with_force.js3
-rw-r--r--jstests/sharding/split_with_force_small.js3
-rw-r--r--jstests/sharding/top_chunk_autosplit.js16
-rw-r--r--jstests/sharding/write_cmd_auto_split.js2
-rw-r--r--jstests/sharding/zbigMapReduce.js2
-rw-r--r--jstests/ssl/libs/ssl_helpers.js8
-rw-r--r--jstests/ssl/sharding_with_x509.js122
-rw-r--r--jstests/tool/dumprestore9.js2
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/base/error_codes.err1
-rw-r--r--src/mongo/bson/bson_validate_test.cpp2
-rw-r--r--src/mongo/bson/util/bson_check.h11
-rw-r--r--src/mongo/client/connpool.h12
-rw-r--r--src/mongo/client/dbclient.cpp8
-rw-r--r--src/mongo/client/dbclient_rs.cpp121
-rw-r--r--src/mongo/client/dbclient_rs.h11
-rw-r--r--src/mongo/client/dbclient_rs_test.cpp3
-rw-r--r--src/mongo/client/fetcher.cpp173
-rw-r--r--src/mongo/client/remote_command_runner_impl.cpp8
-rw-r--r--src/mongo/client/remote_command_targeter.h16
-rw-r--r--src/mongo/client/remote_command_targeter_factory_mock.cpp8
-rw-r--r--src/mongo/client/remote_command_targeter_mock.cpp5
-rw-r--r--src/mongo/client/remote_command_targeter_mock.h4
-rw-r--r--src/mongo/client/remote_command_targeter_rs.cpp12
-rw-r--r--src/mongo/client/remote_command_targeter_rs.h4
-rw-r--r--src/mongo/client/remote_command_targeter_standalone.cpp6
-rw-r--r--src/mongo/client/remote_command_targeter_standalone.h4
-rw-r--r--src/mongo/client/replica_set_monitor.cpp72
-rw-r--r--src/mongo/client/replica_set_monitor.h22
-rw-r--r--src/mongo/client/replica_set_monitor_internal.h2
-rw-r--r--src/mongo/client/replica_set_monitor_read_preference_test.cpp78
-rw-r--r--src/mongo/client/replica_set_monitor_test.cpp6
-rw-r--r--src/mongo/db/SConscript3
-rw-r--r--src/mongo/db/auth/SConscript1
-rw-r--r--src/mongo/db/auth/authorization_session.cpp51
-rw-r--r--src/mongo/db/auth/authorization_session.h11
-rw-r--r--src/mongo/db/catalog/SConscript10
-rw-r--r--src/mongo/db/catalog/collection.cpp5
-rw-r--r--src/mongo/db/catalog/database.cpp11
-rw-r--r--src/mongo/db/catalog/index_create.cpp17
-rw-r--r--src/mongo/db/commands.cpp25
-rw-r--r--src/mongo/db/commands.h57
-rw-r--r--src/mongo/db/commands/SConscript16
-rw-r--r--src/mongo/db/commands/apply_ops.cpp16
-rw-r--r--src/mongo/db/commands/apply_ops_cmd_common.cpp227
-rw-r--r--src/mongo/db/commands/apply_ops_cmd_common.h55
-rw-r--r--src/mongo/db/commands/explain_cmd.cpp8
-rw-r--r--src/mongo/db/concurrency/locker.h14
-rw-r--r--src/mongo/db/concurrency/locker_noop.h4
-rw-r--r--src/mongo/db/db_raii.h1
-rw-r--r--src/mongo/db/dbcommands.cpp3
-rw-r--r--src/mongo/db/dbhelpers.cpp2
-rw-r--r--src/mongo/db/exec/collection_scan.cpp14
-rw-r--r--src/mongo/db/instance.cpp12
-rw-r--r--src/mongo/db/pipeline/SConscript1
-rw-r--r--src/mongo/db/pipeline/document_source.h6
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp59
-rw-r--r--src/mongo/db/pipeline/document_source_test.cpp12
-rw-r--r--src/mongo/db/repl/SConscript14
-rw-r--r--src/mongo/db/repl/bgsync.cpp22
-rw-r--r--src/mongo/db/repl/initial_sync.cpp12
-rw-r--r--src/mongo/db/repl/minvalid.cpp195
-rw-r--r--src/mongo/db/repl/minvalid.h51
-rw-r--r--src/mongo/db/repl/oplog.cpp41
-rw-r--r--src/mongo/db/repl/oplogreader.cpp52
-rw-r--r--src/mongo/db/repl/oplogreader.h7
-rw-r--r--src/mongo/db/repl/replication_coordinator.h2
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state.h16
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.cpp142
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.h4
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_mock.cpp22
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_mock.h16
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp82
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.h3
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_elect.cpp10
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp260
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp11
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_test.cpp7
-rw-r--r--src/mongo/db/repl/replication_coordinator_mock.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_mock.h2
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.cpp27
-rw-r--r--src/mongo/db/repl/replication_coordinator_test_fixture.h19
-rw-r--r--src/mongo/db/repl/roll_back_local_operations.cpp19
-rw-r--r--src/mongo/db/repl/roll_back_local_operations.h3
-rw-r--r--src/mongo/db/repl/roll_back_local_operations_test.cpp12
-rw-r--r--src/mongo/db/repl/rs_initialsync.cpp22
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp27
-rw-r--r--src/mongo/db/repl/rs_rollback_test.cpp7
-rw-r--r--src/mongo/db/repl/rs_sync.cpp4
-rw-r--r--src/mongo/db/repl/storage_interface.h11
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp18
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h1
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h4
-rw-r--r--src/mongo/db/repl/sync_tail.cpp280
-rw-r--r--src/mongo/db/repl/sync_tail.h35
-rw-r--r--src/mongo/db/repl/topology_coordinator.h4
-rw-r--r--src/mongo/db/repl/topology_coordinator_impl.cpp122
-rw-r--r--src/mongo/db/repl/topology_coordinator_impl.h10
-rw-r--r--src/mongo/db/repl/topology_coordinator_impl_test.cpp29
-rw-r--r--src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp18
-rw-r--r--src/mongo/db/s/migration_source_manager.cpp18
-rw-r--r--src/mongo/db/s/sharding_state.cpp20
-rw-r--r--src/mongo/db/server_options_helpers.cpp12
-rw-r--r--src/mongo/db/storage/devnull/devnull_kv_engine.cpp2
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h2
-rw-r--r--src/mongo/db/storage/mmap_v1/heap_record_store_btree.h4
-rw-r--r--src/mongo/db/storage/mmap_v1/record_store_v1_base.h2
-rw-r--r--src/mongo/db/storage/record_store.h16
-rw-r--r--src/mongo/db/storage/record_store_test_capped_visibility.cpp24
-rw-r--r--src/mongo/db/storage/snapshot_name.h5
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp162
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h24
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp85
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp6
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp19
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp5
-rw-r--r--src/mongo/dbtests/replica_set_monitor_test.cpp12
-rw-r--r--src/mongo/dbtests/repltests.cpp30
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp23
-rw-r--r--src/mongo/executor/async_secure_stream.cpp2
-rw-r--r--src/mongo/executor/async_stream.cpp2
-rw-r--r--src/mongo/executor/async_stream_common.cpp7
-rw-r--r--src/mongo/executor/async_stream_common.h10
-rw-r--r--src/mongo/executor/async_timer_asio.cpp11
-rw-r--r--src/mongo/executor/connection_pool.cpp19
-rw-r--r--src/mongo/executor/connection_pool.h10
-rw-r--r--src/mongo/executor/connection_pool_asio.cpp18
-rw-r--r--src/mongo/executor/downconvert_find_and_getmore_commands.cpp135
-rw-r--r--src/mongo/executor/downconvert_find_and_getmore_commands.h25
-rw-r--r--src/mongo/executor/network_interface_asio.cpp35
-rw-r--r--src/mongo/executor/network_interface_asio_command.cpp7
-rw-r--r--src/mongo/executor/network_interface_asio_integration_test.cpp149
-rw-r--r--src/mongo/executor/network_interface_asio_test_utils.h26
-rw-r--r--src/mongo/executor/network_interface_factory.cpp5
-rw-r--r--src/mongo/executor/network_interface_factory.h4
-rw-r--r--src/mongo/s/chunk_manager.cpp18
-rw-r--r--src/mongo/s/client/shard_registry.cpp8
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/cluster_explain_cmd.cpp8
-rw-r--r--src/mongo/s/commands/commands_public.cpp11
-rw-r--r--src/mongo/s/d_merge.cpp18
-rw-r--r--src/mongo/s/query/async_results_merger.cpp31
-rw-r--r--src/mongo/s/query/async_results_merger.h6
-rw-r--r--src/mongo/s/query/async_results_merger_test.cpp76
-rw-r--r--src/mongo/s/s_only.cpp2
-rw-r--r--src/mongo/s/server.cpp13
-rw-r--r--src/mongo/s/sharding_initialization.cpp43
-rw-r--r--src/mongo/shell/assert.js3
-rw-r--r--src/mongo/shell/dbshell.cpp3
-rw-r--r--src/mongo/shell/mongo.js2
-rw-r--r--src/mongo/shell/replsettest.js26
-rw-r--r--src/mongo/shell/shardingtest.js14
-rw-r--r--src/mongo/shell/shell_utils_launcher.cpp39
-rw-r--r--src/mongo/shell/shell_utils_launcher.h2
-rw-r--r--src/mongo/unittest/unittest.cpp7
-rw-r--r--src/mongo/unittest/unittest.h5
-rw-r--r--src/mongo/util/heap_profiler.cpp4
-rw-r--r--src/mongo/util/net/hostandport.cpp10
-rw-r--r--src/mongo/util/net/hostandport.h6
-rw-r--r--src/third_party/SConscript2
-rwxr-xr-xsrc/third_party/pcre-8.39/132html (renamed from src/third_party/pcre-8.38/132html)0
-rw-r--r--src/third_party/pcre-8.39/AUTHORS (renamed from src/third_party/pcre-8.38/AUTHORS)6
-rw-r--r--src/third_party/pcre-8.39/COPYING (renamed from src/third_party/pcre-8.38/COPYING)0
-rw-r--r--src/third_party/pcre-8.39/ChangeLog (renamed from src/third_party/pcre-8.38/ChangeLog)94
-rwxr-xr-xsrc/third_party/pcre-8.39/CheckMan (renamed from src/third_party/pcre-8.38/CheckMan)0
-rwxr-xr-xsrc/third_party/pcre-8.39/CleanTxt (renamed from src/third_party/pcre-8.38/CleanTxt)0
-rwxr-xr-xsrc/third_party/pcre-8.39/Detrail (renamed from src/third_party/pcre-8.38/Detrail)0
-rw-r--r--src/third_party/pcre-8.39/HACKING (renamed from src/third_party/pcre-8.38/HACKING)0
-rw-r--r--src/third_party/pcre-8.39/INSTALL (renamed from src/third_party/pcre-8.38/INSTALL)0
-rw-r--r--src/third_party/pcre-8.39/LICENCE (renamed from src/third_party/pcre-8.38/LICENCE)6
-rw-r--r--src/third_party/pcre-8.39/NEWS (renamed from src/third_party/pcre-8.38/NEWS)9
-rw-r--r--src/third_party/pcre-8.39/NON-AUTOTOOLS-BUILD (renamed from src/third_party/pcre-8.38/NON-AUTOTOOLS-BUILD)0
-rw-r--r--src/third_party/pcre-8.39/NON-UNIX-USE (renamed from src/third_party/pcre-8.38/NON-UNIX-USE)0
-rwxr-xr-xsrc/third_party/pcre-8.39/PrepareRelease (renamed from src/third_party/pcre-8.38/PrepareRelease)0
-rw-r--r--src/third_party/pcre-8.39/README (renamed from src/third_party/pcre-8.38/README)0
-rwxr-xr-xsrc/third_party/pcre-8.39/RunGrepTest (renamed from src/third_party/pcre-8.38/RunGrepTest)9
-rwxr-xr-xsrc/third_party/pcre-8.39/RunTest (renamed from src/third_party/pcre-8.38/RunTest)32
-rw-r--r--src/third_party/pcre-8.39/RunTest.bat (renamed from src/third_party/pcre-8.38/RunTest.bat)0
-rw-r--r--src/third_party/pcre-8.39/SConscript (renamed from src/third_party/pcre-8.38/SConscript)0
-rwxr-xr-xsrc/third_party/pcre-8.39/ar-lib (renamed from src/third_party/pcre-8.38/ar-lib)0
-rw-r--r--src/third_party/pcre-8.39/build_posix/config.h (renamed from src/third_party/pcre-8.38/build_posix/config.h)6
-rw-r--r--src/third_party/pcre-8.39/build_solaris/config.h (renamed from src/third_party/pcre-8.38/build_solaris/config.h)6
-rwxr-xr-x[-rw-r--r--]src/third_party/pcre-8.39/build_windows/config.h (renamed from src/third_party/pcre-8.38/build_windows/config.h)114
-rw-r--r--src/third_party/pcre-8.39/dftables.c (renamed from src/third_party/pcre-8.38/dftables.c)0
-rw-r--r--src/third_party/pcre-8.39/libpcre.pc.in (renamed from src/third_party/pcre-8.38/libpcre.pc.in)0
-rw-r--r--src/third_party/pcre-8.39/libpcre16.pc.in (renamed from src/third_party/pcre-8.38/libpcre16.pc.in)0
-rw-r--r--src/third_party/pcre-8.39/libpcre32.pc.in (renamed from src/third_party/pcre-8.38/libpcre32.pc.in)0
-rw-r--r--src/third_party/pcre-8.39/libpcrecpp.pc.in (renamed from src/third_party/pcre-8.38/libpcrecpp.pc.in)0
-rw-r--r--src/third_party/pcre-8.39/libpcreposix.pc.in (renamed from src/third_party/pcre-8.38/libpcreposix.pc.in)0
-rw-r--r--src/third_party/pcre-8.39/makevp.bat (renamed from src/third_party/pcre-8.38/makevp.bat)0
-rw-r--r--src/third_party/pcre-8.39/makevp_c.txt (renamed from src/third_party/pcre-8.38/makevp_c.txt)0
-rw-r--r--src/third_party/pcre-8.39/makevp_l.txt (renamed from src/third_party/pcre-8.38/makevp_l.txt)0
-rw-r--r--src/third_party/pcre-8.39/pcre-config.in (renamed from src/third_party/pcre-8.38/pcre-config.in)0
-rw-r--r--src/third_party/pcre-8.39/pcre.h (renamed from src/third_party/pcre-8.38/pcre.h)4
-rw-r--r--src/third_party/pcre-8.39/pcre.h.generic (renamed from src/third_party/pcre-8.38/pcre.h.generic)4
-rw-r--r--src/third_party/pcre-8.39/pcre.h.in (renamed from src/third_party/pcre-8.38/pcre.h.in)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_byte_order.c (renamed from src/third_party/pcre-8.38/pcre16_byte_order.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_chartables.c (renamed from src/third_party/pcre-8.38/pcre16_chartables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_compile.c (renamed from src/third_party/pcre-8.38/pcre16_compile.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_config.c (renamed from src/third_party/pcre-8.38/pcre16_config.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_dfa_exec.c (renamed from src/third_party/pcre-8.38/pcre16_dfa_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_exec.c (renamed from src/third_party/pcre-8.38/pcre16_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_fullinfo.c (renamed from src/third_party/pcre-8.38/pcre16_fullinfo.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_get.c (renamed from src/third_party/pcre-8.38/pcre16_get.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_globals.c (renamed from src/third_party/pcre-8.38/pcre16_globals.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_jit_compile.c (renamed from src/third_party/pcre-8.38/pcre16_jit_compile.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_maketables.c (renamed from src/third_party/pcre-8.38/pcre16_maketables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_newline.c (renamed from src/third_party/pcre-8.38/pcre16_newline.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_ord2utf16.c (renamed from src/third_party/pcre-8.38/pcre16_ord2utf16.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_printint.c (renamed from src/third_party/pcre-8.38/pcre16_printint.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_refcount.c (renamed from src/third_party/pcre-8.38/pcre16_refcount.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_string_utils.c (renamed from src/third_party/pcre-8.38/pcre16_string_utils.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_study.c (renamed from src/third_party/pcre-8.38/pcre16_study.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_tables.c (renamed from src/third_party/pcre-8.38/pcre16_tables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_ucd.c (renamed from src/third_party/pcre-8.38/pcre16_ucd.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_utf16_utils.c (renamed from src/third_party/pcre-8.38/pcre16_utf16_utils.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_valid_utf16.c (renamed from src/third_party/pcre-8.38/pcre16_valid_utf16.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_version.c (renamed from src/third_party/pcre-8.38/pcre16_version.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre16_xclass.c (renamed from src/third_party/pcre-8.38/pcre16_xclass.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_byte_order.c (renamed from src/third_party/pcre-8.38/pcre32_byte_order.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_chartables.c (renamed from src/third_party/pcre-8.38/pcre32_chartables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_compile.c (renamed from src/third_party/pcre-8.38/pcre32_compile.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_config.c (renamed from src/third_party/pcre-8.38/pcre32_config.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_dfa_exec.c (renamed from src/third_party/pcre-8.38/pcre32_dfa_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_exec.c (renamed from src/third_party/pcre-8.38/pcre32_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_fullinfo.c (renamed from src/third_party/pcre-8.38/pcre32_fullinfo.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_get.c (renamed from src/third_party/pcre-8.38/pcre32_get.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_globals.c (renamed from src/third_party/pcre-8.38/pcre32_globals.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_jit_compile.c (renamed from src/third_party/pcre-8.38/pcre32_jit_compile.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_maketables.c (renamed from src/third_party/pcre-8.38/pcre32_maketables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_newline.c (renamed from src/third_party/pcre-8.38/pcre32_newline.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_ord2utf32.c (renamed from src/third_party/pcre-8.38/pcre32_ord2utf32.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_printint.c (renamed from src/third_party/pcre-8.38/pcre32_printint.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_refcount.c (renamed from src/third_party/pcre-8.38/pcre32_refcount.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_string_utils.c (renamed from src/third_party/pcre-8.38/pcre32_string_utils.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_study.c (renamed from src/third_party/pcre-8.38/pcre32_study.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_tables.c (renamed from src/third_party/pcre-8.38/pcre32_tables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_ucd.c (renamed from src/third_party/pcre-8.38/pcre32_ucd.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_utf32_utils.c (renamed from src/third_party/pcre-8.38/pcre32_utf32_utils.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_valid_utf32.c (renamed from src/third_party/pcre-8.38/pcre32_valid_utf32.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_version.c (renamed from src/third_party/pcre-8.38/pcre32_version.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre32_xclass.c (renamed from src/third_party/pcre-8.38/pcre32_xclass.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_byte_order.c (renamed from src/third_party/pcre-8.38/pcre_byte_order.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_chartables.c (renamed from src/third_party/pcre-8.38/pcre_chartables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_chartables.c.dist (renamed from src/third_party/pcre-8.38/pcre_chartables.c.dist)0
-rw-r--r--src/third_party/pcre-8.39/pcre_compile.c (renamed from src/third_party/pcre-8.38/pcre_compile.c)183
-rw-r--r--src/third_party/pcre-8.39/pcre_config.c (renamed from src/third_party/pcre-8.38/pcre_config.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_dfa_exec.c (renamed from src/third_party/pcre-8.38/pcre_dfa_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_exec.c (renamed from src/third_party/pcre-8.38/pcre_exec.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_fullinfo.c (renamed from src/third_party/pcre-8.38/pcre_fullinfo.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_get.c (renamed from src/third_party/pcre-8.38/pcre_get.c)23
-rw-r--r--src/third_party/pcre-8.39/pcre_globals.c (renamed from src/third_party/pcre-8.38/pcre_globals.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_internal.h (renamed from src/third_party/pcre-8.38/pcre_internal.h)8
-rw-r--r--src/third_party/pcre-8.39/pcre_jit_compile.c (renamed from src/third_party/pcre-8.38/pcre_jit_compile.c)2875
-rw-r--r--src/third_party/pcre-8.39/pcre_jit_test.c (renamed from src/third_party/pcre-8.38/pcre_jit_test.c)36
-rw-r--r--src/third_party/pcre-8.39/pcre_maketables.c (renamed from src/third_party/pcre-8.38/pcre_maketables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_newline.c (renamed from src/third_party/pcre-8.38/pcre_newline.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_ord2utf8.c (renamed from src/third_party/pcre-8.38/pcre_ord2utf8.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_printint.c (renamed from src/third_party/pcre-8.38/pcre_printint.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_refcount.c (renamed from src/third_party/pcre-8.38/pcre_refcount.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_scanner.cc (renamed from src/third_party/pcre-8.38/pcre_scanner.cc)0
-rw-r--r--src/third_party/pcre-8.39/pcre_scanner.h (renamed from src/third_party/pcre-8.38/pcre_scanner.h)0
-rw-r--r--src/third_party/pcre-8.39/pcre_scanner_unittest.cc (renamed from src/third_party/pcre-8.38/pcre_scanner_unittest.cc)0
-rw-r--r--src/third_party/pcre-8.39/pcre_string_utils.c (renamed from src/third_party/pcre-8.38/pcre_string_utils.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_stringpiece.cc (renamed from src/third_party/pcre-8.38/pcre_stringpiece.cc)0
-rw-r--r--src/third_party/pcre-8.39/pcre_stringpiece.h (renamed from src/third_party/pcre-8.38/pcre_stringpiece.h)0
-rw-r--r--src/third_party/pcre-8.39/pcre_stringpiece.h.in (renamed from src/third_party/pcre-8.38/pcre_stringpiece.h.in)0
-rw-r--r--src/third_party/pcre-8.39/pcre_stringpiece_unittest.cc (renamed from src/third_party/pcre-8.38/pcre_stringpiece_unittest.cc)0
-rw-r--r--src/third_party/pcre-8.39/pcre_study.c (renamed from src/third_party/pcre-8.38/pcre_study.c)2
-rw-r--r--src/third_party/pcre-8.39/pcre_tables.c (renamed from src/third_party/pcre-8.38/pcre_tables.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_ucd.c (renamed from src/third_party/pcre-8.38/pcre_ucd.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_valid_utf8.c (renamed from src/third_party/pcre-8.38/pcre_valid_utf8.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_version.c (renamed from src/third_party/pcre-8.38/pcre_version.c)0
-rw-r--r--src/third_party/pcre-8.39/pcre_xclass.c (renamed from src/third_party/pcre-8.38/pcre_xclass.c)0
-rw-r--r--src/third_party/pcre-8.39/pcrecpp.cc (renamed from src/third_party/pcre-8.38/pcrecpp.cc)130
-rw-r--r--src/third_party/pcre-8.39/pcrecpp.h (renamed from src/third_party/pcre-8.38/pcrecpp.h)0
-rw-r--r--src/third_party/pcre-8.39/pcrecpp_internal.h (renamed from src/third_party/pcre-8.38/pcrecpp_internal.h)0
-rw-r--r--src/third_party/pcre-8.39/pcrecpp_unittest.cc (renamed from src/third_party/pcre-8.38/pcrecpp_unittest.cc)0
-rw-r--r--src/third_party/pcre-8.39/pcrecpparg.h (renamed from src/third_party/pcre-8.38/pcrecpparg.h)0
-rw-r--r--src/third_party/pcre-8.39/pcrecpparg.h.in (renamed from src/third_party/pcre-8.38/pcrecpparg.h.in)0
-rw-r--r--src/third_party/pcre-8.39/pcredemo.c (renamed from src/third_party/pcre-8.38/pcredemo.c)0
-rw-r--r--src/third_party/pcre-8.39/pcregexp.pas (renamed from src/third_party/pcre-8.38/pcregexp.pas)0
-rw-r--r--src/third_party/pcre-8.39/pcregrep.c (renamed from src/third_party/pcre-8.38/pcregrep.c)2
-rw-r--r--src/third_party/pcre-8.39/pcreposix.c (renamed from src/third_party/pcre-8.38/pcreposix.c)6
-rw-r--r--src/third_party/pcre-8.39/pcreposix.h (renamed from src/third_party/pcre-8.38/pcreposix.h)0
-rw-r--r--src/third_party/pcre-8.39/pcretest.c (renamed from src/third_party/pcre-8.38/pcretest.c)20
-rwxr-xr-xsrc/third_party/pcre-8.39/perltest.pl (renamed from src/third_party/pcre-8.38/perltest.pl)0
-rw-r--r--src/third_party/pcre-8.39/ucp.h (renamed from src/third_party/pcre-8.38/ucp.h)0
-rw-r--r--src/third_party/scripts/pcre_get_sources.sh60
-rw-r--r--src/third_party/wiredtiger/.gitignore123
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/config.c504
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/config_opt.h14
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/doxy.c111
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/idle_table_cycle.c101
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/misc.c42
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-50r50u.wtperf3
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-80r20u.wtperf3
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/runners/checkpoint-stress.wtperf1
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/runners/evict-btree-stress-multi.wtperf1
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/stress/btree-split-stress.wtperf (renamed from src/third_party/wiredtiger/bench/wtperf/runners/btree-split-stress.wtperf)0
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/stress/shared-cache-stress.wtperf (renamed from src/third_party/wiredtiger/bench/wtperf/runners/shared-cache-stress.wtperf)0
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/track.c122
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/wtperf.c1636
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/wtperf.h121
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/wtperf_opt.i27
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/wtperf_throttle.c10
-rw-r--r--src/third_party/wiredtiger/bench/wtperf/wtperf_truncate.c63
-rw-r--r--src/third_party/wiredtiger/build_posix/Make.base3
-rw-r--r--src/third_party/wiredtiger/build_posix/Make.subdirs1
-rw-r--r--src/third_party/wiredtiger/build_posix/aclocal/options.m434
-rw-r--r--src/third_party/wiredtiger/build_posix/configure.ac.in2
-rw-r--r--src/third_party/wiredtiger/build_win/wiredtiger_config.h6
-rw-r--r--src/third_party/wiredtiger/dist/api_data.py29
-rw-r--r--src/third_party/wiredtiger/dist/filelist5
-rw-r--r--src/third_party/wiredtiger/dist/flags.py10
-rw-r--r--src/third_party/wiredtiger/dist/log.py34
-rw-r--r--src/third_party/wiredtiger/dist/log_data.py10
-rw-r--r--src/third_party/wiredtiger/dist/s_define.list6
-rwxr-xr-xsrc/third_party/wiredtiger/dist/s_docs24
-rw-r--r--src/third_party/wiredtiger/dist/s_errno73
-rwxr-xr-xsrc/third_party/wiredtiger/dist/s_export2
-rwxr-xr-xsrc/third_party/wiredtiger/dist/s_stat19
-rw-r--r--src/third_party/wiredtiger/dist/s_string.ok28
-rwxr-xr-xsrc/third_party/wiredtiger/dist/s_style2
-rw-r--r--src/third_party/wiredtiger/dist/s_void12
-rw-r--r--src/third_party/wiredtiger/dist/stat.py23
-rw-r--r--src/third_party/wiredtiger/dist/stat_data.py289
-rw-r--r--src/third_party/wiredtiger/dist/wtperf_config.py25
-rw-r--r--src/third_party/wiredtiger/examples/c/ex_all.c33
-rw-r--r--src/third_party/wiredtiger/examples/java/com/wiredtiger/examples/ex_all.java29
-rw-r--r--src/third_party/wiredtiger/ext/collators/revint/revint_collator.c24
-rw-r--r--src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c41
-rw-r--r--src/third_party/wiredtiger/ext/compressors/snappy/snappy_compress.c119
-rw-r--r--src/third_party/wiredtiger/ext/compressors/zlib/zlib_compress.c309
-rw-r--r--src/third_party/wiredtiger/ext/compressors/zstd/Makefile.am11
-rw-r--r--src/third_party/wiredtiger/ext/compressors/zstd/zstd_compress.c359
-rw-r--r--src/third_party/wiredtiger/ext/datasources/helium/helium.c569
-rw-r--r--src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c23
-rw-r--r--src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c44
-rw-r--r--src/third_party/wiredtiger/ext/extractors/csv/csv_extractor.c55
-rw-r--r--src/third_party/wiredtiger/ext/test/kvs_bdb/kvs_bdb.c267
-rw-r--r--src/third_party/wiredtiger/src/async/async_api.c15
-rw-r--r--src/third_party/wiredtiger/src/async/async_worker.c2
-rw-r--r--src/third_party/wiredtiger/src/block/block_ckpt.c28
-rw-r--r--src/third_party/wiredtiger/src/block/block_ext.c3
-rw-r--r--src/third_party/wiredtiger/src/bloom/bloom.c11
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_cursor.c28
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_handle.c13
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_io.c11
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_read.c33
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_split.c15
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_stat.c29
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_sync.c99
-rw-r--r--src/third_party/wiredtiger/src/cache/cache_las.c2
-rw-r--r--src/third_party/wiredtiger/src/checksum/power8/README.md2
-rw-r--r--src/third_party/wiredtiger/src/checksum/power8/crc32.sx (renamed from src/third_party/wiredtiger/src/checksum/power8/crc32.S)0
-rw-r--r--src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.sx (renamed from src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.S)0
-rw-r--r--src/third_party/wiredtiger/src/config/config_api.c9
-rw-r--r--src/third_party/wiredtiger/src/config/config_check.c3
-rw-r--r--src/third_party/wiredtiger/src/config/config_collapse.c4
-rw-r--r--src/third_party/wiredtiger/src/config/config_def.c87
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_api.c121
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_cache.c82
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_ckpt.c46
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_dhandle.c33
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_handle.c21
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_log.c13
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_stat.c24
-rw-r--r--src/third_party/wiredtiger/src/conn/conn_sweep.c2
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_backup.c77
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_file.c10
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_index.c4
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_join.c28
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_json.c84
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_metadata.c5
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_stat.c80
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_table.c41
-rw-r--r--src/third_party/wiredtiger/src/docs/build-posix.dox6
-rw-r--r--src/third_party/wiredtiger/src/docs/compression.dox44
-rw-r--r--src/third_party/wiredtiger/src/docs/file-formats.dox6
-rw-r--r--src/third_party/wiredtiger/src/docs/spell.ok3
-rw-r--r--src/third_party/wiredtiger/src/docs/style/header-web.html73
-rw-r--r--src/third_party/wiredtiger/src/docs/style/header.html4
-rwxr-xr-xsrc/third_party/wiredtiger/src/docs/tools/doxfilter.py4
-rw-r--r--src/third_party/wiredtiger/src/docs/tune-compression.dox39
-rw-r--r--src/third_party/wiredtiger/src/docs/wtperf.dox85
-rw-r--r--src/third_party/wiredtiger/src/evict/evict_lru.c172
-rw-r--r--src/third_party/wiredtiger/src/evict/evict_page.c13
-rw-r--r--src/third_party/wiredtiger/src/evict/evict_stat.c138
-rw-r--r--src/third_party/wiredtiger/src/include/api.h4
-rw-r--r--src/third_party/wiredtiger/src/include/btmem.h4
-rw-r--r--src/third_party/wiredtiger/src/include/btree.h27
-rw-r--r--src/third_party/wiredtiger/src/include/btree.i97
-rw-r--r--src/third_party/wiredtiger/src/include/cache.h1
-rw-r--r--src/third_party/wiredtiger/src/include/cache.i2
-rw-r--r--src/third_party/wiredtiger/src/include/connection.h21
-rw-r--r--src/third_party/wiredtiger/src/include/cursor.h16
-rw-r--r--src/third_party/wiredtiger/src/include/dhandle.h2
-rw-r--r--src/third_party/wiredtiger/src/include/extern.h18
-rw-r--r--src/third_party/wiredtiger/src/include/extern_posix.h2
-rw-r--r--src/third_party/wiredtiger/src/include/extern_win.h2
-rw-r--r--src/third_party/wiredtiger/src/include/flags.h8
-rw-r--r--src/third_party/wiredtiger/src/include/lsm.h24
-rw-r--r--src/third_party/wiredtiger/src/include/meta.h5
-rw-r--r--src/third_party/wiredtiger/src/include/misc.i6
-rw-r--r--src/third_party/wiredtiger/src/include/mutex.h24
-rw-r--r--src/third_party/wiredtiger/src/include/mutex.i45
-rw-r--r--src/third_party/wiredtiger/src/include/schema.h8
-rw-r--r--src/third_party/wiredtiger/src/include/session.h3
-rw-r--r--src/third_party/wiredtiger/src/include/stat.h119
-rw-r--r--src/third_party/wiredtiger/src/include/txn.h10
-rw-r--r--src/third_party/wiredtiger/src/include/txn.i6
-rw-r--r--src/third_party/wiredtiger/src/include/wiredtiger.in753
-rw-r--r--src/third_party/wiredtiger/src/include/wt_internal.h4
-rw-r--r--src/third_party/wiredtiger/src/log/log.c94
-rw-r--r--src/third_party/wiredtiger/src/log/log_auto.c36
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_cursor.c277
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_cursor_bulk.c16
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_manager.c21
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_merge.c15
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_stat.c22
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_tree.c8
-rw-r--r--src/third_party/wiredtiger/src/lsm/lsm_work_unit.c2
-rw-r--r--src/third_party/wiredtiger/src/meta/meta_ckpt.c2
-rw-r--r--src/third_party/wiredtiger/src/os_posix/os_mtx_cond.c31
-rw-r--r--src/third_party/wiredtiger/src/os_posix/os_time.c23
-rw-r--r--src/third_party/wiredtiger/src/os_win/os_mtx_cond.c30
-rw-r--r--src/third_party/wiredtiger/src/os_win/os_time.c6
-rw-r--r--src/third_party/wiredtiger/src/reconcile/rec_write.c36
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_create.c9
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_drop.c3
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_rename.c9
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_stat.c2
-rw-r--r--src/third_party/wiredtiger/src/schema/schema_util.c39
-rw-r--r--src/third_party/wiredtiger/src/session/session_api.c60
-rw-r--r--src/third_party/wiredtiger/src/session/session_compact.c9
-rw-r--r--src/third_party/wiredtiger/src/session/session_dhandle.c11
-rw-r--r--src/third_party/wiredtiger/src/support/err.c52
-rw-r--r--src/third_party/wiredtiger/src/support/hazard.c6
-rw-r--r--src/third_party/wiredtiger/src/support/huffman.c3
-rw-r--r--src/third_party/wiredtiger/src/support/mtx_rw.c40
-rw-r--r--src/third_party/wiredtiger/src/support/rand.c6
-rw-r--r--src/third_party/wiredtiger/src/support/stat.c204
-rw-r--r--src/third_party/wiredtiger/src/support/thread_group.c10
-rw-r--r--src/third_party/wiredtiger/src/txn/txn.c65
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_ckpt.c82
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_log.c18
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_nsnap.c55
-rw-r--r--src/third_party/wiredtiger/src/txn/txn_recover.c19
-rw-r--r--src/third_party/wiredtiger/test/bloom/test_bloom.c2
-rwxr-xr-xsrc/third_party/wiredtiger/test/checkpoint/smoke.sh3
-rw-r--r--src/third_party/wiredtiger/test/checkpoint/test_checkpoint.c2
-rw-r--r--src/third_party/wiredtiger/test/csuite/Makefile.am6
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt1965_col_efficiency/main.c2
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2246_col_append/main.c4
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2323_join_visibility/main.c9
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2403_lsm_workload/main.c241
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2447_join_main_table/main.c2
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2535_insert_race/main.c4
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2592_join_schema/main.c8
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2695_checksum/main.c4
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2719_reconfig/main.c6
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2834_join_bloom_fix/main.c18
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2853_perf/main.c4
-rw-r--r--src/third_party/wiredtiger/test/csuite/wt2999_join_extractor/main.c168
-rw-r--r--src/third_party/wiredtiger/test/cursor_order/cursor_order.c2
-rw-r--r--src/third_party/wiredtiger/test/fops/t.c2
-rw-r--r--src/third_party/wiredtiger/test/format/config.c34
-rw-r--r--src/third_party/wiredtiger/test/format/config.h2
-rw-r--r--src/third_party/wiredtiger/test/format/format.h3
-rw-r--r--src/third_party/wiredtiger/test/format/lrt.c16
-rw-r--r--src/third_party/wiredtiger/test/format/t.c6
-rw-r--r--src/third_party/wiredtiger/test/format/util.c22
-rw-r--r--src/third_party/wiredtiger/test/format/wts.c14
-rw-r--r--src/third_party/wiredtiger/test/huge/huge.c2
-rw-r--r--src/third_party/wiredtiger/test/java/com/wiredtiger/test/ConcurrentCloseTest.java21
-rw-r--r--src/third_party/wiredtiger/test/manydbs/manydbs.c2
-rw-r--r--src/third_party/wiredtiger/test/packing/intpack-test.c2
-rw-r--r--src/third_party/wiredtiger/test/packing/intpack-test2.c2
-rw-r--r--src/third_party/wiredtiger/test/packing/intpack-test3.c2
-rw-r--r--src/third_party/wiredtiger/test/packing/packing-test.c2
-rw-r--r--src/third_party/wiredtiger/test/readonly/readonly.c2
-rw-r--r--src/third_party/wiredtiger/test/recovery/random-abort.c86
-rwxr-xr-xsrc/third_party/wiredtiger/test/recovery/smoke.sh1
-rw-r--r--src/third_party/wiredtiger/test/recovery/truncated-log.c2
-rw-r--r--src/third_party/wiredtiger/test/salvage/salvage.c2
-rw-r--r--src/third_party/wiredtiger/test/suite/helper.py187
-rw-r--r--src/third_party/wiredtiger/test/suite/test_async03.py7
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup01.py22
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup02.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup03.py16
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup04.py7
-rw-r--r--src/third_party/wiredtiger/test/suite/test_backup06.py156
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug004.py16
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug005.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug006.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug008.py106
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug011.py15
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug012.py7
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug014.py14
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug015.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bug017.py46
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bulk01.py44
-rw-r--r--src/third_party/wiredtiger/test/suite/test_bulk02.py9
-rw-r--r--src/third_party/wiredtiger/test/suite/test_checkpoint01.py64
-rw-r--r--src/third_party/wiredtiger/test/suite/test_checkpoint02.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_colgap.py28
-rw-r--r--src/third_party/wiredtiger/test/suite/test_compact01.py15
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor06.py40
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor09.py37
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_compare.py89
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_pin.py59
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_random.py39
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_random02.py30
-rw-r--r--src/third_party/wiredtiger/test/suite/test_drop.py15
-rw-r--r--src/third_party/wiredtiger/test/suite/test_drop02.py5
-rw-r--r--src/third_party/wiredtiger/test/suite/test_dump.py56
-rw-r--r--src/third_party/wiredtiger/test/suite/test_dupc.py20
-rw-r--r--src/third_party/wiredtiger/test/suite/test_empty.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_encrypt01.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_inmem01.py41
-rw-r--r--src/third_party/wiredtiger/test/suite/test_inmem02.py62
-rw-r--r--src/third_party/wiredtiger/test/suite/test_join09.py115
-rw-r--r--src/third_party/wiredtiger/test/suite/test_jsondump01.py71
-rw-r--r--src/third_party/wiredtiger/test/suite/test_lsm01.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_lsm02.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_lsm03.py10
-rw-r--r--src/third_party/wiredtiger/test/suite/test_nsnap01.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_nsnap02.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_nsnap03.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_nsnap04.py6
-rw-r--r--src/third_party/wiredtiger/test/suite/test_overwrite.py47
-rw-r--r--src/third_party/wiredtiger/test/suite/test_readonly03.py7
-rw-r--r--src/third_party/wiredtiger/test/suite/test_rebalance.py17
-rw-r--r--src/third_party/wiredtiger/test/suite/test_reconfig01.py6
-rw-r--r--src/third_party/wiredtiger/test/suite/test_reconfig02.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_reconfig03.py10
-rw-r--r--src/third_party/wiredtiger/test/suite/test_rename.py24
-rw-r--r--src/third_party/wiredtiger/test/suite/test_shared_cache01.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_shared_cache02.py1
-rw-r--r--src/third_party/wiredtiger/test/suite/test_split.py3
-rw-r--r--src/third_party/wiredtiger/test/suite/test_stat01.py13
-rw-r--r--src/third_party/wiredtiger/test/suite/test_stat02.py119
-rw-r--r--src/third_party/wiredtiger/test/suite/test_stat03.py44
-rw-r--r--src/third_party/wiredtiger/test/suite/test_stat05.py28
-rw-r--r--src/third_party/wiredtiger/test/suite/test_truncate01.py99
-rw-r--r--src/third_party/wiredtiger/test/suite/test_truncate02.py29
-rw-r--r--src/third_party/wiredtiger/test/suite/test_truncate03.py25
-rw-r--r--src/third_party/wiredtiger/test/suite/test_txn06.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_txn11.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_upgrade.py14
-rw-r--r--src/third_party/wiredtiger/test/suite/test_util02.py4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_util13.py53
-rw-r--r--src/third_party/wiredtiger/test/suite/wtdataset.py302
-rw-r--r--src/third_party/wiredtiger/test/thread/t.c2
-rw-r--r--src/third_party/wiredtiger/test/utility/misc.c4
-rw-r--r--src/third_party/wiredtiger/test/utility/parse_opts.c18
-rw-r--r--src/third_party/wiredtiger/test/utility/test_util.h68
-rw-r--r--src/third_party/wiredtiger/tools/wtstats/stat_data.py40
-rwxr-xr-xsrc/third_party/wiredtiger/tools/wtstats/wtstats.py3
-rw-r--r--version.json4
614 files changed, 15305 insertions, 8137 deletions
diff --git a/SConstruct b/SConstruct
index e64a03bb2da..7eb1536dc6e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1812,6 +1812,12 @@ def doConfigure(myenv):
# see: http://stackoverflow.com/questions/21755206/how-to-get-around-gcc-void-b-4-may-be-used-uninitialized-in-this-funct
AddToCXXFLAGSIfSupported(myenv, "-Wno-maybe-uninitialized")
+ # Disable warning about templates that can't be implicitly instantiated. It is an attempt to
+ # make a link error into an easier-to-debug compiler failure, but it triggers false
+ # positives if explicit instantiation is used in a TU that can see the full definition. This
+ # is a problem at least for the S2 headers.
+ AddToCXXFLAGSIfSupported(myenv, "-Wno-undefined-var-template")
+
# Check if we need to disable null-conversion warnings
if myenv.ToolchainIs('clang'):
def CheckNullConversion(context):
diff --git a/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml b/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml
index bdf8ec02506..43fbcaa94f6 100644
--- a/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml
+++ b/buildscripts/resmokeconfig/suites/sharding_continuous_config_stepdown.yml
@@ -14,7 +14,7 @@ selector:
# Count/write/aggregate/group commands against the config shard do not support retries yet
- jstests/sharding/addshard1.js
- jstests/sharding/addshard2.js
- - jstests/sharding/auto1.js
+ - jstests/sharding/autosplit.js
- jstests/sharding/auto_rebalance.js
- jstests/sharding/autosplit_heuristics.js
- jstests/sharding/balance_tags1.js
@@ -72,12 +72,13 @@ selector:
- jstests/sharding/printShardingStatus.js
# Balancer writes (direct write to config database with no retries)
- jstests/sharding/remove2.js
- - jstests/sharding/auto2.js
+ - jstests/sharding/autosplit_with_balancer.js
- jstests/sharding/features3.js
- jstests/sharding/in_memory_sort_limit.js
- jstests/sharding/parallel.js
- jstests/sharding/migrateBig.js
- jstests/sharding/sharding_rs1.js
+ - jstests/sharding/jumbo1.js
# Calls the config server primary directly (not through mongos)
- jstests/sharding/moveprimary_ignore_sharded.js
- jstests/sharding/min_optime_recovery.js
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index 2137ce282a7..0dc36dd54cf 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -1912,7 +1912,7 @@ tasks:
notary-client.py --key-name "server-3.2" --auth-token-file ${workdir}/src/signing_auth_token --comment "Evergreen Automatic Signing ${revision} - ${build_variant} - ${branch_name}" --notary-url http://notary-service.build.10gen.cc:5000 --skip-missing mongodb-${push_name}-${push_arch}-${suffix}.${ext|tgz} mongodb-shell-${push_name}-${push_arch}-${suffix}.${ext|tgz} mongodb-${push_name}-${push_arch}-debugsymbols-${suffix}.${ext|tgz} mongodb-win32-${push_arch}-${suffix}.msi mongodb-src-${src_suffix}.${ext|tar.gz}
if [ "${has_packages|}" = "true" ]; then
- CURATOR_RELEASE="bb867ba2f0aa10b5fd223b31ef99a054527a386c"
+ CURATOR_RELEASE="fab90fac3cb45c0ef411e647b3d621cbe053fe19"
curl -L -O http://boxes.10gen.com/build/curator/curator-dist-rhel70-$CURATOR_RELEASE.tar.gz
tar -zxvf curator-dist-rhel70-$CURATOR_RELEASE.tar.gz
./curator repo --config ./etc/repo_config.yaml --distro ${packager_distro} --edition ${repo_edition} --version ${version} --arch ${packager_arch} --packages repo
@@ -4324,7 +4324,7 @@ buildvariants:
gorootvars: PATH=/opt/mongodbtoolchain/v2/bin:$PATH
tooltags: -gccgoflags "$(pkg-config --libs --cflags libssl libsasl2)" -tags 'sasl ssl'
# We need to compensate for SMT8 setting the cpu count very high and lower the amount of parallelism down
- compile_flags: --ssl MONGO_DISTMOD=rhel71 --release -j$(echo "$(grep -c processor /proc/cpuinfo)/2" | bc) CCFLAGS="-mcpu=power8 -mtune=power8 -mcmodel=medium" CC=/opt/mongodbtoolchain/v1/bin/gcc CXX=/opt/mongodbtoolchain/v1/bin/g++
+ compile_flags: --ssl MONGO_DISTMOD=rhel71 --release -j$(echo "$(grep -c processor /proc/cpuinfo)/2" | bc) CCFLAGS="-mcpu=power8 -mtune=power8 -mcmodel=medium" CPPDEFINES=BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS CC=/opt/mongodbtoolchain/v1/bin/gcc CXX=/opt/mongodbtoolchain/v1/bin/g++
num_jobs_available: $(echo "$(grep -c processor /proc/cpuinfo)/4" | bc)
test_flags: --continueOnFailure --excludeWithAnyTags=requires_mmapv1
has_packages: true
diff --git a/etc/longevity.yml b/etc/longevity.yml
index 34d2616567c..0b820841616 100644
--- a/etc/longevity.yml
+++ b/etc/longevity.yml
@@ -5,21 +5,48 @@ pre:
- command: shell.track
post:
+ - command: shell.exec
+ params:
+ working_dir: work
+ script: |
+ set -v
+ source ./dsienv.sh
+ $DSI_PATH/bin/make_artifact.sh
+ - command: s3.put
+ params:
+ aws_key: ${aws_key}
+ aws_secret: ${aws_secret}
+ local_file: work/reports.tgz
+ remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/${task_name}-${build_id}.${ext|tgz}
+ bucket: mciuploads
+ permissions: public-read
+ content_type: ${content_type|application/x-gzip}
+ display_name: test-log
+ - command: s3.put
+ params:
+ aws_key: ${aws_key}
+ aws_secret: ${aws_secret}
+ local_file: work/reports/graphs/timeseries-p1.html
+ remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/timeseries-p1-${task_name}-${build_id}.html
+ bucket: mciuploads
+ permissions: public-read
+ content_type: text/html
+ display_name: timeseries-p1.html
- command: attach.results
params:
- file_location: src/report.json
- - command: shell.cleanup
+ file_location: work/report.json
- command: shell.exec
# destroy the cluster
params:
- working_dir: dsi
+ working_dir: work
script: |
set -e
set -o verbose
- cd ./clusters/${cluster}
# call terraform destroy twice to avoid AWS timeout
yes yes | ./terraform destroy
yes yes | ./terraform destroy
+ # clean all file to be safe
+ rm -rf *
echo "Cluster DESTROYED."
functions:
@@ -29,172 +56,159 @@ functions:
script: |
rm -rf ./*
mkdir src
+ mkdir work
+ mkdir bin
+ mkdir keys
+ pwd
+ ls
+ - command: manifest.load
+ - command: git.get_project
+ params:
+ directory: src
+ revisions: # for each module include revision as <module_name> : ${<module_name>_rev}
+ dsi: ${dsi_rev}
+ YCSB: ${YCSB_rev}
+ - command: git.apply_patch
+ params:
+ directory: src
+ - command: shell.exec
+ params:
+ silent: true
+ script: |
+ # generate aws private key file
+ echo "${terraform_secret}" > secret
+ chmod 400 secret
+ echo "${ec2_pem}" > keys/aws.pem
+ chmod 400 keys/aws.pem
+ - command: shell.exec
+ params:
+ working_dir: work
+ # setup execution environment
+ # configure environment
+ script: |
+ virtualenv ./venv
+ source ./venv/bin/activate
+ pip install -r ../src/dsi/dsi/requirements.txt
+ python ../src/dsi/dsi/bin/setup_work_env.py --cluster-type ${cluster} --aws-key-name ${terraform_key} --ssh-keyfile-path ../keys/aws.pem --aws-secret-file ../secret --production
- command: shell.exec
- # checkout and setup DSI environment
params:
script: |
- set -e
set -v
- git clone git@github.com:10gen/dsi.git
- cd dsi
- # setup execution environment
- ./bin/setup-dsi-env.sh
+ source work/dsienv.sh
+ $DSI_PATH/bin/setup-dsi-env.sh
+ cp terraform/* work/
+ ls work
- command: shell.exec
- # configure environment, has private information, no logging
params:
- working_dir: dsi
- silent: true
+ working_dir: work
script: |
- cd ./clusters/${cluster}
- # stage aws credential for terraform
- ../../bin/make_terraform_env.sh ${terraform_key} ${terraform_secret} https://s3.amazonaws.com/mciuploads/longevity-v3.2/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
- # generate aws private key file
- echo "${ec2_pem}" > ../../keys/aws.pem
- chmod 400 ../../keys/aws.pem
+ set -v
+ ./terraform get --update
"bring up 3 shard cluster":
- command: shell.exec
# bring up the cluster
params:
- working_dir: dsi
+ working_dir: work
silent: true
script: |
# to create a mongod EC2 cluster
set -e
set -o verbose
- cd ./clusters/${cluster}
- ../../bin/setup-cluster.sh ${cluster}
- echo "EC2 Cluster CREATED."
+ source ./dsienv.sh
+ $DSI_PATH/bin/setup-cluster.sh ${cluster} false
"configure mongodb cluster":
- command: shell.exec
# bring up the mongod
params:
- working_dir: dsi
+ working_dir: work
script: |
set -e
set -o verbose
+ source ./dsienv.sh
source ./venv/bin/activate
- cd ./clusters/${cluster}
cp mongodb_setup.${setup}.${storageEngine}.yml mongodb_setup.yml
- ../../bin/mongodb_setup.py --config
+ $DSI_PATH/bin/mongodb_setup.py --mongodb-binary-archive https://s3.amazonaws.com/mciuploads/longevity-v3.2/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
"run test":
- command: shell.exec
params:
- working_dir: dsi
+ working_dir: work
script: |
set -e
set -v
- cd ./clusters/${cluster}
echo "Run test for ${test}-${storageEngine} with setup ${setup}"
- ../../bin/run-${test}.sh ${storageEngine} ${setup} ${cluster}
- cd ..
- cat perf.json >> perf_all.json
+ source ./dsienv.sh
+ source ./venv/bin/activate
+ # Copy over the proper test control file
+ cp $DSI_PATH/test_control/test_control.${test}.longevity.yml test_control.yml
+ $DSI_PATH/bin/run-${test}.sh ${storageEngine} ${setup} ${cluster}
echo "Complete test for ${test} with setup ${setup}!"
- command: "json.send"
params:
name: "perf"
- file: "dsi/clusters/perf.json"
+ file: "work/perf.json"
"destroy cluster":
- command: shell.exec
# destroy the cluster
params:
- working_dir: dsi
+ working_dir: work
script: |
set -e
set -o verbose
- cd ./clusters/${cluster}
+ source ./dsienv.sh
# destroy the EC2 cluster
yes yes | ./terraform destroy
yes yes | ./terraform destroy
- # clean all file to be safe
- rm -rf *
echo "Cluster DESTROYED."
echo "All perf results"
cd ..
- cat perf_all.json | egrep "name|ops_per_sec"
+ cat perf.json | egrep "name|ops_per_sec"
"make test log artifact":
- command: shell.exec
- # destroy the cluster
params:
- working_dir: dsi
+ working_dir: work
script: |
- set -e
- set -o verbose
- cd ./clusters/${cluster}/reports
- # move additional file here
- cp ../infrastructure_provisioning.out.yml .
- cp ../ips.sh .
- cp ../ips.py .
- cp ../../perf_all.json .
- cd ..
- ../../bin/retrieve-diag-data.sh
- ../../bin/generate-timeseries-html.sh
- rm -rf ../reports
- mv ./reports ..
-
- - command: archive.targz_pack
- params:
- target: "reports.tgz"
- source_dir: "dsi/clusters"
- include:
- - "reports/**"
-
- "upload log file":
- - command: s3.put
- params:
- aws_key: ${aws_key}
- aws_secret: ${aws_secret}
- local_file: reports.tgz
- remote_file: longevity-v3.2/${build_variant}/${revision}/logs/${test}-${build_id}.${ext|tgz}
- bucket: mciuploads
- permissions: public-read
- content_type: ${content_type|application/x-gzip}
- display_name: ${test}-longevity-testlog
- - command: s3.put
- params:
- aws_key: ${aws_key}
- aws_secret: ${aws_secret}
- local_file: dsi/clusters/reports/graphs/timeseries-p1.html
- remote_file: dsi/${build_variant}/${revision}/${task_id}/${version_id}/logs/timeseries-p1-${test}-${build_id}.html
- bucket: mciuploads
- permissions: public-read
- content_type: text/html
- display_name: timeseries-p1.html
+ set -v
+ source ./dsienv.sh
+ $DSI_PATH/bin/make_artifact.sh
"analyze":
- command: json.get_history
params:
task: ${task_name}
- file: "src/history.json"
+ file: "work/history.json"
name: "perf"
- command: json.get_history
params:
tags: true
task: ${task_name}
- file: "src/tags.json"
+ file: "work/tags.json"
name: "perf"
- command: shell.exec
# post_run_check.py and longevity_override.json for DSI tests are part of dsi repo
type : test
params:
- working_dir: src
+ working_dir: work
script: |
set -o errexit
set -o verbose
- python -u ../dsi/analysis/post_run_check.py --ycsb-throughput-analysis . --rev ${revision} -f history.json -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/analysis/v3.2/longevity_override.json --project_id mongo-longevity --task_name ${task_name} --variant ${build_variant}
-
+ TAG="3.0.12-Baseline"
+ PROJECT="mongo-longevity"
+ OVERRIDEFILE="../src/dsi/dsi/analysis/v3.2/longevity_override.json"
+ python -u ../src/dsi/dsi/analysis/post_run_check.py ${script_flags} --reports-analysis reports --perf-file reports/perf.json --rev ${revision} -f history.json -t tags.json --refTag $TAG --overrideFile $OVERRIDEFILE --project_id $PROJECT --task_name ${task_name} --variant ${build_variant}
tasks:
- name: compile
commands:
+ - command: manifest.load
- command: git.get_project
params:
directory: src
- - command: git.apply_patch
+ - command: shell.exec
params:
directory: src
- command: shell.exec
@@ -238,65 +252,85 @@ tasks:
storageEngine: "wiredTiger"
test: "ycsb"
- func: "make test log artifact"
- - func: "upload log file"
+ - func: "analyze"
vars:
- test: "ycsb"
+ script_flags: --ycsb-throughput-analysis reports
- func: "destroy cluster"
- - func: "analyze"
- name: shard_cluster_MMAPv1_test
depends_on:
- name: compile
- exec_timeout_secs: 28800
+ exec_timeout_secs: 216000
commands:
- func: "prepare environment"
- func: "bring up 3 shard cluster"
- func: "configure mongodb cluster"
- func: "run test"
vars:
- test: "ycsb"
storageEngine: "mmapv1"
test: "ycsb"
- func: "make test log artifact"
- - func: "upload log file"
+ - func: "analyze"
vars:
- test: "ycsb"
+ script_flags: --ycsb-throughput-analysis reports
- func: "destroy cluster"
- - func: "analyze"
+
+
+#######################################
+# Modules #
+#######################################
+# if a module is added and to be added to the manifest
+# be sure to add the module to git.get_project revisions parameter
+modules:
+- name: dsi
+ repo: git@github.com:10gen/dsi.git
+ prefix: dsi
+ branch: master
+
+- name: YCSB
+ repo: git@github.com:mongodb-labs/YCSB.git
+ prefix: YCSB
+ branch: evergreen
+
+
buildvariants:
- name: linux-wt-shard
display_name: Linux WT Shard
batchtime: 40320 # 4 weeks
+ modules: &modules
+ - dsi
+ - YCSB
expansions:
compile_flags: -j$(grep -c ^processor /proc/cpuinfo) CC=/opt/mongodbtoolchain/bin/gcc CXX=/opt/mongodbtoolchain/bin/g++ --release
setup: shard
cluster: longevity
numShard: 3
storageEngine: wiredTiger
-
+ project: &project longevity-v3.2
run_on:
- "rhel70-perf-longevity"
tasks:
- name: compile
distros:
- - rhel55
+ - rhel70
- name: shard_cluster_test
- name: linux-mmapv1-shard
display_name: Linux MMAPv1 Shard
batchtime: 40320 # 4 week
+ modules: *modules
expansions:
compile_flags: -j$(grep -c ^processor /proc/cpuinfo) CC=/opt/mongodbtoolchain/bin/gcc CXX=/opt/mongodbtoolchain/bin/g++ --release
setup: shard
cluster: longevity
numShard: 3
storageEngine: mmapv1
-
+ project: *project
run_on:
- "rhel70-perf-longevity"
tasks:
- name: compile
distros:
- - rhel55
+ - rhel70
- name: shard_cluster_MMAPv1_test
diff --git a/etc/perf.yml b/etc/perf.yml
index ec092a9512d..1bfa19bff9e 100644
--- a/etc/perf.yml
+++ b/etc/perf.yml
@@ -145,7 +145,7 @@ functions:
cmd_flags=$([ "${reports_analysis}" = "true" ] && echo "$reports_analysis_flags" || echo "")
REFTAG="3.0.12-Baseline"
OVERRIDE="../dsi/analysis/v3.2/perf_override.json" # Note use of v3.2 here cannot use ${project} perf
- python ../dsi/analysis/perf_regression_check.py $cmd_flags -f history.json --rev ${revision} -t tags.json --refTag $REFTAG --overrideFile $OVERRIDE --variant ${build_variant} --threshold 0.10 --threadThreshold 0.15
+ python ../dsi/analysis/perf_regression_check.py $cmd_flags -f history.json --rev ${revision} -t tags.json --refTag $REFTAG --overrideFile $OVERRIDE --variant ${build_variant} --task ${task_name} --threshold 0.10 --threadThreshold 0.15
"run perf tests":
- command: shell.exec
params:
@@ -393,220 +393,6 @@ tasks:
variant1 : "linux-wt-standalone"
variant2 : "linux-wt-repl"
- func: "analyze"
-- name: singleThreaded-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : singleThreaded
- variant : linux-wt-standalone
- status : "*"
- - name : singleThreaded
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "singleThreaded"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: query-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : query
- variant : linux-wt-standalone
- status : "*"
- - name : query
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "query"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: where-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : where
- variant : linux-wt-standalone
- status : "*"
- - name : where
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "where"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: geo-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : geo
- variant : linux-wt-standalone
- status : "*"
- - name : geo
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "geo"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: insert-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : insert
- variant : linux-wt-standalone
- status : "*"
- - name : insert
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "insert"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: update-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : update
- variant : linux-wt-standalone
- status : "*"
- - name : update
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "update"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: misc-wt-mmap-standalone-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : misc
- variant : linux-wt-standalone
- status : "*"
- - name : misc
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "misc"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: aggregation-wt-mmap-standalone-comp
- depends_on:
- - name : aggregation
- variant : linux-wt-standalone
- status : "*"
- - name : aggregation
- variant : linux-mmap-standalone
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "aggregation"
- variant2 : "linux-wt-standalone"
- variant1 : "linux-mmap-standalone"
- - func: "analyze"
-- name: singleThreaded-wt-mmap-repl-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : singleThreaded
- variant : linux-wt-repl
- status : "*"
- - name : singleThreaded
- variant : linux-mmap-repl
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "singleThreaded"
- variant2 : "linux-wt-repl"
- variant1 : "linux-mmap-repl"
- - func: "analyze"
-- name: insert-wt-mmap-repl-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : insert
- variant : linux-wt-repl
- status : "*"
- - name : insert
- variant : linux-mmap-repl
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "insert"
- variant2 : "linux-wt-repl"
- variant1 : "linux-mmap-repl"
- - func: "analyze"
-- name: update-wt-mmap-repl-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : update
- variant : linux-wt-repl
- status : "*"
- - name : update
- variant : linux-mmap-repl
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "update"
- variant2 : "linux-wt-repl"
- variant1 : "linux-mmap-repl"
- - func: "analyze"
-- name: misc-wt-mmap-repl-comp
- depends_on:
- - name : compile
- variant : linux-wt-standalone
- - name : misc
- variant : linux-wt-repl
- status : "*"
- - name : misc
- variant : linux-mmap-repl
- status : "*"
- commands:
- - func: "download analysis scripts"
- - func: "compare"
- vars:
- compare_task : "misc"
- variant2 : "linux-wt-repl"
- variant1 : "linux-mmap-repl"
- - func: "analyze"
- name: singleThreaded-mmap-repl-comp
depends_on:
- name : compile
@@ -803,34 +589,3 @@ buildvariants:
- name: insert-mmap-repl-comp
- name: update-mmap-repl-comp
- name: misc-mmap-repl-comp
-
-- name: linux-wt-mmap-standalone-compare
- display_name: Standalone Linux WT Comparison MMAPv1
- batchtime: 90 # 1.5 hours
- expansions:
- project: *project
- run_on:
- - "centos6-perf"
-# - "ubuntu1404-docker"
- tasks:
- - name: singleThreaded-wt-mmap-standalone-comp
- - name: insert-wt-mmap-standalone-comp
- - name: update-wt-mmap-standalone-comp
- - name: misc-wt-mmap-standalone-comp
- - name: query-wt-mmap-standalone-comp
- - name: where-wt-mmap-standalone-comp
- - name: geo-wt-mmap-standalone-comp
- - name: aggregation-wt-mmap-standalone-comp
-- name: linux-wt-mmap-repl-compare
- display_name: 1-Node ReplSet Linux WT Comparison MMAPv1
- batchtime: 360 # 6 hours
- expansions:
- project: *project
- run_on:
- - "centos6-perf"
-# - "ubuntu1404-docker"
- tasks:
- - name: singleThreaded-wt-mmap-repl-comp
- - name: insert-wt-mmap-repl-comp
- - name: update-wt-mmap-repl-comp
- - name: misc-wt-mmap-repl-comp
diff --git a/etc/system_perf.yml b/etc/system_perf.yml
index 007202c160a..bf39c984d1d 100644
--- a/etc/system_perf.yml
+++ b/etc/system_perf.yml
@@ -5,6 +5,32 @@ pre:
- command: shell.track
post:
+ - command: shell.exec
+ params:
+ working_dir: work
+ script: |
+ source ./dsienv.sh
+ $DSI_PATH/bin/make_artifact.sh
+ - command: s3.put
+ params:
+ aws_key: ${aws_key}
+ aws_secret: ${aws_secret}
+ local_file: work/reports.tgz
+ remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/${task_name}-${build_id}.${ext|tgz}
+ bucket: mciuploads
+ permissions: public-read
+ content_type: ${content_type|application/x-gzip}
+ display_name: test-log
+ - command: s3.put
+ params:
+ aws_key: ${aws_key}
+ aws_secret: ${aws_secret}
+ local_file: work/reports/graphs/timeseries-p1.html
+ remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/timeseries-p1-${task_name}-${build_id}.html
+ bucket: mciuploads
+ permissions: public-read
+ content_type: text/html
+ display_name: timeseries-p1.html
- command: attach.results
params:
file_location: work/report.json
@@ -108,16 +134,12 @@ functions:
set -o verbose
source ./dsienv.sh
if [ ! -e $HOME/infrastructure_provisioning/terraform/provisioned.${cluster} ]; then
- echo "Provisinging new resources."
- $DSI_PATH/bin/setup-cluster.sh ${cluster} ../terraform
+ EXISTING="false"
else
- # on host with pre-existing resources, call terraform directly to
- # avoid recreating instances due to terraform apply concurrency limitation
- echo "Update expire-on tag for existing resources."
- ./terraform apply -var-file=cluster.json
- # call env.sh to generate ips.sh, etc. for downstream modules
- $DSI_PATH/bin/env.sh
+ EXISTING="true"
fi
+
+ $DSI_PATH/bin/setup-cluster.sh ${cluster} $EXISTING
# handle the case when setup-cluster.sh fail
if [ $? -eq 0 ]; then
echo "Resource provisioned/updated."
@@ -188,47 +210,8 @@ functions:
params:
working_dir: work
script: |
- set -e
- set -o verbose
source ./dsienv.sh
- cd reports
- # move additional file here
- cp ../infrastructure_provisioning.out.yml .
- if [ -f "../terraform.log" ]; then cp ../terraform.log .; fi
- cp ../perf.json .
- cd ..
- mkdir -p ./reports/graphs
- touch ./reports/graphs/timeseries-p1.html
- $DSI_PATH/bin/retrieve-diag-data.sh
- $DSI_PATH/bin/generate-timeseries-html.sh || true
- - command: archive.targz_pack
- params:
- target: "reports.tgz"
- source_dir: work
- include:
- - "reports/**"
-
- "upload log file":
- - command: s3.put
- params:
- aws_key: ${aws_key}
- aws_secret: ${aws_secret}
- local_file: reports.tgz
- remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/${test}-${build_id}.${ext|tgz}
- bucket: mciuploads
- permissions: public-read
- content_type: ${content_type|application/x-gzip}
- display_name: ${test}-test-log
- - command: s3.put
- params:
- aws_key: ${aws_key}
- aws_secret: ${aws_secret}
- local_file: work/reports/graphs/timeseries-p1.html
- remote_file: ${project}/${build_variant}/${revision}/${task_id}/${version_id}/logs/timeseries-p1-${test}-${build_id}.html
- bucket: mciuploads
- permissions: public-read
- content_type: text/html
- display_name: timeseries-p1.html
+ $DSI_PATH/bin/make_artifact.sh
"analyze":
- command: json.get_history
@@ -253,7 +236,7 @@ functions:
TAGS="3.1.8-Baseline 3.2.0-Baseline 3.2.9-Baseline"
PROJECT="sys-perf-3.2"
OVERRIDEFILE="../src/dsi/dsi/analysis/v3.2/system_perf_override.json"
- python -u ../src/dsi/dsi/analysis/dashboard_gen.py --rev ${revision} -f history.json -t tags.json --refTag $TAGS --overrideFile $OVERRIDEFILE --project_id sys-perf --task_name ${task_name} --variant ${build_variant} --jira-user ${perf_jira_user} --jira-password ${perf_jira_pw} || true
+ python -u ../src/dsi/dsi/analysis/dashboard_gen.py --rev ${revision} -f history.json -t tags.json --refTag $TAGS --overrideFile $OVERRIDEFILE --project_id sys-perf --variant ${build_variant} --task ${task_name} --jira-user ${perf_jira_user} --jira-password ${perf_jira_pw} || true
- command: "json.send"
params:
name: "dashboard"
@@ -268,7 +251,7 @@ functions:
set -o verbose
TAG="3.0.6-Baseline"
OVERRIDEFILE="../src/dsi/dsi/analysis/v3.2/system_perf_override.json"
- python -u ../src/dsi/dsi/analysis/post_run_check.py ${script_flags} --reports-analysis reports --perf-file reports/perf.json --rev ${revision} -f history.json -t tags.json --refTag $TAG --overrideFile $OVERRIDEFILE --project_id sys-perf --task_name ${task_name} --variant ${build_variant}
+ python -u ../src/dsi/dsi/analysis/post_run_check.py ${script_flags} --reports-analysis reports --perf-file reports/perf.json --rev ${revision} -f history.json -t tags.json --refTag $TAG --overrideFile $OVERRIDEFILE --project_id sys-perf --variant ${build_variant} --task ${task_name}
"compare":
- command: shell.exec
@@ -362,9 +345,6 @@ tasks:
test: "ycsb"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "ycsb"
- func: "analyze"
vars:
script_flags: --ycsb-throughput-analysis reports
@@ -385,9 +365,6 @@ tasks:
test: "ycsb"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "ycsb"
- func: "analyze"
vars:
script_flags: --ycsb-throughput-analysis reports
@@ -408,9 +385,6 @@ tasks:
test: "benchRun"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "core_workloads_WT"
- func: "analyze"
- name: core_workloads_MMAPv1
@@ -429,9 +403,6 @@ tasks:
test: "benchRun"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "core_workloads_MMAPv1"
- func: "analyze"
- name: industry_benchmarks_WT_oplog_comp
@@ -514,9 +485,6 @@ tasks:
test: "initialSync"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "initialsync_WT"
- func: "analyze"
- name: initialsync_MMAPv1
@@ -535,9 +503,6 @@ tasks:
test: "initialSync"
testList: "default"
- func: "make test log artifact"
- - func: "upload log file"
- vars:
- test: "initialsync_MMAPv1"
- func: "analyze"
diff --git a/jstests/aggregation/bugs/lookup_unwind_getmore.js b/jstests/aggregation/bugs/lookup_unwind_getmore.js
index 6c8d886b78f..0650b2b940b 100644
--- a/jstests/aggregation/bugs/lookup_unwind_getmore.js
+++ b/jstests/aggregation/bugs/lookup_unwind_getmore.js
@@ -7,27 +7,30 @@
(function() {
'use strict';
- // We use a batch size of 1 to ensure that the mongo shell issues a getMore when unwinding the
+ // We use a batch size of 2 to ensure that the mongo shell issues a getMore when unwinding the
// results from the 'dest' collection for the same document in the 'source' collection under a
// different OperationContext.
- const batchSize = 1;
+ const batchSize = 2;
- db.source.drop();
- db.dest.drop();
+ const conn =
+ MongoRunner.runMongod({setParameter: "internalAggregationLookupBatchSize=" + batchSize});
+ assert.neq(null, conn, "mongod failed to start up");
+ const testDB = conn.getDB("test");
- assert.writeOK(db.source.insert({local: 1}));
+ testDB.source.drop();
+ testDB.dest.drop();
- // We insert documents in the 'dest' collection such that their combined size is greater than
- // 16MB in order to ensure that the DBDirectClient used by the $lookup stage issues a getMore
- // under a different OperationContext.
- const numMatches = 3;
- const largeStr = new Array(6 * 1024 * 1024 + 1).join('x');
+ assert.writeOK(testDB.source.insert({local: 1}));
+ // The cursor batching logic actually requests one more document than it needs to fill the
+ // first batch, so if we want to leave the $lookup stage paused with a cursor open we'll
+ // need two more matching documents than the batch size.
+ const numMatches = batchSize + 2;
for (var i = 0; i < numMatches; ++i) {
- assert.writeOK(db.dest.insert({foreign: 1, largeStr: largeStr}));
+ assert.writeOK(testDB.dest.insert({foreign: 1}));
}
- var res = db.runCommand({
+ var res = testDB.runCommand({
aggregate: 'source',
pipeline: [
{
@@ -50,6 +53,8 @@
});
assert.commandWorked(res);
- var cursor = new DBCommandCursor(db.getMongo(), res, batchSize);
+ var cursor = new DBCommandCursor(conn, res, batchSize);
assert.eq(numMatches, cursor.itcount());
+
+ assert.eq(0, MongoRunner.stopMongod(conn), "expected mongod to shutdown cleanly");
})();
diff --git a/jstests/aggregation/bugs/lookup_unwind_killcursor.js b/jstests/aggregation/bugs/lookup_unwind_killcursor.js
new file mode 100644
index 00000000000..1668e68c436
--- /dev/null
+++ b/jstests/aggregation/bugs/lookup_unwind_killcursor.js
@@ -0,0 +1,127 @@
+/**
+ * Tests that the server can successfully kill the cursor of an aggregation pipeline which is
+ * using a $lookup stage with its own cursor.
+ *
+ * This test was designed to reproduce SERVER-24386.
+ */
+(function() {
+ 'use strict';
+
+ // Use a low batch size for the aggregation commands to ensure that the mongo shell does not
+ // exhaust cursors during the first batch.
+ const batchSize = 2;
+
+ // Use a small batch in the $lookup stage to ensure it needs to issue a getMore. This will help
+ // ensure that the $lookup stage has an open cursor when the first batch is returned.
+ var conn =
+ MongoRunner.runMongod({setParameter: "internalAggregationLookupBatchSize=" + batchSize});
+ assert.neq(null, conn, 'mongod was unable to start up');
+ const testDB = conn.getDB("test");
+
+ function setup() {
+ testDB.source.drop();
+ testDB.dest.drop();
+
+ assert.writeOK(testDB.source.insert({local: 1}));
+
+ // The cursor batching logic actually requests one more document than it needs to fill the
+ // first batch, so if we want to leave the $lookup stage paused with a cursor open we'll
+ // need two more matching documents than the batch size.
+ const numMatches = batchSize + 2;
+ for (var i = 0; i < numMatches; ++i) {
+ assert.writeOK(testDB.dest.insert({foreign: 1}));
+ }
+ }
+
+ setup();
+
+ const cmdObj = {
+ aggregate: 'source',
+ pipeline: [
+ {
+ $lookup: {
+ from: 'dest',
+ localField: 'local',
+ foreignField: 'foreign',
+ as: 'matches',
+ }
+ },
+ {
+ $unwind: {
+ path: '$matches',
+ },
+ },
+ ],
+ cursor: {
+ batchSize: batchSize,
+ },
+ };
+
+ var res = testDB.runCommand(cmdObj);
+ assert.commandWorked(res);
+
+ var cursor = new DBCommandCursor(conn, res, batchSize);
+ cursor.close(); // Closing the cursor will issue a killCursor command.
+
+ // Ensure the $lookup stage can be killed by dropping the collection.
+ res = testDB.runCommand(cmdObj);
+ assert.commandWorked(res);
+
+ cursor = new DBCommandCursor(conn, res, batchSize);
+ testDB.source.drop();
+
+ assert.throws(function() {
+ cursor.itcount();
+ }, [], "expected cursor to have been destroyed during collection drop");
+
+ // Ensure the $lookup stage can be killed by dropping the database.
+ setup();
+ res = testDB.runCommand(cmdObj);
+ assert.commandWorked(res);
+
+ cursor = new DBCommandCursor(conn, res, batchSize);
+ assert.commandWorked(testDB.dropDatabase());
+
+ assert.throws(function() {
+ cursor.itcount();
+ }, [], "expected cursor to have been destroyed during database drop");
+
+ // Ensure the $lookup stage can be killed by the ClientCursorMonitor.
+ setup();
+ res = testDB.runCommand(cmdObj);
+ assert.commandWorked(res);
+ cursor = new DBCommandCursor(conn, res, batchSize);
+
+ var serverStatus = assert.commandWorked(testDB.serverStatus());
+ const expectedNumTimedOutCursors = serverStatus.metrics.cursor.timedOut + 1;
+
+ // Wait until the idle cursor background job has killed the aggregation cursor.
+ assert.commandWorked(testDB.adminCommand({setParameter: 1, cursorTimeoutMillis: 1000}));
+ const cursorTimeoutFrequencySeconds = 4;
+ assert.soon(
+ function() {
+ serverStatus = assert.commandWorked(testDB.serverStatus());
+ // Use >= here since we may time out the $lookup stage's cursor as well.
+ return serverStatus.metrics.cursor.timedOut >= expectedNumTimedOutCursors;
+ },
+ function() {
+ return "aggregation cursor failed to time out: " + tojson(serverStatus);
+ },
+ cursorTimeoutFrequencySeconds * 1000 * 5);
+
+ assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus));
+
+ // We attempt to exhaust the aggregation cursor to verify that sending a getMore returns an
+ // error due to the cursor being killed.
+ var err = assert.throws(function() {
+ cursor.itcount();
+ });
+ assert.eq(ErrorCodes.CursorNotFound, err.code, tojson(err));
+
+ // Ensure the $lookup stage can be killed by shutting down the server.
+ setup();
+ res = testDB.runCommand(cmdObj);
+ assert.commandWorked(res);
+
+ assert.eq(0, MongoRunner.stopMongod(conn), "expected mongod to shutdown cleanly");
+})();
diff --git a/jstests/core/apply_ops1.js b/jstests/core/apply_ops1.js
index 8a19caa9f23..1770baef860 100644
--- a/jstests/core/apply_ops1.js
+++ b/jstests/core/apply_ops1.js
@@ -60,6 +60,139 @@
db.adminCommand({applyOps: [{op: 'c', ns: ''}]}),
'applyOps should fail on non-"n" operation type with empty "ns" field value');
+ // Excessively nested applyOps commands gracefully fail.
+ assert.commandFailed(db.adminCommand({
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts":
+ {"$timestamp": {"t": 1, "i": 100}},
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {
+ "$timestamp":
+ {"t": 1, "i": 100}
+ },
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {
+ "$timestamp": {
+ "t": 1,
+ "i": 100
+ }
+ },
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns": "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {
+ "$timestamp":
+ {
+ "t":
+ 1,
+ "i":
+ 100
+ }
+ },
+ "h": 0,
+ "v": 2,
+ "op": "c",
+ "ns":
+ "test.$cmd",
+ "o": {
+ "applyOps": [{
+ "ts": {
+ "$timestamp": {
+ "t":
+ 1,
+ "i":
+ 100
+ }
+ },
+ "h": 0,
+ "v": 2,
+ "op":
+ "c",
+ "ns":
+ "test.$cmd",
+ "o": {
+ "applyOps":
+ []
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }
+ }]
+ }),
+ "Excessively nested applyOps should be rejected");
+
// Missing 'o' field value in an operation of type 'i' on 'system.indexes' collection.
assert.commandFailedWithCode(
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes'}]}),
@@ -147,7 +280,8 @@
"Applying an insert operation on a non-existent collection should fail");
assert.commandWorked(db.createCollection(t.getName()));
- var a = db.adminCommand({applyOps: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}]});
+ var a = assert.commandWorked(
+ db.adminCommand({applyOps: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}]}));
assert.eq(1, t.find().count(), "Valid insert failed");
assert.eq(true, a.results[0], "Bad result value for valid insert");
@@ -162,12 +296,12 @@
};
assert.eq(o, t.findOne(), "Mismatching document inserted.");
- var res = db.runCommand({
+ var res = assert.commandWorked(db.runCommand({
applyOps: [
{op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$inc: {x: 1}}},
{op: "u", ns: t.getFullName(), o2: {_id: 5}, o: {$inc: {x: 1}}}
]
- });
+ }));
o.x++;
o.x++;
diff --git a/jstests/multiVersion/initialize_from_old_node.js b/jstests/multiVersion/initialize_from_old_node.js
index 79aaafb6a4e..83f82845160 100644
--- a/jstests/multiVersion/initialize_from_old_node.js
+++ b/jstests/multiVersion/initialize_from_old_node.js
@@ -19,4 +19,5 @@
var config = rst.getReplSetConfig();
var response = oldNode.getDB("admin").runCommand({replSetInitiate: config});
assert.eq(response.ok, 1);
+ rst.awaitReplication();
})();
diff --git a/jstests/multiVersion/large_document_sync.js b/jstests/multiVersion/large_document_sync.js
new file mode 100644
index 00000000000..3728ddfa75d
--- /dev/null
+++ b/jstests/multiVersion/large_document_sync.js
@@ -0,0 +1,62 @@
+/**
+ * Tests syncing large objects from a 3.0 node to a 3.2 node. In particular, this test was designed
+ * to stress the logic for upconverting an OP_REPLY that contains more than 16MB worth of documents.
+ *
+ * This test was designed to reproduce SERVER-26182.
+ */
+(function() {
+ // Create a replica set with one "3.0" node and one "3.2" node.
+ var replSetName = "testset";
+ var nodes = [{binVersion: "3.0"}, {binVersion: "latest"}];
+
+ var rst = ReplSetTest({name: replSetName, nodes: nodes, nodeOptions: {vv: ''}});
+ rst.startSet();
+
+ // Rig the election so that the 3.0 node becomes primary.
+ var replSetConfig = rst.getReplSetConfig();
+ replSetConfig.members[1].priority = 0;
+ rst.initiate(replSetConfig);
+
+ var primaryDB = rst.getPrimary().getDB("test");
+
+ primaryDB.c.drop();
+
+ var docCloseTo1MB = {
+ x: new Array(900 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo1MB), 0.5 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo1MB), 1 * 1024 * 1024);
+
+ var docCloseTo4MB = {
+ x: new Array(3.5 * 1024 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo4MB), 3 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo4MB), 4 * 1024 * 1024);
+
+ var docCloseTo16MB = {
+ x: new Array(15.5 * 1024 * 1024).join("x")
+ };
+ assert.gte(Object.bsonsize(docCloseTo16MB), 15 * 1024 * 1024);
+ assert.lt(Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ assert.gt(Object.bsonsize(docCloseTo4MB) + Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ assert.gt(Object.bsonsize(docCloseTo1MB) + Object.bsonsize(docCloseTo16MB), 16 * 1024 * 1024);
+
+ rst.getPrimary().forceWriteMode("commands");
+ // The first find has a threshold of 1MB, so put almost 1MB in, then a huge
+ // document to make the total over 16MB.
+ assert.writeOK(primaryDB.c.insert(docCloseTo1MB));
+ assert.writeOK(primaryDB.c.insert(docCloseTo16MB));
+
+ // The first getMore batch should contain both the ~4 MB and the ~16 MB
+ // document, leading to more than 16 MB of user data in the batch.
+ assert.writeOK(primaryDB.c.insert(docCloseTo4MB));
+ assert.writeOK(primaryDB.c.insert(docCloseTo16MB));
+
+ rst.awaitReplication();
+ assert.commandWorked(rst.getSecondary().adminCommand({isMaster: 1}),
+ "expected secondary to survive syncing large documents");
+
+ rst.stopSet();
+})();
diff --git a/jstests/noPassthrough/index_no_retry.js b/jstests/noPassthrough/index_no_retry.js
new file mode 100644
index 00000000000..e3c00182a17
--- /dev/null
+++ b/jstests/noPassthrough/index_no_retry.js
@@ -0,0 +1,66 @@
+// Check index rebuild is disabled with --noIndexBuildRetry when MongoDB is killed.
+//
+// This test requires persistence beacuase it assumes data/indices will survive a restart.
+// This test requires journaling because the information that an index build was started
+// must be made durable when the process aborts.
+// @tags: [requires_persistence, requires_journaling]
+(function() {
+ 'use strict';
+ var baseName = 'index_no_retry';
+ var dbpath = MongoRunner.dataPath + baseName;
+
+ var conn = MongoRunner.runMongod({dbpath: dbpath});
+ assert.neq(null, conn, 'failed to start mongod');
+
+ var test = conn.getDB("test");
+ var pid = test.serverStatus().pid;
+
+ var name = 'jstests_slownightly_' + baseName;
+ var t = test.getCollection(name);
+ t.drop();
+
+ var bulk = t.initializeUnorderedBulkOp();
+ for (var i = 0; i < 100; ++i) {
+ bulk.insert({a: i});
+ }
+
+ // Make sure the documents are journaled
+ assert.writeOK(bulk.execute({j: true}));
+
+ assert.eq(100, t.count(), 'unexpected number of documents after bulk insert.');
+
+ function abortDuringIndexBuild(options) {
+ var createIdx = startParallelShell(function() {
+ var coll = db.getSiblingDB('test').getCollection('jstests_slownightly_index_no_retry');
+
+ // Fail point will handle journal flushing and killing the mongod
+ assert.commandWorked(db.adminCommand(
+ {configureFailPoint: 'crashAfterStartingIndexBuild', mode: 'alwaysOn'}));
+ coll.createIndex({a: 1}, {background: true});
+ }, conn.port);
+
+ var exitCode = createIdx({checkExitSuccess: false});
+ assert.neq(0, exitCode, "expected shell to exit abnormally due to mongod being terminated");
+ }
+
+ abortDuringIndexBuild();
+
+ var EXIT_TEST = 101;
+ assert.eq(waitProgram(pid),
+ EXIT_TEST,
+ "mongod should have crashed due to the 'crashAfterStartingIndexBuild' " +
+ "failpoint being set.");
+
+ conn = MongoRunner.runMongod({dbpath: dbpath, noIndexBuildRetry: '', restart: true});
+ test = conn.getDB("test");
+ t = test.getCollection(name);
+
+ assert.throws(function() {
+ t.find({}, {_id: 0, a: 1}).hint({a: 1}).next();
+ }, null, 'index {a: 1} was rebuilt in spite of --noIndexBuildRetry');
+
+ var indexes = t.getIndexes();
+ assert.eq(1, indexes.length, 'unfinished indexes in listIndexes result: ' + tojson(indexes));
+
+ MongoRunner.stopMongod(conn.port);
+}());
diff --git a/jstests/noPassthrough/index_retry.js b/jstests/noPassthrough/index_retry.js
new file mode 100644
index 00000000000..a221f199471
--- /dev/null
+++ b/jstests/noPassthrough/index_retry.js
@@ -0,0 +1,67 @@
+// Check index rebuild when MongoDB is killed.
+//
+// This test requires persistence because it assumes data/indices will survive a restart.
+// This test requires journaling because the information that an index build was started
+// must be made durable when the process aborts.
+// @tags: [requires_persistence, requires_journaling]
+(function() {
+ 'use strict';
+ var baseName = 'index_retry';
+ var dbpath = MongoRunner.dataPath + baseName;
+
+ var conn = MongoRunner.runMongod({dbpath: dbpath});
+ assert.neq(null, conn, 'failed to start mongod');
+
+ var test = conn.getDB("test");
+ var pid = test.serverStatus().pid;
+
+ var name = 'jstests_slownightly_' + baseName;
+ var t = test.getCollection(name);
+ t.drop();
+
+ var bulk = t.initializeUnorderedBulkOp();
+ for (var i = 0; i < 100; ++i) {
+ bulk.insert({a: i});
+ }
+
+ // Make sure the documents are journaled
+ assert.writeOK(bulk.execute({j: true}));
+
+ assert.eq(100, t.count(), 'unexpected number of documents after bulk insert.');
+
+ function abortDuringIndexBuild() {
+ var createIdx = startParallelShell(function() {
+ var coll = db.getSiblingDB('test').getCollection('jstests_slownightly_index_retry');
+
+ // Fail point will handle journal flushing and killing the mongod
+ assert.commandWorked(db.adminCommand(
+ {configureFailPoint: 'crashAfterStartingIndexBuild', mode: 'alwaysOn'}));
+ coll.createIndex({a: 1}, {background: true});
+ }, conn.port);
+
+ var exitCode = createIdx({checkExitSuccess: false});
+ assert.neq(0, exitCode, "expected shell to exit abnormally due to mongod being terminated");
+ }
+
+ abortDuringIndexBuild();
+
+ var EXIT_TEST = 101;
+ assert.eq(waitProgram(pid),
+ EXIT_TEST,
+ "mongod should have crashed due to the 'crashAfterStartingIndexBuild' " +
+ "failpoint being set.");
+
+ conn = MongoRunner.runMongod({dbpath: dbpath, restart: true});
+ test = conn.getDB("test");
+ t = test.getCollection(name);
+
+ assert.eq(100,
+ t.find({}, {_id: 0, a: 1}).hint({a: 1}).itcount(),
+ 'index {a: 1} was expected to be rebuilt on startup');
+ var indexes = t.getIndexes();
+ assert.eq(2,
+ indexes.length,
+ 'unexpected number of indexes in listIndexes result: ' + tojson(indexes));
+
+ MongoRunner.stopMongod(conn.port);
+}());
diff --git a/jstests/noPassthrough/minvalid.js b/jstests/noPassthrough/minvalid.js
index 6f22e65e2ca..d31f6d58da7 100644
--- a/jstests/noPassthrough/minvalid.js
+++ b/jstests/noPassthrough/minvalid.js
@@ -21,9 +21,10 @@ var lastOp = local.oplog.rs.find().sort({$natural: -1}).limit(1).next();
printjson(lastOp);
print("3: change minvalid");
-// primaries don't populate minvalid by default
-local.replset.minvalid.insert(
- {ts: new Timestamp(lastOp.ts.t, lastOp.ts.i + 1), h: new NumberLong("1234567890")});
+assert.writeOK(local.replset.minvalid.update(
+ {},
+ {$set: {ts: new Timestamp(lastOp.ts.t, lastOp.ts.i + 1), h: new NumberLong("1234567890")}},
+ {upsert: true}));
printjson(local.replset.minvalid.findOne());
print("4: restart");
diff --git a/jstests/noPassthrough/minvalid2.js b/jstests/noPassthrough/minvalid2.js
index 4d53a13d16e..9cac5a0c3b4 100644
--- a/jstests/noPassthrough/minvalid2.js
+++ b/jstests/noPassthrough/minvalid2.js
@@ -75,9 +75,15 @@ print("8: start up former master");
clearRawMongoProgramOutput();
replTest.restart(masterId);
-print("9: check former master does not roll back");
+print("9: check former master " + replTest.nodes[masterId].host + " does not select former slave " +
+ slave.host + " as sync source");
+replTest.waitForState(replTest.nodes[masterId], ReplSetTest.State.RECOVERING, 90000);
+
+// Sync source selection will log this message if it does not detect min valid in the sync
+// source candidate's oplog.
assert.soon(function() {
- return rawMongoProgramOutput().match("need to rollback, but in inconsistent state");
+ return rawMongoProgramOutput().match(
+ 'it does not contain the necessary operations for us to reach a consistent state');
});
replTest.stopSet(15);
diff --git a/jstests/noPassthrough/wt_malformed_creation_string.js b/jstests/noPassthrough/wt_malformed_creation_string.js
new file mode 100644
index 00000000000..f64d45260b3
--- /dev/null
+++ b/jstests/noPassthrough/wt_malformed_creation_string.js
@@ -0,0 +1,63 @@
+/**
+ * Tests that a null embedded malformed string is rejected gracefully.
+ */
+(function() {
+ 'use strict';
+
+ var engine = 'wiredTiger';
+ if (jsTest.options().storageEngine) {
+ engine = jsTest.options().storageEngine;
+ }
+
+ // Skip this test if not running with the right storage engine.
+ if (engine !== 'wiredTiger' && engine !== 'inMemory') {
+ jsTest.log('Skipping test because storageEngine is not "wiredTiger" or "inMemory"');
+ return;
+ }
+
+ // Build an array of malformed strings to test
+ var malformedStrings = ["\u0000000", "\0,", "bl\0ah", "split_pct=30,\0split_pct=35,"];
+
+ // Start up a mongod.
+ // Test that collection and index creation with malformed creation strings fail gracefully.
+ runTest();
+
+ function runTest() {
+ var dbpath = MongoRunner.dataPath + 'wt_malformed_creation_string';
+ resetDbpath(dbpath);
+
+ // Start a mongod
+ var conn = MongoRunner.runMongod({
+ dbpath: dbpath,
+ noCleanData: true,
+ });
+ assert.neq(null, conn, 'mongod was unable to start up');
+
+ var testDB = conn.getDB('test');
+
+ // Collection creation with malformed string should fail
+ for (var i = 0; i < malformedStrings.length; i++) {
+ assert.commandFailedWithCode(
+ testDB.createCollection(
+ 'coll', {storageEngine: {[engine]: {configString: malformedStrings[i]}}}),
+ ErrorCodes.FailedToParse);
+ }
+
+ // Create collection to test index creation on
+ assert.commandWorked(testDB.createCollection('coll'));
+
+ // Index creation with malformed string should fail
+ for (var i = 0; i < malformedStrings.length; i++) {
+ assert.commandFailedWithCode(
+ testDB.coll.createIndex(
+ {a: 1},
+ {
+ name: 'with_malformed_str',
+ storageEngine: {[engine]: {configString: malformedStrings[i]}}
+ }),
+ ErrorCodes.FailedToParse);
+ }
+
+ MongoRunner.stopMongod(conn);
+ }
+})();
diff --git a/jstests/noPassthrough/wt_nojournal_skip_recovery.js b/jstests/noPassthrough/wt_nojournal_skip_recovery.js
index fa0d32b93c4..5ee9f1e2cc2 100644
--- a/jstests/noPassthrough/wt_nojournal_skip_recovery.js
+++ b/jstests/noPassthrough/wt_nojournal_skip_recovery.js
@@ -47,14 +47,17 @@
// After some journaled write operations have been performed against the mongod, send a SIGKILL
// to the process to trigger an unclean shutdown.
- assert.soon(function() {
- var count = conn.getDB('test').nojournal.count({journaled: {$exists: true}});
- if (count >= 100) {
- MongoRunner.stopMongod(conn, 9);
- return true;
- }
- return false;
- }, 'the parallel shell did not perform at least 100 journaled inserts');
+ assert.soon(
+ function() {
+ var count = conn.getDB('test').nojournal.count({journaled: {$exists: true}});
+ if (count >= 100) {
+ MongoRunner.stopMongod(conn, 9);
+ return true;
+ }
+ return false;
+ },
+ 'the parallel shell did not perform at least 100 journaled inserts',
+ 5 * 60 * 1000 /*timeout ms*/);
var exitCode = awaitShell({checkExitSuccess: false});
assert.neq(0, exitCode, 'expected shell to exit abnormally due to mongod being terminated');
diff --git a/jstests/noPassthroughWithMongod/index_no_retry.js b/jstests/noPassthroughWithMongod/index_no_retry.js
deleted file mode 100644
index ff09b70d039..00000000000
--- a/jstests/noPassthroughWithMongod/index_no_retry.js
+++ /dev/null
@@ -1,94 +0,0 @@
-// Check index rebuild is disabled with --noIndexBuildRetry when MongoDB is killed.
-//
-// This test requires persistence beacuase it assumes data/indices will survive a restart.
-// @tags: [requires_persistence]
-(function() {
- 'use strict';
- var baseName = 'index_retry';
- var dbpath = MongoRunner.dataPath + baseName;
-
- var conn = MongoRunner.runMongod({dbpath: dbpath, journal: ''});
-
- var test = conn.getDB("test");
-
- var name = 'jstests_slownightly_' + baseName;
- var t = test.getCollection(name);
- t.drop();
-
- // Insert a large number of documents, enough to ensure that an index build on these documents
- // can be interrupted before complete.
- var bulk = t.initializeUnorderedBulkOp();
- for (var i = 0; i < 5e5; ++i) {
- bulk.insert({a: i});
- if (i % 10000 == 0) {
- print("i: " + i);
- }
- }
- assert.writeOK(bulk.execute());
-
- function debug(x) {
- printjson(x);
- }
-
- /**
- * @return if there's a current running index build
- */
- function indexBuildInProgress() {
- var inprog = test.currentOp().inprog;
- debug(inprog);
- var indexBuildOpId = -1;
- inprog.forEach(function(op) {
- // Identify the index build as a createIndexes command.
- // It is assumed that no other clients are concurrently
- // accessing the 'test' database.
- if ((op.op == 'query' || op.op == 'command') && 'createIndexes' in op.query) {
- debug(op.opid);
- var idxSpec = op.query.indexes[0];
- // SERVER-4295 Make sure the index details are there
- // we can't assert these things, since there is a race in reporting
- // but we won't count if they aren't
- if ("a_1" == idxSpec.name && 1 == idxSpec.key.a && idxSpec.background &&
- op.progress && (op.progress.done / op.progress.total) > 0.20) {
- indexBuildOpId = op.opid;
- }
- }
- });
- return indexBuildOpId != -1;
- }
-
- function abortDuringIndexBuild(options) {
- var createIdx = startParallelShell(
- 'db.' + name + '.createIndex({ a: 1 }, { background: true });', conn.port);
-
- // Wait for the index build to start.
- var times = 0;
- assert.soon(function() {
- return indexBuildInProgress() && times++ >= 2;
- });
-
- print("killing the mongod");
- MongoRunner.stopMongod(conn.port, /* signal */ 9);
-
- var exitCode = createIdx({checkExitSuccess: false});
- assert.neq(0, exitCode, "expected shell to exit abnormally due to mongod being terminated");
- }
-
- abortDuringIndexBuild();
-
- conn =
- MongoRunner.runMongod({dbpath: dbpath, journal: '', noIndexBuildRetry: '', restart: true});
- test = conn.getDB("test");
- t = test.getCollection(name);
-
- assert.throws(function() {
- t.find({a: 42}).hint({a: 1}).next();
- }, null, 'index {a: 1} was rebuilt in spite of --noIndexBuildRetry');
-
- var indexes = t.getIndexes();
- assert.eq(1, indexes.length, 'unfinished indexes in listIndexes result: ' + tojson(indexes));
-
- print("Index rebuilding disabled successfully");
-
- MongoRunner.stopMongod(conn.port);
- print("SUCCESS!");
-}());
diff --git a/jstests/noPassthroughWithMongod/index_retry.js b/jstests/noPassthroughWithMongod/index_retry.js
deleted file mode 100644
index cb33de4b95b..00000000000
--- a/jstests/noPassthroughWithMongod/index_retry.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// Check index rebuild when MongoDB is killed.
-//
-// This test requires persistence beacuase it assumes data/indices will survive a restart.
-// @tags: [requires_persistence]
-(function() {
- 'use strict';
- var baseName = 'index_retry';
- var dbpath = MongoRunner.dataPath + baseName;
-
- var conn = MongoRunner.runMongod({dbpath: dbpath, journal: ''});
-
- var test = conn.getDB("test");
-
- var name = 'jstests_slownightly_' + baseName;
- var t = test.getCollection(name);
- t.drop();
-
- // Insert a large number of documents, enough to ensure that an index build on these documents
- // can be interrupted before complete.
- var bulk = t.initializeUnorderedBulkOp();
- for (var i = 0; i < 5e5; ++i) {
- bulk.insert({a: i});
- if (i % 10000 == 0) {
- print("i: " + i);
- }
- }
- assert.writeOK(bulk.execute());
-
- function debug(x) {
- printjson(x);
- }
-
- /**
- * @return if there's a current running index build
- */
- function indexBuildInProgress() {
- var inprog = test.currentOp().inprog;
- debug(inprog);
- var indexBuildOpId = -1;
- inprog.forEach(function(op) {
- // Identify the index build as a createIndexes command.
- // It is assumed that no other clients are concurrently
- // accessing the 'test' database.
- if ((op.op == 'query' || op.op == 'command') && 'createIndexes' in op.query) {
- debug(op.opid);
- var idxSpec = op.query.indexes[0];
- // SERVER-4295 Make sure the index details are there
- // we can't assert these things, since there is a race in reporting
- // but we won't count if they aren't
- if ("a_1" == idxSpec.name && 1 == idxSpec.key.a && idxSpec.background &&
- op.progress && (op.progress.done / op.progress.total) > 0.20) {
- indexBuildOpId = op.opid;
- }
- }
- });
- return indexBuildOpId != -1;
- }
-
- function abortDuringIndexBuild(options) {
- var createIdx = startParallelShell(
- 'db.' + name + '.createIndex({ a: 1 }, { background: true });', conn.port);
-
- // Wait for the index build to start.
- var times = 0;
- assert.soon(function() {
- return indexBuildInProgress() && times++ >= 2;
- });
-
- print("killing the mongod");
- MongoRunner.stopMongod(conn.port, /* signal */ 9);
-
- var exitCode = createIdx({checkExitSuccess: false});
- assert.neq(0, exitCode, "expected shell to exit abnormally due to mongod being terminated");
- }
-
- abortDuringIndexBuild();
-
- conn = MongoRunner.runMongod({dbpath: dbpath, journal: '', restart: true});
- test = conn.getDB("test");
- t = test.getCollection(name);
-
- assert.eq({a: 42},
- t.find({a: 42}, {_id: 0}).hint({a: 1}).next(),
- 'index {a: 1} was rebuilt on startup');
-
- var indexes = t.getIndexes();
- assert.eq(2,
- indexes.length,
- 'unexpected number of indexes in listIndexes result: ' + tojson(indexes));
-
- print("Index built");
-
- MongoRunner.stopMongod(conn.port);
- print("SUCCESS!");
-}());
diff --git a/jstests/replsets/apply_batch_only_goes_forward.js b/jstests/replsets/apply_batch_only_goes_forward.js
index 4744fcf4342..097856cf1b4 100644
--- a/jstests/replsets/apply_batch_only_goes_forward.js
+++ b/jstests/replsets/apply_batch_only_goes_forward.js
@@ -6,7 +6,7 @@
* -- Set minvalid manually on primary (node0) way ahead (5 minutes)
* -- Restart primary (node0)
* -- Ensure restarted primary (node0) comes up in recovering
- * -- Ensure node0 replicates a batch, and keeps the old minvalid
+ * -- Ensure node0 blacklists new primary as a sync source and keeps the old minvalid
* -- Success!
*
* This test requires persistence to test that a restarted primary will stay in the RECOVERING state
@@ -63,12 +63,23 @@
{upsert: true, writeConcern: {w: 1}})));
jsTest.log("restart primary");
+ clearRawMongoProgramOutput();
replTest.restart(master);
printjson(sLocal.adminCommand("isMaster"));
replTest.waitForState(master, ReplSetTest.State.RECOVERING, 90000);
- // Slave is now master... so do a write to get a minvalid entry on the secondary.
- assert.writeOK(replTest.getPrimary().getDB("test").foo.save({}, {writeConcern: {w: 3}}));
+ // Slave is now master... Do a write to advance the optime on the primary so that it will be
+ // considered as a sync source - this is more relevant to PV0 because we do not write a new
+ // entry to the oplog on becoming primary.
+ assert.writeOK(
+ replTest.getPrimary().getDB("test").foo.save({}, {writeConcern: {w: 2, wtimeout: 90000}}));
+
+ // Sync source selection will log this message if it does not detect min valid in the sync
+ // source candidate's oplog.
+ assert.soon(function() {
+ return rawMongoProgramOutput().match(
+ 'it does not contain the necessary operations for us to reach a consistent state');
+ });
assert.soon(function() {
var mv;
diff --git a/jstests/replsets/apply_ops_wc.js b/jstests/replsets/apply_ops_wc.js
index 5783d515c37..6e3467f5906 100644
--- a/jstests/replsets/apply_ops_wc.js
+++ b/jstests/replsets/apply_ops_wc.js
@@ -128,4 +128,8 @@
majorityWriteConcerns.forEach(testMajorityWriteConcerns);
+ // Allow clean shutdown
+ secondaries[0].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'});
+ secondaries[1].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'});
+
})();
diff --git a/jstests/replsets/chaining_removal.js b/jstests/replsets/chaining_removal.js
index 1685bef8853..dbc80148745 100644
--- a/jstests/replsets/chaining_removal.js
+++ b/jstests/replsets/chaining_removal.js
@@ -48,7 +48,7 @@
});
// write that should reach all nodes
- var timeout = 15 * 1000;
+ var timeout = 60 * 1000;
var options = {
writeConcern: {w: numNodes, wtimeout: timeout}
};
diff --git a/jstests/replsets/clean_shutdown_oplog_state.js b/jstests/replsets/clean_shutdown_oplog_state.js
index c0f94a7c6c9..3a856db1245 100644
--- a/jstests/replsets/clean_shutdown_oplog_state.js
+++ b/jstests/replsets/clean_shutdown_oplog_state.js
@@ -19,7 +19,7 @@
conf.members[1].votes = 0;
conf.members[1].priority = 0;
printjson(conf);
- assert.commandWorked(rst.nodes[0].adminCommand({replSetInitiate: conf}));
+ rst.initiate(conf);
var primary = rst.getPrimary(); // Waits for PRIMARY state.
var slave = rst.nodes[1];
@@ -57,9 +57,10 @@
var conn = MongoRunner.runMongod(options);
assert.neq(null, conn, "secondary failed to start");
- // Following a clean shutdown of a 3.2 node, the oplog must exactly match the applied
- // operations. Additionally, the begin field must not be in the minValid document and the ts
- // must match the top of the oplog (SERVER-25353).
+ // Following clean shutdown of a node, the oplog must exactly match the applied operations.
+ // Additionally, the begin field must not be in the minValid document, the ts must match the
+ // top of the oplog (SERVER-25353), and the oplogDeleteFromPoint must be null (SERVER-7200 and
+ // SERVER-25071).
var oplogDoc = conn.getCollection('local.oplog.rs')
.find({ns: 'test.coll'})
.sort({$natural: -1})
@@ -68,9 +69,20 @@
var minValidDoc =
conn.getCollection('local.replset.minvalid').find().sort({$natural: -1}).limit(1)[0];
printjson({oplogDoc: oplogDoc, collDoc: collDoc, minValidDoc: minValidDoc});
- assert.eq(collDoc._id, oplogDoc.o._id);
- assert(!('begin' in minValidDoc), 'begin in minValidDoc');
- assert.eq(minValidDoc.ts, oplogDoc.ts);
+ try {
+ assert.eq(collDoc._id, oplogDoc.o._id);
+ assert(!('begin' in minValidDoc), 'begin in minValidDoc');
+ assert.eq(minValidDoc.ts, oplogDoc.ts);
+ if ('oplogDeleteFromPoint' in minValidDoc) {
+ // If present it must be the null timestamp.
+ assert.eq(minValidDoc.oplogDeleteFromPoint, Timestamp());
+ }
+ } catch (e) {
+ jsTest.log(
+ "Look above and make sure clean shutdown finished without resorting to SIGKILL." +
+ "\nUnfortunately that currently doesn't fail the test.");
+ throw e;
+ }
rst.stopSet();
})();
diff --git a/jstests/replsets/double_rollback.js b/jstests/replsets/double_rollback.js
new file mode 100644
index 00000000000..2286b80e315
--- /dev/null
+++ b/jstests/replsets/double_rollback.js
@@ -0,0 +1,161 @@
+/*
+ * This test causes node 2 to enter rollback, reach the common point, and exit rollback, but before
+ * it can apply operations to bring it back to a consistent state, switch sync sources to the node
+ * that originally gave it the ops it is now rolling back (node 0). This test then verifies that
+ * node 2 refuses to use node0 as a sync source because it doesn't contain the minValid document
+ * it needs to reach consistency. Node 2 is then allowed to reconnect to the node it was
+ * originally rolling back against (node 1) and finish its rollback. This is a regression test
+ * against the case where we *did* allow node 2 to sync from node 0 which gave it the very ops
+ * it rolled back, which could then lead to a double-rollback when node 2 was reconnected
+ * to node 1 and tried to apply its oplog despite not being in a consistent state.
+ */
+(function() {
+ 'use strict';
+
+ var name = "double_rollback";
+ var dbName = "test";
+ var collName = "double_rollback";
+
+ var rst = new ReplSetTest({
+ name: name,
+ nodes: [
+ {},
+ {},
+ {rsConfig: {priority: 0}},
+ {rsConfig: {arbiterOnly: true}},
+ {rsConfig: {arbiterOnly: true}}
+ ],
+ useBridge: true
+ });
+ var nodes = rst.startSet();
+ rst.initiate();
+
+ var timeout = 5 * 60 * 1000;
+
+ function waitForState(node, state) {
+ assert.soonNoExcept(function() {
+ assert.commandWorked(node.adminCommand(
+ {replSetTest: 1, waitForMemberState: state, timeoutMillis: timeout}));
+ return true;
+ });
+ }
+
+ function stepUp(rst, node) {
+ var primary = rst.getPrimary();
+ if (primary != node) {
+ assert.commandWorked(primary.adminCommand({replSetStepDown: 1}));
+ }
+ waitForState(node, ReplSetTest.State.PRIMARY);
+ }
+
+ jsTestLog("Make sure node 0 is primary.");
+ stepUp(rst, nodes[0]);
+ assert.eq(nodes[0], rst.getPrimary());
+ // Wait for all data bearing nodes to get up to date.
+ assert.writeOK(nodes[0].getDB(dbName).getCollection(collName).insert(
+ {a: 1}, {writeConcern: {w: 3, wtimeout: timeout}}));
+
+ jsTestLog("Create two partitions: [1] and [0,2,3,4].");
+ nodes[1].disconnect(nodes[0]);
+ nodes[1].disconnect(nodes[2]);
+ nodes[1].disconnect(nodes[3]);
+ nodes[1].disconnect(nodes[4]);
+
+ jsTestLog("Do a write that is replicated to [0,2,3,4].");
+ assert.writeOK(nodes[0]
+ .getDB(dbName)
+ .getCollection(collName + "2")
+ .insert({a: 2}, {writeConcern: {w: 2, wtimeout: timeout}}));
+
+ jsTestLog("Repartition to: [0,2] and [1,3,4].");
+ nodes[1].reconnect(nodes[3]);
+ nodes[1].reconnect(nodes[4]);
+ nodes[3].disconnect(nodes[0]);
+ nodes[3].disconnect(nodes[2]);
+ nodes[4].disconnect(nodes[0]);
+ nodes[4].disconnect(nodes[2]);
+
+ jsTestLog("Ensure that 0 steps down and that 1 becomes primary.");
+ waitForState(nodes[0], ReplSetTest.State.SECONDARY);
+ waitForState(nodes[1], ReplSetTest.State.PRIMARY);
+ assert.eq(nodes[1], rst.getPrimary());
+
+ jsTestLog("Do a write to node 1 on the [1,3,4] side of the partition.");
+ assert.writeOK(nodes[1].getDB(dbName).getCollection(collName + "3").insert({a: 3}));
+
+ // Turn on failpoint on node 2 to pause rollback after oplog is truncated and minValid is set.
+ assert.commandWorked(nodes[2].getDB('admin').runCommand(
+ {configureFailPoint: 'rollbackHangBeforeFinish', mode: 'alwaysOn'}));
+
+ jsTestLog("Repartition to: [0] and [1,2,3,4].");
+ nodes[2].disconnect(nodes[0]);
+ nodes[2].reconnect(nodes[1]);
+ nodes[2].reconnect(nodes[3]);
+ nodes[2].reconnect(nodes[4]);
+
+ jsTestLog("Wait for node 2 to go into ROLLBACK and start syncing from node 1.");
+ // Since nodes 1 and 2 have now diverged, node 2 should go into rollback.
+ waitForState(nodes[2], ReplSetTest.State.ROLLBACK);
+ rst.awaitSyncSource(nodes[2], nodes[1], timeout);
+
+ jsTestLog("Wait for failpoint on node 2 to pause rollback before it finishes");
+ // Wait for fail point message to be logged.
+ var checkLog = function(node, msg) {
+ assert.soon(function() {
+ var logMessages = assert.commandWorked(node.adminCommand({getLog: 'global'})).log;
+ for (var i = 0; i < logMessages.length; i++) {
+ if (logMessages[i].indexOf(msg) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }, 'Did not see a log entry containing the following message: ' + msg, timeout);
+ };
+ checkLog(nodes[2], 'rollback - rollbackHangBeforeFinish fail point enabled');
+
+ jsTestLog("Repartition to: [1,3,4] and [0,2].");
+ nodes[2].disconnect(nodes[1]);
+ nodes[2].reconnect(nodes[0]);
+
+ // Turn off failpoint on node 2 to allow rollback to finish.
+ assert.commandWorked(nodes[2].getDB('admin').runCommand(
+ {configureFailPoint: 'rollbackHangBeforeFinish', mode: 'off'}));
+
+ jsTestLog("Wait for node 2 exit ROLLBACK state and go into RECOVERING");
+ waitForState(nodes[2], ReplSetTest.State.RECOVERING);
+
+ // At this point node 2 has truncated its oplog back to the common point and is looking
+ // for a sync source it can use to reach minvalid and get back into SECONDARY state. Node 0
+ // is the only node it can reach, but since node 0 doesn't contain node 2's minvalid oplog entry
+ // node 2 will refuse to use it as a sync source.
+ checkLog(nodes[2],
+ "remote oplog does not contain entry with optime matching our required optime");
+
+ var node0RBID = nodes[0].adminCommand('replSetGetRBID').rbid;
+ var node1RBID = nodes[1].adminCommand('replSetGetRBID').rbid;
+
+ jsTestLog("Reconnect all nodes.");
+ nodes[0].reconnect(nodes[1]);
+ nodes[0].reconnect(nodes[3]);
+ nodes[0].reconnect(nodes[4]);
+ nodes[2].reconnect(nodes[1]);
+ nodes[2].reconnect(nodes[3]);
+ nodes[2].reconnect(nodes[4]);
+
+ jsTestLog("Wait for nodes 0 to roll back and both node 0 and 2 to catch up to node 1");
+ waitForState(nodes[0], ReplSetTest.State.SECONDARY);
+ waitForState(nodes[2], ReplSetTest.State.SECONDARY);
+ rst.awaitReplication(timeout);
+
+ // Check that rollback happened on node 0, but not on node 2 since it had already rolled back
+ // and just needed to finish applying ops to reach minValid.
+ assert.neq(node0RBID, nodes[0].adminCommand('replSetGetRBID').rbid);
+ assert.eq(node1RBID, nodes[1].adminCommand('replSetGetRBID').rbid);
+
+ // Node 1 should still be primary, and should now be able to satisfy majority writes again.
+ assert.writeOK(nodes[1]
+ .getDB(dbName)
+ .getCollection(collName + "4")
+ .insert({a: 4}, {writeConcern: {w: 3, wtimeout: timeout}}));
+
+}()); \ No newline at end of file
diff --git a/jstests/replsets/election_timing.js b/jstests/replsets/election_timing.js
index 2fe83be02ed..48139e6bac0 100644
--- a/jstests/replsets/election_timing.js
+++ b/jstests/replsets/election_timing.js
@@ -135,16 +135,8 @@
var resStdDev = Array.stdDev(allResults);
jsTestLog("Results: " + tc.name + " Average over " + allResults.length + " runs: " +
- resAvg + " Min: " + resMin + " Max: " + resMax + " Limit: " +
- tc.electionTimeoutLimitMillis / 1000 + " StdDev: " + resStdDev.toFixed(4));
-
- allResults.forEach(function(failoverElapsedMillis) {
- assert.lte(failoverElapsedMillis,
- tc.electionTimeoutLimitMillis / 1000,
- tc.name + ': failover (' + failoverElapsedMillis +
- ' sec) took too long. limit: ' + tc.electionTimeoutLimitMillis / 1000 +
- ' sec');
- });
+ resAvg + " Min: " + resMin + " Max: " + resMax + " StdDev: " +
+ resStdDev.toFixed(4));
});
jsTestLog("Tests completed in: " + (Date.now() - testStart) / 1000 + " seconds");
diff --git a/jstests/replsets/oplog_replay_on_startup.js b/jstests/replsets/oplog_replay_on_startup.js
new file mode 100644
index 00000000000..19f2504795a
--- /dev/null
+++ b/jstests/replsets/oplog_replay_on_startup.js
@@ -0,0 +1,427 @@
+// SERVER-7200 On startup, replica set nodes delete oplog state past the oplog delete point and
+// apply any remaining unapplied ops before coming up as a secondary.
+//
+// @tags: [requires_persistence]
+(function() {
+ "use strict";
+
+ var ns = "test.coll";
+
+ var rst = new ReplSetTest({
+ nodes: 1,
+ });
+
+ rst.startSet();
+ rst.initiate();
+
+ var conn = rst.getPrimary(); // Waits for PRIMARY state.
+ var nojournal = Array.contains(conn.adminCommand({getCmdLineOpts: 1}).argv, '--nojournal');
+ var storageEngine = jsTest.options().storageEngine;
+ var term = conn.getCollection('local.oplog.rs').find().sort({$natural: -1}).limit(1).next().t;
+ if (typeof(term) == 'undefined') {
+ term = -1; // Use a dummy term for PV0.
+ }
+
+ function runTest({
+ oplogEntries,
+ collectionContents,
+ deletePoint,
+ begin,
+ minValid,
+ expectedState,
+ expectedApplied,
+ }) {
+ if (nojournal && (storageEngine === 'mmapv1') && expectedState === 'FATAL') {
+ // We can't test fatal states on mmap without a journal because it won't be able
+ // to start up again.
+ return;
+ }
+
+ if (term != -1) {
+ term++; // Each test gets a new term on PV1 to ensure OpTimes always move forward.
+ }
+
+ conn = rst.restart(0, {noReplSet: true}); // Restart as a standalone node.
+ assert.neq(null, conn, "failed to restart");
+ var oplog = conn.getCollection('local.oplog.rs');
+ var minValidColl = conn.getCollection('local.replset.minvalid');
+ var coll = conn.getCollection(ns);
+
+ // Reset state to empty.
+ assert.commandWorked(oplog.runCommand('emptycapped'));
+ coll.drop();
+ assert.commandWorked(coll.runCommand('create'));
+
+ var ts = (num) => num === null ? Timestamp() : Timestamp(1000, num);
+
+ oplogEntries.forEach((num) => {
+ assert.writeOK(oplog.insert({
+ ts: ts(num),
+ t: term,
+ h: NumberLong(1),
+ op: 'i',
+ ns: ns,
+ o: {_id: num},
+ }));
+ });
+
+ collectionContents.forEach((num) => {
+ assert.writeOK(coll.insert({_id: num}));
+ });
+
+ var injectedMinValidDoc = {
+ _id: ObjectId(),
+
+ // minvalid:
+ ts: ts(minValid),
+ t: term,
+
+ // appliedThrough
+ begin: {
+ ts: ts(begin),
+ t: term,
+ },
+
+ oplogDeleteFromPoint: ts(deletePoint),
+ };
+
+ // This weird mechanism is the only way to bypass mongod's attempt to fill in null
+ // Timestamps.
+ assert.writeOK(minValidColl.remove({}));
+ assert.writeOK(minValidColl.update({}, {$set: injectedMinValidDoc}, {upsert: true}));
+ assert.eq(minValidColl.findOne(),
+ injectedMinValidDoc,
+ "If the Timestamps differ, the server may be filling in the null timestamps");
+
+ try {
+ conn = rst.restart(0); // Restart in replSet mode again.
+ } catch (e) {
+ assert.eq(expectedState, 'FATAL', 'node failed to restart: ' + e);
+ return;
+ }
+
+ // Wait for the node to go to SECONDARY if it is able.
+ assert.soon(
+ () =>
+ conn.adminCommand('serverStatus').metrics.repl.apply.attemptsToBecomeSecondary > 0,
+ () => conn.adminCommand('serverStatus').metrics.repl.apply.attemptsToBecomeSecondary);
+
+ var isMaster = conn.adminCommand('ismaster');
+ switch (expectedState) {
+ case 'SECONDARY':
+ // Primary is also acceptable since once a node becomes secondary, it will try to
+ // become primary if it is eligible and has enough votes (which this node does).
+ // This is supposed to test that we reach secondary, not that we stay there.
+ assert(isMaster.ismaster || isMaster.secondary,
+ 'not PRIMARY or SECONDARY: ' + tojson(isMaster));
+
+ // Wait for node to become primary. This is nesessary to avoid the find below
+ // failing with "NotMasterOrSecondary" errors if it happens to run while the
+ // node is in drain mode while becoming primary.
+ conn = rst.getPrimary();
+ break;
+
+ case 'RECOVERING':
+ assert(!isMaster.ismaster && !isMaster.secondary,
+ 'not in RECOVERING: ' + tojson(isMaster));
+
+ // Restart as a standalone node again so we can read from the collection.
+ conn = rst.restart(0, {noReplSet: true});
+ break;
+
+ case 'FATAL':
+ doassert("server startup didn't fail when it should have");
+ break;
+
+ default:
+ doassert('expectedState ' + expectedState + ' is not supported');
+ }
+
+ // Ensure the oplog has the entries it should have and none that it shouldn't.
+ assert.eq(conn.getCollection('local.oplog.rs')
+ .find({ns: ns, op: 'i'})
+ .sort({$natural: 1})
+ .map((op) => op.o._id),
+ expectedApplied);
+
+ // Ensure that all ops that should have been applied were.
+ conn.setSlaveOk(true);
+ assert.eq(conn.getCollection(ns).find().sort({_id: 1}).map((obj) => obj._id),
+ expectedApplied);
+ }
+
+ //
+ // Normal 3.4 cases
+ //
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: null,
+ minValid: null,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: null,
+ minValid: 2,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: null,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, /*4,*/ 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ //
+ // 3.2 -> 3.4 upgrade cases
+ //
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5],
+ collectionContents: [1, 2, 3],
+ deletePoint: null,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3, 4, 5],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5],
+ collectionContents: [1, 2, 3, 4, 5],
+ deletePoint: null,
+ begin: null,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3, 4, 5],
+ });
+
+ //
+ // 3.4 -> 3.2 -> 3.4 downgrade/reupgrade cases
+ //
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, /*4,*/ 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: 4,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: 2,
+ begin: null,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3],
+ deletePoint: 2,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5],
+ collectionContents: [1, 2, 3],
+ deletePoint: 2,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'RECOVERING',
+ expectedApplied: [1, 2, 3, 4, 5],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: 2,
+ begin: 3,
+ minValid: 6,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ //
+ // These states should be impossible to get into.
+ //
+
+ runTest({
+ oplogEntries: [1, 2, 3],
+ collectionContents: [1, 2, 3, 4],
+ deletePoint: null,
+ begin: 4,
+ minValid: null, // doesn't matter.
+ expectedState: 'FATAL',
+ });
+
+ runTest({
+ oplogEntries: [4, 5, 6],
+ collectionContents: [1, 2],
+ deletePoint: 2,
+ begin: 3,
+ minValid: null, // doesn't matter.
+ expectedState: 'FATAL',
+ });
+
+ runTest({
+ oplogEntries: [4, 5, 6],
+ collectionContents: [1, 2],
+ deletePoint: null,
+ begin: 3,
+ minValid: null, // doesn't matter.
+ expectedState: 'FATAL',
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3],
+ deletePoint: 2,
+ begin: 3,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3, 4, 5],
+ deletePoint: null,
+ begin: 5,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5, 6],
+ collectionContents: [1, 2, 3, 4, 5],
+ deletePoint: null,
+ begin: 5,
+ minValid: null,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3, 4, 5, 6],
+ });
+
+ runTest({
+ oplogEntries: [1, 2, 3, 4, 5],
+ collectionContents: [1],
+ deletePoint: 4,
+ begin: 1,
+ minValid: 3,
+ expectedState: 'SECONDARY',
+ expectedApplied: [1, 2, 3],
+ });
+
+ rst.stopSet();
+})();
diff --git a/jstests/replsets/oplog_truncated_on_recovery.js b/jstests/replsets/oplog_truncated_on_recovery.js
index 96be5865cc3..4d469178691 100644
--- a/jstests/replsets/oplog_truncated_on_recovery.js
+++ b/jstests/replsets/oplog_truncated_on_recovery.js
@@ -1,10 +1,10 @@
/**
- * This test will ensure that a failed a batch apply will remove the any oplog
+ * This test will ensure that recovery from a failed batch application will remove the oplog
* entries from that batch.
*
* To do this we:
* -- Create single node replica set
- * -- Set minvalid manually on primary way ahead (5 minutes)
+ * -- Set minvalid manually on primary way ahead (5 days)
* -- Write some oplog entries newer than minvalid.start
* -- Ensure restarted primary comes up in recovering and truncates the oplog
* -- Success!
@@ -40,35 +40,36 @@
// Write op
log(assert.writeOK(testDB.foo.save({_id: 1, a: 1}, {writeConcern: {w: 1}})));
- // Set minvalid to something far in the future for the current primary, to
- // simulate recovery.
- // Note: This is so far in the future (5 days) that it will never become
- // secondary.
+ // Set minvalid to something far in the future for the current primary, to simulate recovery.
+ // Note: This is so far in the future (5 days) that it will never become secondary.
var farFutureTS = new Timestamp(
Math.floor(new Date().getTime() / 1000) + (60 * 60 * 24 * 5 /* in five days */), 0);
var rsgs = assert.commandWorked(localDB.adminCommand("replSetGetStatus"));
log(rsgs);
var primaryOpTime = rsgs.members[0].optime;
- var primaryLastTS = rsgs.members[0].optime.ts;
- log(primaryLastTS);
+ log(primaryOpTime);
// Set the start of the failed batch
- primaryOpTime.ts = new Timestamp(primaryOpTime.ts.t, primaryOpTime.ts.i + 1);
+ // TODO this test should restart in stand-alone mode to futz with the state rather than trying
+ // to do it on a running primary.
- log(primaryLastTS);
jsTest.log("future TS: " + tojson(farFutureTS) + ", date:" + tsToDate(farFutureTS));
- // We do an update in case there is a minvalid document on the primary
- // already.
- // If the doc doesn't exist then upsert:true will create it, and the
- // writeConcern ensures
- // that update returns details of the write, like whether an update or
- // insert was performed.
- log(assert.writeOK(
- minvalidColl.update({},
- {ts: farFutureTS, t: NumberLong(-1), begin: primaryOpTime},
- {upsert: true, writeConcern: {w: 1}})));
+ var divergedTS = new Timestamp(primaryOpTime.ts.t, primaryOpTime.ts.i + 1);
+ // We do an update in case there is a minvalid document on the primary already.
+ // If the doc doesn't exist then upsert:true will create it, and the writeConcern ensures
+ // that update returns details of the write, like whether an update or insert was performed.
+ log(assert.writeOK(minvalidColl.update({},
+ {
+ ts: farFutureTS,
+ t: NumberLong(-1),
+ begin: primaryOpTime,
+ oplogDeleteFromPoint: divergedTS
+ },
+ {upsert: true, writeConcern: {w: 1}})));
- log(assert.writeOK(localDB.oplog.rs.insert({_id: 0, ts: primaryOpTime.ts, op: "n", term: -1})));
+ // Insert a diverged oplog entry that will be truncated after restart.
+ log(assert.writeOK(localDB.oplog.rs.insert(
+ {_id: 0, ts: divergedTS, op: "n", h: NumberLong(0), t: NumberLong(-1)})));
log(localDB.oplog.rs.find().toArray());
log(assert.commandWorked(localDB.adminCommand("replSetGetStatus")));
log("restart primary");
@@ -88,7 +89,7 @@
var lastTS = localDB.oplog.rs.find().sort({$natural: -1}).limit(-1).next().ts;
log(localDB.oplog.rs.find().toArray());
- assert.eq(primaryLastTS, lastTS);
+ assert.eq(primaryOpTime.ts, lastTS);
return true;
});
diff --git a/jstests/replsets/rollback_index.js b/jstests/replsets/rollback_index.js
index 7a0ac7520aa..d3bf747680c 100644
--- a/jstests/replsets/rollback_index.js
+++ b/jstests/replsets/rollback_index.js
@@ -89,7 +89,7 @@ replTest.awaitSecondaryNodes();
// Perform a write that should succeed if there's no unique index on B.
options = {
- writeConcern: {w: 'majority', wtimeout: 10000}
+ writeConcern: {w: 'majority', wtimeout: 60000}
};
assert.writeOK(a_conn.getDB(name).foo.insert({x: 1}, options));
diff --git a/jstests/replsets/slave_delay_clean_shutdown.js b/jstests/replsets/slave_delay_clean_shutdown.js
new file mode 100644
index 00000000000..db08dfab228
--- /dev/null
+++ b/jstests/replsets/slave_delay_clean_shutdown.js
@@ -0,0 +1,61 @@
+// SERVER-21118 don't hang at shutdown or apply ops too soon with slaveDelay.
+//
+// @tags: [requires_persistence]
+load('jstests/replsets/rslib.js');
+(function() {
+ "use strict";
+
+ var ns = "test.coll";
+
+ var rst = new ReplSetTest({
+ nodes: 2,
+ });
+
+ var conf = rst.getReplSetConfig();
+ conf.members[1].votes = 0;
+ conf.members[1].priority = 0;
+ conf.members[1].hidden = true;
+ conf.members[1].slaveDelay = 0; // Set later.
+
+ rst.startSet();
+ rst.initiate(conf);
+
+ var master = rst.getPrimary(); // Waits for PRIMARY state.
+
+ // Push some ops through before setting slave delay.
+ assert.writeOK(master.getCollection(ns).insert([{}, {}, {}], {writeConcern: {w: 2}}));
+
+ // Set slaveDelay and wait for secondary to receive the change.
+ conf = rst.getReplSetConfigFromNode();
+ conf.version++;
+ conf.members[1].slaveDelay = 24 * 60 * 60;
+ reconfig(rst, conf);
+ assert.soon(() => rst.getReplSetConfigFromNode(1).members[1].slaveDelay > 0,
+ () => rst.getReplSetConfigFromNode(1));
+
+ sleep(2000); // The secondary apply loop only checks for slaveDelay changes once per second.
+ var secondary = rst.getSecondary();
+ const lastOp = getLatestOp(secondary);
+
+ assert.writeOK(master.getCollection(ns).insert([{}, {}, {}]));
+ assert.soon(() => secondary.adminCommand('serverStatus').metrics.repl.buffer.count > 0,
+ () => secondary.adminCommand('serverStatus').metrics.repl);
+ assert.neq(getLatestOp(master), lastOp);
+ assert.eq(getLatestOp(secondary), lastOp);
+
+ sleep(2000); // Prevent the test from passing by chance.
+ assert.eq(getLatestOp(secondary), lastOp);
+
+ // Make sure shutdown won't take a long time due to I/O.
+ secondary.adminCommand('fsync');
+
+ // Shutting down shouldn't take long.
+ assert.lt(Date.timeFunc(() => rst.stop(1)), 60 * 1000);
+
+ secondary = rst.restart(1);
+ assert.eq(getLatestOp(secondary), lastOp);
+ sleep(2000); // Prevent the test from passing by chance.
+ assert.eq(getLatestOp(secondary), lastOp);
+
+ rst.stopSet();
+})();
diff --git a/jstests/replsets/slavedelay1.js b/jstests/replsets/slavedelay1.js
index 040c47d3e78..fdab1feada6 100644
--- a/jstests/replsets/slavedelay1.js
+++ b/jstests/replsets/slavedelay1.js
@@ -42,11 +42,11 @@ doTest = function(signal) {
sleep(1000);
}
- // within 30 seconds delayed slave should have it
+ // within 120 seconds delayed slave should have it
assert.soon(function() {
var z = slaves[1].foo.findOne();
return z && z.x == 1;
- });
+ }, 'waiting for inserted document ' + tojson(doc) + ' on delayed slave', 120 * 1000);
/************* Part 2 *******************/
diff --git a/jstests/replsets/stepdown_killop.js b/jstests/replsets/stepdown_killop.js
index c5fc593239b..5c0e0ffae91 100644
--- a/jstests/replsets/stepdown_killop.js
+++ b/jstests/replsets/stepdown_killop.js
@@ -100,4 +100,5 @@
assert.writeOK(primary.getDB(name).foo.remove({}));
exitCode = writer();
assert.eq(0, exitCode);
+ secondary.getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'});
})();
diff --git a/jstests/sharding/addshard2.js b/jstests/sharding/addshard2.js
index 7af23a4ab5b..e2d788930e6 100644
--- a/jstests/sharding/addshard2.js
+++ b/jstests/sharding/addshard2.js
@@ -1,129 +1,191 @@
(function() {
- // Don't start any shards, yet
- var s =
- new ShardingTest({name: "add_shard2", shards: 1, mongos: 1, other: {useHostname: true}});
-
- // Start two new instances, which will be used for shards
- var conn1 = MongoRunner.runMongod({useHostname: true});
- var conn2 = MongoRunner.runMongod({useHostname: true});
-
- var rs1 = new ReplSetTest({"name": "add_shard2_rs1", nodes: 3});
- rs1.startSet();
- rs1.initiate();
- var master1 = rs1.getPrimary();
-
- var rs2 = new ReplSetTest({"name": "add_shard2_rs2", nodes: 3});
- rs2.startSet();
- rs2.initiate();
- var master2 = rs2.getPrimary();
-
- // replica set with set name = 'config'
- var rs3 = new ReplSetTest({'name': 'config', nodes: 3});
- rs3.startSet();
- rs3.initiate();
-
- // replica set with set name = 'admin'
- var rs4 = new ReplSetTest({'name': 'admin', nodes: 3});
- rs4.startSet();
- rs4.initiate();
-
- // replica set with configsvr: true should *not* be allowed to be added as a shard
- var rs5 = new ReplSetTest({
- name: 'csrs',
- nodes: 3,
- nodeOptions: {configsvr: "", journal: "", storageEngine: "wiredTiger"}
- });
- rs5.startSet();
- var conf = rs5.getReplSetConfig();
- conf.configsvr = true;
- rs5.initiate(conf);
-
- // step 1. name given. maxSize zero means no limit. Make sure it is allowed.
- assert.commandWorked(
- s.admin.runCommand({addshard: getHostName() + ":" + conn1.port, name: "bar", maxSize: 0}));
- var shard = s.getDB("config").shards.findOne({"_id": {"$nin": ["shard0000"]}});
- assert(shard, "shard wasn't found");
- assert.eq("bar", shard._id, "shard has incorrect name");
-
- // step 2. replica set
- assert(
- s.admin.runCommand({"addshard": "add_shard2_rs1/" + getHostName() + ":" + master1.port}).ok,
- "failed to add shard in step 2");
- shard = s.getDB("config").shards.findOne({"_id": {"$nin": ["shard0000", "bar"]}});
- assert(shard, "shard wasn't found");
- assert.eq("add_shard2_rs1", shard._id, "t2 name");
-
- // step 3. replica set w/ name given
- assert(s.admin.runCommand({
- "addshard": "add_shard2_rs2/" + getHostName() + ":" + master2.port,
- "name": "myshard"
- }).ok,
- "failed to add shard in step 4");
- shard = s.getDB("config")
- .shards.findOne({"_id": {"$nin": ["shard0000", "bar", "add_shard2_rs1"]}});
- assert(shard, "shard wasn't found");
- assert.eq("myshard", shard._id, "t3 name");
-
- // step 4. no name given
- assert(s.admin.runCommand({"addshard": getHostName() + ":" + conn2.port}).ok,
- "failed to add shard in step 4");
- shard = s.getDB("config").shards.findOne(
- {"_id": {"$nin": ["shard0000", "bar", "add_shard2_rs1", "myshard"]}});
- assert(shard, "shard wasn't found");
- assert.eq("shard0001", shard._id, "t4 name");
-
- assert.eq(s.getDB("config").shards.count(), 5, "unexpected number of shards");
-
- // step 5. replica set w/ a wrong host
+ var addShardRes;
+ var rst;
+
+ var assertAddShardSucceeded = function(res, shardName) {
+ assert.commandWorked(res);
+
+ // If a shard name was specified, make sure that the name the addShard command reports the
+ // shard was added with matches the specified name.
+ if (shardName) {
+ assert.eq(shardName,
+ res.shardAdded,
+ "name returned by addShard does not match name specified in addShard");
+ }
+
+ // Make sure the shard shows up in config.shards with the shardName reported by the
+ // addShard command.
+ assert.neq(null,
+ st.s.getDB('config').shards.findOne({_id: res.shardAdded}),
+ "newly added shard " + res.shardAdded + " not found in config.shards");
+ };
+
+ // Note: this method expects that the failure is *not* that the specified shardName is already
+ // the shardName of an existing shard.
+ var assertAddShardFailed = function(res, shardName) {
+ assert.commandFailed(res);
+
+ // If a shard name was specified in the addShard, make sure no shard with its name shows up
+ // in config.shards.
+ if (shardName) {
+ assert.eq(null,
+ st.s.getDB('config').shards.findOne({_id: shardName}),
+ "addShard for " + shardName +
+ " reported failure, but shard shows up in config.shards");
+ }
+ };
+
+ var removeShardWithName = function(shardName) {
+ var res = st.s.adminCommand({removeShard: shardName});
+ assert.commandWorked(res);
+ assert.eq('started', res.state);
+ assert.soon(function() {
+ res = st.s.adminCommand({removeShard: shardName});
+ assert.commandWorked(res);
+ return ('completed' === res.state);
+ }, "removeShard never completed for shard " + shardName);
+ };
+
+ var st = new ShardingTest({shards: 0, mongos: 1});
+
+ // Add one shard since the last shard cannot be removed.
+ var normalShard = MongoRunner.runMongod();
+ st.s.adminCommand({addShard: normalShard.name, name: 'normalShard'});
+
+ // Allocate a port that can be used to test adding invalid hosts.
var portWithoutHostRunning = allocatePort();
- assert(!s.admin.runCommand(
- {addshard: "add_shard2_rs2/NonExistingHost:" + portWithoutHostRunning}).ok,
- "accepted bad hostname in step 5");
-
- // step 6. replica set w/ mixed wrong/right hosts
- assert(!s.admin.runCommand({
- addshard: "add_shard2_rs2/" + getHostName() + ":" + master2.port + ",foo:" +
- portWithoutHostRunning
- }).ok,
- "accepted bad hostname in step 6");
-
- // Cannot add invalid stand alone host.
- assert.commandFailed(s.admin.runCommand({addshard: 'dummy:12345'}));
-
- //
- // SERVER-17231 Adding replica set w/ set name = 'config'
- //
- var configReplURI = 'config/' + getHostName() + ':' + rs3.getPrimary().port;
-
- assert(!s.admin.runCommand({'addshard': configReplURI}).ok,
- 'accepted replica set shard with set name "config"');
- // but we should be allowed to add that replica set using a different shard name
- assert(s.admin.runCommand({'addshard': configReplURI, name: 'not_config'}).ok,
- 'unable to add replica set using valid replica set name');
-
- shard = s.getDB('config').shards.findOne({'_id': 'not_config'});
- assert(shard, 'shard with name "not_config" not found');
-
- //
- // SERVER-17232 Try inserting into shard with name 'admin'
- //
- assert(
- s.admin.runCommand({'addshard': 'admin/' + getHostName() + ':' + rs4.getPrimary().port}).ok,
- 'adding replica set with name "admin" should work');
- var wRes = s.getDB('test').foo.insert({x: 1});
- assert(!wRes.hasWriteError() && wRes.nInserted === 1,
- 'failed to insert document into "test.foo" unsharded collection');
-
- // SERVER-19545 Should not be able to add config server replsets as shards.
- assert.commandFailed(s.admin.runCommand({addshard: rs5.getURL()}));
-
- s.stop();
-
- rs1.stopSet();
- rs2.stopSet();
- rs3.stopSet();
- rs4.stopSet();
- rs5.stopSet();
+
+ // 1. Test adding a *standalone*
+
+ // 1.a. with or without specifying the shardName.
+
+ var standalone = MongoRunner.runMongod();
+
+ jsTest.log("Adding a standalone *without* a specified shardName should succeed.");
+ addShardRes = st.s.adminCommand({addshard: standalone.name});
+ assertAddShardSucceeded(addShardRes);
+ removeShardWithName(addShardRes.shardAdded);
+
+ jsTest.log("Adding a standalone *with* a specified shardName should succeed.");
+ addShardRes = st.s.adminCommand({addshard: standalone.name, name: "shardName"});
+ assertAddShardSucceeded(addShardRes, "shardName");
+ removeShardWithName(addShardRes.shardAdded);
+
+ MongoRunner.stopMongod(standalone);
+
+ // 1.b. with an invalid hostname.
+
+ jsTest.log("Adding a standalone with a non-existing host should fail.");
+ addShardRes = st.s.adminCommand({addShard: getHostName() + ":" + portWithoutHostRunning});
+ assertAddShardFailed(addShardRes);
+
+ // 2. Test adding a *replica set* with an ordinary set name
+
+ // 2.a. with or without specifying the shardName.
+
+ rst = new ReplSetTest({nodes: 1});
+ rst.startSet();
+ rst.initiate();
+
+ jsTest.log("Adding a replica set without a specified shardName should succeed.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL()});
+ assertAddShardSucceeded(addShardRes);
+ assert.eq(rst.name, addShardRes.shardAdded);
+ removeShardWithName(addShardRes.shardAdded);
+
+ jsTest.log(
+ "Adding a replica set with a specified shardName that matches the set's name should succeed.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL(), name: rst.name});
+ assertAddShardSucceeded(addShardRes, rst.name);
+ removeShardWithName(addShardRes.shardAdded);
+
+ jsTest.log(
+ "Adding a replica set with a specified shardName that differs from the set's name should succeed.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL(), name: "differentShardName"});
+ assertAddShardSucceeded(addShardRes, "differentShardName");
+ removeShardWithName(addShardRes.shardAdded);
+
+ jsTest.log("Adding a replica with a specified shardName of 'config' should fail.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL(), name: "config"});
+ assertAddShardFailed(addShardRes, "config");
+
+ // 2.b. with invalid hostnames.
+
+ jsTest.log("Adding a replica set with only non-existing hosts should fail.");
+ addShardRes =
+ st.s.adminCommand({addShard: rst.name + "/NonExistingHost:" + portWithoutHostRunning});
+ assertAddShardFailed(addShardRes);
+
+ jsTest.log("Adding a replica set with mixed existing/non-existing hosts should fail.");
+ addShardRes = st.s.adminCommand({
+ addShard:
+ rst.name + "/" + rst.getPrimary().name + ",NonExistingHost:" + portWithoutHostRunning
+ });
+ assertAddShardFailed(addShardRes);
+
+ rst.stopSet();
+
+ // 3. Test adding a replica set whose *set name* is "config" with or without specifying the
+ // shardName.
+
+ rst = new ReplSetTest({name: "config", nodes: 1});
+ rst.startSet();
+ rst.initiate();
+
+ jsTest.log(
+ "Adding a replica set whose setName is config without specifying shardName should fail.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL()});
+ assertAddShardFailed(addShardRes);
+
+ jsTest.log(
+ "Adding a replica set whose setName is config with specified shardName 'config' should fail.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL(), name: rst.name});
+ assertAddShardFailed(addShardRes, rst.name);
+
+ jsTest.log(
+ "Adding a replica set whose setName is config with a non-'config' shardName should succeed");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL(), name: "nonConfig"});
+ assertAddShardSucceeded(addShardRes, "nonConfig");
+ removeShardWithName(addShardRes.shardAdded);
+
+ rst.stopSet();
+
+ // 4. Test that a replica set whose *set name* is "admin" can be written to (SERVER-17232).
+
+ rst = new ReplSetTest({name: "admin", nodes: 1});
+ rst.startSet();
+ rst.initiate();
+
+ jsTest.log("A replica set whose set name is 'admin' should be able to be written to.");
+ addShardRes = st.s.adminCommand({addShard: rst.getURL()});
+ assertAddShardSucceeded(addShardRes);
+ assert.writeOK(st.s.getDB('test').foo.insert({x: 1}));
+
+ rst.stopSet();
+
+ // 5. Test adding a --configsvr replica set.
+
+ var configRS = new ReplSetTest({nodes: 1});
+ configRS.startSet({configsvr: '', storageEngine: 'wiredTiger'});
+ configRS.initiate();
+
+ jsTest.log("Adding a config server replica set without a specified shardName should fail.");
+ addShardRes = st.s.adminCommand({addShard: configRS.getURL()});
+ assertAddShardFailed(addShardRes);
+
+ jsTest.log(
+ "Adding a config server replica set with a shardName that matches the set's name should fail.");
+ addShardRes = st.s.adminCommand({addShard: configRS.getURL(), name: configRS.name});
+ assertAddShardFailed(addShardRes, configRS.name);
+
+ jsTest.log(
+ "Adding a config server replica set even with a non-'config' shardName should fail.");
+ addShardRes = st.s.adminCommand({addShard: configRS.getURL(), name: "nonConfig"});
+ assertAddShardFailed(addShardRes, "nonConfig");
+
+ configRS.stopSet();
+
+ st.stop();
})();
diff --git a/jstests/sharding/authCommands.js b/jstests/sharding/authCommands.js
index 810ccaf9292..2fb42801a40 100644
--- a/jstests/sharding/authCommands.js
+++ b/jstests/sharding/authCommands.js
@@ -12,7 +12,7 @@ var doTest = function() {
shards: 2,
chunksize: 2,
rs: rsOpts,
- other: {nopreallocj: 1, useHostname: false}
+ other: {nopreallocj: 1, useHostname: false, enableAutoSplit: true}
});
var mongos = st.s;
diff --git a/jstests/sharding/auto_rebalance.js b/jstests/sharding/auto_rebalance.js
index 3153442e5d0..bf2d2ebca3c 100644
--- a/jstests/sharding/auto_rebalance.js
+++ b/jstests/sharding/auto_rebalance.js
@@ -4,8 +4,14 @@
'use strict';
- var st = new ShardingTest(
- {name: 'auto_rebalance_rs', mongos: 1, shards: 2, chunksize: 1, rs: {nodes: 3}});
+ var st = new ShardingTest({
+ name: 'auto_rebalance_rs',
+ mongos: 1,
+ shards: 2,
+ chunksize: 1,
+ enableAutoSplit: true,
+ rs: {nodes: 3}
+ });
assert.writeOK(st.getDB("config").settings.update(
{_id: "balancer"}, {$set: {"_secondaryThrottle": false}}, {upsert: true}));
diff --git a/jstests/sharding/auto1.js b/jstests/sharding/autosplit.js
index c893902843e..8786e05a646 100644
--- a/jstests/sharding/auto1.js
+++ b/jstests/sharding/autosplit.js
@@ -1,6 +1,6 @@
(function() {
- var s = new ShardingTest({name: "auto1", shards: 2, mongos: 1, other: {enableBalancer: 1}});
+ var s = new ShardingTest({name: "auto1", shards: 2, mongos: 1, other: {enableAutoSplit: true}});
s.adminCommand({enablesharding: "test"});
s.ensurePrimaryShard('test', 'shard0001');
diff --git a/jstests/sharding/autosplit_heuristics.js b/jstests/sharding/autosplit_heuristics.js
index c4d415ce0de..618541e314b 100644
--- a/jstests/sharding/autosplit_heuristics.js
+++ b/jstests/sharding/autosplit_heuristics.js
@@ -3,8 +3,11 @@
// works as expected even after splitting.
//
-var st =
- new ShardingTest({shards: 1, mongos: 1, other: {mongosOptions: {chunkSize: 1, verbose: 2}}});
+var st = new ShardingTest({
+ shards: 1,
+ mongos: 1,
+ other: {mongosOptions: {chunkSize: 1, verbose: 2}, enableAutoSplit: true}
+});
// The balancer is by default stopped, thus it will NOT interfere unpredictably with the chunk
// moves/splits depending on the timing.
diff --git a/jstests/sharding/auto2.js b/jstests/sharding/autosplit_with_balancer.js
index 9e88d667fbe..09054dc9bd2 100644
--- a/jstests/sharding/auto2.js
+++ b/jstests/sharding/autosplit_with_balancer.js
@@ -1,6 +1,6 @@
(function() {
- var s = new ShardingTest({name: "auto2", shards: 2, mongos: 2});
+ var s = new ShardingTest({name: "auto2", shards: 2, mongos: 2, other: {enableAutoSplit: true}});
s.adminCommand({enablesharding: "test"});
s.ensurePrimaryShard('test', 'shard0001');
diff --git a/jstests/sharding/bouncing_count.js b/jstests/sharding/bouncing_count.js
index 5459cc9ca77..8aa8ff7d3bb 100644
--- a/jstests/sharding/bouncing_count.js
+++ b/jstests/sharding/bouncing_count.js
@@ -42,8 +42,6 @@
// Make sure mongos A is up-to-date
mongosA.getDB("admin").runCommand({flushRouterConfig: 1});
- config.printShardingStatus(true);
-
jsTestLog("Running count!");
printjson(collB.count());
diff --git a/jstests/sharding/conf_server_write_concern.js b/jstests/sharding/conf_server_write_concern.js
index c4e08939548..f7c68e477e4 100644
--- a/jstests/sharding/conf_server_write_concern.js
+++ b/jstests/sharding/conf_server_write_concern.js
@@ -22,11 +22,11 @@ function writeToConfigTest() {
/**
* Test write concern with w parameter will not cause an error when writes to mongos
- * would trigger writes to config servers (in this test, split chunks is used).
+ * would trigger writes to config servers (in this test, autosplit is used).
*/
function configTest() {
jsTestLog("Testing metadata writes to config server with write concern");
- var st = new ShardingTest({shards: 1, rs: true, other: {chunkSize: 1}});
+ var st = new ShardingTest({shards: 1, rs: true, other: {chunkSize: 1, enableAutoSplit: true}});
var mongos = st.s;
var testDB = mongos.getDB('test');
diff --git a/jstests/sharding/csrs_upgrade/csrs_upgrade_mongod_using_movechunk.js b/jstests/sharding/csrs_upgrade/csrs_upgrade_mongod_using_movechunk.js
index 9965d9c1b8d..57ca122ad5f 100644
--- a/jstests/sharding/csrs_upgrade/csrs_upgrade_mongod_using_movechunk.js
+++ b/jstests/sharding/csrs_upgrade/csrs_upgrade_mongod_using_movechunk.js
@@ -16,7 +16,7 @@ var st;
var dataCollectionName = testDBName + ".data";
jsTest.log("Setting up CSRS sharded cluster");
- st = new ShardingTest({name: "csrs", mongos: 2, mongosOptions: {noAutoSplit: ""}, shards: 2});
+ st = new ShardingTest({name: "csrs", mongos: 2, shards: 2});
jsTest.log("Enabling sharding on " + testDBName);
assert.commandWorked(st.s0.adminCommand({enablesharding: testDBName}));
diff --git a/jstests/sharding/cursor1.js b/jstests/sharding/cursor1.js
index ab387870751..e7d54ac85e4 100644
--- a/jstests/sharding/cursor1.js
+++ b/jstests/sharding/cursor1.js
@@ -3,8 +3,7 @@
(function() {
// Turn off auto-splitting, because this test handles chunk splitting manually.
- var s = new ShardingTest(
- {name: "sharding_cursor1", shards: 2, other: {mongosOptions: {noAutoSplit: ""}}});
+ var s = new ShardingTest({name: "sharding_cursor1", shards: 2});
s.config.settings.find().forEach(printjson);
// create a sharded 'test.foo', for the moment with just one chunk
diff --git a/jstests/sharding/disable_autosplit.js b/jstests/sharding/disable_autosplit.js
index c6b7b7d5e1f..f5e5cce1f2c 100644
--- a/jstests/sharding/disable_autosplit.js
+++ b/jstests/sharding/disable_autosplit.js
@@ -4,8 +4,7 @@
var chunkSize = 1; // In MB
- var st = new ShardingTest(
- {shards: 1, mongos: 1, other: {chunksize: chunkSize, mongosOptions: {noAutoSplit: ""}}});
+ var st = new ShardingTest({shards: 1, mongos: 1, other: {chunksize: chunkSize}});
var data = "x";
while (data.length < chunkSize * 1024 * 1024) {
diff --git a/jstests/sharding/findandmodify2.js b/jstests/sharding/findandmodify2.js
index 525788e70f9..1de7f8556ee 100644
--- a/jstests/sharding/findandmodify2.js
+++ b/jstests/sharding/findandmodify2.js
@@ -1,5 +1,9 @@
-var s = new ShardingTest(
- {name: "find_and_modify_sharded_2", shards: 2, mongos: 1, other: {chunkSize: 1}});
+var s = new ShardingTest({
+ name: "find_and_modify_sharded_2",
+ shards: 2,
+ mongos: 1,
+ other: {chunkSize: 1, enableAutoSplit: true}
+});
s.adminCommand({enablesharding: "test"});
var db = s.getDB("test");
diff --git a/jstests/sharding/forget_mr_temp_ns.js b/jstests/sharding/forget_mr_temp_ns.js
index fd950bcf43c..6043b300455 100644
--- a/jstests/sharding/forget_mr_temp_ns.js
+++ b/jstests/sharding/forget_mr_temp_ns.js
@@ -2,7 +2,7 @@
// Tests whether we forget M/R's temporary namespaces for sharded output
//
-var st = new ShardingTest({shards: 1, mongos: 1});
+var st = new ShardingTest({shards: 1, mongos: 1, other: {enableAutoSplit: true}});
var mongos = st.s0;
var admin = mongos.getDB("admin");
diff --git a/jstests/sharding/in_memory_sort_limit.js b/jstests/sharding/in_memory_sort_limit.js
index 1c7e8c73447..1dd9bc4e37c 100644
--- a/jstests/sharding/in_memory_sort_limit.js
+++ b/jstests/sharding/in_memory_sort_limit.js
@@ -4,7 +4,7 @@
(function() {
"use strict";
- var st = new ShardingTest({shards: 2});
+ var st = new ShardingTest({shards: 2, other: {enableAutoSplit: true}});
var db = st.s.getDB('test');
var mongosCol = db.getCollection('skip');
db.adminCommand({enableSharding: 'test'});
diff --git a/jstests/sharding/merge_chunks_basic.js b/jstests/sharding/merge_chunks_basic.js
index 8cdcf05f61a..540a0f2355b 100644
--- a/jstests/sharding/merge_chunks_basic.js
+++ b/jstests/sharding/merge_chunks_basic.js
@@ -1,25 +1,20 @@
-//
-// Basic tests for mergeChunks
-//
-
+/**
+ * Perform basic tests for the mergeChunks command against mongos.
+ */
(function() {
'use strict';
- var st = new ShardingTest({mongos: 1, shards: 2});
-
+ var st = new ShardingTest({mongos: 2, shards: 2, other: {chunkSize: 1}});
var mongos = st.s0;
var kDbName = 'db';
- var shards = mongos.getCollection('config.shards').find().toArray();
-
- var shard0 = shards[0]._id;
- var shard1 = shards[1]._id;
+ var shard0 = st.shard0.shardName;
+ var shard1 = st.shard1.shardName;
var ns = kDbName + ".foo";
assert.commandWorked(mongos.adminCommand({enableSharding: kDbName}));
-
st.ensurePrimaryShard(kDbName, shard0);
// Fail if invalid namespace.
@@ -34,24 +29,35 @@
// Errors if either bounds is not a valid shard key.
assert.eq(0, mongos.getDB('config').chunks.count({ns: ns}));
+
assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: {a: 1}}));
assert.eq(1, mongos.getDB('config').chunks.count({ns: ns}));
assert.commandWorked(mongos.adminCommand({split: ns, middle: {a: 0}}));
assert.commandWorked(mongos.adminCommand({split: ns, middle: {a: -1}}));
assert.commandWorked(mongos.adminCommand({split: ns, middle: {a: 1}}));
+ // Fail if a wrong key
assert.commandFailed(mongos.adminCommand({mergeChunks: ns, bounds: [{x: -1}, {a: 1}]}));
-
- // Fail if a wrong key.
assert.commandFailed(mongos.adminCommand({mergeChunks: ns, bounds: [{a: -1}, {x: 1}]}));
- // Fail if chunks do not contain a bound.
+ // Fail if chunks do not contain a bound
assert.commandFailed(mongos.adminCommand({mergeChunks: ns, bounds: [{a: -1}, {a: 10}]}));
- // Validate metadata.
+ // Fail if chunks to be merged are not contiguous on the shard
+ assert.commandWorked(st.s0.adminCommand(
+ {moveChunk: ns, bounds: [{a: -1}, {a: 0}], to: shard1, _waitForDelete: true}));
+ assert.commandFailed(
+ st.s0.adminCommand({mergeChunks: ns, bounds: [{a: MinKey()}, {a: MaxKey()}]}));
+ assert.commandWorked(st.s0.adminCommand(
+ {moveChunk: ns, bounds: [{a: -1}, {a: 0}], to: shard0, _waitForDelete: true}));
+
+ // Validate metadata
// There are four chunks [{$minKey, -1}, {-1, 0}, {0, 1}, {1, $maxKey}]
- assert.eq(4, mongos.getDB('config').chunks.count({ns: ns}));
- assert.commandWorked(mongos.adminCommand({mergeChunks: ns, bounds: [{a: -1}, {a: 1}]}));
+ assert.eq(4, st.s0.getDB('config').chunks.count({ns: ns}));
+
+ // Use the second (stale) mongos to invoke the mergeChunks command so we can exercise the stale
+ // shard version refresh logic
+ assert.commandWorked(st.s1.adminCommand({mergeChunks: ns, bounds: [{a: -1}, {a: 1}]}));
assert.eq(3, mongos.getDB('config').chunks.count({ns: ns}));
assert.eq(1, mongos.getDB('config').chunks.count({ns: ns, min: {a: -1}, max: {a: 1}}));
diff --git a/jstests/sharding/merge_chunks_test.js b/jstests/sharding/merge_chunks_test.js
index 6b00a6532ea..1725657b0ad 100644
--- a/jstests/sharding/merge_chunks_test.js
+++ b/jstests/sharding/merge_chunks_test.js
@@ -9,33 +9,35 @@
var mongos = st.s0;
var staleMongos = st.s1;
var admin = mongos.getDB("admin");
- var shards = mongos.getCollection("config.shards").find().toArray();
var coll = mongos.getCollection("foo.bar");
- assert(admin.runCommand({enableSharding: coll.getDB() + ""}).ok);
- printjson(admin.runCommand({movePrimary: coll.getDB() + "", to: shards[0]._id}));
- assert(admin.runCommand({shardCollection: coll + "", key: {_id: 1}}).ok);
+ assert.commandWorked(admin.runCommand({enableSharding: coll.getDB() + ""}));
+ st.ensurePrimaryShard('foo', st.shard0.shardName);
+ assert.commandWorked(admin.runCommand({shardCollection: coll + "", key: {_id: 1}}));
// Create ranges MIN->0,0->10,(hole),20->40,40->50,50->90,(hole),100->110,110->MAX on first
// shard
jsTest.log("Creating ranges...");
- assert(admin.runCommand({split: coll + "", middle: {_id: 0}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 10}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 20}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 40}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 50}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 90}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 100}}).ok);
- assert(admin.runCommand({split: coll + "", middle: {_id: 110}}).ok);
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 0}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 10}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 20}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 40}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 50}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 90}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 100}}));
+ assert.commandWorked(admin.runCommand({split: coll + "", middle: {_id: 110}}));
- assert(admin.runCommand({moveChunk: coll + "", find: {_id: 10}, to: shards[1]._id}).ok);
- assert(admin.runCommand({moveChunk: coll + "", find: {_id: 90}, to: shards[1]._id}).ok);
+ assert.commandWorked(
+ admin.runCommand({moveChunk: coll + "", find: {_id: 10}, to: st.shard1.shardName}));
+ assert.commandWorked(
+ admin.runCommand({moveChunk: coll + "", find: {_id: 90}, to: st.shard1.shardName}));
st.printShardingStatus();
// Insert some data into each of the consolidated ranges
assert.writeOK(coll.insert({_id: 0}));
+ assert.writeOK(coll.insert({_id: 10}));
assert.writeOK(coll.insert({_id: 40}));
assert.writeOK(coll.insert({_id: 110}));
@@ -47,60 +49,94 @@
// S1: 10->20, 90->100
// Make sure merging non-exact chunks is invalid
-
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 5}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 5}, {_id: 10}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 15}, {_id: 50}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 55}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 105}, {_id: MaxKey}]}).ok);
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 5}]}));
+ assert.commandFailed(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 5}, {_id: 10}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 15}, {_id: 50}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 55}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 105}, {_id: MaxKey}]}));
// Make sure merging single chunks is invalid
-
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 0}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 40}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 110}, {_id: MaxKey}]}).ok);
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 0}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 40}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 110}, {_id: MaxKey}]}));
// Make sure merging over holes is invalid
-
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 0}, {_id: 40}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 40}, {_id: 110}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 50}, {_id: 110}]}).ok);
+ assert.commandFailed(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 0}, {_id: 40}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 40}, {_id: 110}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 50}, {_id: 110}]}));
// Make sure merging between shards is invalid
-
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 0}, {_id: 20}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 10}, {_id: 40}]}).ok);
- assert(!admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 40}, {_id: 100}]}).ok);
-
- assert.eq(3, staleCollection.find().itcount());
+ assert.commandFailed(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 0}, {_id: 20}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 10}, {_id: 40}]}));
+ assert.commandFailed(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 40}, {_id: 100}]}));
+ assert.eq(4, staleCollection.find().itcount());
jsTest.log("Trying merges that should succeed...");
- assert(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 10}]}).ok);
-
- assert.eq(3, staleCollection.find().itcount());
-
+ // Make sure merge including the MinKey works
+ assert.commandWorked(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 10}]}));
+ assert.eq(4, staleCollection.find().itcount());
// S0: min->10, 20->40, 40->50, 50->90, 100->110, 110->max
// S1: 10->20, 90->100
- // Make sure merging three chunks is valid.
-
- jsTest.log(tojson(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 90}]})));
-
+ // Make sure merging three chunks in the middle works
+ assert.commandWorked(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 20}, {_id: 90}]}));
+ assert.eq(4, staleCollection.find().itcount());
// S0: min->10, 20->90, 100->110, 110->max
// S1: 10->20, 90->100
- assert.eq(3, staleCollection.find().itcount());
-
- assert(admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 100}, {_id: MaxKey}]}).ok);
-
- assert.eq(3, staleCollection.find().itcount());
-
+ // Make sure merge including the MaxKey works
+ assert.commandWorked(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 100}, {_id: MaxKey}]}));
+ assert.eq(4, staleCollection.find().itcount());
// S0: min->10, 20->90, 100->max
// S1: 10->20, 90->100
- st.printShardingStatus();
+ // Make sure merging chunks after a chunk has been moved out of a shard succeeds
+ assert.commandWorked(
+ admin.runCommand({moveChunk: coll + "", find: {_id: 110}, to: st.shard1.shardName}));
+ assert.commandWorked(
+ admin.runCommand({moveChunk: coll + "", find: {_id: 10}, to: st.shard0.shardName}));
+ assert.eq(4, staleCollection.find().itcount());
+ // S0: min->10, 10->20, 20->90
+ // S1: 90->100, 100->max
+
+ assert.commandWorked(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: 90}, {_id: MaxKey}]}));
+ assert.eq(4, staleCollection.find().itcount());
+ // S0: min->10, 10->20, 20->90
+ // S1: 90->max
+
+ // Make sure merge on the other shard after a chunk has been merged succeeds
+ assert.commandWorked(
+ admin.runCommand({mergeChunks: coll + "", bounds: [{_id: MinKey}, {_id: 90}]}));
+ // S0: min->90
+ // S1: 90->max
+
+ st.printShardingStatus(true);
+
+ assert.eq(2, st.s0.getDB('config').chunks.find({}).itcount());
+ assert.eq(1,
+ st.s0.getDB('config')
+ .chunks.find({'min._id': MinKey, 'max._id': 90, shard: st.shard0.shardName})
+ .itcount());
+ assert.eq(1,
+ st.s0.getDB('config')
+ .chunks.find({'min._id': 90, 'max._id': MaxKey, shard: st.shard1.shardName})
+ .itcount());
st.stop();
-
})();
diff --git a/jstests/sharding/movechunk_with_default_paranoia.js b/jstests/sharding/movechunk_with_default_paranoia.js
index a6f4704ec90..a5cc5421093 100644
--- a/jstests/sharding/movechunk_with_default_paranoia.js
+++ b/jstests/sharding/movechunk_with_default_paranoia.js
@@ -2,7 +2,7 @@
* This test checks that moveParanoia defaults to off (ie the moveChunk directory will not
* be created).
*/
-var st = new ShardingTest({shards: 2, mongos: 1, other: {chunkSize: 1}});
+var st = new ShardingTest({shards: 2, mongos: 1, other: {chunkSize: 1, enableAutoSplit: true}});
load("jstests/sharding/movechunk_include.js");
setupMoveChunkTest(st);
diff --git a/jstests/sharding/movechunk_with_moveParanoia.js b/jstests/sharding/movechunk_with_moveParanoia.js
index 96348d827bf..73b5e37cd61 100644
--- a/jstests/sharding/movechunk_with_moveParanoia.js
+++ b/jstests/sharding/movechunk_with_moveParanoia.js
@@ -1,8 +1,11 @@
/**
* This test sets moveParanoia flag and then check that the directory is created with the moved data
*/
-var st = new ShardingTest(
- {shards: 2, mongos: 1, other: {chunkSize: 1, shardOptions: {moveParanoia: ""}}});
+var st = new ShardingTest({
+ shards: 2,
+ mongos: 1,
+ other: {chunkSize: 1, enableAutoSplit: true, shardOptions: {moveParanoia: ""}}
+});
load("jstests/sharding/movechunk_include.js");
setupMoveChunkTest(st);
diff --git a/jstests/sharding/movechunk_with_noMoveParanoia.js b/jstests/sharding/movechunk_with_noMoveParanoia.js
index ae8ef5899a8..5d9cb8f090d 100644
--- a/jstests/sharding/movechunk_with_noMoveParanoia.js
+++ b/jstests/sharding/movechunk_with_noMoveParanoia.js
@@ -1,8 +1,11 @@
/**
* This test sets moveParanoia flag and then check that the directory is created with the moved data
*/
-var st = new ShardingTest(
- {shards: 2, mongos: 1, other: {chunkSize: 1, shardOptions: {noMoveParanoia: ""}}});
+var st = new ShardingTest({
+ shards: 2,
+ mongos: 1,
+ other: {chunkSize: 1, enableAutoSplit: true, shardOptions: {noMoveParanoia: ""}}
+});
load("jstests/sharding/movechunk_include.js");
setupMoveChunkTest(st);
diff --git a/jstests/sharding/mrShardedOutput.js b/jstests/sharding/mrShardedOutput.js
index 6224b49f2dc..3ba3dacdaf9 100644
--- a/jstests/sharding/mrShardedOutput.js
+++ b/jstests/sharding/mrShardedOutput.js
@@ -4,7 +4,7 @@
// collection input twice the size of the first and outputs it to the new sharded
// collection created in the first pass.
-var st = new ShardingTest({shards: 2, other: {chunkSize: 1}});
+var st = new ShardingTest({shards: 2, other: {chunkSize: 1, enableAutoSplit: true}});
var config = st.getDB("config");
st.adminCommand({enablesharding: "test"});
diff --git a/jstests/sharding/mrShardedOutputAuth.js b/jstests/sharding/mrShardedOutputAuth.js
index 5e3b25e67cc..5fc1282d5b2 100644
--- a/jstests/sharding/mrShardedOutputAuth.js
+++ b/jstests/sharding/mrShardedOutputAuth.js
@@ -42,7 +42,7 @@
name: "mrShardedOutputAuth",
shards: 1,
mongos: 1,
- other: {extraOptions: {"keyFile": "jstests/libs/key1"}}
+ other: {enableAutoSplit: true, extraOptions: {"keyFile": "jstests/libs/key1"}}
});
// Setup the users to the input, output and admin databases
diff --git a/jstests/sharding/sharding_balance4.js b/jstests/sharding/sharding_balance4.js
index aaa9cbd02b2..5ca3c00ee5a 100644
--- a/jstests/sharding/sharding_balance4.js
+++ b/jstests/sharding/sharding_balance4.js
@@ -1,8 +1,12 @@
// Check that doing updates done during a migrate all go to the right place
(function() {
- var s = new ShardingTest(
- {name: "slow_sharding_balance4", shards: 2, mongos: 1, other: {chunkSize: 1}});
+ var s = new ShardingTest({
+ name: "slow_sharding_balance4",
+ shards: 2,
+ mongos: 1,
+ other: {chunkSize: 1, enableAutoSplit: true}
+ });
s.adminCommand({enablesharding: "test"});
s.ensurePrimaryShard('test', 'shard0001');
diff --git a/jstests/sharding/split_with_force.js b/jstests/sharding/split_with_force.js
index c66d2f145eb..3337e3e4a59 100644
--- a/jstests/sharding/split_with_force.js
+++ b/jstests/sharding/split_with_force.js
@@ -3,8 +3,7 @@
//
var options = {
- chunkSize: 1, // MB
- mongosOptions: {noAutoSplit: ""}
+ chunkSize: 1 // MB
};
var st = new ShardingTest({shards: 1, mongos: 1, other: options});
diff --git a/jstests/sharding/split_with_force_small.js b/jstests/sharding/split_with_force_small.js
index ad14f8642cb..0148c924993 100644
--- a/jstests/sharding/split_with_force_small.js
+++ b/jstests/sharding/split_with_force_small.js
@@ -3,8 +3,7 @@
//
var options = {
- chunkSize: 1, // MB
- mongosOptions: {noAutoSplit: ""}
+ chunkSize: 1 // MB
};
var st = new ShardingTest({shards: 1, mongos: 1, other: options});
diff --git a/jstests/sharding/top_chunk_autosplit.js b/jstests/sharding/top_chunk_autosplit.js
index 74a3e942cae..e91d5872b59 100644
--- a/jstests/sharding/top_chunk_autosplit.js
+++ b/jstests/sharding/top_chunk_autosplit.js
@@ -100,7 +100,8 @@ function runTest(test) {
// Main
var dbName = "test";
var collName = "topchunk";
-var st = shardSetup({name: "topchunk", shards: 4, chunkSize: 1}, dbName, collName);
+var st = shardSetup(
+ {name: "topchunk", shards: 4, chunkSize: 1, other: {enableAutoSplit: true}}, dbName, collName);
var db = st.getDB(dbName);
var coll = db[collName];
var configDB = st.s.getDB('config');
@@ -294,7 +295,9 @@ for (var i = 0; i < tests.length; i++) {
st.stop();
// Single node shard Tests
-st = shardSetup({name: "singleNode", shards: 1, chunkSize: 1}, dbName, collName);
+st = shardSetup({name: "singleNode", shards: 1, chunkSize: 1, other: {enableAutoSplit: true}},
+ dbName,
+ collName);
db = st.getDB(dbName);
coll = db[collName];
configDB = st.s.getDB('config');
@@ -331,7 +334,14 @@ st.stop();
// maxSize test
// To set maxSize, must manually add the shards
st = shardSetup(
- {name: "maxSize", shards: 2, chunkSize: 1, other: {manualAddShard: true}}, dbName, collName);
+ {
+ name: "maxSize",
+ shards: 2,
+ chunkSize: 1,
+ other: {manualAddShard: true, enableAutoSplit: true}
+ },
+ dbName,
+ collName);
db = st.getDB(dbName);
coll = db[collName];
configDB = st.s.getDB('config');
diff --git a/jstests/sharding/write_cmd_auto_split.js b/jstests/sharding/write_cmd_auto_split.js
index 110fa7ddd9f..ebd554b480d 100644
--- a/jstests/sharding/write_cmd_auto_split.js
+++ b/jstests/sharding/write_cmd_auto_split.js
@@ -4,7 +4,7 @@
(function() {
'use strict';
- var st = new ShardingTest({shards: 1, other: {chunkSize: 1}});
+ var st = new ShardingTest({shards: 1, other: {chunkSize: 1, enableAutoSplit: true}});
var configDB = st.s.getDB('config');
assert.commandWorked(configDB.adminCommand({enableSharding: 'test'}));
diff --git a/jstests/sharding/zbigMapReduce.js b/jstests/sharding/zbigMapReduce.js
index fda81e12df8..4872592e533 100644
--- a/jstests/sharding/zbigMapReduce.js
+++ b/jstests/sharding/zbigMapReduce.js
@@ -9,7 +9,7 @@ function setupTest() {
numReplicas: 2,
chunkSize: 1,
rsOptions: {oplogSize: 50},
- enableBalancer: 1
+ enableBalancer: true
}
});
diff --git a/jstests/ssl/libs/ssl_helpers.js b/jstests/ssl/libs/ssl_helpers.js
index 5fab2f1f030..5b9ad0142a5 100644
--- a/jstests/ssl/libs/ssl_helpers.js
+++ b/jstests/ssl/libs/ssl_helpers.js
@@ -62,8 +62,12 @@ var replShouldFail = function(name, opt1, opt2) {
*/
function mixedShardTest(options1, options2, shouldSucceed) {
try {
- var st = new ShardingTest(
- {mongos: [options1], config: [options1], shards: [options1, options2]});
+ var st = new ShardingTest({
+ mongos: [options1],
+ config: [options1],
+ shards: [options1, options2],
+ other: {enableAutoSplit: true}
+ });
st.stopBalancer();
// Test mongos talking to config servers
diff --git a/jstests/ssl/sharding_with_x509.js b/jstests/ssl/sharding_with_x509.js
index 6e497ab290e..0a43e8229fc 100644
--- a/jstests/ssl/sharding_with_x509.js
+++ b/jstests/ssl/sharding_with_x509.js
@@ -1,73 +1,75 @@
-// Tests basic sharding with x509 cluster auth
-// The purpose is to verify the connectivity between mongos and the shards
+// Tests basic sharding with x509 cluster auth. The purpose is to verify the connectivity between
+// mongos and the shards.
+(function() {
+ 'use strict';
-var x509_options = {
- sslMode: "requireSSL",
- sslPEMKeyFile: "jstests/libs/server.pem",
- sslCAFile: "jstests/libs/ca.pem",
- sslClusterFile: "jstests/libs/cluster_cert.pem",
- sslAllowInvalidHostnames: "",
- clusterAuthMode: "x509"
-};
+ var x509_options = {
+ sslMode: "requireSSL",
+ sslPEMKeyFile: "jstests/libs/server.pem",
+ sslCAFile: "jstests/libs/ca.pem",
+ sslClusterFile: "jstests/libs/cluster_cert.pem",
+ sslAllowInvalidHostnames: "",
+ clusterAuthMode: "x509"
+ };
-// Start ShardingTest with enableBalancer because ShardingTest attempts to turn
-// off the balancer otherwise, which it will not be authorized to do. Once SERVER-14017
-// is fixed the "enableBalancer" line could be removed.
-var st = new ShardingTest({
- name: "sharding_with_x509",
- shards: 2,
- mongos: 1,
- other: {
- enableBalancer: true,
- configOptions: x509_options,
- mongosOptions: x509_options,
- rsOptions: x509_options,
- shardOptions: x509_options
- }
-});
+ // Start ShardingTest with enableBalancer because ShardingTest attempts to turn off the balancer
+ // otherwise, which it will not be authorized to do. Once SERVER-14017 is fixed the
+ // "enableBalancer" line could be removed.
+ var st = new ShardingTest({
+ shards: 2,
+ mongos: 1,
+ other: {
+ enableBalancer: true,
+ configOptions: x509_options,
+ mongosOptions: x509_options,
+ rsOptions: x509_options,
+ shardOptions: x509_options
+ }
+ });
-st.s.getDB('admin').createUser({user: 'admin', pwd: 'pwd', roles: ['root']});
-st.s.getDB('admin').auth('admin', 'pwd');
-var coll = st.s.getCollection("test.foo");
+ st.s.getDB('admin').createUser({user: 'admin', pwd: 'pwd', roles: ['root']});
+ st.s.getDB('admin').auth('admin', 'pwd');
-st.shardColl(coll, {_id: 1}, false);
+ var coll = st.s.getCollection("test.foo");
-// Create an index so we can find by num later
-coll.ensureIndex({insert: 1});
+ st.shardColl(coll, {_id: 1}, false);
-print("starting insertion phase");
+ // Create an index so we can find by num later
+ coll.ensureIndex({insert: 1});
-// Insert a bunch of data
-var toInsert = 2000;
-var bulk = coll.initializeUnorderedBulkOp();
-for (var i = 0; i < toInsert; i++) {
- bulk.insert({my: "test", data: "to", insert: i});
-}
-assert.writeOK(bulk.execute());
+ print("starting insertion phase");
-print("starting updating phase");
+ // Insert a bunch of data
+ var toInsert = 2000;
+ var bulk = coll.initializeUnorderedBulkOp();
+ for (var i = 0; i < toInsert; i++) {
+ bulk.insert({my: "test", data: "to", insert: i});
+ }
+ assert.writeOK(bulk.execute());
-// Update a bunch of data
-var toUpdate = toInsert;
-bulk = coll.initializeUnorderedBulkOp();
-for (var i = 0; i < toUpdate; i++) {
- var id = coll.findOne({insert: i})._id;
- bulk.find({insert: i, _id: id}).update({$inc: {counter: 1}});
-}
-assert.writeOK(bulk.execute());
+ print("starting updating phase");
-print("starting deletion");
+ // Update a bunch of data
+ var toUpdate = toInsert;
+ bulk = coll.initializeUnorderedBulkOp();
+ for (var i = 0; i < toUpdate; i++) {
+ var id = coll.findOne({insert: i})._id;
+ bulk.find({insert: i, _id: id}).update({$inc: {counter: 1}});
+ }
+ assert.writeOK(bulk.execute());
-// Remove a bunch of data
-var toDelete = toInsert / 2;
-bulk = coll.initializeUnorderedBulkOp();
-for (var i = 0; i < toDelete; i++) {
- bulk.find({insert: i}).remove();
-}
-assert.writeOK(bulk.execute());
+ print("starting deletion");
+
+ // Remove a bunch of data
+ var toDelete = toInsert / 2;
+ bulk = coll.initializeUnorderedBulkOp();
+ for (var i = 0; i < toDelete; i++) {
+ bulk.find({insert: i}).remove();
+ }
+ assert.writeOK(bulk.execute());
-// Make sure the right amount of data is there
-assert.eq(coll.find().count(), toInsert / 2);
+ // Make sure the right amount of data is there
+ assert.eq(coll.find().itcount({my: 'test'}), toInsert / 2);
-// Finish
-st.stop();
+ st.stop();
+})();
diff --git a/jstests/tool/dumprestore9.js b/jstests/tool/dumprestore9.js
index 69797603c16..fb0a9d694e3 100644
--- a/jstests/tool/dumprestore9.js
+++ b/jstests/tool/dumprestore9.js
@@ -13,7 +13,7 @@ if (0) {
name: "dumprestore9a",
shards: 2,
mongos: 3,
- other: {chunkSize: 1, enableBalancer: 1}
+ other: {chunkSize: 1, enableBalancer: true}
});
step("Shard collection");
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index ebc3e3cd2d9..e4b6c1dc770 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -246,7 +246,6 @@ mongodLibDeps = [
"db/mongodandmongos",
"db/mongodwebserver",
"db/serveronly",
- "db/repl/storage_interface_impl",
"executor/network_interface_factory",
's/commands/shared_cluster_commands',
"util/ntservice",
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 30bf2a8cc1b..7657112cac6 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -155,6 +155,7 @@ error_code("OplogOutOfOrder", 152)
error_code("CanRepairToDowngrade", 157)
error_code("MustUpgrade", 158)
error_code("QueryPlanKilled", 173)
+error_code("ReceivedOpReplyMessage", 198);
# Non-sequential error codes (for compatibility only)
error_code("RecvStaleConfig", 9996)
diff --git a/src/mongo/bson/bson_validate_test.cpp b/src/mongo/bson/bson_validate_test.cpp
index 41215a8db0b..d1648fc2c10 100644
--- a/src/mongo/bson/bson_validate_test.cpp
+++ b/src/mongo/bson/bson_validate_test.cpp
@@ -124,7 +124,7 @@ TEST(BSONValidate, MuckingData1) {
char* data = const_cast<char*>(mine.objdata());
- data[i] = 200;
+ data[i] = 0xc8U;
numToRun++;
if (mine.valid()) {
diff --git a/src/mongo/bson/util/bson_check.h b/src/mongo/bson/util/bson_check.h
index 4f2585e9e75..10a04e005fe 100644
--- a/src/mongo/bson/util/bson_check.h
+++ b/src/mongo/bson/util/bson_check.h
@@ -81,4 +81,15 @@ Status bsonCheckOnlyHasFields(StringData objectName,
return bsonCheckOnlyHasFields(objectName, o, &legals[0], legals + N);
}
+/**
+ * Throws a uassert if the type of the elem does not match that provided in expectedType
+ */
+inline void checkBSONType(BSONType expectedType, const BSONElement& elem) {
+ uassert(elem.type() == BSONType::EOO ? ErrorCodes::NoSuchKey : ErrorCodes::TypeMismatch,
+ str::stream() << "Wrong type for '" << elem.fieldNameStringData() << "'. Expected a "
+ << typeName(expectedType) << ", got a " << typeName(elem.type()) << '.',
+ elem.type() == expectedType);
+}
+
+
} // namespace mongo
diff --git a/src/mongo/client/connpool.h b/src/mongo/client/connpool.h
index 586f1958319..509965971ff 100644
--- a/src/mongo/client/connpool.h
+++ b/src/mongo/client/connpool.h
@@ -51,6 +51,8 @@ struct ConnectionPoolStats;
* thread safety is handled by DBConnectionPool
*/
class PoolForHost {
+ MONGO_DISALLOW_COPYING(PoolForHost);
+
public:
// Sentinel value indicating pool has no cleanup limit
static const int kPoolSizeUnlimited;
@@ -62,16 +64,6 @@ public:
_maxPoolSize(kPoolSizeUnlimited),
_checkedOut(0) {}
- PoolForHost(const PoolForHost& other)
- : _created(other._created),
- _minValidCreationTimeMicroSec(other._minValidCreationTimeMicroSec),
- _type(other._type),
- _maxPoolSize(other._maxPoolSize),
- _checkedOut(other._checkedOut) {
- verify(_created == 0);
- verify(other._pool.size() == 0);
- }
-
~PoolForHost();
/**
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp
index 0c357a22615..706c5ed8838 100644
--- a/src/mongo/client/dbclient.cpp
+++ b/src/mongo/client/dbclient.cpp
@@ -1542,12 +1542,12 @@ void DBClientConnection::handleNotMasterResponse(const BSONElement& elemToCheck)
return;
}
- MONGO_LOG_COMPONENT(1, logger::LogComponent::kReplication)
- << "got not master from: " << _serverAddress << " of repl set: " << _parentReplSetName;
-
ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(_parentReplSetName);
if (monitor) {
- monitor->failedHost(_serverAddress);
+ monitor->failedHost(_serverAddress,
+ {ErrorCodes::NotMaster,
+ str::stream() << "got not master from: " << _serverAddress
+ << " of repl set: " << _parentReplSetName});
}
_failed = true;
diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp
index 5c1af64d8fd..fcf96255081 100644
--- a/src/mongo/client/dbclient_rs.cpp
+++ b/src/mongo/client/dbclient_rs.cpp
@@ -80,6 +80,11 @@ public:
} _populateReadPrefSecOkCmdList;
/**
+ * Maximum number of retries to make for auto-retry logic when performing a slave ok operation.
+ */
+const size_t MAX_RETRY = 3;
+
+/**
* Extracts the read preference settings from the query document. Note that this method
* assumes that the query is ok for secondaries so it defaults to
* ReadPreference::SecondaryPreferred when nothing is specified. Supports the following
@@ -124,13 +129,13 @@ ReadPreferenceSetting* _extractReadPref(const BSONObj& query, int queryOptions)
: mongo::ReadPreference::PrimaryOnly;
return new ReadPreferenceSetting(pref, TagSet());
}
+
} // namespace
// --------------------------------
// ----- DBClientReplicaSet ---------
// --------------------------------
-const size_t DBClientReplicaSet::MAX_RETRY = 3;
bool DBClientReplicaSet::_authPooledSecondaryConn = true;
DBClientReplicaSet::DBClientReplicaSet(const string& name,
@@ -288,7 +293,8 @@ DBClientConnection* DBClientReplicaSet::checkMaster() {
if (!_master->isFailed())
return _master.get();
- monitor->failedHost(_masterHost);
+ monitor->failedHost(
+ _masterHost, {ErrorCodes::fromInt(40332), "Last known master host cannot be reached"});
h = monitor->getMasterOrUassert(); // old master failed, try again.
}
@@ -309,11 +315,11 @@ DBClientConnection* DBClientReplicaSet::checkMaster() {
}
if (newConn == NULL || !errmsg.empty()) {
- monitor->failedHost(_masterHost);
- uasserted(ErrorCodes::FailedToSatisfyReadPreference,
- str::stream() << "can't connect to new replica set master ["
- << _masterHost.toString() << "]"
- << (errmsg.empty() ? "" : ", err: ") << errmsg);
+ const std::string message = str::stream() << "can't connect to new replica set master ["
+ << _masterHost.toString() << "]"
+ << (errmsg.empty() ? "" : ", err: ") << errmsg;
+ monitor->failedHost(_masterHost, {ErrorCodes::fromInt(40333), message});
+ uasserted(ErrorCodes::FailedToSatisfyReadPreference, message);
}
resetMaster();
@@ -341,7 +347,8 @@ bool DBClientReplicaSet::checkLastHost(const ReadPreferenceSetting* readPref) {
// Make sure we don't think the host is down.
if (_lastSlaveOkConn->isFailed() || !_getMonitor()->isHostUp(_lastSlaveOkHost)) {
- invalidateLastSlaveOkCache();
+ _invalidateLastSlaveOkCache(
+ {ErrorCodes::fromInt(40334), "Last slave connection is no longer available"});
return false;
}
@@ -367,7 +374,7 @@ void DBClientReplicaSet::logoutAll(DBClientConnection* conn) {
conn->logout(i->first, response);
} catch (const UserException& ex) {
warning() << "Failed to logout: " << conn->getServerAddress() << " on db: " << i->first
- << endl;
+ << causedBy(ex);
}
}
}
@@ -440,13 +447,11 @@ void DBClientReplicaSet::_auth(const BSONObj& params) {
throw;
}
- StringBuilder errMsgB;
- errMsgB << "can't authenticate against replica set node "
- << _lastSlaveOkHost.toString();
- lastNodeStatus = ex.toStatus(errMsgB.str());
-
- LOG(1) << lastNodeStatus.reason() << endl;
- invalidateLastSlaveOkCache();
+ const Status status = ex.toStatus();
+ lastNodeStatus = {status.code(),
+ str::stream() << "can't authenticate against replica set node "
+ << _lastSlaveOkHost << ": " << status.reason()};
+ _invalidateLastSlaveOkCache(lastNodeStatus);
}
}
@@ -515,6 +520,7 @@ unique_ptr<DBClientCursor> DBClientReplicaSet::query(const string& ns,
: "[not cached]") << ")" << endl;
string lastNodeErrMsg;
+
for (size_t retry = 0; retry < MAX_RETRY; retry++) {
try {
DBClientConnection* conn = selectNodeUsingTags(readPref);
@@ -527,14 +533,11 @@ unique_ptr<DBClientCursor> DBClientReplicaSet::query(const string& ns,
ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions, batchSize);
return checkSlaveQueryResult(std::move(cursor));
- } catch (const DBException& dbExcep) {
- StringBuilder errMsgBuilder;
- errMsgBuilder << "can't query replica set node " << _lastSlaveOkHost.toString()
- << ": " << causedBy(dbExcep);
- lastNodeErrMsg = errMsgBuilder.str();
-
- LOG(1) << lastNodeErrMsg << endl;
- invalidateLastSlaveOkCache();
+ } catch (const DBException& ex) {
+ const Status status = ex.toStatus();
+ lastNodeErrMsg = str::stream() << "can't query replica set node "
+ << _lastSlaveOkHost << ": " << status.reason();
+ _invalidateLastSlaveOkCache({status.code(), lastNodeErrMsg});
}
}
@@ -577,14 +580,12 @@ BSONObj DBClientReplicaSet::findOne(const string& ns,
}
return conn->findOne(ns, query, fieldsToReturn, queryOptions);
- } catch (const DBException& dbExcep) {
- StringBuilder errMsgBuilder;
- errMsgBuilder << "can't findone replica set node " << _lastSlaveOkHost.toString()
- << ": " << causedBy(dbExcep);
- lastNodeErrMsg = errMsgBuilder.str();
-
- LOG(1) << lastNodeErrMsg << endl;
- invalidateLastSlaveOkCache();
+ } catch (const DBException& ex) {
+ const Status status = ex.toStatus();
+ lastNodeErrMsg = str::stream() << "can't findone replica set node "
+ << _lastSlaveOkHost.toString() << ": "
+ << status.reason();
+ _invalidateLastSlaveOkCache({status.code(), lastNodeErrMsg});
}
}
@@ -611,12 +612,13 @@ void DBClientReplicaSet::killCursor(long long cursorID) {
}
void DBClientReplicaSet::isntMaster() {
- log() << "got not master for: " << _masterHost << endl;
- // Can't use _getMonitor because that will create a new monitor from the cached seed if
- // the monitor doesn't exist.
+ // Can't use _getMonitor because that will create a new monitor from the cached seed if the
+ // monitor doesn't exist.
ReplicaSetMonitorPtr monitor = ReplicaSetMonitor::get(_setName);
if (monitor) {
- monitor->failedHost(_masterHost);
+ monitor->failedHost(
+ _masterHost,
+ {ErrorCodes::NotMaster, str::stream() << "got not master for: " << _masterHost});
}
resetMaster();
@@ -647,9 +649,11 @@ unique_ptr<DBClientCursor> DBClientReplicaSet::checkSlaveQueryResult(
}
void DBClientReplicaSet::isntSecondary() {
- log() << "slave no longer has secondary status: " << _lastSlaveOkHost << endl;
// Failover to next slave
- _getMonitor()->failedHost(_lastSlaveOkHost);
+ _getMonitor()->failedHost(
+ _lastSlaveOkHost,
+ {ErrorCodes::NotMasterOrSecondary,
+ str::stream() << "slave no longer has secondary status: " << _lastSlaveOkHost});
resetSlaveOkConn();
}
@@ -764,14 +768,13 @@ void DBClientReplicaSet::say(Message& toSend, bool isRetry, string* actualServer
_lazyState._lastOp = lastOp;
_lazyState._secondaryQueryOk = true;
_lazyState._lastClient = conn;
- } catch (const DBException& DBExcep) {
- StringBuilder errMsgBuilder;
- errMsgBuilder << "can't callLazy replica set node "
- << _lastSlaveOkHost.toString() << ": " << causedBy(DBExcep);
- lastNodeErrMsg = errMsgBuilder.str();
-
- LOG(1) << lastNodeErrMsg << endl;
- invalidateLastSlaveOkCache();
+ } catch (const DBException& e) {
+ const Status status = e.toStatus();
+ lastNodeErrMsg = str::stream() << "can't callLazy replica set node "
+ << _lastSlaveOkHost.toString() << ": "
+ << status.reason();
+ _invalidateLastSlaveOkCache({status.code(), lastNodeErrMsg});
+
continue;
}
@@ -930,10 +933,10 @@ rpc::UniqueReply DBClientReplicaSet::runCommandWithMetadata(StringData database,
// and we retry.
return conn->runCommandWithMetadata(database, command, metadata, commandArgs);
} catch (const DBException& ex) {
- log() << exceptionToStatus();
- invalidateLastSlaveOkCache();
+ _invalidateLastSlaveOkCache(ex.toStatus());
}
}
+
uasserted(ErrorCodes::NodeNotFound,
str::stream() << "Could not satisfy $readPreference of '" << readPref.toBSON() << "' "
<< "while attempting to run command " << command);
@@ -974,14 +977,15 @@ bool DBClientReplicaSet::call(Message& toSend,
}
return conn->call(toSend, response, assertOk, nullptr);
- } catch (const DBException& dbExcep) {
- LOG(1) << "can't call replica set node " << _lastSlaveOkHost << ": "
- << causedBy(dbExcep) << endl;
-
+ } catch (const DBException& ex) {
if (actualServer)
*actualServer = "";
- invalidateLastSlaveOkCache();
+ const Status status = ex.toStatus();
+ _invalidateLastSlaveOkCache({status.code(),
+ str::stream() << "can't call replica set node "
+ << _lastSlaveOkHost << ": "
+ << status.reason()});
}
}
@@ -1016,12 +1020,11 @@ bool DBClientReplicaSet::call(Message& toSend,
return true;
}
-void DBClientReplicaSet::invalidateLastSlaveOkCache() {
- /* This is not wrapped in with if (_lastSlaveOkConn && _lastSlaveOkConn->isFailed())
- * because there are certain exceptions that will not make the connection be labeled
- * as failed. For example, asserts 13079, 13080, 16386
- */
- _getMonitor()->failedHost(_lastSlaveOkHost);
+void DBClientReplicaSet::_invalidateLastSlaveOkCache(const Status& status) {
+ // This is not wrapped in with if (_lastSlaveOkConn && _lastSlaveOkConn->isFailed()) because
+ // there are certain exceptions that will not make the connection be labeled as failed. For
+ // example, asserts 13079, 13080, 16386
+ _getMonitor()->failedHost(_lastSlaveOkHost, status);
resetSlaveOkConn();
}
diff --git a/src/mongo/client/dbclient_rs.h b/src/mongo/client/dbclient_rs.h
index be3eaccf26a..f52d94057f8 100644
--- a/src/mongo/client/dbclient_rs.h
+++ b/src/mongo/client/dbclient_rs.h
@@ -260,9 +260,10 @@ private:
bool checkLastHost(const ReadPreferenceSetting* readPref);
/**
- * Destroys all cached information about the last slaveOk operation.
+ * Destroys all cached information about the last slaveOk operation and reports the host as
+ * failed in the replica set monitor with the specified 'status'.
*/
- void invalidateLastSlaveOkCache();
+ void _invalidateLastSlaveOkCache(const Status& status);
void _authConnection(DBClientConnection* conn);
@@ -282,12 +283,6 @@ private:
*/
void resetSlaveOkConn();
- /**
- * Maximum number of retries to make for auto-retry logic when performing a slave ok
- * operation.
- */
- static const size_t MAX_RETRY;
-
// TODO: remove this when processes other than mongos uses the driver version.
static bool _authPooledSecondaryConn;
diff --git a/src/mongo/client/dbclient_rs_test.cpp b/src/mongo/client/dbclient_rs_test.cpp
index 3c79eb92478..8292e4b67fa 100644
--- a/src/mongo/client/dbclient_rs_test.cpp
+++ b/src/mongo/client/dbclient_rs_test.cpp
@@ -706,7 +706,8 @@ TEST_F(TaggedFiveMemberRS, ConnShouldNotPinIfHostMarkedAsFailed) {
// This is the only difference from ConnShouldPinIfSameSettings which tests that we *do* pin
// in if the host is still marked as up. Note that this only notifies the RSM, and does not
// directly effect the DBClientRS.
- ReplicaSetMonitor::get(replSet->getSetName())->failedHost(HostAndPort(dest));
+ ReplicaSetMonitor::get(replSet->getSetName())
+ ->failedHost(HostAndPort(dest), {ErrorCodes::InternalError, "Test error"});
{
Query query;
diff --git a/src/mongo/client/fetcher.cpp b/src/mongo/client/fetcher.cpp
index 97e17d0fbda..aad747945df 100644
--- a/src/mongo/client/fetcher.cpp
+++ b/src/mongo/client/fetcher.cpp
@@ -31,9 +31,12 @@
#include "mongo/client/fetcher.h"
+#include "mongo/base/data_range_cursor.h"
+#include "mongo/base/data_type_validated.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/rpc/object_check.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -53,27 +56,19 @@ const char* kNamespaceFieldName = "ns";
const char* kFirstBatchFieldName = "firstBatch";
const char* kNextBatchFieldName = "nextBatch";
-/**
- * Parses cursor response in command result for cursor ID, namespace and documents.
- * 'batchFieldName' will be 'firstBatch' for the initial remote command invocation and
- * 'nextBatch' for getMore.
- */
-Status parseCursorResponse(const BSONObj& obj,
- const std::string& batchFieldName,
- Fetcher::QueryResponse* batchData) {
- invariant(batchFieldName == kFirstBatchFieldName || batchFieldName == kNextBatchFieldName);
- invariant(batchData);
-
- BSONElement cursorElement = obj.getField(kCursorFieldName);
+Status parseCursorResponseFromResponseObj(const BSONObj& responseObj,
+ const std::string& batchFieldName,
+ Fetcher::QueryResponse* batchData) {
+ BSONElement cursorElement = responseObj.getField(kCursorFieldName);
if (cursorElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
if (!cursorElement.isABSONObj()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName
- << "' field must be an object: " << obj);
+ << "' field must be an object: " << responseObj);
}
BSONObj cursorObj = cursorElement.Obj();
@@ -81,13 +76,13 @@ Status parseCursorResponse(const BSONObj& obj,
if (cursorIdElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName << "."
- << kCursorIdFieldName << "' field: " << obj);
+ << kCursorIdFieldName << "' field: " << responseObj);
}
if (cursorIdElement.type() != mongo::NumberLong) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << kCursorIdFieldName
<< "' field must be a 'long' but was a '"
- << typeName(cursorIdElement.type()) << "': " << obj);
+ << typeName(cursorIdElement.type()) << "': " << responseObj);
}
batchData->cursorId = cursorIdElement.numberLong();
@@ -96,18 +91,18 @@ Status parseCursorResponse(const BSONObj& obj,
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain "
<< "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
if (namespaceElement.type() != mongo::String) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' field must be a string: " << obj);
+ << "' field must be a string: " << responseObj);
}
NamespaceString tempNss(namespaceElement.valuestrsafe());
if (!tempNss.isValid()) {
return Status(ErrorCodes::BadValue,
str::stream() << "'" << kCursorFieldName << "." << kNamespaceFieldName
- << "' contains an invalid namespace: " << obj);
+ << "' contains an invalid namespace: " << responseObj);
}
batchData->nss = tempNss;
@@ -115,12 +110,12 @@ Status parseCursorResponse(const BSONObj& obj,
if (batchElement.eoo()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "cursor response must contain '" << kCursorFieldName << "."
- << batchFieldName << "' field: " << obj);
+ << batchFieldName << "' field: " << responseObj);
}
if (!batchElement.isABSONObj()) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "'" << kCursorFieldName << "." << batchFieldName
- << "' field must be an array: " << obj);
+ << "' field must be an array: " << responseObj);
}
BSONObj batchObj = batchElement.Obj();
for (auto itemElement : batchObj) {
@@ -128,7 +123,7 @@ Status parseCursorResponse(const BSONObj& obj,
return Status(ErrorCodes::FailedToParse,
str::stream() << "found non-object " << itemElement << " in "
<< "'" << kCursorFieldName << "." << batchFieldName
- << "' field: " << obj);
+ << "' field: " << responseObj);
}
batchData->documents.push_back(itemElement.Obj().getOwned());
}
@@ -136,6 +131,130 @@ Status parseCursorResponse(const BSONObj& obj,
return Status::OK();
}
+/**
+ * Extracts the CursorId and array of results from a Message representing an OP_REPLY. Returns a
+ * non-OK status if Message does not represent a well-formed OP_REPLY.
+ */
+StatusWith<std::tuple<CursorId, std::vector<BSONObj>>> getBatchFromReply(const Message* response) {
+ auto header = response->header();
+ if (header.getNetworkOp() != mongo::opReply) {
+ return {ErrorCodes::ProtocolError,
+ str::stream() << "Expected to be decoding an OP_REPLY but got "
+ << mongo::networkOpToString(header.getNetworkOp())};
+ }
+
+ if ((header.dataLen() < 0) ||
+ (static_cast<std::size_t>(header.dataLen()) > mongo::MaxMessageSizeBytes)) {
+ return {ErrorCodes::InvalidLength,
+ str::stream() << "Received message has invalid length field with value "
+ << header.dataLen()};
+ }
+
+ QueryResult::View qr = response->header().view2ptr();
+
+ auto resultFlags = qr.getResultFlags();
+
+ if (resultFlags & ResultFlag_CursorNotFound) {
+ return {ErrorCodes::CursorNotFound,
+ str::stream() << "Cursor with id '" << qr.getCursorId() << "' not found"};
+ }
+
+ // Use CDRC directly instead of DocumentRange as DocumentRange has a throwing API.
+ ConstDataRangeCursor cdrc{qr.data(), qr.data() + header.dataLen()};
+
+ if (resultFlags & ResultFlag_ErrSet) {
+ if (qr.getNReturned() != 1) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "ResultFlag_ErrSet flag set on reply, but nReturned was '"
+ << qr.getNReturned() << "' - expected 1"};
+ }
+ // Convert error document to a Status.
+ // Will throw if first document is invalid BSON.
+ auto first = cdrc.readAndAdvance<Validated<BSONObj>>();
+ if (!first.isOK()) {
+ return first.getStatus();
+ }
+
+ // Convert error document to a status.
+ return getStatusFromCommandResult(first.getValue());
+ }
+
+ const int32_t nReturned = qr.getNReturned();
+ std::vector<BSONObj> batch;
+ batch.reserve(qr.getNReturned());
+
+ int32_t nParsed = 0;
+ Validated<BSONObj> nextObj;
+ while (!cdrc.empty() && nParsed < nReturned) {
+ auto readStatus = cdrc.readAndAdvance(&nextObj);
+ if (!readStatus.isOK()) {
+ return readStatus;
+ }
+ ++nParsed;
+ batch.emplace_back(nextObj.val.getOwned());
+ }
+ if (nParsed != nReturned) {
+ return {ErrorCodes::InvalidLength,
+ str::stream() << "Count of documents in OP_REPLY message (" << nParsed
+ << ") did not match the value specified in the nReturned field ("
+ << nReturned << ")"};
+ }
+
+ return {std::make_tuple(qr.getCursorId(), std::move(batch))};
+}
+
+Status parseCursorResponseFromRawMessage(const Message* message,
+ Fetcher::QueryResponse* batchData) {
+ auto batchStatus = getBatchFromReply(message);
+ if (!batchStatus.isOK()) {
+ return batchStatus.getStatus();
+ }
+
+ std::tie(batchData->cursorId, batchData->documents) = batchStatus.getValue();
+ return Status::OK();
+}
+
+/**
+ * Parses cursor response in command result for cursor ID, namespace and documents.
+ * 'batchFieldName' will be 'firstBatch' for the initial remote command invocation and 'nextBatch'
+ * for getMore.
+ */
+Status parseCursorResponse(const RemoteCommandResponse& response,
+ const std::string& batchFieldName,
+ Fetcher::QueryResponse* batchData) {
+ invariant(batchFieldName == kFirstBatchFieldName || batchFieldName == kNextBatchFieldName);
+ invariant(batchData);
+
+ // If we are talking to a 3.0 mongod, then the response will have come back as an OP_QUERY, and
+ // we'll need to parse the raw message to populate 'batchData'. Otherwise, we ran a find or
+ // getMore command, and need to parse the BSON that is returned from those commands.
+ Status status = getStatusFromCommandResult(response.data);
+ if (status.isOK()) {
+ return parseCursorResponseFromResponseObj(response.data, batchFieldName, batchData);
+ } else if (status.code() == ErrorCodes::ReceivedOpReplyMessage) {
+ auto ns = response.data["ns"];
+ if (!ns) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "expected 'ns' field to be present in response: "
+ << response.data};
+ }
+ if (ns.type() != String) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "expected 'ns' field to be a string, was "
+ << typeName(ns.type()) << ": " << response.data};
+ }
+ auto nss = NamespaceString(ns.String());
+ if (!nss.isValid()) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << "invalid 'ns' field in response: " << response.data};
+ }
+ batchData->nss = nss;
+ return parseCursorResponseFromRawMessage(response.message.get(), batchData);
+ } else {
+ return status;
+ }
+}
+
} // namespace
Fetcher::Fetcher(executor::TaskExecutor* executor,
@@ -244,16 +363,8 @@ void Fetcher::_callback(const RemoteCommandCallbackArgs& rcbd, const char* batch
return;
}
- const BSONObj& queryResponseObj = rcbd.response.getValue().data;
- Status status = getStatusFromCommandResult(queryResponseObj);
- if (!status.isOK()) {
- _work(StatusWith<Fetcher::QueryResponse>(status), nullptr, nullptr);
- _finishCallback();
- return;
- }
-
QueryResponse batchData;
- status = parseCursorResponse(queryResponseObj, batchFieldName, &batchData);
+ auto status = parseCursorResponse(rcbd.response.getValue(), batchFieldName, &batchData);
if (!status.isOK()) {
_work(StatusWith<Fetcher::QueryResponse>(status), nullptr, nullptr);
_finishCallback();
diff --git a/src/mongo/client/remote_command_runner_impl.cpp b/src/mongo/client/remote_command_runner_impl.cpp
index e856173a314..7b6caab1805 100644
--- a/src/mongo/client/remote_command_runner_impl.cpp
+++ b/src/mongo/client/remote_command_runner_impl.cpp
@@ -83,7 +83,7 @@ Status getStatusFromCursorResult(DBClientCursor& cursor) {
using RequestDownconverter = StatusWith<Message>(*)(const RemoteCommandRequest&);
using ReplyUpconverter = StatusWith<RemoteCommandResponse>(*)(std::uint32_t requestId,
StringData cursorNamespace,
- const Message& response);
+ Message* response);
template <RequestDownconverter downconvertRequest, ReplyUpconverter upconvertReply>
StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* conn,
@@ -104,7 +104,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* co
auto messageId = requestMsg.header().getId();
- return upconvertReply(messageId, DbMessage(requestMsg).getns(), responseMsg);
+ return upconvertReply(messageId, DbMessage(requestMsg).getns(), &responseMsg);
}
/**
@@ -114,7 +114,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedCommand(DBClientConnection* co
StatusWith<RemoteCommandResponse> runDownconvertedFindCommand(DBClientConnection* conn,
const RemoteCommandRequest& request) {
return runDownconvertedCommand<executor::downconvertFindCommandRequest,
- executor::upconvertLegacyQueryResponse>(conn, request);
+ executor::prepareOpReplyErrorResponse>(conn, request);
}
/**
@@ -124,7 +124,7 @@ StatusWith<RemoteCommandResponse> runDownconvertedFindCommand(DBClientConnection
StatusWith<RemoteCommandResponse> runDownconvertedGetMoreCommand(
DBClientConnection* conn, const RemoteCommandRequest& request) {
return runDownconvertedCommand<executor::downconvertGetMoreCommandRequest,
- executor::upconvertLegacyGetMoreResponse>(conn, request);
+ executor::prepareOpReplyErrorResponse>(conn, request);
}
} // namespace
diff --git a/src/mongo/client/remote_command_targeter.h b/src/mongo/client/remote_command_targeter.h
index 020cc68945c..1381c6e8315 100644
--- a/src/mongo/client/remote_command_targeter.h
+++ b/src/mongo/client/remote_command_targeter.h
@@ -73,18 +73,18 @@ public:
Milliseconds maxWait = Milliseconds(0)) = 0;
/**
- * Reports to the targeter that a NotMaster response was received when communicating with
- * "host', and so it should update its bookkeeping to avoid giving out the host again on a
- * subsequent request for the primary.
+ * Reports to the targeter that a 'status' indicating a not master error was received when
+ * communicating with 'host', and so it should update its bookkeeping to avoid giving out the
+ * host again on a subsequent request for the primary.
*/
- virtual void markHostNotMaster(const HostAndPort& host) = 0;
+ virtual void markHostNotMaster(const HostAndPort& host, const Status& status) = 0;
/**
- * Similar to markHostNotMaster(). Reports to the targeter that a HostUnreachable response was
- * received when communicating with "host". The targeter should update its bookkeeping to avoid
- * giving out the same host on a subsequent request.
+ * Reports to the targeter that a 'status' indicating a network error was received when trying
+ * to communicate with 'host', and so it should update its bookkeeping to avoid giving out the
+ * host again on a subsequent request for the primary.
*/
- virtual void markHostUnreachable(const HostAndPort& host) = 0;
+ virtual void markHostUnreachable(const HostAndPort& host, const Status& status) = 0;
/**
* Based on the remaining time of the operation and the default max wait time for findHost,
diff --git a/src/mongo/client/remote_command_targeter_factory_mock.cpp b/src/mongo/client/remote_command_targeter_factory_mock.cpp
index 2972528d704..aab38425444 100644
--- a/src/mongo/client/remote_command_targeter_factory_mock.cpp
+++ b/src/mongo/client/remote_command_targeter_factory_mock.cpp
@@ -52,12 +52,12 @@ public:
return _mock->findHost(readPref, maxWait);
}
- void markHostNotMaster(const HostAndPort& host) override {
- _mock->markHostNotMaster(host);
+ void markHostNotMaster(const HostAndPort& host, const Status& status) override {
+ _mock->markHostNotMaster(host, status);
}
- void markHostUnreachable(const HostAndPort& host) override {
- _mock->markHostUnreachable(host);
+ void markHostUnreachable(const HostAndPort& host, const Status& status) override {
+ _mock->markHostUnreachable(host, status);
}
private:
diff --git a/src/mongo/client/remote_command_targeter_mock.cpp b/src/mongo/client/remote_command_targeter_mock.cpp
index 73ee8de2e0e..7d6cffe2111 100644
--- a/src/mongo/client/remote_command_targeter_mock.cpp
+++ b/src/mongo/client/remote_command_targeter_mock.cpp
@@ -57,9 +57,10 @@ StatusWith<HostAndPort> RemoteCommandTargeterMock::findHost(const ReadPreference
return _findHostReturnValue;
}
-void RemoteCommandTargeterMock::markHostNotMaster(const HostAndPort& host) {}
+void RemoteCommandTargeterMock::markHostNotMaster(const HostAndPort& host, const Status& status) {}
-void RemoteCommandTargeterMock::markHostUnreachable(const HostAndPort& host) {}
+void RemoteCommandTargeterMock::markHostUnreachable(const HostAndPort& host, const Status& status) {
+}
void RemoteCommandTargeterMock::setConnectionStringReturnValue(const ConnectionString returnValue) {
_connectionStringReturnValue = std::move(returnValue);
diff --git a/src/mongo/client/remote_command_targeter_mock.h b/src/mongo/client/remote_command_targeter_mock.h
index d4c8835b619..4083f4a1187 100644
--- a/src/mongo/client/remote_command_targeter_mock.h
+++ b/src/mongo/client/remote_command_targeter_mock.h
@@ -60,12 +60,12 @@ public:
/**
* No-op for the mock.
*/
- void markHostNotMaster(const HostAndPort& host) override;
+ void markHostNotMaster(const HostAndPort& host, const Status& status) override;
/**
* No-op for the mock.
*/
- void markHostUnreachable(const HostAndPort& host) override;
+ void markHostUnreachable(const HostAndPort& host, const Status& status) override;
/**
* Sets the return value for the next call to connectionString.
diff --git a/src/mongo/client/remote_command_targeter_rs.cpp b/src/mongo/client/remote_command_targeter_rs.cpp
index 429798ec40d..9af56905370 100644
--- a/src/mongo/client/remote_command_targeter_rs.cpp
+++ b/src/mongo/client/remote_command_targeter_rs.cpp
@@ -63,16 +63,12 @@ StatusWith<HostAndPort> RemoteCommandTargeterRS::findHost(const ReadPreferenceSe
return _rsMonitor->getHostOrRefresh(readPref, maxWait);
}
-void RemoteCommandTargeterRS::markHostNotMaster(const HostAndPort& host) {
- invariant(_rsMonitor);
-
- _rsMonitor->failedHost(host);
+void RemoteCommandTargeterRS::markHostNotMaster(const HostAndPort& host, const Status& status) {
+ _rsMonitor->failedHost(host, status);
}
-void RemoteCommandTargeterRS::markHostUnreachable(const HostAndPort& host) {
- invariant(_rsMonitor);
-
- _rsMonitor->failedHost(host);
+void RemoteCommandTargeterRS::markHostUnreachable(const HostAndPort& host, const Status& status) {
+ _rsMonitor->failedHost(host, status);
}
} // namespace mongo
diff --git a/src/mongo/client/remote_command_targeter_rs.h b/src/mongo/client/remote_command_targeter_rs.h
index 28decd97232..11b76945293 100644
--- a/src/mongo/client/remote_command_targeter_rs.h
+++ b/src/mongo/client/remote_command_targeter_rs.h
@@ -55,9 +55,9 @@ public:
StatusWith<HostAndPort> findHost(const ReadPreferenceSetting& readPref,
Milliseconds maxWait) override;
- void markHostNotMaster(const HostAndPort& host) override;
+ void markHostNotMaster(const HostAndPort& host, const Status& status) override;
- void markHostUnreachable(const HostAndPort& host) override;
+ void markHostUnreachable(const HostAndPort& host, const Status& status) override;
private:
// Name of the replica set which this targeter maintains
diff --git a/src/mongo/client/remote_command_targeter_standalone.cpp b/src/mongo/client/remote_command_targeter_standalone.cpp
index 45123540752..0c744cd453e 100644
--- a/src/mongo/client/remote_command_targeter_standalone.cpp
+++ b/src/mongo/client/remote_command_targeter_standalone.cpp
@@ -47,11 +47,13 @@ StatusWith<HostAndPort> RemoteCommandTargeterStandalone::findHost(
return _hostAndPort;
}
-void RemoteCommandTargeterStandalone::markHostNotMaster(const HostAndPort& host) {
+void RemoteCommandTargeterStandalone::markHostNotMaster(const HostAndPort& host,
+ const Status& status) {
dassert(host == _hostAndPort);
}
-void RemoteCommandTargeterStandalone::markHostUnreachable(const HostAndPort& host) {
+void RemoteCommandTargeterStandalone::markHostUnreachable(const HostAndPort& host,
+ const Status& status) {
dassert(host == _hostAndPort);
}
diff --git a/src/mongo/client/remote_command_targeter_standalone.h b/src/mongo/client/remote_command_targeter_standalone.h
index 603f8167bd0..4fff8f029f2 100644
--- a/src/mongo/client/remote_command_targeter_standalone.h
+++ b/src/mongo/client/remote_command_targeter_standalone.h
@@ -46,9 +46,9 @@ public:
StatusWith<HostAndPort> findHost(const ReadPreferenceSetting& readPref,
Milliseconds maxWait) override;
- void markHostNotMaster(const HostAndPort& host) override;
+ void markHostNotMaster(const HostAndPort& host, const Status& status) override;
- void markHostUnreachable(const HostAndPort& host) override;
+ void markHostUnreachable(const HostAndPort& host, const Status& status) override;
private:
const HostAndPort _hostAndPort;
diff --git a/src/mongo/client/replica_set_monitor.cpp b/src/mongo/client/replica_set_monitor.cpp
index 8cf534b3fd5..38bdc97f1d3 100644
--- a/src/mongo/client/replica_set_monitor.cpp
+++ b/src/mongo/client/replica_set_monitor.cpp
@@ -334,11 +334,11 @@ Refresher ReplicaSetMonitor::startOrContinueRefresh() {
return out;
}
-void ReplicaSetMonitor::failedHost(const HostAndPort& host) {
+void ReplicaSetMonitor::failedHost(const HostAndPort& host, const Status& status) {
stdx::lock_guard<stdx::mutex> lk(_state->mutex);
Node* node = _state->findNode(host);
if (node)
- node->markFailed();
+ node->markFailed(status);
DEV _state->checkInvariants();
}
@@ -580,9 +580,10 @@ void Refresher::receivedIsMaster(const HostAndPort& from,
_scan->waitingFor.erase(from);
const IsMasterReply reply(from, latencyMicros, replyObj);
+
// Handle various failure cases
if (!reply.ok) {
- failedHost(from);
+ failedHost(from, {ErrorCodes::CommandFailed, "Failed to execute 'ismaster' command"});
return;
}
@@ -600,17 +601,18 @@ void Refresher::receivedIsMaster(const HostAndPort& from,
warning() << "node: " << from << " isn't a part of set: " << _set->name
<< " ismaster: " << replyObj;
}
- failedHost(from);
+
+ failedHost(from,
+ {ErrorCodes::InconsistentReplicaSetNames,
+ str::stream() << "Target replica set name " << reply.setName
+ << " does not match the monitored set name " << _set->name});
return;
}
if (reply.isMaster) {
- if (!receivedIsMasterFromMaster(reply)) {
- log() << "node " << from << " believes it is primary, but its election id of "
- << reply.electionId << " and config version of " << reply.configVersion
- << " is older than the most recent election id " << _set->maxElectionId
- << " and config version of " << _set->configVersion;
- failedHost(from);
+ Status status = receivedIsMasterFromMaster(from, reply);
+ if (!status.isOK()) {
+ failedHost(from, status);
return;
}
}
@@ -633,7 +635,7 @@ void Refresher::receivedIsMaster(const HostAndPort& from,
DEV _set->checkInvariants();
}
-void Refresher::failedHost(const HostAndPort& host) {
+void Refresher::failedHost(const HostAndPort& host, const Status& status) {
_scan->waitingFor.erase(host);
// Failed hosts can't pass criteria, so the only way they'd effect the _refreshUntilMatches
@@ -643,7 +645,7 @@ void Refresher::failedHost(const HostAndPort& host) {
Node* node = _set->findNode(host);
if (node)
- node->markFailed();
+ node->markFailed(status);
}
ScanStatePtr Refresher::startNewScan(const SetState* set) {
@@ -681,13 +683,17 @@ ScanStatePtr Refresher::startNewScan(const SetState* set) {
return scan;
}
-bool Refresher::receivedIsMasterFromMaster(const IsMasterReply& reply) {
+Status Refresher::receivedIsMasterFromMaster(const HostAndPort& from, const IsMasterReply& reply) {
invariant(reply.isMaster);
// Reject if config version is older. This is for backwards compatibility with nodes in pv0
// since they don't have the same ordering with pv1 electionId.
if (reply.configVersion < _set->configVersion) {
- return false;
+ return {
+ ErrorCodes::NotMaster,
+ str::stream() << "Node " << from << " believes it is primary, but its config version "
+ << reply.configVersion << " is older than the most recent config version "
+ << _set->configVersion};
}
if (reply.electionId.isSet()) {
@@ -696,7 +702,11 @@ bool Refresher::receivedIsMasterFromMaster(const IsMasterReply& reply) {
// because configVersion needs to be incremented whenever the protocol version is changed.
if (reply.configVersion == _set->configVersion && _set->maxElectionId.isSet() &&
_set->maxElectionId.compare(reply.electionId) > 0) {
- return false;
+ return {
+ ErrorCodes::NotMaster,
+ str::stream() << "Node " << from << " believes it is primary, but its election id "
+ << reply.electionId << " is older than the most recent election id "
+ << _set->maxElectionId};
}
_set->maxElectionId = reply.electionId;
@@ -779,7 +789,7 @@ bool Refresher::receivedIsMasterFromMaster(const IsMasterReply& reply) {
_scan->foundUpMaster = true;
_set->lastSeenMaster = reply.host;
- return true;
+ return Status::OK();
}
void Refresher::receivedIsMasterBeforeFoundMaster(const IsMasterReply& reply) {
@@ -824,27 +834,28 @@ HostAndPort Refresher::_refreshUntilMatches(const ReadPreferenceSetting* criteri
continue;
case NextStep::CONTACT_HOST: {
- BSONObj reply; // empty on error
+ StatusWith<BSONObj> isMasterReplyStatus{ErrorCodes::InternalError,
+ "Uninitialized variable"};
int64_t pingMicros = 0;
- lk.unlock(); // relocked after attempting to call isMaster
+ // Do not do network calls while holding a mutex
+ lk.unlock();
try {
ScopedDbConnection conn(ConnectionString(ns.host), socketTimeoutSecs);
bool ignoredOutParam = false;
Timer timer;
+ BSONObj reply;
conn->isMaster(ignoredOutParam, &reply);
+ isMasterReplyStatus = reply;
pingMicros = timer.micros();
conn.done(); // return to pool on success.
} catch (const DBException& ex) {
// If this mongos is being used in an illegal configuration and must be
// upgraded, bubble up the error.
- Status status = ex.toStatus();
- if (status == ErrorCodes::MustUpgrade) {
+ isMasterReplyStatus = ex.toStatus();
+ if (isMasterReplyStatus == ErrorCodes::MustUpgrade) {
throw;
}
- reply = BSONObj(); // should be a no-op but want to be sure
- } catch (...) {
- reply = BSONObj(); // should be a no-op but want to be sure
}
lk.lock();
@@ -853,10 +864,10 @@ HostAndPort Refresher::_refreshUntilMatches(const ReadPreferenceSetting* criteri
if (_scan != _set->currentScan)
return criteria ? _set->getMatchingHost(*criteria) : HostAndPort();
- if (reply.isEmpty())
- failedHost(ns.host);
+ if (isMasterReplyStatus.isOK())
+ receivedIsMaster(ns.host, pingMicros, isMasterReplyStatus.getValue());
else
- receivedIsMaster(ns.host, pingMicros, reply);
+ failedHost(ns.host, isMasterReplyStatus.getStatus());
}
}
}
@@ -907,10 +918,13 @@ void IsMasterReply::parse(const BSONObj& obj) {
Node::Node(const HostAndPort& host) : host(host), latencyMicros(unknownLatency) {}
-void Node::markFailed() {
- LOG(1) << "Marking host " << host << " as failed";
+void Node::markFailed(const Status& status) {
+ if (isUp) {
+ log() << "Marking host " << host << " as failed" << causedBy(status);
+
+ isUp = false;
+ }
- isUp = false;
isMaster = false;
}
diff --git a/src/mongo/client/replica_set_monitor.h b/src/mongo/client/replica_set_monitor.h
index 10f9558d6a2..026287d398b 100644
--- a/src/mongo/client/replica_set_monitor.h
+++ b/src/mongo/client/replica_set_monitor.h
@@ -97,12 +97,14 @@ public:
Refresher startOrContinueRefresh();
/**
- * Notifies this Monitor that a host has failed and should be considered down.
+ * Notifies this Monitor that a host has failed because of the specified error 'status' and
+ * should be considered down.
*
- * Call this when you get a connection error. If you get an error while trying to refresh
- * our view of a host, call Refresher::hostFailed() instead.
+ * Call this when you get a connection error. If you get an error while trying to refresh our
+ * view of a host, call Refresher::failedHost instead because it bypasses taking the monitor's
+ * mutex.
*/
- void failedHost(const HostAndPort& host);
+ void failedHost(const HostAndPort& host, const Status& status);
/**
* Returns true if this node is the master based ONLY on local data. Be careful, return may
@@ -326,7 +328,7 @@ public:
/**
* Call this if a host returned from getNextStep failed to reply to an isMaster call.
*/
- void failedHost(const HostAndPort& host);
+ void failedHost(const HostAndPort& host, const Status& status);
/**
* True if this Refresher started a new full scan rather than joining an existing one.
@@ -342,15 +344,17 @@ public:
private:
/**
- * First, checks that the "reply" is not from a stale primary by
- * comparing the electionId of "reply" to the maxElectionId recorded by the SetState.
- * Returns true if "reply" belongs to a non-stale primary.
+ * First, checks that the "reply" is not from a stale primary by comparing the electionId of
+ * "reply" to the maxElectionId recorded by the SetState and returns OK status if "reply"
+ * belongs to a non-stale primary. Otherwise returns a failed status.
+ *
+ * The 'from' parameter specifies the node from which the response is received.
*
* Updates _set and _scan based on set-membership information from a master.
* Applies _scan->unconfirmedReplies to confirmed nodes.
* Does not update this host's node in _set->nodes.
*/
- bool receivedIsMasterFromMaster(const IsMasterReply& reply);
+ Status receivedIsMasterFromMaster(const HostAndPort& from, const IsMasterReply& reply);
/**
* Adjusts the _scan work queue based on information from this host.
diff --git a/src/mongo/client/replica_set_monitor_internal.h b/src/mongo/client/replica_set_monitor_internal.h
index 1b669f1ab9b..428da8e90cb 100644
--- a/src/mongo/client/replica_set_monitor_internal.h
+++ b/src/mongo/client/replica_set_monitor_internal.h
@@ -90,7 +90,7 @@ public:
struct Node {
explicit Node(const HostAndPort& host);
- void markFailed();
+ void markFailed(const Status& status);
bool matches(const ReadPreference pref) const;
diff --git a/src/mongo/client/replica_set_monitor_read_preference_test.cpp b/src/mongo/client/replica_set_monitor_read_preference_test.cpp
index 99346574b04..dde9d53c8af 100644
--- a/src/mongo/client/replica_set_monitor_read_preference_test.cpp
+++ b/src/mongo/client/replica_set_monitor_read_preference_test.cpp
@@ -36,9 +36,9 @@
#include "mongo/stdx/memory.h"
#include "mongo/unittest/unittest.h"
+namespace mongo {
namespace {
-using namespace mongo;
using std::set;
using std::vector;
@@ -145,7 +145,7 @@ TEST(ReplSetMonitorReadPref, PrimaryOnlyPriNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -183,7 +183,7 @@ TEST(ReplSetMonitorReadPref, PriPrefWithPriNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -197,7 +197,7 @@ TEST(ReplSetMonitorReadPref, SecOnly) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -211,8 +211,8 @@ TEST(ReplSetMonitorReadPref, SecOnlyOnlyPriOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[0].markFailed();
- nodes[2].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -225,7 +225,7 @@ TEST(ReplSetMonitorReadPref, SecPref) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -239,8 +239,8 @@ TEST(ReplSetMonitorReadPref, SecPrefWithNoSecOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[0].markFailed();
- nodes[2].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -254,9 +254,9 @@ TEST(ReplSetMonitorReadPref, SecPrefWithNoNodeOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getDefaultTagSet());
- nodes[0].markFailed();
- nodes[1].markFailed();
- nodes[2].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -315,7 +315,7 @@ TEST(ReplSetMonitorReadPref, PriPrefPriNotOkWithTags) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getP2TagSet());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -341,7 +341,7 @@ TEST(ReplSetMonitorReadPref, PriPrefPriNotOkWithTagsNoMatch) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getSingleNoMatchTag());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -397,7 +397,7 @@ TEST(ReplSetMonitorReadPref, SecPrefSecNotOkWithTags) {
<< "nyc"));
TagSet tags(arrayBuilder.arr());
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -423,7 +423,7 @@ TEST(ReplSetMonitorReadPref, SecPrefPriNotOkWithTagsNoMatch) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getSingleNoMatchTag());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -487,7 +487,7 @@ TEST(ReplSetMonitorReadPref, MultiPriOnlyPriNotOkTag) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getMultiNoMatchTag());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -595,7 +595,7 @@ private:
TEST_F(MultiTags, MultiTagsMatchesFirst) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -611,8 +611,8 @@ TEST_F(MultiTags, MultiTagsMatchesFirst) {
TEST_F(MultiTags, PriPrefPriNotOkMatchesFirstNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
- nodes[1].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -628,7 +628,7 @@ TEST_F(MultiTags, PriPrefPriNotOkMatchesFirstNotOk) {
TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondTest) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -644,8 +644,8 @@ TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondTest) {
TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondNotOkTest) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[1].markFailed();
- nodes[2].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -661,7 +661,7 @@ TEST_F(MultiTags, PriPrefPriNotOkMatchesSecondNotOkTest) {
TEST_F(MultiTags, PriPrefPriNotOkMatchesLastTest) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -677,8 +677,8 @@ TEST_F(MultiTags, PriPrefPriNotOkMatchesLastTest) {
TEST_F(MultiTags, PriPrefPriNotOkMatchesLastNotOkTest) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
- nodes[1].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -707,7 +707,7 @@ TEST(MultiTags, PriPrefPriNotOkNoMatch) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getMultiNoMatchTag());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -733,7 +733,7 @@ TEST_F(MultiTags, SecOnlyMatchesFirstTest) {
TEST_F(MultiTags, SecOnlyMatchesFirstNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -763,7 +763,7 @@ TEST_F(MultiTags, SecOnlyMatchesSecond) {
TEST_F(MultiTags, SecOnlyMatchesSecondNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -790,7 +790,7 @@ TEST_F(MultiTags, SecOnlyMatchesLast) {
TEST_F(MultiTags, SecOnlyMatchesLastNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(
@@ -838,7 +838,7 @@ TEST_F(MultiTags, SecPrefMatchesFirst) {
TEST_F(MultiTags, SecPrefMatchesFirstNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -868,7 +868,7 @@ TEST_F(MultiTags, SecPrefMatchesSecond) {
TEST_F(MultiTags, SecPrefMatchesSecondNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -898,7 +898,7 @@ TEST_F(MultiTags, SecPrefMatchesLast) {
TEST_F(MultiTags, SecPrefMatchesLastNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(nodes,
@@ -941,7 +941,7 @@ TEST(MultiTags, SecPrefMultiTagsNoMatchPriNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
TagSet tags(getMultiNoMatchTag());
- nodes[1].markFailed();
+ nodes[1].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -972,7 +972,7 @@ TEST(MultiTags, NearestMatchesFirstNotOk) {
TagSet tags(arrayBuilder.arr());
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -1006,7 +1006,7 @@ TEST_F(MultiTags, NearestMatchesSecondNotOk) {
TagSet tags(arrayBuilder.arr());
- nodes[2].markFailed();
+ nodes[2].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host =
@@ -1030,7 +1030,7 @@ TEST_F(MultiTags, NearestMatchesLast) {
TEST_F(MultiTags, NeatestMatchesLastNotOk) {
vector<Node> nodes = getThreeMemberWithTags();
- nodes[0].markFailed();
+ nodes[0].markFailed({ErrorCodes::InternalError, "Test error"});
bool isPrimarySelected = false;
HostAndPort host = selectNode(
@@ -1065,4 +1065,6 @@ TEST(TagSet, DefaultConstructorMatchesAll) {
TagSet tags;
ASSERT_EQUALS(tags.getTagBSON(), BSON_ARRAY(BSONObj()));
}
-}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/client/replica_set_monitor_test.cpp b/src/mongo/client/replica_set_monitor_test.cpp
index 845b66e8366..a56e5071f33 100644
--- a/src/mongo/client/replica_set_monitor_test.cpp
+++ b/src/mongo/client/replica_set_monitor_test.cpp
@@ -28,14 +28,13 @@
#include "mongo/platform/basic.h"
-
#include "mongo/client/replica_set_monitor.h"
#include "mongo/client/replica_set_monitor_internal.h"
#include "mongo/unittest/unittest.h"
+namespace mongo {
namespace {
-using namespace mongo;
using std::set;
// Pull nested types to top-level scope
@@ -768,7 +767,7 @@ TEST(ReplicaSetMonitor, OutOfBandFailedHost) {
if (i >= 1) {
HostAndPort a("a");
- rsm->failedHost(a);
+ rsm->failedHost(a, {ErrorCodes::InternalError, "Test error"});
Node* node = state->findNode(a);
ASSERT(node);
ASSERT(!node->isUp);
@@ -1065,3 +1064,4 @@ TEST(ReplicaSetMonitorTests, TwoPrimaries2ndHasOlderConfigVersion) {
}
} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 8932a2f6555..628bb239813 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -517,7 +517,6 @@ serverOnlyFiles = [
"catalog/cursor_manager.cpp",
"catalog/database.cpp",
"catalog/database_holder.cpp",
- "catalog/document_validation.cpp",
"catalog/drop_collection.cpp",
"catalog/drop_database.cpp",
"catalog/drop_indexes.cpp",
@@ -616,6 +615,7 @@ serverOnlyFiles = [
"repl/resync.cpp",
"repl/rs_initialsync.cpp",
"repl/rs_sync.cpp",
+ "repl/storage_interface_impl.cpp",
"repl/sync_source_feedback.cpp",
"service_context_d.cpp",
"stats/fill_locker_info.cpp",
@@ -655,6 +655,7 @@ serveronlyLibdeps = [
"$BUILD_DIR/third_party/shim_snappy",
"auth/authmongod",
"catalog/collection_options",
+ "catalog/document_validation",
"catalog/index_key_validate",
"commands/killcursors_common",
"collection_index_usage_tracker",
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index a0b78e4d3b2..90c7a01e72c 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -36,6 +36,7 @@ env.Library('authcore', ['action_set.cpp',
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
'$BUILD_DIR/mongo/bson/util/bson_extract',
'$BUILD_DIR/mongo/crypto/scramauth',
+ '$BUILD_DIR/mongo/db/catalog/document_validation',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/ops/update_driver',
'$BUILD_DIR/mongo/db/namespace_string',
diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp
index 1007316052b..4f80f19439e 100644
--- a/src/mongo/db/auth/authorization_session.cpp
+++ b/src/mongo/db/auth/authorization_session.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/security_key.h"
+#include "mongo/db/catalog/document_validation.h"
#include "mongo/db/client.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
@@ -243,12 +244,14 @@ Status AuthorizationSession::checkAuthForGetMore(const NamespaceString& ns,
return Status::OK();
}
-Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns,
+Status AuthorizationSession::checkAuthForInsert(OperationContext* txn,
+ const NamespaceString& ns,
const BSONObj& document) {
if (ns.coll() == StringData("system.indexes", StringData::LiteralTag())) {
BSONElement nsElement = document["ns"];
if (nsElement.type() != String) {
- return Status(ErrorCodes::Unauthorized,
+ return Status(nsElement.type() == BSONType::EOO ? ErrorCodes::NoSuchKey
+ : ErrorCodes::TypeMismatch,
"Cannot authorize inserting into "
"system.indexes documents without a string-typed \"ns\" field.");
}
@@ -258,7 +261,14 @@ Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns,
str::stream() << "not authorized to create index on " << indexNS.ns());
}
} else {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) {
+ ActionSet required;
+ required.addAction(ActionType::insert);
+
+ if (documentValidationDisabled(txn)) {
+ required.addAction(ActionType::bypassDocumentValidation);
+ }
+
+ if (!isAuthorizedForActionsOnNamespace(ns, required)) {
return Status(ErrorCodes::Unauthorized,
str::stream() << "not authorized for insert on " << ns.ns());
}
@@ -267,28 +277,35 @@ Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns,
return Status::OK();
}
-Status AuthorizationSession::checkAuthForUpdate(const NamespaceString& ns,
+Status AuthorizationSession::checkAuthForUpdate(OperationContext* txn,
+ const NamespaceString& ns,
const BSONObj& query,
const BSONObj& update,
bool upsert) {
- if (!upsert) {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::update)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for update on " << ns.ns());
- }
- } else {
- ActionSet required;
- required.addAction(ActionType::update);
+ ActionSet required;
+ required.addAction(ActionType::update);
+ StringData operationType = "update";
+
+ if (upsert) {
required.addAction(ActionType::insert);
- if (!isAuthorizedForActionsOnNamespace(ns, required)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for upsert on " << ns.ns());
- }
+ operationType = "upsert";
+ }
+
+ if (documentValidationDisabled(txn)) {
+ required.addAction(ActionType::bypassDocumentValidation);
}
+
+ if (!isAuthorizedForActionsOnNamespace(ns, required)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for " << operationType << " on " << ns.ns());
+ }
+
return Status::OK();
}
-Status AuthorizationSession::checkAuthForDelete(const NamespaceString& ns, const BSONObj& query) {
+Status AuthorizationSession::checkAuthForDelete(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& query) {
if (!isAuthorizedForActionsOnNamespace(ns, ActionType::remove)) {
return Status(ErrorCodes::Unauthorized,
str::stream() << "not authorized to remove from " << ns.ns());
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index da11de0da2d..7b5ce4799fa 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -150,7 +150,8 @@ public:
// Checks if this connection has the privileges necessary to perform the given update on the
// given namespace.
- Status checkAuthForUpdate(const NamespaceString& ns,
+ Status checkAuthForUpdate(OperationContext* txn,
+ const NamespaceString& ns,
const BSONObj& query,
const BSONObj& update,
bool upsert);
@@ -158,11 +159,15 @@ public:
// Checks if this connection has the privileges necessary to insert the given document
// to the given namespace. Correctly interprets inserts to system.indexes and performs
// the proper auth checks for index building.
- Status checkAuthForInsert(const NamespaceString& ns, const BSONObj& document);
+ Status checkAuthForInsert(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& document);
// Checks if this connection has the privileges necessary to perform a delete on the given
// namespace.
- Status checkAuthForDelete(const NamespaceString& ns, const BSONObj& query);
+ Status checkAuthForDelete(OperationContext* txn,
+ const NamespaceString& ns,
+ const BSONObj& query);
// Checks if this connection has the privileges necessary to perform a killCursor on
// the identified cursor, supposing that cursor is associated with the supplied namespace
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 6640eb268f9..2baa7a6b8e6 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -8,6 +8,16 @@ env.CppUnitTest('collection_options_test', ['collection_options_test.cpp'],
LIBDEPS=['collection_options'])
env.Library(
+ target='document_validation',
+ source=[
+ "document_validation.cpp",
+ ],
+ LIBDEPS=[
+ "$BUILD_DIR/mongo/db/service_context",
+ ],
+)
+
+env.Library(
target='index_key_validate',
source=[
"index_key_validate.cpp",
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp
index 12c2506b524..0043b20a4c5 100644
--- a/src/mongo/db/catalog/collection.cpp
+++ b/src/mongo/db/catalog/collection.cpp
@@ -196,10 +196,11 @@ Collection::Collection(OperationContext* txn,
Collection::~Collection() {
verify(ok());
- _magic = 0;
- if (_cappedNotifier) {
+ if (isCapped()) {
+ _recordStore->setCappedCallback(nullptr);
_cappedNotifier->kill();
}
+ _magic = 0;
}
bool Collection::requiresIdIndex() const {
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp
index b94c465a4e4..a54a5e4798c 100644
--- a/src/mongo/db/catalog/database.cpp
+++ b/src/mongo/db/catalog/database.cpp
@@ -386,11 +386,12 @@ Status Database::dropCollection(OperationContext* txn, StringData fullns) {
Top::get(txn->getClient()->getServiceContext()).collectionDropped(fullns);
- s = _dbEntry->dropCollection(txn, fullns);
-
- // we want to do this always
+ // We want to destroy the Collection object before telling the StorageEngine to destroy the
+ // RecordStore.
_clearCollectionCache(txn, fullns, "collection dropped");
+ s = _dbEntry->dropCollection(txn, fullns);
+
if (!s.isOK())
return s;
@@ -505,12 +506,10 @@ Collection* Database::createCollection(OperationContext* txn,
audit::logCreateCollection(&cc(), ns);
- txn->recoveryUnit()->registerChange(new AddCollectionChange(txn, this, ns));
-
Status status = _dbEntry->createCollection(txn, ns, options, true /*allocateDefaultSpace*/);
massertNoTraceStatusOK(status);
-
+ txn->recoveryUnit()->registerChange(new AddCollectionChange(txn, this, ns));
Collection* collection = _getOrCreateCollectionInstance(txn, ns);
invariant(collection);
_collections[ns] = collection;
diff --git a/src/mongo/db/catalog/index_create.cpp b/src/mongo/db/catalog/index_create.cpp
index 0814277262f..b18e90ac880 100644
--- a/src/mongo/db/catalog/index_create.cpp
+++ b/src/mongo/db/catalog/index_create.cpp
@@ -34,7 +34,6 @@
#include "mongo/db/catalog/index_create.h"
-
#include "mongo/base/error_codes.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/db/audit.h"
@@ -49,9 +48,12 @@
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/operation_context.h"
#include "mongo/stdx/mutex.h"
+#include "mongo/util/fail_point.h"
+#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/progress_meter.h"
+#include "mongo/util/quick_exit.h"
namespace mongo {
@@ -59,6 +61,8 @@ using std::unique_ptr;
using std::string;
using std::endl;
+MONGO_FP_DECLARE(crashAfterStartingIndexBuild);
+
/**
* On rollback sets MultiIndexBlock::_needToCleanup to true.
*/
@@ -213,6 +217,17 @@ Status MultiIndexBlock::init(const std::vector<BSONObj>& indexSpecs) {
_backgroundOperation.reset(new BackgroundOperation(ns));
wunit.commit();
+
+ if (MONGO_FAIL_POINT(crashAfterStartingIndexBuild)) {
+ log() << "Index build interrupted due to 'crashAfterStartingIndexBuild' failpoint. Exiting "
+ "after waiting for changes to become durable.";
+ Locker::LockSnapshot lockInfo;
+ _txn->lockState()->saveLockStateAndUnlock(&lockInfo);
+ if (_txn->recoveryUnit()->waitUntilDurable()) {
+ quickExit(EXIT_TEST);
+ }
+ }
+
return Status::OK();
}
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 65560607e50..4b5b6c527ab 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -312,6 +312,12 @@ Status Command::parseCommandCursorOptions(const BSONObj& cmdObj,
return Status::OK();
}
+Status Command::checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return checkAuthForCommand(txn->getClient(), dbname, cmdObj);
+}
+
Status Command::checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
@@ -341,16 +347,17 @@ void Command::logIfSlow(const Timer& timer, const string& msg) {
}
static Status _checkAuthorizationImpl(Command* c,
- ClientBasic* client,
+ OperationContext* txn,
const std::string& dbname,
const BSONObj& cmdObj) {
namespace mmb = mutablebson;
+ auto client = txn->getClient();
if (c->adminOnly() && dbname != "admin") {
return Status(ErrorCodes::Unauthorized,
str::stream() << c->name << " may only be run against the admin database.");
}
if (AuthorizationSession::get(client)->getAuthorizationManager().isAuthEnabled()) {
- Status status = c->checkAuthForCommand(client, dbname, cmdObj);
+ Status status = c->checkAuthForOperation(txn, dbname, cmdObj);
if (status == ErrorCodes::Unauthorized) {
mmb::Document cmdToLog(cmdObj, mmb::Document::kInPlaceDisabled);
c->redactForLogging(&cmdToLog);
@@ -370,16 +377,16 @@ static Status _checkAuthorizationImpl(Command* c,
return Status::OK();
}
-Status Command::_checkAuthorization(Command* c,
- ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
+Status Command::checkAuthorization(Command* c,
+ OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
namespace mmb = mutablebson;
- Status status = _checkAuthorizationImpl(c, client, dbname, cmdObj);
+ Status status = _checkAuthorizationImpl(c, txn, dbname, cmdObj);
if (!status.isOK()) {
- log(LogComponent::kAccessControl) << status << std::endl;
+ log(LogComponent::kAccessControl) << status;
}
- audit::logCommandAuthzCheck(client, dbname, cmdObj, c, status.code());
+ audit::logCommandAuthzCheck(txn->getClient(), dbname, cmdObj, c, status.code());
return status;
}
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 37550c64131..71ba638117b 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -199,12 +199,12 @@ public:
}
/**
- * Checks if the given client is authorized to run this command on database "dbname"
- * with the invocation described by "cmdObj".
+ * Checks if the client associated with the given OperationContext, "txn", is authorized to run
+ * this command on database "dbname" with the invocation described by "cmdObj".
*/
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj);
+ virtual Status checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
/**
* Redacts "cmdObj" in-place to a form suitable for writing to logs.
@@ -259,18 +259,6 @@ public:
Command(StringData _name, bool webUI = false, StringData oldName = StringData());
protected:
- /**
- * Appends to "*out" the privileges required to run this command on database "dbname" with
- * the invocation described by "cmdObj". New commands shouldn't implement this, they should
- * implement checkAuthForCommand instead.
- */
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- // The default implementation of addRequiredPrivileges should never be hit.
- fassertFailed(16940);
- }
-
BSONObj getQuery(const BSONObj& cmdObj) {
if (cmdObj["query"].type() == Object)
return cmdObj["query"].embeddedObject();
@@ -451,19 +439,40 @@ public:
*/
static void registerError(OperationContext* txn, const DBException& exception);
-private:
/**
- * Checks to see if the client is authorized to run the given command with the given
- * parameters on the given named database.
+ * Checks to see if the client executing "txn" is authorized to run the given command with the
+ * given parameters on the given named database.
*
* Returns Status::OK() if the command is authorized. Most likely returns
* ErrorCodes::Unauthorized otherwise, but any return other than Status::OK implies not
* authorized.
*/
- static Status _checkAuthorization(Command* c,
- ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj);
+ static Status checkAuthorization(Command* c,
+ OperationContext* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+private:
+ /**
+ * Checks if the given client is authorized to run this command on database "dbname"
+ * with the invocation described by "cmdObj".
+ *
+ * NOTE: Implement checkAuthForOperation that takes an OperationContext* instead.
+ */
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+ /**
+ * Appends to "*out" the privileges required to run this command on database "dbname" with
+ * the invocation described by "cmdObj". New commands shouldn't implement this, they should
+ * implement checkAuthForCommand instead.
+ */
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // The default implementation of addRequiredPrivileges should never be hit.
+ fassertFailed(16940);
+ }
};
void runCommands(OperationContext* txn,
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index d36f661e414..b4e3cebf04d 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -48,6 +48,7 @@ env.Library(
'$BUILD_DIR/mongo/db/auth/authorization_manager_global',
'$BUILD_DIR/mongo/db/auth/serverauth',
'$BUILD_DIR/mongo/db/commands',
+ '$BUILD_DIR/mongo/db/commands/apply_ops_cmd_common',
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/curop',
@@ -95,6 +96,21 @@ env.Library(
],
)
+env.Library(
+ target='apply_ops_cmd_common',
+ source=[
+ 'apply_ops_cmd_common.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/catalog/document_validation',
+ '$BUILD_DIR/mongo/db/commands',
+ '$BUILD_DIR/mongo/db/auth/authcore',
+ '$BUILD_DIR/mongo/db/namespace_string',
+ '$BUILD_DIR/mongo/db/service_context',
+ ],
+)
+
env.CppUnitTest(
target="index_filter_commands_test",
source=[
diff --git a/src/mongo/db/commands/apply_ops.cpp b/src/mongo/db/commands/apply_ops.cpp
index 12dc70ceb86..f4f0b930a8c 100644
--- a/src/mongo/db/commands/apply_ops.cpp
+++ b/src/mongo/db/commands/apply_ops.cpp
@@ -37,13 +37,12 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/auth/privilege.h"
-#include "mongo/db/auth/resource_pattern.h"
#include "mongo/db/catalog/apply_ops.h"
#include "mongo/db/catalog/document_validation.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/dbhash.h"
+#include "mongo/db/commands/apply_ops_cmd_common.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/dbdirectclient.h"
@@ -78,18 +77,21 @@ public:
help << "internal (sharding)\n{ applyOps : [ ] , preCondition : [ { ns : ... , q : ... , "
"res : ... } ] }";
}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- // applyOps can do pretty much anything, so require all privileges.
- RoleGraph::generateUniversalPrivileges(out);
+
+ virtual Status checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) final {
+ return checkAuthForApplyOpsCommand(txn, dbname, cmdObj);
}
+
virtual bool run(OperationContext* txn,
const string& dbname,
BSONObj& cmdObj,
int,
string& errmsg,
BSONObjBuilder& result) {
+ validateApplyOpsCommand(cmdObj);
+
boost::optional<DisableDocumentValidation> maybeDisableValidation;
if (shouldBypassDocumentValidationForCommand(cmdObj))
maybeDisableValidation.emplace(txn);
diff --git a/src/mongo/db/commands/apply_ops_cmd_common.cpp b/src/mongo/db/commands/apply_ops_cmd_common.cpp
new file mode 100644
index 00000000000..91809b77d20
--- /dev/null
+++ b/src/mongo/db/commands/apply_ops_cmd_common.cpp
@@ -0,0 +1,227 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/db/commands/apply_ops_cmd_common.h"
+
+#include <stack>
+
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/util/bson_check.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/privilege.h"
+#include "mongo/db/auth/resource_pattern.h"
+#include "mongo/db/catalog/document_validation.h"
+#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/namespace_string.h"
+
+namespace mongo {
+
+namespace {
+
+Status checkOperationAuthorization(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& oplogEntry,
+ bool alwaysUpsert) {
+ AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient());
+
+ BSONElement opTypeElem = oplogEntry["op"];
+ checkBSONType(BSONType::String, opTypeElem);
+ const StringData opType = opTypeElem.checkAndGetStringData();
+
+ if (opType == "n") {
+ // oplog notes require cluster permissions, and may not have a ns
+ if (!authSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
+ ActionType::appendOplogNote)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ return Status::OK();
+ }
+
+ BSONElement nsElem = oplogEntry["ns"];
+ checkBSONType(BSONType::String, nsElem);
+ NamespaceString ns(oplogEntry["ns"].checkAndGetStringData());
+
+ BSONElement oElem = oplogEntry["o"];
+ checkBSONType(BSONType::Object, oElem);
+ BSONObj o = oElem.Obj();
+
+ if (opType == "c") {
+ Command* command = Command::findCommand(o.firstElement().fieldNameStringData());
+ if (!command) {
+ return Status(ErrorCodes::FailedToParse, "Unrecognized command in op");
+ }
+
+ return Command::checkAuthorization(command, txn, dbname, o);
+ }
+
+ if (opType == "i") {
+ return authSession->checkAuthForInsert(txn, ns, o);
+ } else if (opType == "u") {
+ BSONElement o2Elem = oplogEntry["o2"];
+ checkBSONType(BSONType::Object, o2Elem);
+ BSONObj o2 = o2Elem.Obj();
+
+ BSONElement bElem = oplogEntry["b"];
+ if (!bElem.eoo()) {
+ checkBSONType(BSONType::Bool, bElem);
+ }
+ bool b = bElem.trueValue();
+
+ const bool upsert = b || alwaysUpsert;
+
+ return authSession->checkAuthForUpdate(txn, ns, o, o2, upsert);
+ } else if (opType == "d") {
+ return authSession->checkAuthForDelete(txn, ns, o);
+ } else if (opType == "db") {
+ // It seems that 'db' isn't used anymore. Require all actions to prevent casual use.
+ ActionSet allActions;
+ allActions.addAllActions();
+ if (!authSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyResource(),
+ allActions)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ return Status::OK();
+ }
+
+ return Status(ErrorCodes::FailedToParse, "Unrecognized opType");
+}
+} // namespace
+
+ApplyOpsValidity validateApplyOpsCommand(const BSONObj& cmdObj) {
+ const size_t maxApplyOpsDepth = 10;
+ std::stack<std::pair<size_t, BSONObj>> toCheck;
+
+ auto operationContainsApplyOps = [](const BSONObj& opObj) {
+ BSONElement opTypeElem = opObj["op"];
+ checkBSONType(BSONType::String, opTypeElem);
+ const StringData opType = opTypeElem.checkAndGetStringData();
+
+ if (opType == "c") {
+ BSONElement oElem = opObj["o"];
+ checkBSONType(BSONType::Object, oElem);
+ BSONObj o = oElem.Obj();
+
+ if (o.firstElement().fieldNameStringData() == "applyOps") {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // Insert the top level applyOps command into the stack.
+ toCheck.emplace(std::make_pair(0, cmdObj));
+
+ while (!toCheck.empty()) {
+ std::pair<size_t, BSONObj> item = toCheck.top();
+ toCheck.pop();
+
+ checkBSONType(BSONType::Array, item.second.firstElement());
+ // Check if the applyOps command is empty. This is probably not something that should
+ // happen, so require a superuser to do this.
+ if (item.second.firstElement().Array().empty()) {
+ return ApplyOpsValidity::kNeedsSuperuser;
+ }
+
+ // For each applyOps command, iterate the ops.
+ for (BSONElement element : item.second.firstElement().Array()) {
+ checkBSONType(BSONType::Object, element);
+ BSONObj elementObj = element.Obj();
+
+ // If the op itself contains an applyOps...
+ if (operationContainsApplyOps(elementObj)) {
+ // And we've recursed too far, then bail out.
+ uassert(ErrorCodes::FailedToParse,
+ "Too many nested applyOps",
+ item.first < maxApplyOpsDepth);
+
+ // Otherwise, if the op contains an applyOps, but we haven't recursed too far:
+ // extract the applyOps command, and insert it into the stack.
+ checkBSONType(BSONType::Object, elementObj["o"]);
+ BSONObj oObj = elementObj["o"].Obj();
+ toCheck.emplace(std::make_pair(item.first + 1, std::move(oObj)));
+ }
+ }
+ }
+
+ return ApplyOpsValidity::kOk;
+}
+
+Status checkAuthForApplyOpsCommand(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient());
+
+ ApplyOpsValidity validity = validateApplyOpsCommand(cmdObj);
+ if (validity == ApplyOpsValidity::kNeedsSuperuser) {
+ std::vector<Privilege> universalPrivileges;
+ RoleGraph::generateUniversalPrivileges(&universalPrivileges);
+ if (!authSession->isAuthorizedForPrivileges(universalPrivileges)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ return Status::OK();
+ }
+ fassert(40314, validity == ApplyOpsValidity::kOk);
+
+ boost::optional<DisableDocumentValidation> maybeDisableValidation;
+ if (shouldBypassDocumentValidationForCommand(cmdObj))
+ maybeDisableValidation.emplace(txn);
+
+
+ const bool alwaysUpsert =
+ cmdObj.hasField("alwaysUpsert") ? cmdObj["alwaysUpsert"].trueValue() : true;
+
+ checkBSONType(BSONType::Array, cmdObj.firstElement());
+ for (const BSONElement& e : cmdObj.firstElement().Array()) {
+ checkBSONType(BSONType::Object, e);
+ Status status = checkOperationAuthorization(txn, dbname, e.Obj(), alwaysUpsert);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
+ BSONElement preconditions = cmdObj["preCondition"];
+ if (!preconditions.eoo()) {
+ for (const BSONElement& precondition : preconditions.Array()) {
+ checkBSONType(BSONType::Object, precondition);
+ BSONElement nsElem = precondition.Obj()["ns"];
+ checkBSONType(BSONType::String, nsElem);
+ NamespaceString nss(nsElem.checkAndGetStringData());
+
+ if (!authSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(nss), ActionType::find)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized to check precondition");
+ }
+ }
+ }
+
+ return Status::OK();
+}
+} // namespace mongo
diff --git a/src/mongo/db/commands/apply_ops_cmd_common.h b/src/mongo/db/commands/apply_ops_cmd_common.h
new file mode 100644
index 00000000000..443e862b798
--- /dev/null
+++ b/src/mongo/db/commands/apply_ops_cmd_common.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace mongo {
+
+class BSONObj;
+class OperationContext;
+class Status;
+
+/**
+ * Returns Status::OK if the associated client is authorized to perform the command in cmdObj.
+ */
+Status checkAuthForApplyOpsCommand(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+enum class ApplyOpsValidity { kOk, kNeedsSuperuser };
+
+/**
+ * Returns either kNeedsSuperuser, if the provided applyOps command contains an empty applyOps
+ * command, or kOk if no other handlable conditions detected. May throw exceptions if the input
+ * is malformed.
+ */
+ApplyOpsValidity validateApplyOpsCommand(const BSONObj& cmdObj);
+
+} // namespace mongo
diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp
index 2af6f18fed5..ffae7056bc7 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -88,9 +88,9 @@ public:
* the command that you are explaining. The auth check is performed recursively
* on the nested command.
*/
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
+ virtual Status checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
if (Object != cmdObj.firstElement().type()) {
return Status(ErrorCodes::BadValue, "explain command requires a nested object");
}
@@ -104,7 +104,7 @@ public:
return Status(ErrorCodes::CommandNotFound, ss);
}
- return commToExplain->checkAuthForCommand(client, dbname, explainObj);
+ return commToExplain->checkAuthForOperation(txn, dbname, explainObj);
}
virtual bool run(OperationContext* txn,
diff --git a/src/mongo/db/concurrency/locker.h b/src/mongo/db/concurrency/locker.h
index cdc490e1379..c664b3841d7 100644
--- a/src/mongo/db/concurrency/locker.h
+++ b/src/mongo/db/concurrency/locker.h
@@ -49,7 +49,19 @@ public:
virtual ~Locker() {}
/**
- * Require global lock attempts with obtain tickets from 'reading' (for MODE_S and MODE_IS),
+ * Returns true if this is an instance of LockerNoop. Because LockerNoop doesn't implement many
+ * methods, some users may need to check this first to find out what is safe to call. LockerNoop
+ * is only used in unittests and for a brief period at startup, so you can assume you hold the
+ * equivalent of a MODE_X lock when using it.
+ *
+ * TODO get rid of this once we kill LockerNoop.
+ */
+ virtual bool isNoop() const {
+ return false;
+ }
+
+ /**
+ * Require global lock attempts to obtain tickets from 'reading' (for MODE_S and MODE_IS),
* and from 'writing' (for MODE_IX), which must have static lifetimes. There is no throttling
* for MODE_X, as there can only ever be a single locker using this mode. The throttling is
* intended to defend against arge drops in throughput under high load due to too much
diff --git a/src/mongo/db/concurrency/locker_noop.h b/src/mongo/db/concurrency/locker_noop.h
index cfbd3234c2c..9e8d3b6300a 100644
--- a/src/mongo/db/concurrency/locker_noop.h
+++ b/src/mongo/db/concurrency/locker_noop.h
@@ -41,6 +41,10 @@ class LockerNoop : public Locker {
public:
LockerNoop() {}
+ virtual bool isNoop() const {
+ return true;
+ }
+
virtual ClientState getClientState() const {
invariant(false);
}
diff --git a/src/mongo/db/db_raii.h b/src/mongo/db/db_raii.h
index 59a43d67d64..6da01c151fb 100644
--- a/src/mongo/db/db_raii.h
+++ b/src/mongo/db/db_raii.h
@@ -151,7 +151,6 @@ public:
}
private:
- void _init(const std::string& ns, StringData coll);
void _ensureMajorityCommittedSnapshotIsValid(const NamespaceString& nss);
const Timer _timer;
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index f2b87b6c399..e65da17dd07 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -1230,8 +1230,7 @@ void Command::execCommand(OperationContext* txn,
}
ImpersonationSessionGuard guard(txn);
- uassertStatusOK(
- _checkAuthorization(command, txn->getClient(), dbname, request.getCommandArgs()));
+ uassertStatusOK(checkAuthorization(command, txn, dbname, request.getCommandArgs()));
repl::ReplicationCoordinator* replCoord =
repl::ReplicationCoordinator::get(txn->getClient()->getServiceContext());
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 3180ed87ae1..e0d6da23040 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -343,7 +343,7 @@ long long Helpers::removeRange(OperationContext* txn,
while (1) {
// Scoping for write lock.
{
- OldClientWriteContext ctx(txn, ns);
+ AutoGetCollection ctx(txn, NamespaceString(ns), MODE_IX);
Collection* collection = ctx.getCollection();
if (!collection)
break;
diff --git a/src/mongo/db/exec/collection_scan.cpp b/src/mongo/db/exec/collection_scan.cpp
index 74d64c39cc8..db07439fea9 100644
--- a/src/mongo/db/exec/collection_scan.cpp
+++ b/src/mongo/db/exec/collection_scan.cpp
@@ -97,6 +97,20 @@ PlanStage::StageState CollectionScan::work(WorkingSetID* out) {
try {
if (needToMakeCursor) {
const bool forward = _params.direction == CollectionScanParams::FORWARD;
+
+ if (forward && !_params.tailable && _params.collection->ns().isOplog()) {
+ // Forward, non-tailable scans from the oplog need to wait until all oplog entries
+ // before the read begins to be visible. This isn't needed for reverse scans because
+ // we only hide oplog entries from forward scans, and it isn't necessary for tailing
+ // cursors because they ignore EOF and will eventually see all writes. Forward,
+ // non-tailable scans are the only case where a meaningful EOF will be seen that
+ // might not include writes that finished before the read started. This also must be
+ // done before we create the cursor as that is when we establish the endpoint for
+ // the cursor.
+ _params.collection->getRecordStore()->waitForAllEarlierOplogWritesToBeVisible(
+ getOpCtx());
+ }
+
_cursor = _params.collection->getCursor(getOpCtx(), forward);
if (!_lastSeenId.isNull()) {
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 4f393385127..2bd2d003b49 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -669,8 +669,8 @@ void receivedUpdate(OperationContext* txn, const NamespaceString& nsString, Mess
op.setQuery_inlock(query);
}
- Status status =
- AuthorizationSession::get(client)->checkAuthForUpdate(nsString, query, toupdate, upsert);
+ Status status = AuthorizationSession::get(client)
+ ->checkAuthForUpdate(txn, nsString, query, toupdate, upsert);
audit::logUpdateAuthzCheck(client, nsString, query, toupdate, upsert, multi, status.code());
uassertStatusOK(status);
@@ -818,7 +818,7 @@ void receivedDelete(OperationContext* txn, const NamespaceString& nsString, Mess
op.setNS_inlock(nsString.ns());
}
- Status status = AuthorizationSession::get(client)->checkAuthForDelete(nsString, pattern);
+ Status status = AuthorizationSession::get(client)->checkAuthForDelete(txn, nsString, pattern);
audit::logDeleteAuthzCheck(client, nsString, pattern, status.code());
uassertStatusOK(status);
@@ -1181,7 +1181,7 @@ void receivedInsert(OperationContext* txn, const NamespaceString& nsString, Mess
// Check auth for insert (also handles checking if this is an index build and checks
// for the proper privileges in that case).
Status status =
- AuthorizationSession::get(txn->getClient())->checkAuthForInsert(nsString, obj);
+ AuthorizationSession::get(txn->getClient())->checkAuthForInsert(txn, nsString, obj);
audit::logInsertAuthzCheck(txn->getClient(), nsString, obj, status.code());
uassertStatusOK(status);
}
@@ -1273,8 +1273,6 @@ void exitCleanly(ExitCode code) {
getGlobalServiceContext()->setKillAllOperations();
- repl::getGlobalReplicationCoordinator()->shutdown();
-
Client& client = cc();
ServiceContext::UniqueOperationContext uniqueTxn;
OperationContext* txn = client.getOperationContext();
@@ -1283,6 +1281,8 @@ void exitCleanly(ExitCode code) {
txn = uniqueTxn.get();
}
+
+ repl::getGlobalReplicationCoordinator()->shutdown(txn);
ShardingState::get(txn)->shutDown(txn);
// We should always be able to acquire the global lock at shutdown.
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index c8d379f88ce..1e70c1fa376 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -122,6 +122,7 @@ docSourceEnv.Library(
'expression',
'$BUILD_DIR/mongo/client/clientdriver',
'$BUILD_DIR/mongo/db/matcher/expressions',
+ '$BUILD_DIR/mongo/db/query/lite_parsed_query',
'$BUILD_DIR/mongo/db/service_context',
'$BUILD_DIR/mongo/db/storage/wiredtiger/storage_wiredtiger_customization_hooks',
'$BUILD_DIR/third_party/shim_snappy',
diff --git a/src/mongo/db/pipeline/document_source.h b/src/mongo/db/pipeline/document_source.h
index 3276a3d3d16..6a2845ecddc 100644
--- a/src/mongo/db/pipeline/document_source.h
+++ b/src/mongo/db/pipeline/document_source.h
@@ -1295,11 +1295,17 @@ private:
std::string localField,
std::string foreignField,
const boost::intrusive_ptr<ExpressionContext>& pExpCtx);
+ ~DocumentSourceLookUp() final;
Value serialize(bool explain = false) const final {
invariant(false);
}
+ /**
+ * Builds the required query and executes it.
+ */
+ std::unique_ptr<DBClientCursor> doQuery(const Document& docToLookUp) const;
+
boost::optional<Document> unwindResult();
BSONObj queryForInput(const Document& input) const;
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index 83708c8756c..ff23fb8eb48 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -31,17 +31,24 @@
#include "document_source.h"
#include "mongo/base/init.h"
+#include "mongo/db/client.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/expression.h"
#include "mongo/db/pipeline/expression_context.h"
#include "mongo/db/pipeline/value.h"
+#include "mongo/db/query/lite_parsed_query.h"
+#include "mongo/db/server_parameters.h"
#include "mongo/stdx/memory.h"
namespace mongo {
using boost::intrusive_ptr;
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(internalAggregationLookupBatchSize,
+ int,
+ LiteParsedQuery::kDefaultBatchSize);
+
DocumentSourceLookUp::DocumentSourceLookUp(NamespaceString fromNs,
std::string as,
std::string localField,
@@ -54,12 +61,56 @@ DocumentSourceLookUp::DocumentSourceLookUp(NamespaceString fromNs,
_foreignField(foreignField),
_foreignFieldFieldName(std::move(foreignField)) {}
+DocumentSourceLookUp::~DocumentSourceLookUp() {
+ DESTRUCTOR_GUARD(
+ // A DBClientCursor will issue a killCursors command through its parent DBDirectClient when
+ // it goes out of scope. To issue a killCursors command, a DBDirectClient needs a valid
+ // OperationContext. So here we set the OperationContext on the DBDirectClient, then
+ // aggressively destroy the DBClientCursor.
+ // Note that we cannot rely on any sort of callback from above to provide a valid
+ // OperationContext, since we might be destroyed from the destructor of a CursorManager,
+ // which does not have an OperationContext. Thus, we unfortunately have to make a new one or
+ // use the one on our thread's Client.
+ if (_mongod && _cursor) {
+ auto& client = cc();
+ if (auto opCtx = client.getOperationContext()) {
+ pExpCtx->opCtx = opCtx;
+ _mongod->setOperationContext(opCtx);
+ _cursor.reset();
+ } else {
+ auto newOpCtx = client.makeOperationContext();
+ pExpCtx->opCtx = newOpCtx.get();
+ _mongod->setOperationContext(newOpCtx.get());
+ _cursor.reset();
+ }
+ });
+}
+
REGISTER_DOCUMENT_SOURCE(lookup, DocumentSourceLookUp::createFromBson);
const char* DocumentSourceLookUp::getSourceName() const {
return "$lookup";
}
+std::unique_ptr<DBClientCursor> DocumentSourceLookUp::doQuery(const Document& docToLookUp) const {
+ auto query = DocumentSourceLookUp::queryForInput(docToLookUp);
+
+ // Defaults for everything except batch size.
+ const int nToReturn = 0;
+ const int nToSkip = 0;
+ const BSONObj* fieldsToReturn = nullptr;
+ const int queryOptions = 0;
+
+ const int batchSize = internalAggregationLookupBatchSize;
+ return _mongod->directClient()->query(_fromNs.ns(),
+ std::move(query),
+ nToReturn,
+ nToSkip,
+ fieldsToReturn,
+ queryOptions,
+ batchSize);
+}
+
boost::optional<Document> DocumentSourceLookUp::getNext() {
pExpCtx->checkForInterrupt();
@@ -72,8 +123,7 @@ boost::optional<Document> DocumentSourceLookUp::getNext() {
boost::optional<Document> input = pSource->getNext();
if (!input)
return {};
- BSONObj query = queryForInput(*input);
- std::unique_ptr<DBClientCursor> cursor = _mongod->directClient()->query(_fromNs.ns(), query);
+ auto cursor = doQuery(*input);
std::vector<Value> results;
int objsize = 0;
@@ -82,7 +132,8 @@ boost::optional<Document> DocumentSourceLookUp::getNext() {
objsize += result.objsize();
uassert(4568,
str::stream() << "Total size of documents in " << _fromNs.coll() << " matching "
- << query << " exceeds maximum document size",
+ << DocumentSourceLookUp::queryForInput(*input)
+ << " exceeds maximum document size",
objsize <= BSONObjMaxInternalSize);
results.push_back(Value(result));
}
@@ -136,8 +187,8 @@ boost::optional<Document> DocumentSourceLookUp::unwindResult() {
if (!_input)
return {};
- _cursor = _mongod->directClient()->query(_fromNs.ns(), queryForInput(*_input));
_cursorIndex = 0;
+ _cursor = doQuery(*_input);
if (_unwindSrc->preserveNullAndEmptyArrays() && !_cursor->more()) {
// There were no results for this cursor, but the $unwind was asked to preserve empty
diff --git a/src/mongo/db/pipeline/document_source_test.cpp b/src/mongo/db/pipeline/document_source_test.cpp
index bdf9ac79a39..71bd9bea499 100644
--- a/src/mongo/db/pipeline/document_source_test.cpp
+++ b/src/mongo/db/pipeline/document_source_test.cpp
@@ -1347,13 +1347,15 @@ TEST_F(SampleFromRandomCursorBasics, MimicNonOptimized) {
ASSERT_TRUE((*doc).hasRandMetaField());
secondTotal += (*doc).getRandMetaField();
}
- // The average random meta value of the first document should be about 0.75.
- ASSERT_GTE(firstTotal / nTrials, 0.74);
- ASSERT_LTE(firstTotal / nTrials, 0.76);
+ // The average random meta value of the first document should be about 0.75. We assume that
+ // 10000 trials is sufficient for us to apply the Central Limit Theorem. Using an error
+ // tolerance of 0.02 gives us a spurious failure rate approximately equal to 10^-24.
+ ASSERT_GTE(firstTotal / nTrials, 0.73);
+ ASSERT_LTE(firstTotal / nTrials, 0.77);
// The average random meta value of the second document should be about 0.5.
- ASSERT_GTE(secondTotal / nTrials, 0.49);
- ASSERT_LTE(secondTotal / nTrials, 0.51);
+ ASSERT_GTE(secondTotal / nTrials, 0.48);
+ ASSERT_LTE(secondTotal / nTrials, 0.52);
}
} // namespace DocumentSourceSampleFromRandomCursor
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 5d2d8f1fe06..007a37c5c25 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -54,17 +54,6 @@ env.Library('storage_interface',
])
env.Library(
- target='storage_interface_impl',
- source=[
- 'storage_interface_impl.cpp',
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/db/serveronly', # For OperationContextImpl
- '$BUILD_DIR/mongo/db/service_context',
- 'storage_interface',
- ])
-
-env.Library(
target='replication_executor',
source=[
'replication_executor.cpp',
@@ -217,6 +206,7 @@ env.Library(
'$BUILD_DIR/mongo/db/curop',
'$BUILD_DIR/mongo/util/concurrency/thread_pool',
'repl_coordinator_global',
+ 'storage_interface',
],
LIBDEPS_TAGS=[
# Many undefined symbols in sync_tail.cpp
@@ -720,7 +710,9 @@ env.Library(
'roll_back_local_operations.cpp',
],
LIBDEPS=[
+ 'optime',
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/bson/util/bson_extract',
'$BUILD_DIR/mongo/util/foundation',
],
)
diff --git a/src/mongo/db/repl/bgsync.cpp b/src/mongo/db/repl/bgsync.cpp
index e34c4f73335..a0312fa30f2 100644
--- a/src/mongo/db/repl/bgsync.cpp
+++ b/src/mongo/db/repl/bgsync.cpp
@@ -289,7 +289,14 @@ void BackgroundSync::_produce(OperationContext* txn) {
_syncSourceHost = HostAndPort();
}
OplogReader syncSourceReader;
- syncSourceReader.connectToSyncSource(txn, lastOpTimeFetched, _replCoord);
+ OpTime minValid;
+ if (_replCoord->getMemberState().recovering()) {
+ auto minValidSaved = getMinValid(txn);
+ if (minValidSaved > lastOpTimeFetched) {
+ minValid = minValidSaved;
+ }
+ }
+ syncSourceReader.connectToSyncSource(txn, lastOpTimeFetched, minValid, _replCoord);
// no server found
if (syncSourceReader.getHost().empty()) {
@@ -325,6 +332,13 @@ void BackgroundSync::_produce(OperationContext* txn) {
syncSourceReader.resetConnection();
// no more references to oplog reader from here on.
+ // Set the applied point if unset. This is most likely the first time we've established a sync
+ // source since stepping down or otherwise clearing the applied point. We need to set this here,
+ // before the OplogWriter gets a chance to append to the oplog.
+ if (getAppliedThrough(txn).isNull()) {
+ setAppliedThrough(txn, _replCoord->getMyLastAppliedOpTime());
+ }
+
Status fetcherReturnStatus = Status::OK();
auto fetcherCallback = stdx::bind(&BackgroundSync::_fetcherCallback,
this,
@@ -424,13 +438,13 @@ void BackgroundSync::_produce(OperationContext* txn) {
}
// check that we are at minvalid, otherwise we cannot roll back as we may be in an
// inconsistent state
- BatchBoundaries boundaries = getMinValid(txn);
- if (!boundaries.start.isNull() || boundaries.end > lastApplied) {
+ const auto minValid = getMinValid(txn);
+ if (lastApplied < minValid) {
fassertNoTrace(18750,
Status(ErrorCodes::UnrecoverableRollbackError,
str::stream()
<< "need to rollback, but in inconsistent state. "
- << "minvalid: " << boundaries.end.toString()
+ << "minvalid: " << minValid.toString()
<< " > our last optime: " << lastApplied.toString()));
}
diff --git a/src/mongo/db/repl/initial_sync.cpp b/src/mongo/db/repl/initial_sync.cpp
index d9373590463..b0773769e0e 100644
--- a/src/mongo/db/repl/initial_sync.cpp
+++ b/src/mongo/db/repl/initial_sync.cpp
@@ -73,7 +73,7 @@ void InitialSync::_applyOplogUntil(OperationContext* txn, const OpTime& endOpTim
OpQueue ops;
auto replCoord = repl::ReplicationCoordinator::get(txn);
- while (!tryPopAndWaitForMore(txn, &ops)) {
+ while (!tryPopAndWaitForMore(txn, &ops, BatchLimits{})) {
if (inShutdown()) {
return;
}
@@ -95,17 +95,11 @@ void InitialSync::_applyOplogUntil(OperationContext* txn, const OpTime& endOpTim
<< " without seeing it. Rollback?";
fassertFailedNoTrace(18693);
}
-
- // apply replication batch limits
- if (ops.getSize() > replBatchLimitBytes)
- break;
- if (ops.getDeque().size() > replBatchLimitOperations)
- break;
};
if (ops.empty()) {
- severe() << "got no ops for batch...";
- fassertFailedNoTrace(18692);
+ // nothing came back last time, so go again
+ continue;
}
const BSONObj lastOp = ops.back().raw.getOwned();
diff --git a/src/mongo/db/repl/minvalid.cpp b/src/mongo/db/repl/minvalid.cpp
index 90753cff0f4..02a56cfab2f 100644
--- a/src/mongo/db/repl/minvalid.cpp
+++ b/src/mongo/db/repl/minvalid.cpp
@@ -47,121 +47,142 @@ namespace mongo {
namespace repl {
namespace {
-const char initialSyncFlagString[] = "doingInitialSync";
-const BSONObj initialSyncFlag(BSON(initialSyncFlagString << true));
-const char minvalidNS[] = "local.replset.minvalid";
-const char beginFieldName[] = "begin";
-} // namespace
+const char kInitialSyncFlagFieldName[] = "doingInitialSync";
+const BSONObj kInitialSyncFlag(BSON(kInitialSyncFlagFieldName << true));
+NamespaceString minValidNss("local.replset.minvalid");
+const char kBeginFieldName[] = "begin";
+const char kOplogDeleteFromPointFieldName[] = "oplogDeleteFromPoint";
-// Writes
-void clearInitialSyncFlag(OperationContext* txn) {
+BSONObj getMinValidDocument(OperationContext* txn) {
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
+ ScopedTransaction transaction(txn, MODE_IS);
+ Lock::DBLock dblk(txn->lockState(), minValidNss.db(), MODE_IS);
+ Lock::CollectionLock lk(txn->lockState(), minValidNss.ns(), MODE_IS);
+ BSONObj doc;
+ bool found = Helpers::getSingleton(txn, minValidNss.ns().c_str(), doc);
+ invariant(found || doc.isEmpty());
+ return doc;
+ }
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "getMinValidDocument", minValidNss.ns());
+
+ MONGO_UNREACHABLE;
+}
+
+void updateMinValidDocument(OperationContext* txn, const BSONObj& updateSpec) {
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
ScopedTransaction transaction(txn, MODE_IX);
- // TODO: Investigate correctness of taking MODE_IX for DB/Collection locks
- Lock::DBLock dblk(txn->lockState(), "local", MODE_X);
- Helpers::putSingleton(txn, minvalidNS, BSON("$unset" << initialSyncFlag));
+ // For now this needs to be MODE_X because it sometimes creates the collection.
+ Lock::DBLock dblk(txn->lockState(), minValidNss.db(), MODE_X);
+ Helpers::putSingleton(txn, minValidNss.ns().c_str(), updateSpec);
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "clearInitialSyncFlags", minvalidNS);
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "updateMinValidDocument", minValidNss.ns());
+}
+} // namespace
+// Writes
+void clearInitialSyncFlag(OperationContext* txn) {
auto replCoord = repl::ReplicationCoordinator::get(txn);
OpTime time = replCoord->getMyLastAppliedOpTime();
+ updateMinValidDocument(txn,
+ BSON("$unset"
+ << kInitialSyncFlag << "$set"
+ << BSON("ts" << time.getTimestamp() << "t" << time.getTerm()
+ << kBeginFieldName << time.toBSON())));
txn->recoveryUnit()->waitUntilDurable();
replCoord->setMyLastDurableOpTime(time);
LOG(3) << "clearing initial sync flag";
}
void setInitialSyncFlag(OperationContext* txn) {
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction transaction(txn, MODE_IX);
- Lock::DBLock dblk(txn->lockState(), "local", MODE_X);
- Helpers::putSingleton(txn, minvalidNS, BSON("$set" << initialSyncFlag));
- }
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "setInitialSyncFlags", minvalidNS);
-
+ updateMinValidDocument(txn, BSON("$set" << kInitialSyncFlag));
txn->recoveryUnit()->waitUntilDurable();
LOG(3) << "setting initial sync flag";
}
-void setMinValid(OperationContext* txn, const OpTime& endOpTime, const DurableRequirement durReq) {
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction transaction(txn, MODE_IX);
- Lock::DBLock dblk(txn->lockState(), "local", MODE_X);
- Helpers::putSingleton(
- txn,
- minvalidNS,
- BSON("$set" << BSON("ts" << endOpTime.getTimestamp() << "t" << endOpTime.getTerm())
- << "$unset" << BSON(beginFieldName << 1)));
+bool getInitialSyncFlag() {
+ OperationContextImpl txn;
+ return getInitialSyncFlag(&txn);
+}
+bool getInitialSyncFlag(OperationContext* txn) {
+ const BSONObj doc = getMinValidDocument(txn);
+ const auto flag = doc[kInitialSyncFlagFieldName].trueValue();
+ LOG(3) << "returning initial sync flag value of " << flag;
+ return flag;
+}
+
+OpTime getMinValid(OperationContext* txn) {
+ const BSONObj doc = getMinValidDocument(txn);
+ const auto opTimeStatus = OpTime::parseFromOplogEntry(doc);
+ // If any of the keys (fields) are missing from the minvalid document, we return
+ // a null OpTime.
+ if (opTimeStatus == ErrorCodes::NoSuchKey) {
+ return {};
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "setMinValid", minvalidNS);
- if (durReq == DurableRequirement::Strong) {
- txn->recoveryUnit()->waitUntilDurable();
+ if (!opTimeStatus.isOK()) {
+ severe() << "Error parsing minvalid entry: " << doc
+ << ", with status:" << opTimeStatus.getStatus();
+ fassertFailedNoTrace(40052);
}
- LOG(3) << "setting minvalid: " << endOpTime.toString() << "(" << endOpTime.toBSON() << ")";
+
+ OpTime minValid = opTimeStatus.getValue();
+ LOG(3) << "returning minvalid: " << minValid.toString() << "(" << minValid.toBSON() << ")";
+
+ return minValid;
+}
+void setMinValid(OperationContext* txn, const OpTime& minValid) {
+ LOG(3) << "setting minvalid to exactly: " << minValid.toString() << "(" << minValid.toBSON()
+ << ")";
+ updateMinValidDocument(
+ txn, BSON("$set" << BSON("ts" << minValid.getTimestamp() << "t" << minValid.getTerm())));
}
-void setMinValid(OperationContext* txn, const BatchBoundaries& boundaries) {
- const OpTime& start(boundaries.start);
- const OpTime& end(boundaries.end);
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction transaction(txn, MODE_IX);
- Lock::DBLock dblk(txn->lockState(), "local", MODE_X);
- Helpers::putSingleton(txn,
- minvalidNS,
- BSON("$set" << BSON("ts" << end.getTimestamp() << "t" << end.getTerm()
- << beginFieldName << start.toBSON())));
- }
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "setMinValid", minvalidNS);
- // NOTE: No need to ensure durability here since starting a batch isn't a problem unless
- // writes happen after, in which case this marker (minvalid) will be written already.
- LOG(3) << "setting minvalid: " << boundaries.start.toString() << "("
- << boundaries.start.toBSON() << ") -> " << boundaries.end.toString() << "("
- << boundaries.end.toBSON() << ")";
+void setMinValidToAtLeast(OperationContext* txn, const OpTime& minValid) {
+ LOG(3) << "setting minvalid to at least: " << minValid.toString() << "(" << minValid.toBSON()
+ << ")";
+ updateMinValidDocument(
+ txn, BSON("$max" << BSON("ts" << minValid.getTimestamp() << "t" << minValid.getTerm())));
}
-// Reads
-bool getInitialSyncFlag() {
- OperationContextImpl txn;
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction transaction(&txn, MODE_IS);
- Lock::DBLock dblk(txn.lockState(), "local", MODE_IS);
- Lock::CollectionLock lk(txn.lockState(), minvalidNS, MODE_IS);
- BSONObj mv;
- bool found = Helpers::getSingleton(&txn, minvalidNS, mv);
-
- if (found) {
- const auto flag = mv[initialSyncFlagString].trueValue();
- LOG(3) << "return initial flag value of " << flag;
- return flag;
- }
- LOG(3) << "return initial flag value of false";
- return false;
+void setOplogDeleteFromPoint(OperationContext* txn, const Timestamp& timestamp) {
+ LOG(3) << "setting oplog delete from point to: " << timestamp.toStringPretty();
+ updateMinValidDocument(txn, BSON("$set" << BSON(kOplogDeleteFromPointFieldName << timestamp)));
+}
+
+Timestamp getOplogDeleteFromPoint(OperationContext* txn) {
+ const BSONObj doc = getMinValidDocument(txn);
+ Timestamp out = {};
+ if (auto field = doc[kOplogDeleteFromPointFieldName]) {
+ out = field.timestamp();
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(&txn, "getInitialSyncFlags", minvalidNS);
- MONGO_UNREACHABLE;
+ LOG(3) << "returning oplog delete from point: " << out;
+ return out;
}
-BatchBoundaries getMinValid(OperationContext* txn) {
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction transaction(txn, MODE_IS);
- Lock::DBLock dblk(txn->lockState(), "local", MODE_IS);
- Lock::CollectionLock lk(txn->lockState(), minvalidNS, MODE_IS);
- BSONObj mv;
- bool found = Helpers::getSingleton(txn, minvalidNS, mv);
- if (found) {
- auto status = OpTime::parseFromOplogEntry(mv.getObjectField(beginFieldName));
- OpTime start(status.isOK() ? status.getValue() : OpTime{});
- OpTime end(fassertStatusOK(28771, OpTime::parseFromOplogEntry(mv)));
- LOG(3) << "returning minvalid: " << start.toString() << "(" << start.toBSON() << ") -> "
- << end.toString() << "(" << end.toBSON() << ")";
-
- return BatchBoundaries(start, end);
- }
- LOG(3) << "returning empty minvalid";
- return BatchBoundaries{OpTime{}, OpTime{}};
+void setAppliedThrough(OperationContext* txn, const OpTime& optime) {
+ LOG(3) << "setting appliedThrough to: " << optime.toString() << "(" << optime.toBSON() << ")";
+ if (optime.isNull()) {
+ updateMinValidDocument(txn, BSON("$unset" << BSON(kBeginFieldName << 1)));
+ } else {
+ updateMinValidDocument(txn, BSON("$set" << BSON(kBeginFieldName << optime.toBSON())));
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "getMinValid", minvalidNS);
-}
}
+
+OpTime getAppliedThrough(OperationContext* txn) {
+ const BSONObj doc = getMinValidDocument(txn);
+ const auto opTimeStatus = OpTime::parseFromOplogEntry(doc.getObjectField(kBeginFieldName));
+ if (!opTimeStatus.isOK()) {
+ // Return null OpTime on any parse failure, including if "begin" is missing.
+ return {};
+ }
+
+ OpTime appliedThrough = opTimeStatus.getValue();
+ LOG(3) << "returning appliedThrough: " << appliedThrough.toString() << "("
+ << appliedThrough.toBSON() << ")";
+
+ return appliedThrough;
}
+
+} // namespace repl
+} // namespace mongo
diff --git a/src/mongo/db/repl/minvalid.h b/src/mongo/db/repl/minvalid.h
index d92d5d0c4a6..ba28e0f5927 100644
--- a/src/mongo/db/repl/minvalid.h
+++ b/src/mongo/db/repl/minvalid.h
@@ -36,17 +36,6 @@ class OperationContext;
namespace repl {
-struct BatchBoundaries {
- BatchBoundaries(const OpTime s, const OpTime e) : start(s), end(e) {}
- OpTime start;
- OpTime end;
-};
-
-enum class DurableRequirement {
- None, // Does not require any durability of the write.
- Strong, // Requires journal or checkpoint write.
-};
-
/**
* Helper functions for maintaining a single document in the local.replset.minvalid collection.
*
@@ -79,30 +68,42 @@ void setInitialSyncFlag(OperationContext* txn);
/**
* Returns true if the initial sync flag is set (and presumed active).
*/
+bool getInitialSyncFlag(OperationContext* txn);
bool getInitialSyncFlag();
+/**
+ * The minValid value is the earliest (minimum) Timestamp that must be applied in order to
+ * consider the dataset consistent.
+ */
+void setMinValid(OperationContext* txn, const OpTime& minValid);
+OpTime getMinValid(OperationContext* txn);
/**
- * Returns the bounds of the current apply batch, if active. If start is null/missing, and
- * end is equal to the last oplog entry then we are in a consistent state and ready for reads.
+ * Sets minValid only if it is not already higher than endOpTime.
+ * Warning, this compares the term and timestamp independently. Do not use if the current
+ * minValid could be from the other fork of a rollback.
*/
-BatchBoundaries getMinValid(OperationContext* txn);
+void setMinValidToAtLeast(OperationContext* txn, const OpTime& endOpTime);
/**
- * The minValid value is the earliest (minimum) Timestamp that must be applied in order to
- * consider the dataset consistent.
- *
- * This is called when a batch finishes.
- *
- * Wait for durable writes (which will block on journaling/checkpointing) when specified.
- *
+ * On startup all oplog entries with a value >= the oplog delete from point should be deleted.
+ * If null, no documents should be deleted.
*/
-void setMinValid(OperationContext* ctx, const OpTime& endOpTime, const DurableRequirement durReq);
+void setOplogDeleteFromPoint(OperationContext* txn, const Timestamp& timestamp);
+Timestamp getOplogDeleteFromPoint(OperationContext* txn);
/**
- * The bounds indicate an apply is active and we are not in a consistent state to allow reads
- * or transition from a non-visible state to primary/secondary.
+ * The applied through point is a persistent record of where we've applied through. If null, the
+ * applied through point is the top of the oplog.
+ */
+void setAppliedThrough(OperationContext* txn, const OpTime& optime);
+
+/**
+ * You should probably be calling ReplicationCoordinator::getLastAppliedOpTime() instead.
+ *
+ * This reads the value from storage which isn't always updated when the ReplicationCoordinator
+ * is.
*/
-void setMinValid(OperationContext* ctx, const BatchBoundaries& boundaries);
+OpTime getAppliedThrough(OperationContext* txn);
}
}
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 03c2aa06acf..d98db32fd43 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -304,7 +304,7 @@ unique_ptr<OplogDocWriter> _logOpWriter(OperationContext* txn,
}
} // end anon namespace
-// Truncates the oplog to and including the "truncateTimestamp" entry.
+// Truncates the oplog after and including the "truncateTimestamp" entry.
void truncateOplogTo(OperationContext* txn, Timestamp truncateTimestamp) {
const NamespaceString oplogNss(rsOplogName);
ScopedTransaction transaction(txn, MODE_IX);
@@ -318,36 +318,41 @@ void truncateOplogTo(OperationContext* txn, Timestamp truncateTimestamp) {
}
// Scan through oplog in reverse, from latest entry to first, to find the truncateTimestamp.
- bool foundSomethingToTruncate = false;
- RecordId lastRecordId;
- BSONObj lastOplogEntry;
+ RecordId oldestIDToDelete; // Non-null if there is something to delete.
auto oplogRs = oplogCollection->getRecordStore();
- auto oplogReverseCursor = oplogRs->getCursor(txn, false);
- bool first = true;
+ auto oplogReverseCursor = oplogRs->getCursor(txn, /*forward=*/false);
+ size_t count = 0;
while (auto next = oplogReverseCursor->next()) {
- lastOplogEntry = next->data.releaseToBson();
- lastRecordId = next->id;
+ const BSONObj entry = next->data.releaseToBson();
+ const RecordId id = next->id;
+ count++;
- const auto tsElem = lastOplogEntry["ts"];
-
- if (first) {
+ const auto tsElem = entry["ts"];
+ if (count == 1) {
if (tsElem.eoo())
- LOG(2) << "Oplog tail entry: " << lastOplogEntry;
+ LOG(2) << "Oplog tail entry: " << entry;
else
LOG(2) << "Oplog tail entry ts field: " << tsElem;
- first = false;
}
if (tsElem.timestamp() < truncateTimestamp) {
- break;
+ // If count == 1, that means that we have nothing to delete because everything in the
+ // oplog is < truncateTimestamp.
+ if (count != 1) {
+ invariant(!oldestIDToDelete.isNull());
+ oplogCollection->temp_cappedTruncateAfter(
+ txn, oldestIDToDelete, /*inclusive=*/true);
+ }
+ return;
}
- foundSomethingToTruncate = true;
+ oldestIDToDelete = id;
}
- if (foundSomethingToTruncate) {
- oplogCollection->temp_cappedTruncateAfter(txn, lastRecordId, false);
- }
+ severe() << "Reached end of oplog looking for oplog entry before "
+ << truncateTimestamp.toStringPretty()
+ << " but couldn't find any after looking through " << count << " entries.";
+ fassertFailedNoTrace(40296);
}
/* we write to local.oplog.rs:
diff --git a/src/mongo/db/repl/oplogreader.cpp b/src/mongo/db/repl/oplogreader.cpp
index c1b6e37f82f..63832b201af 100644
--- a/src/mongo/db/repl/oplogreader.cpp
+++ b/src/mongo/db/repl/oplogreader.cpp
@@ -137,8 +137,34 @@ HostAndPort OplogReader::getHost() const {
return _host;
}
+Status OplogReader::_compareRequiredOpTimeWithQueryResponse(const OpTime& requiredOpTime) {
+ auto containsMinValid = more();
+ if (!containsMinValid) {
+ return Status(
+ ErrorCodes::NoMatchingDocument,
+ "remote oplog does not contain entry with optime matching our required optime");
+ }
+ auto doc = nextSafe();
+ const auto opTime = fassertStatusOK(40351, OpTime::parseFromOplogEntry(doc));
+ if (requiredOpTime != opTime) {
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "remote oplog contain entry with matching timestamp "
+ << opTime.getTimestamp().toString() << " but optime "
+ << opTime.toString() << " does not "
+ "match our required optime");
+ }
+ if (requiredOpTime.getTerm() != opTime.getTerm()) {
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "remote oplog contain entry with term " << opTime.getTerm()
+ << " that does not "
+ "match the term in our required optime");
+ }
+ return Status::OK();
+}
+
void OplogReader::connectToSyncSource(OperationContext* txn,
const OpTime& lastOpTimeFetched,
+ const OpTime& requiredOpTime,
ReplicationCoordinator* replCoord) {
const Timestamp sentinelTimestamp(duration_cast<Seconds>(Milliseconds(curTimeMillis64())), 0);
const OpTime sentinel(sentinelTimestamp, std::numeric_limits<long long>::max());
@@ -165,10 +191,9 @@ void OplogReader::connectToSyncSource(OperationContext* txn,
log() << "our last optime : " << lastOpTimeFetched;
log() << "oldest available is " << oldestOpTimeSeen;
log() << "See http://dochub.mongodb.org/core/resyncingaverystalereplicasetmember";
- setMinValid(txn, {lastOpTimeFetched, oldestOpTimeSeen});
auto status = replCoord->setMaintenanceMode(true);
if (!status.isOK()) {
- warning() << "Failed to transition into maintenance mode.";
+ warning() << "Failed to transition into maintenance mode: " << status;
}
bool worked = replCoord->setFollowerMode(MemberState::RS_RECOVERING);
if (!worked) {
@@ -204,6 +229,29 @@ void OplogReader::connectToSyncSource(OperationContext* txn,
continue;
}
+ // Check if sync source contains required optime.
+ if (!requiredOpTime.isNull()) {
+ // This query is structured so that it is executed on the sync source using the oplog
+ // start hack (oplogReplay=true and $gt/$gte predicate over "ts").
+ auto ts = requiredOpTime.getTimestamp();
+ tailingQuery(rsOplogName.c_str(), BSON("ts" << BSON("$gte" << ts << "$lte" << ts)));
+ auto status = _compareRequiredOpTimeWithQueryResponse(requiredOpTime);
+ if (!status.isOK()) {
+ const auto blacklistDuration = Seconds(60);
+ const auto until = Date_t::now() + blacklistDuration;
+ warning() << "We cannot use " << candidate.toString()
+ << " as a sync source because it does not contain the necessary "
+ "operations for us to reach a consistent state: " << status
+ << " last fetched optime: " << lastOpTimeFetched
+ << ". required optime: " << requiredOpTime
+ << ". Blacklisting this sync source for " << blacklistDuration
+ << " until: " << until;
+ resetConnection();
+ replCoord->blacklistSyncSource(candidate, until);
+ continue;
+ }
+ resetCursor();
+ }
// TODO: If we were too stale (recovering with maintenance mode on), then turn it off, to
// allow becoming secondary/etc.
diff --git a/src/mongo/db/repl/oplogreader.h b/src/mongo/db/repl/oplogreader.h
index 65e101415a0..1434125697a 100644
--- a/src/mongo/db/repl/oplogreader.h
+++ b/src/mongo/db/repl/oplogreader.h
@@ -157,7 +157,14 @@ public:
*/
void connectToSyncSource(OperationContext* txn,
const OpTime& lastOpTimeFetched,
+ const OpTime& requiredOpTime,
ReplicationCoordinator* replCoord);
+
+private:
+ /**
+ * Checks query response for required optime.
+ */
+ Status _compareRequiredOpTimeWithQueryResponse(const OpTime& requiredOpTime);
};
} // namespace repl
diff --git a/src/mongo/db/repl/replication_coordinator.h b/src/mongo/db/repl/replication_coordinator.h
index 630b9277b48..dd55fcca799 100644
--- a/src/mongo/db/repl/replication_coordinator.h
+++ b/src/mongo/db/repl/replication_coordinator.h
@@ -133,7 +133,7 @@ public:
* components of the replication system to shut down and stop any threads they are using,
* blocking until all replication-related shutdown tasks are complete.
*/
- virtual void shutdown() = 0;
+ virtual void shutdown(OperationContext* txn) = 0;
/**
* Returns a pointer to the ReplicationExecutor.
diff --git a/src/mongo/db/repl/replication_coordinator_external_state.h b/src/mongo/db/repl/replication_coordinator_external_state.h
index aba9726fc04..b595f606a61 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state.h
@@ -80,7 +80,7 @@ public:
* Performs any necessary external state specific shutdown tasks, such as cleaning up
* the threads it started.
*/
- virtual void shutdown() = 0;
+ virtual void shutdown(OperationContext* txn) = 0;
/**
* Creates the oplog, writes the first entry and stores the replica set config document. Sets
@@ -90,10 +90,20 @@ public:
const BSONObj& config,
bool updateReplOpTime) = 0;
+
/**
- * Writes a message about our transition to primary to the oplog.
+ * Called as part of the process of transitioning to primary and run with the global X lock and
+ * the replication coordinator mutex acquired, so no majoirty writes are allowed while in this
+ * state. See the call site in ReplicationCoordinatorImpl for details about when and how it is
+ * called.
+ *
+ * Among other things, this writes a message about our transition to primary to the oplog if
+ * isV1 and and returns the optime of that message. If !isV1, returns the optime of the last op
+ * in the oplog.
+ *
+ * Throws on errors.
*/
- virtual void logTransitionToPrimaryToOplog(OperationContext* txn) = 0;
+ virtual OpTime onTransitionToPrimary(OperationContext* txn, bool isV1ElectionProtocol) = 0;
/**
* Simple wrapper around SyncSourceFeedback::forwardSlaveProgress. Signals to the
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
index 75b08a4fd76..128e18b9f53 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/client.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/dbdirectclient.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/op_observer.h"
@@ -70,6 +71,7 @@
#include "mongo/util/net/hostandport.h"
#include "mongo/util/net/message_port.h"
#include "mongo/util/net/sock.h"
+#include "mongo/util/scopeguard.h"
namespace mongo {
namespace repl {
@@ -116,7 +118,7 @@ void ReplicationCoordinatorExternalStateImpl::startMasterSlave(OperationContext*
repl::startMasterSlave(txn);
}
-void ReplicationCoordinatorExternalStateImpl::shutdown() {
+void ReplicationCoordinatorExternalStateImpl::shutdown(OperationContext* txn) {
stdx::lock_guard<stdx::mutex> lk(_threadMutex);
if (_startedThreads) {
log() << "Stopping replication applier threads";
@@ -129,6 +131,13 @@ void ReplicationCoordinatorExternalStateImpl::shutdown() {
if (_snapshotThread)
_snapshotThread->shutdown();
+
+ if (getOplogDeleteFromPoint(txn).isNull() &&
+ loadLastOpTime(txn) == getAppliedThrough(txn)) {
+ // Clear the appliedThrough marker to indicate we are consistent with the top of the
+ // oplog.
+ setAppliedThrough(txn, {});
+ }
}
}
@@ -169,24 +178,45 @@ Status ReplicationCoordinatorExternalStateImpl::initializeReplSetStorage(Operati
wuow.commit();
}
MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "initiate oplog entry", "local.oplog.rs");
+
+ // This initializes the minvalid document with a null "ts" because older versions (<=3.2)
+ // get angry if the minValid document is present but doesn't have a "ts" field.
+ // Consider removing this once we no longer need to support downgrading to 3.2.
+ setMinValidToAtLeast(txn, {});
} catch (const DBException& ex) {
return ex.toStatus();
}
return Status::OK();
}
-void ReplicationCoordinatorExternalStateImpl::logTransitionToPrimaryToOplog(OperationContext* txn) {
- MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- ScopedTransaction scopedXact(txn, MODE_X);
+OpTime ReplicationCoordinatorExternalStateImpl::onTransitionToPrimary(OperationContext* txn,
+ bool isV1ElectionProtocol) {
+ invariant(txn->lockState()->isW());
+
+ // Clear the appliedThrough marker so on startup we'll use the top of the oplog. This must be
+ // done before we add anything to our oplog.
+ invariant(getOplogDeleteFromPoint(txn).isNull());
+ setAppliedThrough(txn, {});
+
+ if (isV1ElectionProtocol) {
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
+ ScopedTransaction scopedXact(txn, MODE_X);
- WriteUnitOfWork wuow(txn);
- txn->getClient()->getServiceContext()->getOpObserver()->onOpMessage(txn,
- BSON("msg"
- << "new primary"));
- wuow.commit();
+ WriteUnitOfWork wuow(txn);
+ txn->getClient()->getServiceContext()->getOpObserver()->onOpMessage(
+ txn,
+ BSON("msg"
+ << "new primary"));
+ wuow.commit();
+ }
+ MONGO_WRITE_CONFLICT_RETRY_LOOP_END(
+ txn, "logging transition to primary to oplog", "local.oplog.rs");
}
- MONGO_WRITE_CONFLICT_RETRY_LOOP_END(
- txn, "logging transition to primary to oplog", "local.oplog.rs");
+ const auto opTimeToReturn = fassertStatusOK(28665, loadLastOpTime(txn));
+
+ dropAllTempCollections(txn);
+
+ return opTimeToReturn;
}
void ReplicationCoordinatorExternalStateImpl::forwardSlaveProgress() {
@@ -301,18 +331,98 @@ void ReplicationCoordinatorExternalStateImpl::setGlobalTimestamp(const Timestamp
}
void ReplicationCoordinatorExternalStateImpl::cleanUpLastApplyBatch(OperationContext* txn) {
- auto mv = getMinValid(txn);
+ if (getInitialSyncFlag(txn)) {
+ return; // Initial Sync will take over so no cleanup is needed.
+ }
- if (!mv.start.isNull()) {
- // If we are in the middle of a batch, and recoveringm then we need to truncate the oplog.
- LOG(2) << "Recovering from a failed apply batch, start:" << mv.start.toBSON();
- truncateOplogTo(txn, mv.start.getTimestamp());
+ // This initializes the minvalid document with a null "ts" because older versions (<3.2.10)
+ // get angry if the minValid document is present but doesn't have a "ts" field.
+ setMinValidToAtLeast(txn, {});
+
+ const auto deleteFromPoint = getOplogDeleteFromPoint(txn);
+ const auto appliedThrough = getAppliedThrough(txn);
+
+ const bool needToDeleteEndOfOplog = !deleteFromPoint.isNull() &&
+ // This version should never have a non-null deleteFromPoint with a null appliedThrough.
+ // This scenario means that we downgraded after unclean shutdown, then the downgraded node
+ // deleted the ragged end of our oplog, then did a clean shutdown.
+ !appliedThrough.isNull() &&
+ // Similarly we should never have an appliedThrough higher than the deleteFromPoint. This
+ // means that the downgraded node deleted our ragged end then applied ahead of our
+ // deleteFromPoint and then had an unclean shutdown before upgrading. We are ok with
+ // applying these ops because older versions wrote to the oplog from a single thread so we
+ // know they are in order.
+ !(appliedThrough.getTimestamp() >= deleteFromPoint);
+ if (needToDeleteEndOfOplog) {
+ log() << "Removing unapplied entries starting at: " << deleteFromPoint;
+ truncateOplogTo(txn, deleteFromPoint);
+ }
+ setOplogDeleteFromPoint(txn, {}); // clear the deleteFromPoint
+
+ if (appliedThrough.isNull()) {
+ // No follow-up work to do.
+ return;
+ }
+
+ // Check if we have any unapplied ops in our oplog. It is important that this is done after
+ // deleting the ragged end of the oplog.
+ const auto topOfOplog = fassertStatusOK(40290, loadLastOpTime(txn));
+ if (appliedThrough == topOfOplog) {
+ return; // We've applied all the valid oplog we have.
+ } else if (appliedThrough > topOfOplog) {
+ severe() << "Applied op " << appliedThrough << " not found. Top of oplog is " << topOfOplog
+ << '.';
+ fassertFailedNoTrace(40313);
+ }
+
+ log() << "Replaying stored operations from " << appliedThrough << " (exclusive) to "
+ << topOfOplog << " (inclusive).";
+
+ DBDirectClient db(txn);
+ auto cursor = db.query(rsOplogName,
+ QUERY("ts" << BSON("$gte" << appliedThrough.getTimestamp())),
+ /*batchSize*/ 0,
+ /*skip*/ 0,
+ /*projection*/ nullptr,
+ QueryOption_OplogReplay);
+
+ // Check that the first document matches our appliedThrough point then skip it since it's
+ // already been applied.
+ if (!cursor->more()) {
+ // This should really be impossible because we check above that the top of the oplog is
+ // strictly > appliedThrough. If this fails it represents a serious bug in either the
+ // storage engine or query's implementation of OplogReplay.
+ severe() << "Couldn't find any entries in the oplog >= " << appliedThrough
+ << " which should be impossible.";
+ fassertFailedNoTrace(40293);
+ }
+ auto firstOpTimeFound = fassertStatusOK(40291, OpTime::parseFromOplogEntry(cursor->nextSafe()));
+ if (firstOpTimeFound != appliedThrough) {
+ severe() << "Oplog entry at " << appliedThrough << " is missing; actual entry found is "
+ << firstOpTimeFound;
+ fassertFailedNoTrace(40292);
+ }
+
+ // Apply remaining ops one at at time, but don't log them because they are already logged.
+ const bool wereWritesReplicated = txn->writesAreReplicated();
+ ON_BLOCK_EXIT([&] { txn->setReplicatedWrites(wereWritesReplicated); });
+ txn->setReplicatedWrites(false);
+
+ while (cursor->more()) {
+ auto entry = cursor->nextSafe();
+ fassertStatusOK(40294, SyncTail::syncApply(txn, entry, true));
+ setAppliedThrough(txn, fassertStatusOK(40295, OpTime::parseFromOplogEntry(entry)));
}
}
StatusWith<OpTime> ReplicationCoordinatorExternalStateImpl::loadLastOpTime(OperationContext* txn) {
// TODO: handle WriteConflictExceptions below
try {
+ // If we are doing an initial sync do not read from the oplog.
+ if (getInitialSyncFlag(txn)) {
+ return {ErrorCodes::InitialSyncFailure, "In the middle of an initial sync."};
+ }
+
BSONObj oplogEntry;
if (!Helpers::getLast(txn, rsOplogName.c_str(), oplogEntry)) {
return StatusWith<OpTime>(ErrorCodes::NoMatchingDocument,
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.h b/src/mongo/db/repl/replication_coordinator_external_state_impl.h
index 4ddc917d129..26667fbba96 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.h
@@ -53,11 +53,11 @@ public:
virtual ~ReplicationCoordinatorExternalStateImpl();
virtual void startThreads(const ReplSettings& settings) override;
virtual void startMasterSlave(OperationContext* txn);
- virtual void shutdown();
+ virtual void shutdown(OperationContext* txn);
virtual Status initializeReplSetStorage(OperationContext* txn,
const BSONObj& config,
bool updateReplOpTime);
- virtual void logTransitionToPrimaryToOplog(OperationContext* txn);
+ OpTime onTransitionToPrimary(OperationContext* txn, bool isV1ElectionProtocol) override;
virtual void forwardSlaveProgress();
virtual OID ensureMe(OperationContext* txn);
virtual bool isSelf(const HostAndPort& host);
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
index d1f6f567e72..2918d8cdcad 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
@@ -69,7 +69,7 @@ Status ReplicationCoordinatorExternalStateMock::initializeReplSetStorage(Operati
return storeLocalConfigDocument(txn, config);
}
-void ReplicationCoordinatorExternalStateMock::shutdown() {}
+void ReplicationCoordinatorExternalStateMock::shutdown(OperationContext*) {}
void ReplicationCoordinatorExternalStateMock::forwardSlaveProgress() {}
OID ReplicationCoordinatorExternalStateMock::ensureMe(OperationContext*) {
@@ -219,7 +219,11 @@ void ReplicationCoordinatorExternalStateMock::updateCommittedSnapshot(SnapshotNa
void ReplicationCoordinatorExternalStateMock::forceSnapshotCreation() {}
bool ReplicationCoordinatorExternalStateMock::snapshotsEnabled() const {
- return true;
+ return _areSnapshotsEnabled;
+}
+
+void ReplicationCoordinatorExternalStateMock::setAreSnapshotsEnabled(bool val) {
+ _areSnapshotsEnabled = val;
}
void ReplicationCoordinatorExternalStateMock::notifyOplogMetadataWaiters() {}
@@ -230,11 +234,19 @@ double ReplicationCoordinatorExternalStateMock::getElectionTimeoutOffsetLimitFra
bool ReplicationCoordinatorExternalStateMock::isReadCommittedSupportedByStorageEngine(
OperationContext* txn) const {
- return true;
+ return _isReadCommittedSupported;
}
-void ReplicationCoordinatorExternalStateMock::logTransitionToPrimaryToOplog(OperationContext* txn) {
- _lastOpTime = OpTime(Timestamp(1, 0), 1);
+void ReplicationCoordinatorExternalStateMock::setIsReadCommittedEnabled(bool val) {
+ _isReadCommittedSupported = val;
+}
+
+OpTime ReplicationCoordinatorExternalStateMock::onTransitionToPrimary(OperationContext* txn,
+ bool isV1ElectionProtocol) {
+ if (isV1ElectionProtocol) {
+ _lastOpTime = OpTime(Timestamp(1, 0), 1);
+ }
+ return fassertStatusOK(40297, _lastOpTime);
}
} // namespace repl
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.h b/src/mongo/db/repl/replication_coordinator_external_state_mock.h
index c16b0760c0d..929772f5820 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_mock.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.h
@@ -54,11 +54,11 @@ public:
virtual ~ReplicationCoordinatorExternalStateMock();
virtual void startThreads(const ReplSettings& settings) override;
virtual void startMasterSlave(OperationContext*);
- virtual void shutdown();
+ virtual void shutdown(OperationContext*);
virtual Status initializeReplSetStorage(OperationContext* txn,
const BSONObj& config,
bool updateReplOpTime);
- virtual void logTransitionToPrimaryToOplog(OperationContext* txn);
+ OpTime onTransitionToPrimary(OperationContext* txn, bool isV1ElectionProtocol) override;
virtual void forwardSlaveProgress();
virtual OID ensureMe(OperationContext*);
virtual bool isSelf(const HostAndPort& host);
@@ -146,6 +146,16 @@ public:
*/
bool threadsStarted() const;
+ /**
+ * Sets if the storage engine is configured to support ReadConcern::Majority (committed point).
+ */
+ void setIsReadCommittedEnabled(bool val);
+
+ /**
+ * Sets if we are taking snapshots for read concern majority use.
+ */
+ void setAreSnapshotsEnabled(bool val);
+
private:
StatusWith<BSONObj> _localRsConfigDocument;
StatusWith<LastVote> _localRsLastVoteDocument;
@@ -166,6 +176,8 @@ private:
bool _connectionsClosed;
HostAndPort _clientHostAndPort;
bool _threadsStarted;
+ bool _isReadCommittedSupported = true;
+ bool _areSnapshotsEnabled = true;
};
} // namespace repl
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 45c3c0644d4..5dcf7b9b1c3 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -159,6 +159,19 @@ struct ReplicationCoordinatorImpl::WaiterInfo {
list->erase(std::remove(list->begin(), list->end(), this), list->end());
}
+ BSONObj toBSON() const {
+ BSONObjBuilder bob;
+ bob.append("opId", opID);
+ bob.append("opTime", opTime->toBSON());
+ bob.append("master", master);
+ bob.append("writeConcern", writeConcern);
+ return bob.obj();
+ };
+
+ std::string toString() const {
+ return toBSON().toString();
+ };
+
std::vector<WaiterInfo*>* list;
bool master; // Set to false to indicate that stepDown was called while waiting
const unsigned int opID;
@@ -201,6 +214,13 @@ DataReplicatorOptions createDataReplicatorOptions(ReplicationCoordinator* replCo
}
} // namespace
+std::string ReplicationCoordinatorImpl::SnapshotInfo::toString() const {
+ BSONObjBuilder bob;
+ bob.append("optime", opTime.toBSON());
+ bob.append("name-id", name.toString());
+ return bob.obj().toString();
+}
+
ReplicationCoordinatorImpl::ReplicationCoordinatorImpl(
const ReplSettings& settings,
ReplicationCoordinatorExternalState* externalState,
@@ -351,7 +371,7 @@ bool ReplicationCoordinatorImpl::_startLoadLocalConfig(OperationContext* txn) {
fassertFailedNoTrace(28545);
}
- // Returns the last optime from the oplog, possibly truncating first if we need to recover.
+ // Read the last op from the oplog after cleaning up any partially applied batches.
_externalState->cleanUpLastApplyBatch(txn);
auto lastOpTimeStatus = _externalState->loadLastOpTime(txn);
@@ -492,7 +512,7 @@ void ReplicationCoordinatorImpl::startReplication(OperationContext* txn) {
}
}
-void ReplicationCoordinatorImpl::shutdown() {
+void ReplicationCoordinatorImpl::shutdown(OperationContext* txn) {
// Shutdown must:
// * prevent new threads from blocking in awaitReplication
// * wake up all existing threads blocking in awaitReplication
@@ -525,7 +545,7 @@ void ReplicationCoordinatorImpl::shutdown() {
// joining the replication executor is blocking so it must be run outside of the mutex
_replExecutor.shutdown();
_replExecutor.join();
- _externalState->shutdown();
+ _externalState->shutdown(txn);
}
const ReplSettings& ReplicationCoordinatorImpl::getSettings() const {
@@ -675,7 +695,7 @@ void ReplicationCoordinatorImpl::signalDrainComplete(OperationContext* txn) {
// _isWaitingForDrainToComplete, set the flag allowing non-local database writes and
// drop the mutex. At this point, no writes can occur from other threads, due to the
// global exclusive lock.
- // 4.) Drop all temp collections.
+ // 4.) Drop all temp collections, and log the drops to the oplog.
// 5.) Log transition to primary in the oplog and set that OpTime as the floor for what we will
// consider to be committed.
// 6.) Drop the global exclusive lock.
@@ -687,6 +707,9 @@ void ReplicationCoordinatorImpl::signalDrainComplete(OperationContext* txn) {
// external writes will be processed. This is important so that a new temp collection isn't
// introduced on the new primary before we drop all the temp collections.
+ // When we go to drop all temp collections, we must replicate the drops.
+ invariant(txn->writesAreReplicated());
+
stdx::unique_lock<stdx::mutex> lk(_mutex);
if (!_isWaitingForDrainToComplete) {
return;
@@ -697,32 +720,30 @@ void ReplicationCoordinatorImpl::signalDrainComplete(OperationContext* txn) {
ScopedTransaction transaction(txn, MODE_X);
Lock::GlobalWrite globalWriteLock(txn->lockState());
-
lk.lock();
+
if (!_isWaitingForDrainToComplete) {
return;
}
_isWaitingForDrainToComplete = false;
- _canAcceptNonLocalWrites = true;
_drainFinishedCond.notify_all();
- lk.unlock();
- _externalState->dropAllTempCollections(txn);
-
- // This is done for compatibility with PV0 replicas wrt how "n" ops are processed.
- if (isV1ElectionProtocol()) {
- _externalState->logTransitionToPrimaryToOplog(txn);
+ if (!_getMemberState_inlock().primary()) {
+ // We must have decided not to transition to primary while waiting for the applier to drain.
+ // Skip the rest of this function since it should only be done when really transitioning.
+ return;
}
- StatusWith<OpTime> lastOpTime = _externalState->loadLastOpTime(txn);
- fassertStatusOK(28665, lastOpTime.getStatus());
- _setFirstOpTimeOfMyTerm(lastOpTime.getValue());
+ invariant(!_canAcceptNonLocalWrites);
+ _canAcceptNonLocalWrites = true;
+ lk.unlock();
+ _setFirstOpTimeOfMyTerm(_externalState->onTransitionToPrimary(txn, isV1ElectionProtocol()));
lk.lock();
+
// Must calculate the commit level again because firstOpTimeOfMyTerm wasn't set when we logged
- // our election in logTransitionToPrimaryToOplog(), above.
+ // our election in onTransitionToPrimary(), above.
_updateLastCommittedOpTime_inlock();
- lk.unlock();
log() << "transition to primary complete; database writes are now permitted" << rsLog;
}
@@ -1285,25 +1306,30 @@ void ReplicationCoordinatorImpl::interruptAll() {
bool ReplicationCoordinatorImpl::_doneWaitingForReplication_inlock(
const OpTime& opTime, SnapshotName minSnapshot, const WriteConcernOptions& writeConcern) {
+ // The syncMode cannot be unset.
invariant(writeConcern.syncMode != WriteConcernOptions::SyncMode::UNSET);
Status status = _checkIfWriteConcernCanBeSatisfied_inlock(writeConcern);
if (!status.isOK()) {
return true;
}
- if (writeConcern.wMode.empty())
- return _haveNumNodesReachedOpTime_inlock(opTime,
- writeConcern.wNumNodes,
- writeConcern.syncMode ==
- WriteConcernOptions::SyncMode::JOURNAL);
+ const bool useDurableOpTime = writeConcern.syncMode == WriteConcernOptions::SyncMode::JOURNAL;
+
+ if (writeConcern.wMode.empty()) {
+ return _haveNumNodesReachedOpTime_inlock(opTime, writeConcern.wNumNodes, useDurableOpTime);
+ }
StringData patternName;
if (writeConcern.wMode == WriteConcernOptions::kMajority) {
- if (writeConcern.syncMode == WriteConcernOptions::SyncMode::JOURNAL &&
- _externalState->snapshotsEnabled()) {
+ if (useDurableOpTime && _externalState->snapshotsEnabled()) {
+ // Make sure we have a valid snapshot.
if (!_currentCommittedSnapshot) {
return false;
}
+
+ // Wait for the "current" snapshot to advance to/past the opTime.
+ // We cannot have this committed snapshot until we have replicated to a majority,
+ // so we can return true here once that requirement is met.
return (_currentCommittedSnapshot->opTime >= opTime &&
_currentCommittedSnapshot->name >= minSnapshot);
} else {
@@ -1317,10 +1343,7 @@ bool ReplicationCoordinatorImpl::_doneWaitingForReplication_inlock(
if (!tagPattern.isOK()) {
return true;
}
- return _haveTaggedNodesReachedOpTime_inlock(opTime,
- tagPattern.getValue(),
- writeConcern.syncMode ==
- WriteConcernOptions::SyncMode::JOURNAL);
+ return _haveTaggedNodesReachedOpTime_inlock(opTime, tagPattern.getValue(), useDurableOpTime);
}
bool ReplicationCoordinatorImpl::_haveNumNodesReachedOpTime_inlock(const OpTime& targetOpTime,
@@ -1471,7 +1494,8 @@ ReplicationCoordinator::StatusAndDuration ReplicationCoordinatorImpl::_awaitRepl
std::min<Microseconds>(Milliseconds{writeConcern.wTimeout} - elapsed, waitTime);
}
- if (waitTime == Microseconds::max()) {
+ const bool waitForever = waitTime == Microseconds::max();
+ if (waitForever) {
condVar.wait(*lock);
} else {
condVar.wait_for(*lock, waitTime);
diff --git a/src/mongo/db/repl/replication_coordinator_impl.h b/src/mongo/db/repl/replication_coordinator_impl.h
index 3cc0d6e4995..33b93a70027 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.h
+++ b/src/mongo/db/repl/replication_coordinator_impl.h
@@ -113,7 +113,7 @@ public:
virtual void startReplication(OperationContext* txn) override;
- virtual void shutdown() override;
+ virtual void shutdown(OperationContext* txn) override;
virtual ReplicationExecutor* getExecutor() override {
return &_replExecutor;
@@ -440,6 +440,7 @@ private:
bool operator>=(const SnapshotInfo& other) const {
return std::tie(opTime, name) >= std::tie(other.opTime, other.name);
}
+ std::string toString() const;
};
class LoseElectionGuardV1;
diff --git a/src/mongo/db/repl/replication_coordinator_impl_elect.cpp b/src/mongo/db/repl/replication_coordinator_impl_elect.cpp
index e1109d0cc4d..a2cd0eb386e 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_elect.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_elect.cpp
@@ -125,7 +125,8 @@ void ReplicationCoordinatorImpl::_startElectSelf() {
if (lastOpTimeApplied.isNull()) {
log() << "not trying to elect self, "
- "do not yet have a complete set of data from any point in time";
+ "do not yet have a complete set of data from any point in time"
+ " -- lastAppliedOpTime is null";
return;
}
@@ -276,8 +277,11 @@ void ReplicationCoordinatorImpl::_recoverFromElectionTie(
}
auto now = _replExecutor.now();
auto lastOpApplied = getMyLastAppliedOpTime();
- if (_topCoord->checkShouldStandForElection(now, lastOpApplied)) {
- fassert(28817, _topCoord->becomeCandidateIfElectable(now, lastOpApplied));
+ const auto status = _topCoord->checkShouldStandForElection(now, lastOpApplied);
+ if (!status.isOK()) {
+ LOG(2) << "ReplicationCoordinatorImpl::_recoverFromElectionTie -- " << status.reason();
+ } else {
+ fassertStatusOK(28817, _topCoord->becomeCandidateIfElectable(now, lastOpApplied));
_startElectSelf();
}
}
diff --git a/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp
index e29e772b011..9eb0c6aca9c 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_elect_v1_test.cpp
@@ -769,8 +769,70 @@ TEST_F(ReplCoordElectV1Test, ElectionFailsWhenTermChangesDuringActualElection) {
countLogLinesContaining("not becoming primary, we have been superceded already"));
}
-TEST_F(ReplCoordElectV1Test, SchedulesPriorityTakeoverIfNodeHasHigherPriorityThanCurrentPrimary) {
- startCapturingLogMessages();
+class PriorityTakeoverTest : public ReplCoordTest {
+public:
+ void respondToAllHeartbeats(const ReplicaSetConfig& config,
+ Date_t runUntil,
+ const HostAndPort& primaryHostAndPort,
+ const OpTime& otherNodesOpTime) {
+ auto replCoord = getReplCoord();
+
+ auto net = getNet();
+ net->enterNetwork();
+ while (net->now() < runUntil || net->hasReadyRequests()) {
+ if (net->now() < runUntil) {
+ net->runUntil(runUntil);
+ }
+ auto noi = net->getNextReadyRequest();
+ auto&& request = noi->getRequest();
+ log() << request.target << " processing " << request.cmdObj;
+ ASSERT_EQUALS("replSetHeartbeat", request.cmdObj.firstElement().fieldNameStringData());
+ ReplSetHeartbeatArgsV1 hbArgs;
+ if (hbArgs.initialize(request.cmdObj).isOK()) {
+ ReplSetHeartbeatResponse hbResp;
+ hbResp.setSetName(config.getReplSetName());
+ if (request.target == primaryHostAndPort) {
+ hbResp.setState(MemberState::RS_PRIMARY);
+ } else {
+ hbResp.setState(MemberState::RS_SECONDARY);
+ }
+ hbResp.setConfigVersion(config.getConfigVersion());
+ hbResp.setTerm(replCoord->getTerm());
+ hbResp.setAppliedOpTime(otherNodesOpTime);
+ hbResp.setDurableOpTime(otherNodesOpTime);
+ auto response =
+ makeResponseStatus(hbResp.toBSON(replCoord->isV1ElectionProtocol()));
+ net->scheduleResponse(noi, net->now(), response);
+ } else {
+ error() << "Black holing unexpected request to " << request.target << ": "
+ << request.cmdObj;
+ net->blackHole(noi);
+ }
+ }
+ net->runReadyNetworkOperations();
+ net->exitNetwork();
+ }
+
+ void performSuccessfulPriorityTakeover(Date_t priorityTakeoverTime) {
+ startCapturingLogMessages();
+ simulateSuccessfulV1ElectionAt(priorityTakeoverTime);
+ getReplCoord()->waitForElectionFinish_forTest();
+ stopCapturingLogMessages();
+
+ ASSERT(getReplCoord()->getMemberState().primary());
+
+ // Check last vote
+ auto lastVote = getExternalState()->loadLocalLastVoteDocument(nullptr);
+ ASSERT(lastVote.isOK());
+ ASSERT_EQ(0, lastVote.getValue().getCandidateIndex());
+ ASSERT_EQ(1, lastVote.getValue().getTerm());
+
+ ASSERT_EQUALS(1, countLogLinesContaining("Starting an election for a priority takeover"));
+ ASSERT_EQUALS(1, countLogLinesContaining("election succeeded"));
+ }
+};
+
+TEST_F(PriorityTakeoverTest, SchedulesPriorityTakeoverIfNodeHasHigherPriorityThanCurrentPrimary) {
BSONObj configObj = BSON("_id"
<< "mySet"
<< "version" << 1 << "members"
@@ -795,35 +857,8 @@ TEST_F(ReplCoordElectV1Test, SchedulesPriorityTakeoverIfNodeHasHigherPriorityTha
ASSERT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
- auto net = getNet();
- net->enterNetwork();
- auto now = net->now();
- while (net->hasReadyRequests()) {
- auto noi = net->getNextReadyRequest();
- auto&& request = noi->getRequest();
- log() << request.target << " processing " << request.cmdObj;
- ASSERT_EQUALS("replSetHeartbeat", request.cmdObj.firstElement().fieldNameStringData());
- ReplSetHeartbeatArgsV1 hbArgs;
- if (hbArgs.initialize(request.cmdObj).isOK()) {
- ReplSetHeartbeatResponse hbResp;
- hbResp.setSetName(config.getReplSetName());
- if (request.target == HostAndPort("node2", 12345)) {
- hbResp.setState(MemberState::RS_PRIMARY);
- } else {
- hbResp.setState(MemberState::RS_SECONDARY);
- }
- hbResp.setConfigVersion(config.getConfigVersion());
- hbResp.setTerm(replCoord->getTerm());
- auto response = makeResponseStatus(hbResp.toBSON(replCoord->isV1ElectionProtocol()));
- net->scheduleResponse(noi, net->now(), response);
- } else {
- error() << "Black holing unexpected request to " << request.target << ": "
- << request.cmdObj;
- net->blackHole(noi);
- }
- }
- net->runReadyNetworkOperations();
- net->exitNetwork();
+ auto now = getNet()->now();
+ respondToAllHeartbeats(config, now, HostAndPort("node2", 12345), time1);
ASSERT_NOT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0),
@@ -834,6 +869,169 @@ TEST_F(ReplCoordElectV1Test, SchedulesPriorityTakeoverIfNodeHasHigherPriorityTha
ASSERT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
}
+TEST_F(PriorityTakeoverTest, SuccessfulPriorityTakeover) {
+ BSONObj configObj = BSON("_id"
+ << "mySet"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "node1:12345"
+ << "priority" << 2)
+ << BSON("_id" << 2 << "host"
+ << "node2:12345")
+ << BSON("_id" << 3 << "host"
+ << "node3:12345")) << "protocolVersion"
+ << 1);
+ assertStartSuccess(configObj, HostAndPort("node1", 12345));
+ ReplicaSetConfig config = assertMakeRSConfig(configObj);
+
+ auto replCoord = getReplCoord();
+
+ OperationContextNoop txn;
+ OpTime time1(Timestamp(100, 1), 0);
+ replCoord->setMyLastAppliedOpTime(time1);
+ replCoord->setMyLastDurableOpTime(time1);
+ ASSERT(replCoord->setFollowerMode(MemberState::RS_SECONDARY));
+
+ ASSERT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
+
+ auto now = getNet()->now();
+ respondToAllHeartbeats(config, now, HostAndPort("node2", 12345), time1);
+
+ auto priorityTakeoverTime = replCoord->getPriorityTakeover_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), priorityTakeoverTime);
+ ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0), priorityTakeoverTime);
+
+ performSuccessfulPriorityTakeover(priorityTakeoverTime);
+}
+
+TEST_F(PriorityTakeoverTest, DontCallForPriorityTakeoverWhenLaggedSameSecond) {
+ BSONObj configObj = BSON("_id"
+ << "mySet"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "node1:12345"
+ << "priority" << 2)
+ << BSON("_id" << 2 << "host"
+ << "node2:12345")
+ << BSON("_id" << 3 << "host"
+ << "node3:12345")) << "protocolVersion"
+ << 1);
+ assertStartSuccess(configObj, HostAndPort("node1", 12345));
+ ReplicaSetConfig config = assertMakeRSConfig(configObj);
+ HostAndPort primaryHostAndPort("node2", 12345);
+
+ auto replCoord = getReplCoord();
+
+ OperationContextNoop txn;
+ OpTime currentOpTime(Timestamp(100, 5000), 0);
+ OpTime behindOpTime(Timestamp(100, 3999), 0);
+ OpTime closeEnoughOpTime(Timestamp(100, 4000), 0);
+ replCoord->setMyLastAppliedOpTime(behindOpTime);
+ replCoord->setMyLastDurableOpTime(behindOpTime);
+ ASSERT(replCoord->setFollowerMode(MemberState::RS_SECONDARY));
+
+ ASSERT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
+
+ auto now = getNet()->now();
+
+ respondToAllHeartbeats(config, now, primaryHostAndPort, currentOpTime);
+
+ auto priorityTakeoverTime = replCoord->getPriorityTakeover_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), priorityTakeoverTime);
+ ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0), priorityTakeoverTime);
+
+
+ // At this point the other nodes are all ahead of the current node, so it can't call for
+ // priority takeover.
+ startCapturingLogMessages();
+ respondToAllHeartbeats(config, priorityTakeoverTime, primaryHostAndPort, currentOpTime);
+ stopCapturingLogMessages();
+
+
+ ASSERT(replCoord->getMemberState().secondary());
+ ASSERT_EQUALS(1,
+ countLogLinesContaining(
+ "Not standing for election because member is not "
+ "caught up enough to the most up-to-date member to "
+ "call for priority takeover"));
+
+ now = getNet()->now();
+ ASSERT_EQUALS(now, priorityTakeoverTime);
+ priorityTakeoverTime = replCoord->getPriorityTakeover_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), priorityTakeoverTime);
+ ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0), priorityTakeoverTime);
+
+ // Now make us caught up enough to call for priority takeover to succeed.
+ replCoord->setMyLastAppliedOpTime(closeEnoughOpTime);
+ replCoord->setMyLastDurableOpTime(closeEnoughOpTime);
+
+ performSuccessfulPriorityTakeover(priorityTakeoverTime);
+}
+
+TEST_F(PriorityTakeoverTest, DontCallForPriorityTakeoverWhenLaggedDifferentSecond) {
+ BSONObj configObj = BSON("_id"
+ << "mySet"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "node1:12345"
+ << "priority" << 2)
+ << BSON("_id" << 2 << "host"
+ << "node2:12345")
+ << BSON("_id" << 3 << "host"
+ << "node3:12345")) << "protocolVersion"
+ << 1);
+ assertStartSuccess(configObj, HostAndPort("node1", 12345));
+ ReplicaSetConfig config = assertMakeRSConfig(configObj);
+ HostAndPort primaryHostAndPort("node2", 12345);
+
+ auto replCoord = getReplCoord();
+
+ OperationContextNoop txn;
+ OpTime currentOpTime(Timestamp(100, 0), 0);
+ OpTime behindOpTime(Timestamp(97, 0), 0);
+ OpTime closeEnoughOpTime(Timestamp(98, 0), 0);
+ replCoord->setMyLastAppliedOpTime(behindOpTime);
+ replCoord->setMyLastDurableOpTime(behindOpTime);
+ ASSERT(replCoord->setFollowerMode(MemberState::RS_SECONDARY));
+
+ ASSERT_EQUALS(Date_t(), replCoord->getPriorityTakeover_forTest());
+
+ auto now = getNet()->now();
+
+ respondToAllHeartbeats(config, now, primaryHostAndPort, currentOpTime);
+
+ auto priorityTakeoverTime = replCoord->getPriorityTakeover_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), priorityTakeoverTime);
+ ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0), priorityTakeoverTime);
+
+
+ // At this point the other nodes are all ahead of the current node, so it can't call for
+ // priority takeover.
+ startCapturingLogMessages();
+ respondToAllHeartbeats(config, priorityTakeoverTime, primaryHostAndPort, currentOpTime);
+ stopCapturingLogMessages();
+
+
+ ASSERT(replCoord->getMemberState().secondary());
+ ASSERT_EQUALS(1,
+ countLogLinesContaining(
+ "Not standing for election because member is not "
+ "caught up enough to the most up-to-date member to "
+ "call for priority takeover"));
+
+ now = getNet()->now();
+ ASSERT_EQUALS(now, priorityTakeoverTime);
+ priorityTakeoverTime = replCoord->getPriorityTakeover_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), priorityTakeoverTime);
+ ASSERT_EQUALS(now + config.getPriorityTakeoverDelay(0), priorityTakeoverTime);
+
+ // Now make us caught up enough to call for priority takeover to succeed.
+ replCoord->setMyLastAppliedOpTime(closeEnoughOpTime);
+ replCoord->setMyLastDurableOpTime(closeEnoughOpTime);
+
+ performSuccessfulPriorityTakeover(priorityTakeoverTime);
+}
+
} // namespace
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
index d71e7ee4afb..4c23bab4f5b 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
@@ -759,12 +759,15 @@ void ReplicationCoordinatorImpl::_startElectSelfIfEligibleV1(bool isPriorityTake
_cancelAndRescheduleElectionTimeout_inlock();
}
- if (!_topCoord->becomeCandidateIfElectable(_replExecutor.now(), getMyLastDurableOpTime())) {
+ const auto status =
+ _topCoord->becomeCandidateIfElectable(_replExecutor.now(), getMyLastAppliedOpTime());
+ if (!status.isOK()) {
if (isPriorityTakeOver) {
- log() << "Not starting an election for a priority takeover, since we are not "
- "electable";
+ log() << "Not starting an election for a priority takeover, "
+ << "since we are not electable due to: " << status.reason();
} else {
- log() << "Not starting an election, since we are not electable";
+ log() << "Not starting an election, since we are not electable due to: "
+ << status.reason();
}
return;
}
diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
index deb917413a8..f730e4deccd 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp
@@ -91,6 +91,10 @@ struct OpTimeWithTermZero {
return OpTime(timestamp, 0);
}
+ OpTime asOpTime() const {
+ return this->operator mongo::repl::OpTime();
+ }
+
Timestamp timestamp;
};
@@ -2423,9 +2427,10 @@ TEST_F(ReplCoordTest, IsMaster) {
}
TEST_F(ReplCoordTest, LogAMessageWhenShutDownBeforeReplicationStartUpFinished) {
+ OperationContextNoop txn;
init();
startCapturingLogMessages();
- getReplCoord()->shutdown();
+ getReplCoord()->shutdown(&txn);
stopCapturingLogMessages();
ASSERT_EQUALS(1,
countLogLinesContaining("shutdown() called before startReplication() finished"));
diff --git a/src/mongo/db/repl/replication_coordinator_mock.cpp b/src/mongo/db/repl/replication_coordinator_mock.cpp
index 3ea4d8eef42..7d3f987878f 100644
--- a/src/mongo/db/repl/replication_coordinator_mock.cpp
+++ b/src/mongo/db/repl/replication_coordinator_mock.cpp
@@ -52,7 +52,7 @@ void ReplicationCoordinatorMock::startReplication(OperationContext* txn) {
// TODO
}
-void ReplicationCoordinatorMock::shutdown() {
+void ReplicationCoordinatorMock::shutdown(OperationContext* txn) {
// TODO
}
diff --git a/src/mongo/db/repl/replication_coordinator_mock.h b/src/mongo/db/repl/replication_coordinator_mock.h
index 4a21d9ad705..15315cd5a8f 100644
--- a/src/mongo/db/repl/replication_coordinator_mock.h
+++ b/src/mongo/db/repl/replication_coordinator_mock.h
@@ -54,7 +54,7 @@ public:
virtual void startReplication(OperationContext* txn);
- virtual void shutdown();
+ virtual void shutdown(OperationContext* txn);
virtual ReplicationExecutor* getExecutor() override {
return nullptr;
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
index 412d1165a24..e7b8d561d55 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp
@@ -237,21 +237,25 @@ void ReplCoordTest::simulateSuccessfulDryRun() {
}
void ReplCoordTest::simulateSuccessfulV1Election() {
+ auto electionTimeoutWhen = getReplCoord()->getElectionTimeout_forTest();
+ ASSERT_NOT_EQUALS(Date_t(), electionTimeoutWhen);
+ log() << "Election timeout scheduled at " << electionTimeoutWhen << " (simulator time)";
+
+ simulateSuccessfulV1ElectionAt(electionTimeoutWhen);
+}
+
+void ReplCoordTest::simulateSuccessfulV1ElectionAt(Date_t electionTime) {
OperationContextReplMock txn;
ReplicationCoordinatorImpl* replCoord = getReplCoord();
NetworkInterfaceMock* net = getNet();
- auto electionTimeoutWhen = replCoord->getElectionTimeout_forTest();
- ASSERT_NOT_EQUALS(Date_t(), electionTimeoutWhen);
- log() << "Election timeout scheduled at " << electionTimeoutWhen << " (simulator time)";
-
ReplicaSetConfig rsConfig = replCoord->getReplicaSetConfig_forTest();
ASSERT(replCoord->getMemberState().secondary()) << replCoord->getMemberState().toString();
while (!replCoord->getMemberState().primary()) {
log() << "Waiting on network in state " << replCoord->getMemberState();
getNet()->enterNetwork();
- if (net->now() < electionTimeoutWhen) {
- net->runUntil(electionTimeoutWhen);
+ if (net->now() < electionTime) {
+ net->runUntil(electionTime);
}
const NetworkInterfaceMock::NetworkOperationIterator noi = net->getNextReadyRequest();
const RemoteCommandRequest& request = noi->getRequest();
@@ -368,9 +372,10 @@ void ReplCoordTest::simulateSuccessfulElection() {
}
void ReplCoordTest::shutdown() {
+ OperationContextReplMock txn;
invariant(_callShutdown);
_net->exitNetwork();
- _repl->shutdown();
+ _repl->shutdown(&txn);
_callShutdown = false;
}
@@ -414,5 +419,13 @@ void ReplCoordTest::replyToReceivedHeartbeatV1() {
getNet()->exitNetwork();
}
+void ReplCoordTest::disableReadConcernMajoritySupport() {
+ _externalState->setIsReadCommittedEnabled(false);
+}
+
+void ReplCoordTest::disableSnapshots() {
+ _externalState->setAreSnapshotsEnabled(false);
+}
+
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.h b/src/mongo/db/repl/replication_coordinator_test_fixture.h
index 0bea531c7d6..84e525a671a 100644
--- a/src/mongo/db/repl/replication_coordinator_test_fixture.h
+++ b/src/mongo/db/repl/replication_coordinator_test_fixture.h
@@ -196,7 +196,7 @@ protected:
/**
* Brings the repl coord from SECONDARY to PRIMARY by simulating the messages required to
- * elect it.
+ * elect it, after progressing the mocked-out notion of time past the election timeout.
*
* Behavior is unspecified if node does not have a clean config, is not in SECONDARY, etc.
*/
@@ -204,6 +204,13 @@ protected:
void simulateSuccessfulV1Election();
/**
+ * Same as simulateSuccessfulV1Election, but rather than getting the election timeout and
+ * progressing time past that point, takes in what time to expect an election to occur at.
+ * Useful for simulating elections triggered via priority takeover.
+ */
+ void simulateSuccessfulV1ElectionAt(Date_t electionTime);
+
+ /**
* Shuts down the objects under test.
*/
void shutdown();
@@ -225,6 +232,16 @@ protected:
}
+ /**
+ * Disables read concern majority support.
+ */
+ void disableReadConcernMajoritySupport();
+
+ /**
+ * Disables snapshot support.
+ */
+ void disableSnapshots();
+
private:
std::unique_ptr<ReplicationCoordinatorImpl> _repl;
// Owned by ReplicationCoordinatorImpl
diff --git a/src/mongo/db/repl/roll_back_local_operations.cpp b/src/mongo/db/repl/roll_back_local_operations.cpp
index 2117458e9f4..92312d0d21c 100644
--- a/src/mongo/db/repl/roll_back_local_operations.cpp
+++ b/src/mongo/db/repl/roll_back_local_operations.cpp
@@ -41,6 +41,10 @@ namespace repl {
namespace {
+OpTime getOpTime(const OplogInterface::Iterator::Value& oplogValue) {
+ return fassertStatusOK(40298, OpTime::parseFromOplogEntry(oplogValue.first));
+}
+
Timestamp getTimestamp(const BSONObj& operation) {
return operation["ts"].timestamp();
}
@@ -116,7 +120,7 @@ StatusWith<RollBackLocalOperations::RollbackCommonPoint> RollBackLocalOperations
_scanned++;
if (getHash(_localOplogValue) == getHash(operation)) {
return StatusWith<RollbackCommonPoint>(
- std::make_pair(getTimestamp(_localOplogValue), _localOplogValue.second));
+ std::make_pair(getOpTime(_localOplogValue), _localOplogValue.second));
}
auto status = _rollbackOperation(_localOplogValue.first);
if (!status.isOK()) {
@@ -139,14 +143,11 @@ StatusWith<RollBackLocalOperations::RollbackCommonPoint> RollBackLocalOperations
"Need to process additional remote operations.");
}
- if (getTimestamp(_localOplogValue) < getTimestamp(operation)) {
- _scanned++;
- return StatusWith<RollbackCommonPoint>(ErrorCodes::NoSuchKey,
- "Unable to determine common point. "
- "Need to process additional remote operations.");
- }
-
- return RollbackCommonPoint(Timestamp(Seconds(1), 0), RecordId());
+ invariant(getTimestamp(_localOplogValue) < getTimestamp(operation));
+ _scanned++;
+ return StatusWith<RollbackCommonPoint>(ErrorCodes::NoSuchKey,
+ "Unable to determine common point. "
+ "Need to process additional remote operations.");
}
StatusWith<RollBackLocalOperations::RollbackCommonPoint> syncRollBackLocalOperations(
diff --git a/src/mongo/db/repl/roll_back_local_operations.h b/src/mongo/db/repl/roll_back_local_operations.h
index 20eb923083d..87a940ce57b 100644
--- a/src/mongo/db/repl/roll_back_local_operations.h
+++ b/src/mongo/db/repl/roll_back_local_operations.h
@@ -34,6 +34,7 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/db/record_id.h"
#include "mongo/db/repl/oplog_interface.h"
+#include "mongo/db/repl/optime.h"
#include "mongo/stdx/functional.h"
namespace mongo {
@@ -49,7 +50,7 @@ public:
*/
using RollbackOperationFn = stdx::function<Status(const BSONObj&)>;
- using RollbackCommonPoint = std::pair<Timestamp, RecordId>;
+ using RollbackCommonPoint = std::pair<OpTime, RecordId>;
/**
* Initializes rollback processor with a valid local oplog.
diff --git a/src/mongo/db/repl/roll_back_local_operations_test.cpp b/src/mongo/db/repl/roll_back_local_operations_test.cpp
index 06af9890571..80334710d57 100644
--- a/src/mongo/db/repl/roll_back_local_operations_test.cpp
+++ b/src/mongo/db/repl/roll_back_local_operations_test.cpp
@@ -107,7 +107,7 @@ TEST(RollBackLocalOperationsTest, RollbackMultipleLocalOperations) {
RollBackLocalOperations finder(localOplog, rollbackOperation);
auto result = finder.onRemoteOperation(commonOperation.first);
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
ASSERT_FALSE(i == localOperations.cend());
ASSERT_EQUALS(commonOperation.first, i->first);
@@ -164,7 +164,7 @@ TEST(RollBackLocalOperationsTest, SkipRemoteOperations) {
}
auto result = finder.onRemoteOperation(commonOperation.first);
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
ASSERT_FALSE(i == localOperations.cend());
ASSERT_EQUALS(commonOperation.first, i->first);
@@ -197,7 +197,7 @@ TEST(RollBackLocalOperationsTest, SameTimestampDifferentHashess) {
}
auto result = finder.onRemoteOperation(commonOperation.first);
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
ASSERT_FALSE(i == localOperations.cend());
ASSERT_EQUALS(commonOperation.first, i->first);
@@ -269,7 +269,7 @@ TEST(SyncRollBackLocalOperationsTest, RollbackTwoOperations) {
return Status::OK();
});
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
ASSERT_FALSE(i == localOperations.cend());
ASSERT_EQUALS(commonOperation.first, i->first);
@@ -288,7 +288,7 @@ TEST(SyncRollBackLocalOperationsTest, SkipOneRemoteOperation) {
return Status::OK();
});
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
}
@@ -306,7 +306,7 @@ TEST(SyncRollBackLocalOperationsTest, SameTimestampDifferentHashes) {
return Status::OK();
});
ASSERT_OK(result.getStatus());
- ASSERT_EQUALS(commonOperation.first["ts"].timestamp(), result.getValue().first);
+ ASSERT_EQUALS(OpTime::parseFromOplogEntry(commonOperation.first), result.getValue().first);
ASSERT_EQUALS(commonOperation.second, result.getValue().second);
ASSERT_TRUE(called);
}
diff --git a/src/mongo/db/repl/rs_initialsync.cpp b/src/mongo/db/repl/rs_initialsync.cpp
index 23877df274a..d475d53d644 100644
--- a/src/mongo/db/repl/rs_initialsync.cpp
+++ b/src/mongo/db/repl/rs_initialsync.cpp
@@ -79,8 +79,8 @@ MONGO_FP_DECLARE(failInitSyncWithBufferedEntriesLeft);
void truncateAndResetOplog(OperationContext* txn,
ReplicationCoordinator* replCoord,
BackgroundSync* bgsync) {
- // Clear minvalid
- setMinValid(txn, OpTime(), DurableRequirement::None);
+ // Add field to minvalid document to tell us to restart initial sync if we crash
+ setInitialSyncFlag(txn);
AutoGetDb autoDb(txn, "local", MODE_X);
massert(28585, "no local database found", autoDb.getDb());
@@ -317,7 +317,7 @@ Status _initialSync() {
while (r.getHost().empty()) {
// We must prime the sync source selector so that it considers all candidates regardless
// of oplog position, by passing in null OpTime as the last op fetched time.
- r.connectToSyncSource(&txn, OpTime(), replCoord);
+ r.connectToSyncSource(&txn, OpTime(), OpTime(), replCoord);
if (r.getHost().empty()) {
std::string msg =
"no valid sync sources found in current replset to do an initial sync";
@@ -341,9 +341,6 @@ Status _initialSync() {
return Status(ErrorCodes::InitialSyncFailure, msg);
}
- // Add field to minvalid document to tell us to restart initial sync if we crash
- setInitialSyncFlag(&txn);
-
log() << "initial sync drop all databases";
dropAllDatabasesExceptLocal(&txn);
@@ -457,19 +454,10 @@ Status _initialSync() {
log() << "initial sync finishing up";
- {
- ScopedTransaction scopedXact(&txn, MODE_IX);
- AutoGetDb autodb(&txn, "local", MODE_X);
- OpTime lastOpTimeWritten(getGlobalReplicationCoordinator()->getMyLastAppliedOpTime());
- log() << "set minValid=" << lastOpTimeWritten;
-
- // Initial sync is now complete. Flag this by setting minValid to the last thing we synced.
- setMinValid(&txn, lastOpTimeWritten, DurableRequirement::None);
- BackgroundSync::get()->setInitialSyncRequestedFlag(false);
- }
-
+ // Initial sync is now complete.
// Clear the initial sync flag -- cannot be done under a db lock, or recursive.
clearInitialSyncFlag(&txn);
+ BackgroundSync::get()->setInitialSyncRequestedFlag(false);
// Clear maint. mode.
while (replCoord->getMaintenanceMode()) {
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index d52032cb6a4..c05c5a7d69c 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -62,6 +62,7 @@
#include "mongo/db/repl/roll_back_local_operations.h"
#include "mongo/db/repl/rollback_source.h"
#include "mongo/db/repl/rslog.h"
+#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
/* Scenarios
@@ -115,6 +116,10 @@ using std::string;
using std::pair;
namespace repl {
+
+// Failpoint which causes rollback to hang before finishing.
+MONGO_FP_DECLARE(rollbackHangBeforeFinish);
+
namespace {
class RSFatalException : public std::exception {
@@ -159,7 +164,7 @@ struct FixUpInfo {
set<string> collectionsToResyncData;
set<string> collectionsToResyncMetadata;
- Timestamp commonPoint;
+ OpTime commonPoint;
RecordId commonPointOurDiskloc;
int rbid; // remote server's current rollback sequence #
@@ -391,9 +396,12 @@ void syncFixUp(OperationContext* txn,
// we have items we are writing that aren't from a point-in-time. thus best not to come
// online until we get to that point in freshness.
+ // TODO this is still wrong because we don't record that we are in rollback, and we can't really
+ // recover.
OpTime minValid = fassertStatusOK(28774, OpTime::parseFromOplogEntry(newMinValid));
log() << "minvalid=" << minValid;
- setMinValid(txn, {OpTime{}, minValid});
+ setAppliedThrough(txn, {}); // Use top of oplog.
+ setMinValid(txn, minValid);
// any full collection resyncs required?
if (!fixUpInfo.collectionsToResyncData.empty() ||
@@ -497,8 +505,8 @@ void syncFixUp(OperationContext* txn,
} else {
OpTime minValid = fassertStatusOK(28775, OpTime::parseFromOplogEntry(newMinValid));
log() << "minvalid=" << minValid;
- const OpTime start{fixUpInfo.commonPoint, OpTime::kUninitializedTerm};
- setMinValid(txn, {start, minValid});
+ setMinValid(txn, minValid);
+ setAppliedThrough(txn, fixUpInfo.commonPoint);
}
} catch (const DBException& e) {
err = "can't get/set minvalid: ";
@@ -768,7 +776,7 @@ void syncFixUp(OperationContext* txn,
log() << "rollback 6";
// clean up oplog
- LOG(2) << "rollback truncate oplog after " << fixUpInfo.commonPoint.toStringPretty();
+ LOG(2) << "rollback truncate oplog after " << fixUpInfo.commonPoint;
{
const NamespaceString oplogNss(rsOplogName);
ScopedTransaction transaction(txn, MODE_IX);
@@ -883,6 +891,15 @@ Status _syncRollback(OperationContext* txn,
}
replCoord->incrementRollbackID();
+ if (MONGO_FAIL_POINT(rollbackHangBeforeFinish)) {
+ // This log output is used in js tests so please leave it.
+ log() << "rollback - rollbackHangBeforeFinish fail point "
+ "enabled. Blocking until fail point is disabled.";
+ while (MONGO_FAIL_POINT(rollbackHangBeforeFinish)) {
+ mongo::sleepsecs(1);
+ }
+ }
+
// Success; leave "ROLLBACK" state intact until applier thread has reloaded the new minValid.
// Otherwise, the applier could transition the node to SECONDARY with an out-of-date minValid.
return Status::OK();
diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp
index 835389c3494..3f768d79f4b 100644
--- a/src/mongo/db/repl/rs_rollback_test.cpp
+++ b/src/mongo/db/repl/rs_rollback_test.cpp
@@ -159,7 +159,8 @@ void RSRollbackTest::setUp() {
setGlobalReplicationCoordinator(_coordinator);
setOplogCollectionName();
- repl::setMinValid(_txn.get(), {OpTime{}, OpTime{}});
+ repl::setAppliedThrough(_txn.get(), OpTime{});
+ repl::setMinValid(_txn.get(), OpTime{});
}
void RSRollbackTest::tearDown() {
@@ -175,8 +176,8 @@ void RSRollbackTest::tearDown() {
void noSleep(Seconds seconds) {}
TEST_F(RSRollbackTest, InconsistentMinValid) {
- repl::setMinValid(_txn.get(),
- {OpTime(Timestamp(Seconds(0), 0), 0), OpTime(Timestamp(Seconds(1), 0), 0)});
+ repl::setAppliedThrough(_txn.get(), OpTime(Timestamp(Seconds(0), 0), 0));
+ repl::setMinValid(_txn.get(), OpTime(Timestamp(Seconds(1), 0), 0));
auto status = syncRollback(_txn.get(),
OplogInterfaceMock(kEmptyMockOperations),
RollbackSourceMock(std::unique_ptr<OplogInterface>(
diff --git a/src/mongo/db/repl/rs_sync.cpp b/src/mongo/db/repl/rs_sync.cpp
index 9e5c0e7e344..f86fe64135a 100644
--- a/src/mongo/db/repl/rs_sync.cpp
+++ b/src/mongo/db/repl/rs_sync.cpp
@@ -50,6 +50,7 @@
#include "mongo/db/repl/repl_settings.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/repl/rs_initialsync.h"
+#include "mongo/db/repl/storage_interface_impl.h"
#include "mongo/db/repl/sync_tail.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/stats/timer_stats.h"
@@ -118,7 +119,8 @@ void runSyncThread() {
/* we have some data. continue tailing. */
SyncTail tail(BackgroundSync::get(), multiSyncApply);
- tail.oplogApplication();
+ StorageInterfaceImpl storageInterface;
+ tail.oplogApplication(&storageInterface);
} catch (...) {
std::terminate();
}
diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h
index 1f9d0576741..eeb3fedecb2 100644
--- a/src/mongo/db/repl/storage_interface.h
+++ b/src/mongo/db/repl/storage_interface.h
@@ -29,6 +29,8 @@
#pragma once
+#include "mongo/base/status_with.h"
+#include "mongo/db/namespace_string.h"
namespace mongo {
@@ -49,6 +51,15 @@ public:
*/
virtual OperationContext* createOperationContext() = 0;
+ /**
+ * Returns the configured maximum size of the oplog.
+ *
+ * Implementations are allowed to be "fuzzy" and delete documents when the actual size is
+ * slightly above or below this, so callers should not rely on its exact value.
+ */
+ virtual StatusWith<size_t> getOplogMaxSize(OperationContext* txn,
+ const NamespaceString& nss) = 0;
+
protected:
StorageInterface();
};
diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp
index a58f85964b4..8a54a5897d3 100644
--- a/src/mongo/db/repl/storage_interface_impl.cpp
+++ b/src/mongo/db/repl/storage_interface_impl.cpp
@@ -33,7 +33,10 @@
#include "mongo/db/repl/storage_interface_impl.h"
#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/collection_catalog_entry.h"
#include "mongo/db/client.h"
+#include "mongo/db/db_raii.h"
#include "mongo/db/operation_context_impl.h"
namespace mongo {
@@ -50,5 +53,20 @@ OperationContext* StorageInterfaceImpl::createOperationContext() {
return new OperationContextImpl();
}
+StatusWith<size_t> StorageInterfaceImpl::getOplogMaxSize(OperationContext* txn,
+ const NamespaceString& nss) {
+ AutoGetCollectionForRead collection(txn, nss);
+ if (!collection.getCollection()) {
+ return {ErrorCodes::NamespaceNotFound,
+ str::stream() << "Your oplog doesn't exist: " << nss.ns()};
+ }
+
+ const auto options = collection.getCollection()->getCatalogEntry()->getCollectionOptions(txn);
+ if (!options.capped)
+ return {ErrorCodes::BadValue, str::stream() << nss.ns() << " isn't capped"};
+
+ return options.cappedSize;
+}
+
} // namespace repl
} // namespace mongo
diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h
index fa378e537fd..af4ca8029ce 100644
--- a/src/mongo/db/repl/storage_interface_impl.h
+++ b/src/mongo/db/repl/storage_interface_impl.h
@@ -43,6 +43,7 @@ public:
virtual ~StorageInterfaceImpl();
OperationContext* createOperationContext() override;
+ StatusWith<size_t> getOplogMaxSize(OperationContext* txn, const NamespaceString& nss) override;
};
} // namespace repl
diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h
index 8ce76adb642..a26196f667b 100644
--- a/src/mongo/db/repl/storage_interface_mock.h
+++ b/src/mongo/db/repl/storage_interface_mock.h
@@ -43,6 +43,10 @@ public:
virtual ~StorageInterfaceMock();
OperationContext* createOperationContext() override;
+
+ StatusWith<size_t> getOplogMaxSize(OperationContext* txn, const NamespaceString& nss) override {
+ return 1024 * 1024 * 1024;
+ }
};
} // namespace repl
diff --git a/src/mongo/db/repl/sync_tail.cpp b/src/mongo/db/repl/sync_tail.cpp
index 50517c21bf2..a336c8de68d 100644
--- a/src/mongo/db/repl/sync_tail.cpp
+++ b/src/mongo/db/repl/sync_tail.cpp
@@ -108,6 +108,11 @@ static Counter64 opsAppliedStats;
// The oplog entries applied
static ServerStatusMetricField<Counter64> displayOpsApplied("repl.apply.ops", &opsAppliedStats);
+// Number of times we tried to go live as a secondary.
+static Counter64 attemptsToBecomeSecondary;
+static ServerStatusMetricField<Counter64> displayAttemptsToBecomeSecondary(
+ "repl.apply.attemptsToBecomeSecondary", &attemptsToBecomeSecondary);
+
MONGO_FP_DECLARE(rsSyncApplyStop);
// Number and time of each ApplyOps worker pool round
@@ -130,39 +135,6 @@ bool isCrudOpType(const char* field) {
}
return false;
}
-
-void handleSlaveDelay(const Timestamp& ts) {
- ReplicationCoordinator* replCoord = getGlobalReplicationCoordinator();
- int slaveDelaySecs = durationCount<Seconds>(replCoord->getSlaveDelaySecs());
-
- // ignore slaveDelay if the box is still initializing. once
- // it becomes secondary we can worry about it.
- if (slaveDelaySecs > 0 && replCoord->getMemberState().secondary()) {
- long long a = ts.getSecs();
- long long b = time(0);
- long long lag = b - a;
- long long sleeptime = slaveDelaySecs - lag;
- if (sleeptime > 0) {
- uassert(12000,
- "rs slaveDelay differential too big check clocks and systems",
- sleeptime < 0x40000000);
- if (sleeptime < 60) {
- sleepsecs((int)sleeptime);
- } else {
- warning() << "slavedelay causing a long sleep of " << sleeptime << " seconds";
- // sleep(hours) would prevent reconfigs from taking effect & such!
- long long waitUntil = b + sleeptime;
- while (time(0) < waitUntil) {
- sleepsecs(6);
-
- // Handle reconfigs that changed the slave delay
- if (durationCount<Seconds>(replCoord->getSlaveDelaySecs()) != slaveDelaySecs)
- break;
- }
- }
- }
- } // endif slaveDelay
-}
}
namespace {
@@ -528,9 +500,7 @@ void fillWriterVectors(OperationContext* txn,
// Applies a batch of oplog entries, by using a set of threads to apply the operations and then
// writes the oplog entries to the local oplog.
-OpTime SyncTail::multiApply(OperationContext* txn,
- const OpQueue& ops,
- boost::optional<BatchBoundaries> boundaries) {
+OpTime SyncTail::multiApply(OperationContext* txn, const OpQueue& ops) {
invariant(_applyFunc);
if (getGlobalServiceContext()->getGlobalStorageEngine()->isMmapV1()) {
@@ -561,12 +531,8 @@ OpTime SyncTail::multiApply(OperationContext* txn,
fassertFailed(28527);
}
- if (boundaries) {
- setMinValid(txn, *boundaries); // Mark us as in the middle of a batch.
- }
-
- applyOps(writerVectors, &_writerPool, _applyFunc, this);
-
+ // Since we write the oplog from a single thread in-order, we don't need to use the
+ // oplogDeleteFromPoint.
OpTime lastOpTime;
{
ON_BLOCK_EXIT([&] { _writerPool.join(); });
@@ -578,25 +544,28 @@ OpTime SyncTail::multiApply(OperationContext* txn,
lastOpTime = writeOpsToOplog(txn, raws);
}
+ setMinValidToAtLeast(txn, lastOpTime); // Mark us as in the middle of a batch.
+
+ applyOps(writerVectors, &_writerPool, _applyFunc, this);
+ _writerPool.join();
+
// Due to SERVER-24933 we can't enter inShutdown while holding the PBWM lock.
invariant(!inShutdownStrict());
- if (boundaries) {
- setMinValid(txn, boundaries->end, DurableRequirement::None); // Mark batch as complete.
- }
+ setAppliedThrough(txn, lastOpTime); // Mark batch as complete.
return lastOpTime;
}
namespace {
-void tryToGoLiveAsASecondary(OperationContext* txn,
- ReplicationCoordinator* replCoord,
- const BatchBoundaries& minValidBoundaries,
- const OpTime& lastWriteOpTime) {
+void tryToGoLiveAsASecondary(OperationContext* txn, ReplicationCoordinator* replCoord) {
if (replCoord->isInPrimaryOrSecondaryState()) {
return;
}
+ // This needs to happen after the attempt so readers can be sure we've already tried.
+ ON_BLOCK_EXIT([] { attemptsToBecomeSecondary.increment(); });
+
ScopedTransaction transaction(txn, MODE_S);
Lock::GlobalRead readLock(txn->lockState());
@@ -611,15 +580,8 @@ void tryToGoLiveAsASecondary(OperationContext* txn,
return;
}
- // If an apply batch is active then we cannot transition.
- if (!minValidBoundaries.start.isNull()) {
- return;
- }
-
- // Must have applied/written to minvalid, so return if not.
- // -- If 'lastWriteOpTime' is null/uninitialized then we can't transition.
- // -- If 'lastWriteOpTime' is less than the end of the last batch then we can't transition.
- if (lastWriteOpTime.isNull() || minValidBoundaries.end > lastWriteOpTime) {
+ // We can't go to SECONDARY until we reach minvalid.
+ if (replCoord->getMyLastAppliedOpTime() < getMinValid(txn)) {
return;
}
@@ -635,7 +597,8 @@ class SyncTail::OpQueueBatcher {
MONGO_DISALLOW_COPYING(OpQueueBatcher);
public:
- explicit OpQueueBatcher(SyncTail* syncTail) : _syncTail(syncTail), _thread([&] { run(); }) {}
+ explicit OpQueueBatcher(SyncTail* syncTail, StorageInterface* storageInterface)
+ : _syncTail(syncTail), _storageInterface(storageInterface), _thread([&] { run(); }) {}
~OpQueueBatcher() {
_inShutdown.store(true);
_cv.notify_all();
@@ -663,42 +626,23 @@ private:
OperationContextImpl txn;
auto replCoord = ReplicationCoordinator::get(&txn);
+ const auto oplogMaxSize = fassertStatusOK(
+ 40301, _storageInterface->getOplogMaxSize(&txn, NamespaceString(rsOplogName)));
+
+ // Batches are limited to 10% of the oplog.
+ BatchLimits batchLimits;
+ batchLimits.ops = replBatchLimitOperations;
+ batchLimits.bytes = std::min(oplogMaxSize / 10, size_t(replBatchLimitBytes));
while (!_inShutdown.load()) {
- Timer batchTimer;
+ const auto slaveDelay = replCoord->getSlaveDelaySecs();
+ batchLimits.slaveDelayLatestTimestamp = (slaveDelay > Seconds(0))
+ ? (Date_t::now() - slaveDelay)
+ : boost::optional<Date_t>();
OpQueue ops;
- // tryPopAndWaitForMore returns true when we need to end a batch early
- while (!_syncTail->tryPopAndWaitForMore(&txn, &ops) &&
- (ops.getSize() < replBatchLimitBytes) && !_inShutdown.load()) {
- int now = batchTimer.seconds();
-
- // apply replication batch limits
- if (!ops.empty()) {
- if (now > replBatchLimitSeconds)
- break;
- if (ops.getDeque().size() > replBatchLimitOperations)
- break;
- }
-
- const int slaveDelaySecs = durationCount<Seconds>(replCoord->getSlaveDelaySecs());
- if (!ops.empty() && slaveDelaySecs > 0) {
- const BSONObj lastOp = ops.back().raw;
- const unsigned int opTimestampSecs = lastOp["ts"].timestamp().getSecs();
-
- // Stop the batch as the lastOp is too new to be applied. If we continue
- // on, we can get ops that are way ahead of the delay and this will
- // make this thread sleep longer when handleSlaveDelay is called
- // and apply ops much sooner than we like.
- if (opTimestampSecs > static_cast<unsigned int>(time(0) - slaveDelaySecs)) {
- break;
- }
- }
-
- if (MONGO_FAIL_POINT(rsSyncApplyStop)) {
- break;
- }
-
- // keep fetching more ops as long as we haven't filled up a full batch yet
+ // tryPopAndWaitForMore adds to ops and returns true when we need to end a batch early.
+ while (!_inShutdown.load() &&
+ !_syncTail->tryPopAndWaitForMore(&txn, &ops, batchLimits)) {
}
// For pausing replication in tests
@@ -706,6 +650,10 @@ private:
sleepmillis(0);
}
+ if (ops.empty()) {
+ continue; // Don't emit empty batches.
+ }
+
stdx::unique_lock<stdx::mutex> lk(_mutex);
while (!_ops.empty()) {
// Block until the previous batch has been taken.
@@ -720,6 +668,7 @@ private:
AtomicWord<bool> _inShutdown;
SyncTail* const _syncTail;
+ StorageInterface* const _storageInterface;
stdx::mutex _mutex; // Guards _ops.
stdx::condition_variable _cv;
@@ -729,8 +678,8 @@ private:
};
/* tail an oplog. ok to return, will be re-called. */
-void SyncTail::oplogApplication() {
- OpQueueBatcher batcher(this);
+void SyncTail::oplogApplication(StorageInterface* storageInterface) {
+ OpQueueBatcher batcher(this, storageInterface);
OperationContextImpl txn;
auto replCoord = ReplicationCoordinator::get(&txn);
@@ -739,9 +688,6 @@ void SyncTail::oplogApplication() {
? new ApplyBatchFinalizerForJournal(replCoord)
: new ApplyBatchFinalizer(replCoord)};
- auto minValidBoundaries = getMinValid(&txn);
- OpTime originalEndOpTime(minValidBoundaries.end);
- OpTime lastWriteOpTime{replCoord->getMyLastAppliedOpTime()};
while (!inShutdown()) {
OpQueue ops;
@@ -751,7 +697,7 @@ void SyncTail::oplogApplication() {
return;
}
- tryToGoLiveAsASecondary(&txn, replCoord, minValidBoundaries, lastWriteOpTime);
+ tryToGoLiveAsASecondary(&txn, replCoord);
// Blocks up to a second waiting for a batch to be ready to apply. If one doesn't become
// ready in time, we'll loop again so we can do the above checks periodically.
@@ -763,9 +709,7 @@ void SyncTail::oplogApplication() {
invariant(!ops.empty());
- const BSONObj lastOp = ops.back().raw;
-
- if (lastOp.isEmpty()) {
+ if (ops.front().raw.isEmpty()) {
// This means that the network thread has coalesced and we have processed all of its
// data.
invariant(ops.getDeque().size() == 1);
@@ -773,65 +717,40 @@ void SyncTail::oplogApplication() {
replCoord->signalDrainComplete(&txn);
}
- // Reset some values when triggered in case it was from a rollback.
- minValidBoundaries = getMinValid(&txn);
- lastWriteOpTime = replCoord->getMyLastAppliedOpTime();
- originalEndOpTime = minValidBoundaries.end;
-
continue; // This wasn't a real op. Don't try to apply it.
}
- const auto lastOpTime = fassertStatusOK(28773, OpTime::parseFromOplogEntry(lastOp));
- // TODO: Make ">=" once SERVER-21988 is fixed.
- if (lastWriteOpTime > lastOpTime) {
- // Error for the oplog to go back in time.
+ // Extract some info from ops that we'll need after releasing the batch below.
+ const auto firstOpTimeInBatch =
+ fassertStatusOK(40299, OpTime::parseFromOplogEntry(ops.front().raw));
+ const auto lastOpTimeInBatch =
+ fassertStatusOK(28773, OpTime::parseFromOplogEntry(ops.back().raw));
+
+ // Make sure the oplog doesn't go back in time or repeat an entry.
+ if (firstOpTimeInBatch <= replCoord->getMyLastAppliedOpTime()) {
fassert(34361,
Status(ErrorCodes::OplogOutOfOrder,
- str::stream() << "Attempted to apply an earlier oplog entry (ts: "
- << lastOpTime.getTimestamp().toStringPretty()
- << ") when our lastWrittenOptime was "
- << lastWriteOpTime.toString()));
+ str::stream() << "Attempted to apply an oplog entry ("
+ << firstOpTimeInBatch.toString()
+ << ") which is not greater than our last applied OpTime ("
+ << replCoord->getMyLastAppliedOpTime().toString()
+ << ")."));
}
- handleSlaveDelay(lastOpTime.getTimestamp());
-
- // Set minValid to the last OpTime that needs to be applied, in this batch or from the
- // (last) failed batch, whichever is larger.
- // This will cause this node to go into RECOVERING state
- // if we should crash and restart before updating finishing.
- minValidBoundaries.start = OpTime(getLastSetTimestamp(), OpTime::kUninitializedTerm);
-
-
- // Take the max of the first endOptime (if we recovered) and the end of our batch.
-
- // Setting end to the max of originalEndOpTime and lastOpTime (the end of the batch)
- // ensures that we keep pushing out the point where we can become consistent
- // and allow reads. If we recover and end up doing smaller batches we must pass the
- // originalEndOpTime before we are good.
- //
- // For example:
- // batch apply, 20-40, end = 40
- // batch failure,
- // restart
- // batch apply, 20-25, end = max(25, 40) = 40
- // batch apply, 25-45, end = 45
- minValidBoundaries.end = std::max(originalEndOpTime, lastOpTime);
-
-
- lastWriteOpTime = multiApply(&txn, ops, minValidBoundaries);
- if (lastWriteOpTime.isNull()) {
+ const bool fail = multiApply(&txn, ops).isNull();
+ if (fail) {
// fassert if oplog application failed for any reasons other than shutdown.
error() << "Failed to apply " << ops.getDeque().size()
- << " operations - batch start:" << minValidBoundaries.start
- << " end:" << minValidBoundaries.end;
+ << " operations - batch start:" << firstOpTimeInBatch
+ << " end:" << lastOpTimeInBatch;
fassert(34360, inShutdownStrict());
// Return without setting minvalid in the case of shutdown.
return;
}
- setNewTimestamp(lastWriteOpTime.getTimestamp());
- minValidBoundaries.start = {};
- finalizer->record(lastWriteOpTime);
+ // Update various things that care about our last applied optime.
+ setNewTimestamp(lastOpTimeInBatch.getTimestamp());
+ finalizer->record(lastOpTimeInBatch);
}
}
@@ -848,6 +767,8 @@ SyncTail::OplogEntry::OplogEntry(const BSONObj& rawInput) : raw(rawInput.getOwne
version = elem;
} else if (name == "o") {
o = elem;
+ } else if (name == "ts") {
+ ts = elem;
}
}
}
@@ -857,27 +778,62 @@ SyncTail::OplogEntry::OplogEntry(const BSONObj& rawInput) : raw(rawInput.getOwne
// Batch should end early if we encounter a command, or if
// there are no further ops in the bgsync queue to read.
// This function also blocks 1 second waiting for new ops to appear in the bgsync
-// queue. We can't block forever because there are maintenance things we need
-// to periodically check in the loop.
-bool SyncTail::tryPopAndWaitForMore(OperationContext* txn, SyncTail::OpQueue* ops) {
+// queue. We don't block forever so that we can periodically check for things like shutdown or
+// reconfigs.
+bool SyncTail::tryPopAndWaitForMore(OperationContext* txn,
+ SyncTail::OpQueue* ops,
+ const BatchLimits& limits) {
BSONObj op;
// Check to see if there are ops waiting in the bgsync queue
bool peek_success = peek(&op);
if (!peek_success) {
- // if we don't have anything in the queue, wait a bit for something to appear
+ // If we don't have anything in the queue, wait a bit for something to appear.
if (ops->empty()) {
- // block up to 1 second
+ // Block up to 1 second. We still return true in this case because we want this
+ // op to be the first in a new batch with a new start time.
_networkQueue->waitForMore();
- return false;
}
- // otherwise, apply what we have
return true;
}
+ // If this op would put us over the byte limit don't include it unless the batch is empty.
+ // We allow single-op batches to exceed the byte limit so that large ops are able to be
+ // processed.
+ if (!ops->empty() && (ops->getSize() + size_t(op.objsize())) > limits.bytes) {
+ return true; // Return before wasting time parsing the op.
+ }
+
auto entry = OplogEntry(op);
+ if (!entry.raw.isEmpty()) {
+ // check for oplog version change
+ int curVersion = 0;
+ if (entry.version.eoo()) {
+ // missing version means version 1
+ curVersion = 1;
+ } else {
+ curVersion = entry.version.Int();
+ }
+
+ if (curVersion != OPLOG_VERSION) {
+ severe() << "expected oplog version " << OPLOG_VERSION << " but found version "
+ << curVersion << " in oplog entry: " << op;
+ fassertFailedNoTrace(18820);
+ }
+ }
+
+ if (limits.slaveDelayLatestTimestamp &&
+ entry.ts.timestampTime() > *limits.slaveDelayLatestTimestamp) {
+ if (ops->empty()) {
+ // Sleep if we've got nothing to do. Only sleep for 1 second at a time to allow
+ // reconfigs and shutdown to occur.
+ sleepsecs(1);
+ }
+ return true;
+ }
+
// Check for ops that must be processed one at a time.
if (entry.raw.isEmpty() || // sentinel that network queue is drained.
(entry.opType[0] == 'c') || // commands.
@@ -894,26 +850,12 @@ bool SyncTail::tryPopAndWaitForMore(OperationContext* txn, SyncTail::OpQueue* op
return true;
}
- // check for oplog version change
- int curVersion = 0;
- if (entry.version.eoo())
- // missing version means version 1
- curVersion = 1;
- else
- curVersion = entry.version.Int();
-
- if (curVersion != OPLOG_VERSION) {
- severe() << "expected oplog version " << OPLOG_VERSION << " but found version "
- << curVersion << " in oplog entry: " << op;
- fassertFailedNoTrace(18820);
- }
-
// Copy the op to the deque and remove it from the bgsync queue.
ops->push_back(std::move(entry));
_networkQueue->consume();
- // Go back for more ops
- return false;
+ // Go back for more ops, unless we've hit the limit.
+ return ops->getDeque().size() >= limits.ops;
}
void SyncTail::setHostname(const std::string& hostname) {
diff --git a/src/mongo/db/repl/sync_tail.h b/src/mongo/db/repl/sync_tail.h
index c6c62b66276..313a16bf48b 100644
--- a/src/mongo/db/repl/sync_tail.h
+++ b/src/mongo/db/repl/sync_tail.h
@@ -34,6 +34,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/db/repl/minvalid.h"
+#include "mongo/db/repl/storage_interface.h"
#include "mongo/db/storage/mmap_v1/dur.h"
#include "mongo/stdx/functional.h"
#include "mongo/util/concurrency/old_thread_pool.h"
@@ -94,7 +95,7 @@ public:
static Status syncApply(OperationContext* txn, const BSONObj& o, bool convertUpdateToUpsert);
- void oplogApplication();
+ void oplogApplication(StorageInterface* storageInterface);
bool peek(BSONObj* obj);
/**
@@ -117,6 +118,7 @@ public:
BSONElement version;
BSONElement o;
BSONElement o2;
+ BSONElement ts;
};
class OpQueue {
@@ -141,14 +143,34 @@ public:
return _deque.back();
}
+ const OplogEntry& front() const {
+ invariant(!_deque.empty());
+ return _deque.front();
+ }
+
private:
std::deque<OplogEntry> _deque;
size_t _size;
};
- // returns true if we should continue waiting for BSONObjs, false if we should
- // stop waiting and apply the queue we have. Only returns false if !ops.empty().
- bool tryPopAndWaitForMore(OperationContext* txn, OpQueue* ops);
+ struct BatchLimits {
+ size_t bytes = replBatchLimitBytes;
+ size_t ops = replBatchLimitOperations;
+
+ // If provided, the batch will not include any operations with timestamps after this point.
+ // This is intended for implementing slaveDelay, so it should be some number of seconds
+ // before now.
+ boost::optional<Date_t> slaveDelayLatestTimestamp = {};
+ };
+
+ /**
+ * Attempts to pop an OplogEntry off the BGSync queue and add it to ops.
+ *
+ * Returns true if the (possibly empty) batch in ops should be ended and a new one started.
+ * If ops is empty on entry and nothing can be added yet, will wait up to a second before
+ * returning true.
+ */
+ bool tryPopAndWaitForMore(OperationContext* txn, OpQueue* ops, const BatchLimits& limits);
/**
* Fetch a single document referenced in the operation from the sync source.
@@ -172,15 +194,12 @@ protected:
// Cap the batches using the limit on journal commits.
// This works out to be 100 MB (64 bit) or 50 MB (32 bit)
static const unsigned int replBatchLimitBytes = dur::UncommittedBytesLimit;
- static const int replBatchLimitSeconds = 1;
static const unsigned int replBatchLimitOperations = 5000;
// Apply a batch of operations, using multiple threads.
// If boundries is supplied, will update minValid document at begin and end of batch.
// Returns the last OpTime applied during the apply batch, ops.end["ts"] basically.
- OpTime multiApply(OperationContext* txn,
- const OpQueue& ops,
- boost::optional<BatchBoundaries> boundaries = {});
+ OpTime multiApply(OperationContext* txn, const OpQueue& ops);
private:
class OpQueueBatcher;
diff --git a/src/mongo/db/repl/topology_coordinator.h b/src/mongo/db/repl/topology_coordinator.h
index 11b61cb4490..de01144f796 100644
--- a/src/mongo/db/repl/topology_coordinator.h
+++ b/src/mongo/db/repl/topology_coordinator.h
@@ -400,7 +400,7 @@ public:
* Considers whether or not this node should stand for election, and returns true
* if the node has transitioned to candidate role as a result of the call.
*/
- virtual bool checkShouldStandForElection(Date_t now, const OpTime& lastOpApplied) const = 0;
+ virtual Status checkShouldStandForElection(Date_t now, const OpTime& lastOpApplied) const = 0;
/**
* Set the outgoing heartbeat message from self
@@ -456,7 +456,7 @@ public:
/**
* Transitions to the candidate role if the node is electable.
*/
- virtual bool becomeCandidateIfElectable(const Date_t now, const OpTime& lastOpApplied) = 0;
+ virtual Status becomeCandidateIfElectable(const Date_t now, const OpTime& lastOpApplied) = 0;
/**
* Updates the storage engine read committed support in the TopologyCoordinator options after
diff --git a/src/mongo/db/repl/topology_coordinator_impl.cpp b/src/mongo/db/repl/topology_coordinator_impl.cpp
index 32d857be5c8..e245ddb3276 100644
--- a/src/mongo/db/repl/topology_coordinator_impl.cpp
+++ b/src/mongo/db/repl/topology_coordinator_impl.cpp
@@ -48,8 +48,9 @@
#include "mongo/db/repl/repl_set_request_votes_args.h"
#include "mongo/db/repl/replication_executor.h"
#include "mongo/db/repl/rslog.h"
-#include "mongo/s/catalog/catalog_manager.h"
+#include "mongo/db/server_parameters.h"
#include "mongo/rpc/metadata/repl_set_metadata.h"
+#include "mongo/s/catalog/catalog_manager.h"
#include "mongo/util/hex.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -62,6 +63,10 @@ using std::vector;
const Seconds TopologyCoordinatorImpl::VoteLease::leaseTime = Seconds(30);
+// Controls how caught up in replication a secondary with higher priority than the current primary
+// must be before it will call for a priority takeover election.
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(priorityTakeoverFreshnessWindowSeconds, int, 2);
+
namespace {
template <typename T>
@@ -1289,52 +1294,58 @@ HeartbeatResponseAction TopologyCoordinatorImpl::_updatePrimaryFromHBData(
return HeartbeatResponseAction::makeNoAction();
}
- // At this point, there is no primary anywhere. Check to see if we should become a
- // candidate.
- if (!checkShouldStandForElection(now, lastOpApplied)) {
+ // At this point, there is no primary anywhere. Check to see if we should become a candidate.
+ const auto status = checkShouldStandForElection(now, lastOpApplied);
+ if (!status.isOK()) {
+ // NOTE: This log line is checked in unit test(s).
+ LOG(2) << "TopologyCoordinatorImpl::_updatePrimaryFromHBData - " << status.reason();
return HeartbeatResponseAction::makeNoAction();
}
- fassert(28816, becomeCandidateIfElectable(now, lastOpApplied));
+ fassertStatusOK(28816, becomeCandidateIfElectable(now, lastOpApplied));
return HeartbeatResponseAction::makeElectAction();
}
-bool TopologyCoordinatorImpl::checkShouldStandForElection(Date_t now,
- const OpTime& lastOpApplied) const {
+Status TopologyCoordinatorImpl::checkShouldStandForElection(Date_t now,
+ const OpTime& lastOpApplied) const {
if (_currentPrimaryIndex != -1) {
- return false;
+ return {ErrorCodes::NodeNotElectable, "Not standing for election since there is a Primary"};
}
invariant(_role != Role::leader);
if (_role == Role::candidate) {
- LOG(2) << "Not standing for election again; already candidate";
- return false;
+ return {ErrorCodes::NodeNotElectable, "Not standing for election again; already candidate"};
}
const UnelectableReasonMask unelectableReason = _getMyUnelectableReason(now, lastOpApplied);
if (NotCloseEnoughToLatestOptime & unelectableReason) {
- LOG(2) << "Not standing for election because "
- << _getUnelectableReasonString(unelectableReason) << "; my last optime is "
- << lastOpApplied << " and the newest is " << _latestKnownOpTime(lastOpApplied);
- return false;
+ return {ErrorCodes::NodeNotElectable,
+ str::stream() << "Not standing for election because "
+ << _getUnelectableReasonString(unelectableReason)
+ << "; my last optime is " << lastOpApplied.toString()
+ << " and the newest is "
+ << _latestKnownOpTime(lastOpApplied).toString()};
}
if (unelectableReason) {
- LOG(2) << "Not standing for election because "
- << _getUnelectableReasonString(unelectableReason);
- return false;
+ return {ErrorCodes::NodeNotElectable,
+ str::stream() << "Not standing for election because "
+ << _getUnelectableReasonString(unelectableReason)};
}
if (_electionSleepUntil > now) {
if (_rsConfig.getProtocolVersion() == 1) {
- LOG(2) << "Not standing for election before "
- << dateToISOStringLocal(_electionSleepUntil)
- << " because I stood up or learned about a new term too recently";
+ return {
+ ErrorCodes::NodeNotElectable,
+ str::stream() << "Not standing for election before "
+ << dateToISOStringLocal(_electionSleepUntil)
+ << " because I stood up or learned about a new term too recently"};
} else {
- LOG(2) << "Not standing for election before "
- << dateToISOStringLocal(_electionSleepUntil) << " because I stood too recently";
+ return {ErrorCodes::NodeNotElectable,
+ str::stream() << "Not standing for election before "
+ << dateToISOStringLocal(_electionSleepUntil)
+ << " because I stood too recently"};
}
- return false;
}
// All checks passed. Start election proceedings.
- return true;
+ return Status::OK();
}
bool TopologyCoordinatorImpl::_aMajoritySeemsToBeUp() const {
@@ -1357,6 +1368,34 @@ bool TopologyCoordinatorImpl::_isOpTimeCloseEnoughToLatestToElect(
return otherOpTime.getSecs() + 10 >= (latestKnownOpTime.getSecs());
}
+bool TopologyCoordinatorImpl::_amIFreshEnoughForPriorityTakeover(
+ const OpTime& ourLastOpApplied) const {
+ const OpTime latestKnownOpTime = _latestKnownOpTime(ourLastOpApplied);
+
+ // Rules are:
+ // - If the terms don't match, we don't call for priority takeover.
+ // - If our optime and the latest optime happen in different seconds, our optime must be within
+ // at least priorityTakeoverFreshnessWindowSeconds seconds of the latest optime.
+ // - If our optime and the latest optime happen in the same second, our optime must be within
+ // at least 1000 oplog entries of the latest optime (i.e. the increment portion of the timestamp
+ // must be within 1000). This is to handle the case where a primary had its clock set far into
+ // the future, took some writes, then had its clock set back. In that case the timestamp
+ // component of all future oplog entries generated will be the same, until real world time
+ // passes the timestamp component of the last oplog entry.
+
+ if (ourLastOpApplied.getTerm() != latestKnownOpTime.getTerm()) {
+ return false;
+ }
+
+ if (ourLastOpApplied.getTimestamp().getSecs() != latestKnownOpTime.getTimestamp().getSecs()) {
+ return ourLastOpApplied.getTimestamp().getSecs() + priorityTakeoverFreshnessWindowSeconds >=
+ latestKnownOpTime.getTimestamp().getSecs();
+ } else {
+ return ourLastOpApplied.getTimestamp().getInc() + 1000 >=
+ latestKnownOpTime.getTimestamp().getInc();
+ }
+}
+
bool TopologyCoordinatorImpl::_iAmPrimary() const {
if (_role == Role::leader) {
invariant(_currentPrimaryIndex == _selfIndex);
@@ -1958,8 +1997,8 @@ TopologyCoordinatorImpl::UnelectableReasonMask TopologyCoordinatorImpl::_getMyUn
result |= NotSecondary;
}
- // Election rules only for protocol version 0.
if (_rsConfig.getProtocolVersion() == 0) {
+ // Election rules only for protocol version 0.
if (_voteLease.whoId != -1 &&
_voteLease.whoId != _rsConfig.getMemberAt(_selfIndex).getId() &&
_voteLease.when + VoteLease::leaseTime >= now) {
@@ -1968,6 +2007,13 @@ TopologyCoordinatorImpl::UnelectableReasonMask TopologyCoordinatorImpl::_getMyUn
if (!_isOpTimeCloseEnoughToLatestToElect(lastApplied, lastApplied)) {
result |= NotCloseEnoughToLatestOptime;
}
+ } else {
+ // Election rules only for protocol version 1.
+ invariant(_rsConfig.getProtocolVersion() == 1);
+ bool isPriorityTakeover = _currentPrimaryIndex != -1;
+ if (isPriorityTakeover && !_amIFreshEnoughForPriorityTakeover(lastApplied)) {
+ result |= NotCloseEnoughToLatestForPriorityTakeover;
+ }
}
return result;
}
@@ -2031,6 +2077,14 @@ std::string TopologyCoordinatorImpl::_getUnelectableReasonString(
hasWrittenToStream = true;
ss << "member is more than 10 seconds behind the most up-to-date member";
}
+ if (ur & NotCloseEnoughToLatestForPriorityTakeover) {
+ if (hasWrittenToStream) {
+ ss << "; ";
+ }
+ hasWrittenToStream = true;
+ ss << "member is not caught up enough to the most up-to-date member to call for priority "
+ "takeover - must be within " << priorityTakeoverFreshnessWindowSeconds << " seconds";
+ }
if (ur & NotInitialized) {
if (hasWrittenToStream) {
ss << "; ";
@@ -2432,29 +2486,27 @@ void TopologyCoordinatorImpl::setPrimaryIndex(long long primaryIndex) {
_currentPrimaryIndex = primaryIndex;
}
-bool TopologyCoordinatorImpl::becomeCandidateIfElectable(const Date_t now,
- const OpTime& lastOpApplied) {
+Status TopologyCoordinatorImpl::becomeCandidateIfElectable(const Date_t now,
+ const OpTime& lastOpApplied) {
if (_role == Role::leader) {
- LOG(2) << "Not standing for election again; already primary";
- return false;
+ return {ErrorCodes::NodeNotElectable, "Not standing for election again; already primary"};
}
if (_role == Role::candidate) {
- LOG(2) << "Not standing for election again; already candidate";
- return false;
+ return {ErrorCodes::NodeNotElectable, "Not standing for election again; already candidate"};
}
const UnelectableReasonMask unelectableReason = _getMyUnelectableReason(now, lastOpApplied);
if (unelectableReason) {
- LOG(2) << "Not standing for election because "
- << _getUnelectableReasonString(unelectableReason);
- return false;
+ return {ErrorCodes::NodeNotElectable,
+ str::stream() << "Not standing for election because "
+ << _getUnelectableReasonString(unelectableReason)};
}
// All checks passed, become a candidate and start election proceedings.
_role = Role::candidate;
- return true;
+ return Status::OK();
}
void TopologyCoordinatorImpl::setStorageEngineSupportsReadCommitted(bool supported) {
diff --git a/src/mongo/db/repl/topology_coordinator_impl.h b/src/mongo/db/repl/topology_coordinator_impl.h
index 9c8fad87bfe..fb4b7786cb3 100644
--- a/src/mongo/db/repl/topology_coordinator_impl.h
+++ b/src/mongo/db/repl/topology_coordinator_impl.h
@@ -217,7 +217,7 @@ public:
virtual void setElectionInfo(OID electionId, Timestamp electionOpTime);
virtual void processWinElection(OID electionId, Timestamp electionOpTime);
virtual void processLoseElection();
- virtual bool checkShouldStandForElection(Date_t now, const OpTime& lastOpApplied) const;
+ virtual Status checkShouldStandForElection(Date_t now, const OpTime& lastOpApplied) const;
virtual void setMyHeartbeatMessage(const Date_t now, const std::string& message);
virtual bool stepDown(Date_t until, bool force, const OpTime& lastOpApplied);
virtual bool stepDownIfPending();
@@ -238,7 +238,7 @@ public:
virtual HeartbeatResponseAction setMemberAsDown(Date_t now,
const int memberIndex,
const OpTime& myLastOpApplied);
- virtual bool becomeCandidateIfElectable(const Date_t now, const OpTime& lastOpApplied);
+ virtual Status becomeCandidateIfElectable(const Date_t now, const OpTime& lastOpApplied);
virtual void setStorageEngineSupportsReadCommitted(bool supported);
////////////////////////////////////////////////////////////
@@ -280,7 +280,8 @@ private:
NoData = 1 << 6,
NotInitialized = 1 << 7,
VotedTooRecently = 1 << 8,
- RefusesToStand = 1 << 9
+ RefusesToStand = 1 << 9,
+ NotCloseEnoughToLatestForPriorityTakeover = 1 << 10,
};
typedef int UnelectableReasonMask;
@@ -309,6 +310,9 @@ private:
bool _isOpTimeCloseEnoughToLatestToElect(const OpTime& otherOpTime,
const OpTime& ourLastOpApplied) const;
+ // Is our optime close enough to the latest known optime to call for a priority takeover.
+ bool _amIFreshEnoughForPriorityTakeover(const OpTime& ourLastOpApplied) const;
+
// Returns reason why "self" member is unelectable
UnelectableReasonMask _getMyUnelectableReason(const Date_t now,
const OpTime& lastOpApplied) const;
diff --git a/src/mongo/db/repl/topology_coordinator_impl_test.cpp b/src/mongo/db/repl/topology_coordinator_impl_test.cpp
index 5a73906d164..8d3ae2b6516 100644
--- a/src/mongo/db/repl/topology_coordinator_impl_test.cpp
+++ b/src/mongo/db/repl/topology_coordinator_impl_test.cpp
@@ -5236,7 +5236,9 @@ TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileAwareOfPrimary) {
heartbeatFromMember(
HostAndPort("h2"), "rs0", MemberState::RS_PRIMARY, OpTime(Timestamp(1, 0), 0));
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime()));
+ const auto status = getTopoCoord().checkShouldStandForElection(now()++, OpTime());
+ ASSERT_EQ(ErrorCodes::NodeNotElectable, status);
+ ASSERT_STRING_CONTAINS(status.reason(), "there is a Primary");
}
TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileTooStale) {
@@ -5253,7 +5255,10 @@ TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileTooStale) {
heartbeatFromMember(
HostAndPort("h2"), "rs0", MemberState::RS_SECONDARY, OpTime(Timestamp(10000, 0), 0));
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(100, 0), 0)));
+ const auto status =
+ getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(100, 0), 0));
+ ASSERT_EQ(ErrorCodes::NodeNotElectable, status);
+ ASSERT_STRING_CONTAINS(status.reason(), "my last optime is");
}
TEST_F(TopoCoordTest, VoteForMyselfFailsWhileNotCandidate) {
@@ -5285,12 +5290,10 @@ TEST_F(TopoCoordTest, NodeReturnsArbiterWhenGetMemberStateRunsAgainstArbiter) {
}
TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileRemovedFromTheConfig) {
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(3));
- startCapturingLogMessages();
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0)));
- stopCapturingLogMessages();
- ASSERT_EQUALS(1, countLogLinesContaining("not a member of a valid replica set config"));
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Log());
+ const auto status =
+ getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0));
+ ASSERT_EQ(ErrorCodes::NodeNotElectable, status);
+ ASSERT_STRING_CONTAINS(status.reason(), "not a member of a valid replica set config");
}
TEST_F(TopoCoordTest, ShouldNotStandForElectionWhenAPositiveResponseWasGivenInTheVoteLeasePeriod) {
@@ -5327,12 +5330,10 @@ TEST_F(TopoCoordTest, ShouldNotStandForElectionWhenAPositiveResponseWasGivenInTh
ASSERT_EQUALS(1, response["vote"].Int());
ASSERT_EQUALS(remoteRound, response["round"].OID());
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(3));
- startCapturingLogMessages();
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0)));
- stopCapturingLogMessages();
- ASSERT_EQUALS(1, countLogLinesContaining("I recently voted for "));
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Log());
+ const auto status =
+ getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0));
+ ASSERT_EQ(ErrorCodes::NodeNotElectable, status);
+ ASSERT_STRING_CONTAINS(status.reason(), "I recently voted for ");
}
TEST_F(TopoCoordTest, NodeDoesNotGrantVotesToTwoDifferentNodesInTheSameTerm) {
diff --git a/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp b/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp
index c8e533b1f86..265971f32d1 100644
--- a/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp
+++ b/src/mongo/db/repl/topology_coordinator_impl_v1_test.cpp
@@ -2069,7 +2069,7 @@ TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileAwareOfPrimary) {
heartbeatFromMember(
HostAndPort("h2"), "rs0", MemberState::RS_PRIMARY, OpTime(Timestamp(1, 0), 0));
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime()));
+ ASSERT_NOT_OK(getTopoCoord().checkShouldStandForElection(now()++, OpTime()));
}
TEST_F(TopoCoordTest, ShouldStandForElectionDespiteNotCloseEnoughToLastOptime) {
@@ -2086,7 +2086,7 @@ TEST_F(TopoCoordTest, ShouldStandForElectionDespiteNotCloseEnoughToLastOptime) {
heartbeatFromMember(
HostAndPort("h2"), "rs0", MemberState::RS_SECONDARY, OpTime(Timestamp(10000, 0), 0));
- ASSERT_TRUE(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(100, 0), 0)));
+ ASSERT_OK(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(100, 0), 0)));
}
TEST_F(TopoCoordTest, VoteForMyselfFailsWhileNotCandidate) {
@@ -2118,12 +2118,10 @@ TEST_F(TopoCoordTest, NodeReturnsArbiterWhenGetMemberStateRunsAgainstArbiter) {
}
TEST_F(TopoCoordTest, ShouldNotStandForElectionWhileRemovedFromTheConfig) {
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(3));
- startCapturingLogMessages();
- ASSERT_FALSE(getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0)));
- stopCapturingLogMessages();
- ASSERT_EQUALS(1, countLogLinesContaining("not a member of a valid replica set config"));
- logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Log());
+ const auto status =
+ getTopoCoord().checkShouldStandForElection(now()++, OpTime(Timestamp(10, 0), 0));
+ ASSERT_NOT_OK(status);
+ ASSERT_STRING_CONTAINS(status.reason(), "not a member of a valid replica set config");
}
TEST_F(TopoCoordTest, NodeDoesNotGrantVotesToTwoDifferentNodesInTheSameTerm) {
@@ -3392,7 +3390,7 @@ TEST_F(HeartbeatResponseTestV1,
ASSERT_NO_ACTION(nextAction.getAction());
ASSERT_TRUE(TopologyCoordinator::Role::follower == getTopoCoord().getRole());
// We are electable now.
- ASSERT_TRUE(getTopoCoord().becomeCandidateIfElectable(now(), lastOpTimeApplied));
+ ASSERT_OK(getTopoCoord().becomeCandidateIfElectable(now(), lastOpTimeApplied));
ASSERT_TRUE(TopologyCoordinator::Role::candidate == getTopoCoord().getRole());
}
@@ -3424,7 +3422,7 @@ TEST_F(HeartbeatResponseTestV1, ScheduleElectionWhenPrimaryIsMarkedDownAndWeAreE
ASSERT_EQUALS(-1, getCurrentPrimaryIndex());
ASSERT_TRUE(TopologyCoordinator::Role::follower == getTopoCoord().getRole());
// We are electable now.
- ASSERT_TRUE(getTopoCoord().becomeCandidateIfElectable(now(), lastOpTimeApplied));
+ ASSERT_OK(getTopoCoord().becomeCandidateIfElectable(now(), lastOpTimeApplied));
ASSERT_TRUE(TopologyCoordinator::Role::candidate == getTopoCoord().getRole());
}
diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp
index 5a0f2795a3d..96dd8de9b68 100644
--- a/src/mongo/db/s/migration_source_manager.cpp
+++ b/src/mongo/db/s/migration_source_manager.cpp
@@ -305,7 +305,8 @@ bool MigrationSourceManager::transferMods(OperationContext* txn,
long long size = 0;
{
- AutoGetCollectionForRead ctx(txn, _getNS());
+ ScopedTransaction scopedXact(txn, MODE_IS);
+ AutoGetCollection autoColl(txn, _getNS(), MODE_IS);
stdx::lock_guard<stdx::mutex> sl(_mutex);
@@ -324,9 +325,13 @@ bool MigrationSourceManager::transferMods(OperationContext* txn,
return false;
}
- // TODO: fix SERVER-16540 race
- _xfer(txn, _nss.ns(), ctx.getDb(), &_deleted, b, "deleted", size, false);
- _xfer(txn, _nss.ns(), ctx.getDb(), &_reload, b, "reload", size, true);
+ if (!autoColl.getCollection()) {
+ errmsg = str::stream() << "collection " << _nss.toString() << " does not exist";
+ return false;
+ }
+
+ _xfer(txn, _nss.ns(), autoColl.getDb(), &_deleted, b, "deleted", size, false);
+ _xfer(txn, _nss.ns(), autoColl.getDb(), &_reload, b, "reload", size, true);
}
b.append("size", size);
@@ -338,6 +343,7 @@ bool MigrationSourceManager::storeCurrentLocs(OperationContext* txn,
long long maxChunkSize,
string& errmsg,
BSONObjBuilder& result) {
+ ScopedTransaction scopedXact(txn, MODE_IS);
AutoGetCollection autoColl(txn, _getNS(), MODE_IS);
Collection* collection = autoColl.getCollection();
@@ -460,7 +466,6 @@ bool MigrationSourceManager::storeCurrentLocs(OperationContext* txn,
log() << "moveChunk number of documents: " << cloneLocsRemaining() << migrateLog;
- txn->recoveryUnit()->abandonSnapshot();
return true;
}
@@ -473,6 +478,7 @@ bool MigrationSourceManager::clone(OperationContext* txn,
int allocSize = 0;
{
+ ScopedTransaction scopedXact(txn, MODE_IS);
AutoGetCollection autoColl(txn, _getNS(), MODE_IS);
stdx::lock_guard<stdx::mutex> sl(_mutex);
@@ -504,6 +510,7 @@ bool MigrationSourceManager::clone(OperationContext* txn,
bool isBufferFilled = false;
BSONArrayBuilder clonedDocsArrayBuilder(allocSize);
while (!isBufferFilled) {
+ ScopedTransaction scopedXact(txn, MODE_IS);
AutoGetCollection autoColl(txn, _getNS(), MODE_IS);
stdx::lock_guard<stdx::mutex> sl(_mutex);
@@ -521,7 +528,6 @@ bool MigrationSourceManager::clone(OperationContext* txn,
return false;
}
- // TODO: fix SERVER-16540 race
Collection* collection = autoColl.getCollection();
if (!collection) {
errmsg = str::stream() << "collection " << _nss.toString() << " does not exist";
diff --git a/src/mongo/db/s/sharding_state.cpp b/src/mongo/db/s/sharding_state.cpp
index d184ef099dc..70c38aca21b 100644
--- a/src/mongo/db/s/sharding_state.cpp
+++ b/src/mongo/db/s/sharding_state.cpp
@@ -644,7 +644,6 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
shared_ptr<CollectionMetadata> remoteMetadata(std::make_shared<CollectionMetadata>());
Timer refreshTimer;
- long long refreshMillis;
{
Status status = mdLoader.makeCollectionMetadata(txn,
@@ -653,7 +652,6 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
getShardName(),
fullReload ? NULL : beforeMetadata.get(),
remoteMetadata.get());
- refreshMillis = refreshTimer.millis();
if (status.code() == ErrorCodes::NamespaceNotFound) {
remoteMetadata.reset();
@@ -794,8 +792,8 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
<< "need to retry loading metadata for " << ns
<< ", collection may have been dropped or recreated during load"
<< " (loaded shard version : " << remoteShardVersion.toString()
- << ", stored shard versions : " << localShardVersionMsg << ", took " << refreshMillis
- << "ms)";
+ << ", stored shard versions : " << localShardVersionMsg << ", took "
+ << refreshTimer.millis() << " ms)";
warning() << errMsg;
return Status(ErrorCodes::RemoteChangeDetected, errMsg);
@@ -804,7 +802,7 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
if (choice == VersionChoice::Local) {
LOG(0) << "metadata of collection " << ns
<< " already up to date (shard version : " << afterShardVersion.toString()
- << ", took " << refreshMillis << "ms)";
+ << ", took " << refreshTimer.millis() << " ms)";
return Status::OK();
}
@@ -813,20 +811,22 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
switch (installType) {
case InstallType_New:
LOG(0) << "collection " << ns << " was previously unsharded"
- << ", new metadata loaded with shard version " << remoteShardVersion;
+ << ", new metadata loaded with shard version " << remoteShardVersion << ", took "
+ << refreshTimer.millis() << " ms";
break;
case InstallType_Update:
LOG(0) << "updating metadata for " << ns << " from shard version "
- << localShardVersionMsg << " to shard version " << remoteShardVersion;
+ << localShardVersionMsg << " to shard version " << remoteShardVersion
+ << ", took " << refreshTimer.millis() << " ms";
break;
case InstallType_Replace:
LOG(0) << "replacing metadata for " << ns << " at shard version "
<< localShardVersionMsg << " with a new epoch (shard version "
- << remoteShardVersion << ")";
+ << remoteShardVersion << "), took " << refreshTimer.millis() << " ms";
break;
case InstallType_Drop:
LOG(0) << "dropping metadata for " << ns << " at shard version " << localShardVersionMsg
- << ", took " << refreshMillis << "ms";
+ << ", took " << refreshTimer.millis() << " ms";
break;
default:
verify(false);
@@ -835,7 +835,7 @@ Status ShardingState::_refreshMetadata(OperationContext* txn,
if (installType != InstallType_Drop) {
LOG(0) << "collection version was loaded at version " << remoteCollVersion << ", took "
- << refreshMillis << "ms";
+ << refreshTimer.millis() << " ms";
}
return Status::OK();
diff --git a/src/mongo/db/server_options_helpers.cpp b/src/mongo/db/server_options_helpers.cpp
index 807627ed4b8..331f285ca8c 100644
--- a/src/mongo/db/server_options_helpers.cpp
+++ b/src/mongo/db/server_options_helpers.cpp
@@ -541,6 +541,18 @@ Status validateServerOptions(const moe::Environment& params) {
if (authMechParameter != parameters.end() && authMechParameter->second.empty()) {
haveAuthenticationMechanisms = false;
}
+
+ // Make sure 'internalLookupStageBatchSize' can only be set if test commands are enabled.
+ auto lookupBatchSizeParameter = parameters.find("internalLookupStageBatchSize");
+ auto enableTestCommandsParameter = parameters.find("enableTestCommands");
+ if (lookupBatchSizeParameter != parameters.end()) {
+ if (enableTestCommandsParameter == parameters.end() ||
+ enableTestCommandsParameter->second != "1") {
+ return Status(
+ ErrorCodes::BadValue,
+ "internalLookupStageBatchSize can only be set if test commands are enabled.");
+ }
+ }
}
if ((params.count("security.authorization") &&
params["security.authorization"].as<std::string>() == "enabled") ||
diff --git a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
index 9c82309d5c9..d601702623f 100644
--- a/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
+++ b/src/mongo/db/storage/devnull/devnull_kv_engine.cpp
@@ -163,6 +163,8 @@ public:
return Status::OK();
}
+ void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const override {}
+
virtual void updateStatsAfterRepair(OperationContext* txn,
long long numRecords,
long long dataSize) {}
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
index 324a30653eb..3d1ef8f111b 100644
--- a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store.h
@@ -121,6 +121,8 @@ public:
virtual boost::optional<RecordId> oplogStartHack(OperationContext* txn,
const RecordId& startingPosition) const;
+ void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const override {}
+
virtual void updateStatsAfterRepair(OperationContext* txn,
long long numRecords,
long long dataSize) {
diff --git a/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h b/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
index a496f1ff31e..2c40bcb61ab 100644
--- a/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
+++ b/src/mongo/db/storage/mmap_v1/heap_record_store_btree.h
@@ -154,6 +154,10 @@ public:
invariant(false);
}
+ void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const override {
+ invariant(false);
+ }
+
virtual void updateStatsAfterRepair(OperationContext* txn,
long long numRecords,
long long dataSize) {
diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_base.h b/src/mongo/db/storage/mmap_v1/record_store_v1_base.h
index d34c5a9b3f0..6b7b6c1630e 100644
--- a/src/mongo/db/storage/mmap_v1/record_store_v1_base.h
+++ b/src/mongo/db/storage/mmap_v1/record_store_v1_base.h
@@ -256,6 +256,8 @@ public:
/* return which "deleted bucket" for this size object */
static int bucket(int size);
+ void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const override {}
+
virtual void updateStatsAfterRepair(OperationContext* txn,
long long numRecords,
long long dataSize) {
diff --git a/src/mongo/db/storage/record_store.h b/src/mongo/db/storage/record_store.h
index b6e973a16ab..499bcbacdd2 100644
--- a/src/mongo/db/storage/record_store.h
+++ b/src/mongo/db/storage/record_store.h
@@ -127,12 +127,11 @@ struct BsonRecord {
* IMPORTANT NOTE FOR DOCUMENT-LOCKING ENGINES: If you implement capped collections with a
* "visibility" system such that documents that exist in your snapshot but were inserted after
* the last uncommitted document are hidden, you must follow the following rules:
- * - next() must never return invisible documents.
+ * - next() on forward cursors must never return invisible documents.
* - If next() on a forward cursor hits an invisible document, it should behave as if it hit
* the end of the collection.
- * - When next() on a reverse cursor seeks to the end of the collection it must return the
- * newest visible document. This should only return boost::none if there are no visible
- * documents in the collection.
+ * - Reverse cursors must ignore the visibility filter. That means that they initially return the
+ * newest committed record in the collection and may skip over uncommitted records.
* - SeekableRecordCursor::seekExact() must ignore the visibility filter and return the requested
* document even if it is supposed to be invisible.
* TODO SERVER-18934 Handle this above the storage engine layer so storage engines don't have to
@@ -580,6 +579,15 @@ public:
}
/**
+ * Waits for all writes that completed before this call to be visible to forward scans.
+ * See the comment on RecordCursor for more details about the visibility rules.
+ *
+ * It is only legal to call this on an oplog. It is illegal to call this inside a
+ * WriteUnitOfWork.
+ */
+ virtual void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const = 0;
+
+ /**
* Called after a repair operation is run with the recomputed numRecords and dataSize.
*/
virtual void updateStatsAfterRepair(OperationContext* txn,
diff --git a/src/mongo/db/storage/record_store_test_capped_visibility.cpp b/src/mongo/db/storage/record_store_test_capped_visibility.cpp
index 1f7d7afa87c..3d1765564b5 100644
--- a/src/mongo/db/storage/record_store_test_capped_visibility.cpp
+++ b/src/mongo/db/storage/record_store_test_capped_visibility.cpp
@@ -69,9 +69,9 @@ TEST(RecordStore_CappedVisibility, EmptyInitialState) {
RecordId lowestHiddenId = doInsert(longLivedOp, rs);
- // Collection still looks empty to iteration but not seekExact.
+ // Collection still looks empty to forward iteration but not reverse or seekExact.
ASSERT(!rs->getCursor(longLivedOp.get(), true)->next());
- ASSERT(!rs->getCursor(longLivedOp.get(), false)->next());
+ ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), lowestHiddenId);
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get())->seekExact(lowestHiddenId), lowestHiddenId);
RecordId otherId;
@@ -88,21 +88,22 @@ TEST(RecordStore_CappedVisibility, EmptyInitialState) {
otherId = doInsert(txn, rs);
ASSERT(!rs->getCursor(txn.get(), true)->next());
- ASSERT(!rs->getCursor(txn.get(), false)->next());
+ ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), otherId);
ASSERT_ID_EQ(rs->getCursor(txn.get())->seekExact(otherId), otherId);
wuow.commit();
ASSERT(!rs->getCursor(txn.get(), true)->next());
- ASSERT(!rs->getCursor(txn.get(), false)->next());
+ ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), otherId);
ASSERT_ID_EQ(rs->getCursor(txn.get())->seekExact(otherId), otherId);
ASSERT(!rs->getCursor(txn.get())->seekExact(lowestHiddenId));
}
+ // longLivedOp is still on old snapshot so it can't see otherId yet.
ASSERT(!rs->getCursor(longLivedOp.get(), true)->next());
- ASSERT(!rs->getCursor(longLivedOp.get(), false)->next());
+ ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), lowestHiddenId);
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get())->seekExact(lowestHiddenId), lowestHiddenId);
- ASSERT(!rs->getCursor(longLivedOp.get())->seekExact(otherId)); // still on old snapshot.
+ ASSERT(!rs->getCursor(longLivedOp.get())->seekExact(otherId));
// This makes all documents visible and lets longLivedOp get a new snapshot.
longLivedWuow.commit();
@@ -140,7 +141,7 @@ TEST(RecordStore_CappedVisibility, NonEmptyInitialState) {
// Collection still looks like it only has a single doc to iteration but not seekExact.
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), true)->next(), initialId);
- ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), initialId);
+ ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), lowestHiddenId);
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get())->seekExact(initialId), initialId);
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get())->seekExact(lowestHiddenId), lowestHiddenId);
@@ -159,21 +160,22 @@ TEST(RecordStore_CappedVisibility, NonEmptyInitialState) {
otherId = doInsert(txn, rs);
ASSERT_ID_EQ(rs->getCursor(txn.get(), true)->next(), initialId);
- ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), initialId);
+ ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), otherId);
ASSERT_ID_EQ(rs->getCursor(txn.get())->seekExact(otherId), otherId);
wuow.commit();
ASSERT_ID_EQ(rs->getCursor(txn.get(), true)->next(), initialId);
- ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), initialId);
+ ASSERT_ID_EQ(rs->getCursor(txn.get(), false)->next(), otherId);
ASSERT_ID_EQ(rs->getCursor(txn.get())->seekExact(otherId), otherId);
ASSERT(!rs->getCursor(txn.get())->seekExact(lowestHiddenId));
}
+ // longLivedOp is still on old snapshot so it can't see otherId yet.
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), true)->next(), initialId);
- ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), initialId);
+ ASSERT_ID_EQ(rs->getCursor(longLivedOp.get(), false)->next(), lowestHiddenId);
ASSERT_ID_EQ(rs->getCursor(longLivedOp.get())->seekExact(lowestHiddenId), lowestHiddenId);
- ASSERT(!rs->getCursor(longLivedOp.get())->seekExact(otherId)); // still on old snapshot.
+ ASSERT(!rs->getCursor(longLivedOp.get())->seekExact(otherId));
// This makes all documents visible and lets longLivedOp get a new snapshot.
longLivedWuow.commit();
diff --git a/src/mongo/db/storage/snapshot_name.h b/src/mongo/db/storage/snapshot_name.h
index b4e8eabf288..5038d11c74f 100644
--- a/src/mongo/db/storage/snapshot_name.h
+++ b/src/mongo/db/storage/snapshot_name.h
@@ -31,6 +31,8 @@
#include <cstdint>
#include <limits>
+#include "mongo/util/mongoutils/str.h"
+
namespace mongo {
class SnapshotName {
@@ -58,6 +60,9 @@ public:
return _value;
}
+ std::string toString() const {
+ return (str::stream() << _value);
+ }
bool operator==(const SnapshotName& rhs) const {
return _value == rhs._value;
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index 7edd75781c5..bd6bdceb5e9 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -39,6 +39,7 @@
#include "mongo/base/checked_cast.h"
#include "mongo/bson/util/builder.h"
+#include "mongo/db/client.h"
#include "mongo/db/concurrency/locker.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/namespace_string.h"
@@ -91,7 +92,7 @@ bool shouldUseOplogHack(OperationContext* opCtx, const std::string& uri) {
} // namespace
MONGO_FP_DECLARE(WTWriteConflictException);
-MONGO_FP_DECLARE(WTEmulateOutOfOrderNextRecordId);
+MONGO_FP_DECLARE(WTPausePrimaryOplogDurabilityLoop);
const std::string kWiredTigerEngineName = "wiredTiger";
@@ -445,31 +446,7 @@ public:
WT_CURSOR* c = _cursor->get();
- bool mustAdvance = !_skipNextAdvance;
- if (_lastReturnedId.isNull() && !_forward && _rs._isCapped) {
- // In this case we need to seek to the highest visible record.
- const RecordId reverseCappedInitialSeekPoint =
- _readUntilForOplog.isNull() ? _rs.lowestCappedHiddenRecord() : _readUntilForOplog;
-
- if (!reverseCappedInitialSeekPoint.isNull()) {
- c->set_key(c, _makeKey(reverseCappedInitialSeekPoint));
- int cmp;
- int seekRet = WT_OP_CHECK(c->search_near(c, &cmp));
- if (seekRet == WT_NOTFOUND) {
- _eof = true;
- return {};
- }
- invariantWTOK(seekRet);
-
- // If we landed at or past the lowest hidden record, we must advance to be in
- // the visible range.
- mustAdvance = _rs.isCappedHidden(reverseCappedInitialSeekPoint)
- ? (cmp >= 0)
- : (cmp > 0); // No longer hidden.
- }
- }
-
- if (mustAdvance) {
+ if (!_skipNextAdvance) {
// Nothing after the next line can throw WCEs.
// Note that an unpositioned (or eof) WT_CURSOR returns the first/last entry in the
// table when you call next/prev.
@@ -486,13 +463,6 @@ public:
invariantWTOK(c->get_key(c, &key));
RecordId id = _fromKey(key);
- if (_forward && MONGO_FAIL_POINT(WTEmulateOutOfOrderNextRecordId)) {
- log() << "WTEmulateOutOfOrderNextRecordId fail point has triggerd so RecordId is now "
- "RecordId(1) instead of " << id;
- // Replace the found RecordId with a (small) fake one.
- id = RecordId{1};
- }
-
if (_forward && _lastReturnedId >= id) {
log() << "WTCursor::next -- c->next_key ( " << id
<< ") was not greater than _lastReturnedId (" << _lastReturnedId
@@ -612,6 +582,9 @@ private:
if (!_rs._isCapped)
return true;
+ if (!_forward)
+ return true;
+
if (_readUntilForOplog.isNull() || !_rs._isOplog) {
// this is the normal capped case
return !_rs.isCappedHidden(id);
@@ -866,11 +839,18 @@ WiredTigerRecordStore::WiredTigerRecordStore(OperationContext* ctx,
if (WiredTigerKVEngine::initRsOplogBackgroundThread(ns)) {
_oplogStones = std::make_shared<OplogStones>(ctx, this);
}
+
+ if (_isOplog) {
+ _oplogJournalThread = stdx::thread(&WiredTigerRecordStore::_oplogJournalThreadLoop,
+ this,
+ WiredTigerRecoveryUnit::get(ctx)->getSessionCache());
+ }
}
WiredTigerRecordStore::~WiredTigerRecordStore() {
{
stdx::lock_guard<boost::timed_mutex> lk(_cappedDeleterMutex); // NOLINT
+ stdx::lock_guard<stdx::mutex> lk2(_uncommittedRecordIdsMutex);
_shuttingDown = true;
}
@@ -882,6 +862,11 @@ WiredTigerRecordStore::~WiredTigerRecordStore() {
if (_oplogStones) {
_oplogStones->kill();
}
+
+ if (_oplogJournalThread.joinable()) {
+ _opsWaitingForJournalCV.notify_one();
+ _oplogJournalThread.join();
+ }
}
const char* WiredTigerRecordStore::name() const {
@@ -1121,6 +1106,7 @@ int64_t WiredTigerRecordStore::cappedDeleteAsNeeded_inlock(OperationContext* txn
++docsRemoved;
sizeSaved += old_value.size;
+ stdx::lock_guard<stdx::mutex> cappedCallbackLock(_cappedCallbackMutex);
if (_cappedCallback) {
uassertStatusOK(_cappedCallback->aboutToDeleteCapped(
txn,
@@ -1337,10 +1323,25 @@ StatusWith<RecordId> WiredTigerRecordStore::insertRecord(OperationContext* txn,
return StatusWith<RecordId>(records[0].id);
}
-void WiredTigerRecordStore::_dealtWithCappedId(SortedRecordIds::iterator it) {
+void WiredTigerRecordStore::_dealtWithCappedId(SortedRecordIds::iterator it, bool didCommit) {
invariant(&(*it) != NULL);
stdx::lock_guard<stdx::mutex> lk(_uncommittedRecordIdsMutex);
- _uncommittedRecordIds.erase(it);
+ if (didCommit && _isOplog && *it != _oplog_highestSeen) {
+ // Defer removal from _uncommittedRecordIds until it is durable. We don't need to wait for
+ // durability of ops that didn't commit because they won't become durable.
+ // As an optimization, we only defer visibility until durable if new ops were created while
+ // we were pending. This makes single-threaded w>1 workloads faster and is safe because
+ // durability follows commit order for commits that are fully sequenced (B doesn't call
+ // commit until after A's commit call returns).
+ const bool wasEmpty = _opsWaitingForJournal.empty();
+ _opsWaitingForJournal.push_back(it);
+ if (wasEmpty) {
+ _opsWaitingForJournalCV.notify_one();
+ }
+ } else {
+ _uncommittedRecordIds.erase(it);
+ _opsBecameVisibleCV.notify_all();
+ }
}
bool WiredTigerRecordStore::isCappedHidden(const RecordId& id) const {
@@ -1431,10 +1432,13 @@ std::unique_ptr<SeekableRecordCursor> WiredTigerRecordStore::getCursor(Operation
bool forward) const {
if (_isOplog && forward) {
WiredTigerRecoveryUnit* wru = WiredTigerRecoveryUnit::get(txn);
- if (!wru->inActiveTxn() || wru->getOplogReadTill().isNull()) {
- // if we don't have a session, we have no snapshot, so we can update our view
- _oplogSetStartHack(wru);
+ // If we already have a snapshot we don't know what it can see, unless we know no one
+ // else could be writing (because we hold an exclusive lock).
+ if (wru->inActiveTxn() && !txn->lockState()->isNoop() &&
+ !txn->lockState()->isCollectionLockedForMode(_ns, MODE_X)) {
+ throw WriteConflictException();
}
+ _oplogSetStartHack(wru);
}
return stdx::make_unique<Cursor>(txn, *this, forward);
@@ -1627,22 +1631,78 @@ public:
: _rs(rs), _it(it) {}
virtual void commit() {
+ _rs->_dealtWithCappedId(_it, true);
// Do not notify here because all committed inserts notify, always.
- _rs->_dealtWithCappedId(_it);
}
virtual void rollback() {
// Notify on rollback since it might make later commits visible.
- _rs->_dealtWithCappedId(_it);
+ _rs->_dealtWithCappedId(_it, false);
+ stdx::lock_guard<stdx::mutex> lk(_rs->_cappedCallbackMutex);
if (_rs->_cappedCallback)
_rs->_cappedCallback->notifyCappedWaitersIfNeeded();
}
private:
- WiredTigerRecordStore* _rs;
- SortedRecordIds::iterator _it;
+ WiredTigerRecordStore* const _rs;
+ const SortedRecordIds::iterator _it;
};
+void WiredTigerRecordStore::_oplogJournalThreadLoop(WiredTigerSessionCache* sessionCache) try {
+ Client::initThread("WTOplogJournalThread");
+ while (true) {
+ stdx::unique_lock<stdx::mutex> lk(_uncommittedRecordIdsMutex);
+ _opsWaitingForJournalCV.wait(
+ lk, [&] { return _shuttingDown || !_opsWaitingForJournal.empty(); });
+
+ while (!_shuttingDown && MONGO_FAIL_POINT(WTPausePrimaryOplogDurabilityLoop)) {
+ lk.unlock();
+ sleepmillis(10);
+ lk.lock();
+ }
+
+ if (_shuttingDown)
+ return;
+
+ decltype(_opsWaitingForJournal) opsAboutToBeJournaled = {};
+ _opsWaitingForJournal.swap(opsAboutToBeJournaled);
+
+ lk.unlock();
+ sessionCache->waitUntilDurable(/*forceCheckpoint=*/false);
+ lk.lock();
+
+ for (auto&& op : opsAboutToBeJournaled) {
+ _uncommittedRecordIds.erase(op);
+ }
+
+ _opsBecameVisibleCV.notify_all();
+ lk.unlock();
+
+ stdx::lock_guard<stdx::mutex> cappedCallbackLock(_cappedCallbackMutex);
+ if (_cappedCallback) {
+ _cappedCallback->notifyCappedWaitersIfNeeded();
+ }
+ }
+} catch (...) {
+ std::terminate();
+}
+
+void WiredTigerRecordStore::waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const {
+ invariant(txn->lockState()->isNoop() || !txn->lockState()->inAWriteUnitOfWork());
+
+ // This function must not start a WT transaction, otherwise we will get stuck in an infinite
+ // loop of WCE handling when the getCursor() is called.
+
+ stdx::unique_lock<stdx::mutex> lk(_uncommittedRecordIdsMutex);
+ const auto waitingFor = _oplog_highestSeen;
+ while (!_uncommittedRecordIds.empty() && _uncommittedRecordIds.front() <= waitingFor) {
+ // We can't use a simple wait() here because we need to wake up periodically to check for
+ // interrupt and OperationContext::waitForConditionOrInterrupt doesn't exist on this branch.
+ txn->checkForInterrupt();
+ _opsBecameVisibleCV.wait_for(lk, Microseconds(Seconds(10)));
+ }
+}
+
void WiredTigerRecordStore::_addUncommitedRecordId_inlock(OperationContext* txn,
const RecordId& id) {
// todo: make this a dassert at some point
@@ -1784,13 +1844,17 @@ void WiredTigerRecordStore::temp_cappedTruncateAfter(OperationContext* txn,
}
// Compute the number and associated sizes of the records to delete.
- do {
- if (_cappedCallback) {
- uassertStatusOK(_cappedCallback->aboutToDeleteCapped(txn, record->id, record->data));
- }
- recordsRemoved++;
- bytesRemoved += record->data.size();
- } while ((record = cursor.next()));
+ {
+ stdx::lock_guard<stdx::mutex> cappedCallbackLock(_cappedCallbackMutex);
+ do {
+ if (_cappedCallback) {
+ uassertStatusOK(
+ _cappedCallback->aboutToDeleteCapped(txn, record->id, record->data));
+ }
+ recordsRemoved++;
+ bytesRemoved += record->data.size();
+ } while ((record = cursor.next()));
+ }
// Truncate the collection starting from the record located at 'firstRemovedId' to the end of
// the collection.
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
index 3335e774c4c..1fab25f8be4 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
@@ -39,8 +39,10 @@
#include "mongo/db/storage/capped_callback.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/platform/atomic_word.h"
+#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/concurrency/synchronization.h"
+#include "mongo/stdx/thread.h"
#include "mongo/util/fail_point_service.h"
/**
@@ -53,6 +55,7 @@ namespace mongo {
class RecoveryUnit;
class WiredTigerCursor;
+class WiredTigerSessionCache;
class WiredTigerRecoveryUnit;
class WiredTigerSizeStorer;
@@ -193,6 +196,9 @@ public:
long long numRecords,
long long dataSize);
+
+ void waitForAllEarlierOplogWritesToBeVisible(OperationContext* txn) const override;
+
bool isOplog() const {
return _isOplog;
}
@@ -201,8 +207,10 @@ public:
}
void setCappedCallback(CappedCallback* cb) {
+ stdx::lock_guard<stdx::mutex> lk(_cappedCallbackMutex);
_cappedCallback = cb;
}
+
int64_t cappedMaxDocs() const;
int64_t cappedMaxSize() const;
@@ -255,7 +263,7 @@ private:
static int64_t _makeKey(const RecordId& id);
static RecordId _fromKey(int64_t k);
- void _dealtWithCappedId(SortedRecordIds::iterator it);
+ void _dealtWithCappedId(SortedRecordIds::iterator it, bool didCommit);
void _addUncommitedRecordId_inlock(OperationContext* txn, const RecordId& id);
RecordId _nextId();
@@ -265,6 +273,7 @@ private:
void _increaseDataSize(OperationContext* txn, int64_t amount);
RecordData _getData(const WiredTigerCursor& cursor) const;
void _oplogSetStartHack(WiredTigerRecoveryUnit* wru) const;
+ void _oplogJournalThreadLoop(WiredTigerSessionCache* sessionCache);
const std::string _uri;
const uint64_t _tableId; // not persisted
@@ -284,6 +293,7 @@ private:
AtomicInt64 _cappedSleep;
AtomicInt64 _cappedSleepMS;
CappedCallback* _cappedCallback;
+ stdx::mutex _cappedCallbackMutex; // guards _cappedCallback.
// See comment in ::cappedDeleteAsNeeded
int _cappedDeleteCheckCount;
@@ -292,7 +302,6 @@ private:
const bool _useOplogHack;
SortedRecordIds _uncommittedRecordIds;
- RecordId _oplog_visibleTo;
RecordId _oplog_highestSeen;
mutable stdx::mutex _uncommittedRecordIdsMutex;
@@ -307,8 +316,19 @@ private:
// Non-null if this record store is underlying the active oplog.
std::shared_ptr<OplogStones> _oplogStones;
+
+ // These use the _uncommittedRecordIdsMutex and are only used when _isOplog is true.
+ stdx::condition_variable _opsWaitingForJournalCV;
+ mutable stdx::condition_variable _opsBecameVisibleCV;
+ std::vector<SortedRecordIds::iterator> _opsWaitingForJournal;
+ stdx::thread _oplogJournalThread;
};
// WT failpoint to throw write conflict exceptions randomly
MONGO_FP_FORWARD_DECLARE(WTWriteConflictException);
+
+// Prevents oplog writes from being considered durable on the primary. Once activated, new writes
+// will not be considered durable until deactivated. It is unspecified whether writes that commit
+// before activation will become visible while active.
+MONGO_FP_FORWARD_DECLARE(WTPausePrimaryOplogDurabilityLoop);
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp
index 7dcc4033f70..b65c933e744 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp
@@ -48,6 +48,8 @@
#include "mongo/db/storage/wiredtiger/wiredtiger_util.h"
#include "mongo/unittest/temp_dir.h"
#include "mongo/unittest/unittest.h"
+#include "mongo/util/fail_point.h"
+#include "mongo/util/scopeguard.h"
namespace mongo {
@@ -754,7 +756,7 @@ TEST(WiredTigerRecordStoreTest, CappedCursorRollover) {
ASSERT(!cursor->next());
}
-RecordId _oplogOrderInsertOplog(OperationContext* txn, unique_ptr<RecordStore>& rs, int inc) {
+RecordId _oplogOrderInsertOplog(OperationContext* txn, const unique_ptr<RecordStore>& rs, int inc) {
Timestamp opTime = Timestamp(5, inc);
WiredTigerRecordStore* wrs = checked_cast<WiredTigerRecordStore*>(rs.get());
Status status = wrs->oplogDiskLocRegister(txn, opTime);
@@ -831,6 +833,8 @@ TEST(WiredTigerRecordStoreTest, OplogOrder) {
w1.commit();
}
+ rs->waitForAllEarlierOplogWritesToBeVisible(harnessHelper->newOperationContext().get());
+
{ // now all 3 docs should be visible
unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
auto cursor = rs->getCursor(opCtx.get());
@@ -885,6 +889,8 @@ TEST(WiredTigerRecordStoreTest, OplogOrder) {
w1.commit();
}
+ rs->waitForAllEarlierOplogWritesToBeVisible(harnessHelper->newOperationContext().get());
+
{ // now all 3 docs should be visible
unique_ptr<OperationContext> opCtx(harnessHelper->newOperationContext());
auto cursor = rs->getCursor(opCtx.get());
@@ -896,6 +902,83 @@ TEST(WiredTigerRecordStoreTest, OplogOrder) {
}
}
+// Test that even when the oplog durability loop is paused, we can still advance the commit point as
+// long as the commit for each insert comes before the next insert starts.
+TEST(WiredTigerRecordStoreTest, OplogDurableVisibilityInOrder) {
+ ON_BLOCK_EXIT([] { WTPausePrimaryOplogDurabilityLoop.setMode(FailPoint::off); });
+ WTPausePrimaryOplogDurabilityLoop.setMode(FailPoint::alwaysOn);
+
+ unique_ptr<WiredTigerHarnessHelper> harnessHelper(new WiredTigerHarnessHelper());
+ unique_ptr<RecordStore> rs(harnessHelper->newCappedRecordStore("local.oplog.foo", 100000, -1));
+ auto wtrs = checked_cast<WiredTigerRecordStore*>(rs.get());
+
+ {
+ auto opCtx(harnessHelper->newOperationContext());
+ WriteUnitOfWork uow(opCtx.get());
+ RecordId id = _oplogOrderInsertOplog(opCtx.get(), rs, 1);
+ ASSERT(wtrs->isCappedHidden(id));
+ uow.commit();
+ ASSERT(!wtrs->isCappedHidden(id));
+ }
+
+ {
+ auto opCtx(harnessHelper->newOperationContext());
+ WriteUnitOfWork uow(opCtx.get());
+ RecordId id = _oplogOrderInsertOplog(opCtx.get(), rs, 2);
+ ASSERT(wtrs->isCappedHidden(id));
+ uow.commit();
+ ASSERT(!wtrs->isCappedHidden(id));
+ }
+}
+
+// Test that Oplog entries inserted while there are hidden entries do not become visible until the
+// op and all earlier ops are durable.
+TEST(WiredTigerRecordStoreTest, OplogDurableVisibilityOutOfOrder) {
+ ON_BLOCK_EXIT([] { WTPausePrimaryOplogDurabilityLoop.setMode(FailPoint::off); });
+ WTPausePrimaryOplogDurabilityLoop.setMode(FailPoint::alwaysOn);
+
+ unique_ptr<WiredTigerHarnessHelper> harnessHelper(new WiredTigerHarnessHelper());
+ unique_ptr<RecordStore> rs(harnessHelper->newCappedRecordStore("local.oplog.foo", 100000, -1));
+
+ auto wtrs = checked_cast<WiredTigerRecordStore*>(rs.get());
+
+ auto longLivedOp(harnessHelper->newOperationContext());
+ WriteUnitOfWork uow(longLivedOp.get());
+ RecordId id1 = _oplogOrderInsertOplog(longLivedOp.get(), rs, 1);
+ ASSERT(wtrs->isCappedHidden(id1));
+
+
+ RecordId id2;
+ {
+ auto innerClient = harnessHelper->serviceContext()->makeClient("inner");
+ auto opCtx(harnessHelper->newOperationContext(innerClient.get()));
+ WriteUnitOfWork uow(opCtx.get());
+ id2 = _oplogOrderInsertOplog(opCtx.get(), rs, 2);
+ ASSERT(wtrs->isCappedHidden(id2));
+ uow.commit();
+ }
+
+ ASSERT(wtrs->isCappedHidden(id1));
+ ASSERT(wtrs->isCappedHidden(id2));
+
+ uow.commit();
+
+ ASSERT(wtrs->isCappedHidden(id1));
+ ASSERT(wtrs->isCappedHidden(id2));
+
+ // Wait a bit and check again to make sure they don't become visible automatically.
+ sleepsecs(1);
+ ASSERT(wtrs->isCappedHidden(id1));
+ ASSERT(wtrs->isCappedHidden(id2));
+
+ WTPausePrimaryOplogDurabilityLoop.setMode(FailPoint::off);
+
+ rs->waitForAllEarlierOplogWritesToBeVisible(longLivedOp.get());
+
+ ASSERT(!wtrs->isCappedHidden(id1));
+ ASSERT(!wtrs->isCappedHidden(id2));
+}
+
TEST(WiredTigerRecordStoreTest, StorageSizeStatisticsDisabled) {
WiredTigerHarnessHelper harnessHelper("statistics=(none)");
unique_ptr<RecordStore> rs(harnessHelper.newNonCappedRecordStore("a.b"));
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp
index c87e036d16f..58fd74c2ee0 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp
@@ -152,11 +152,6 @@ void WiredTigerRecoveryUnit::_ensureSession() {
bool WiredTigerRecoveryUnit::waitUntilDurable() {
invariant(!_inUnitOfWork);
- // For inMemory storage engines, the data is "as durable as it's going to get".
- // That is, a restart is equivalent to a complete node failure.
- if (_sessionCache->isEphemeral()) {
- return true;
- }
// _session may be nullptr. We cannot _ensureSession() here as that needs shutdown protection.
_sessionCache->waitUntilDurable(false);
return true;
@@ -218,6 +213,7 @@ void WiredTigerRecoveryUnit::_txnClose(bool commit) {
}
_active = false;
_mySnapshotId = nextSnapshotId.fetchAndAdd(1);
+ _oplogReadTill = RecordId();
}
SnapshotId WiredTigerRecoveryUnit::getSnapshotId() const {
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp
index 836d42b9e42..54fff70be4d 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp
@@ -160,6 +160,12 @@ void WiredTigerSessionCache::shuttingDown() {
}
void WiredTigerSessionCache::waitUntilDurable(bool forceCheckpoint) {
+ // For inMemory storage engines, the data is "as durable as it's going to get".
+ // That is, a restart is equivalent to a complete node failure.
+ if (isEphemeral()) {
+ return;
+ }
+
const int shuttingDown = _shuttingDown.fetchAndAdd(1);
ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); });
@@ -205,7 +211,7 @@ void WiredTigerSessionCache::waitUntilDurable(bool forceCheckpoint) {
JournalListener::Token token = _journalListener->getToken();
// Use the journal when available, or a checkpoint otherwise.
- if (_engine->isDurable()) {
+ if (_engine && _engine->isDurable()) {
invariantWTOK(s->log_flush(s, "sync=on"));
LOG(4) << "flushed journal";
} else {
@@ -272,10 +278,13 @@ void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) {
ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); });
if (shuttingDown & kShuttingDownMask) {
- // Leak the session in order to avoid race condition with clean shutdown, where the
- // storage engine is ripped from underneath transactions, which are not "active"
- // (i.e., do not have any locks), but are just about to delete the recovery unit.
- // See SERVER-16031 for more information.
+ // There is a race condition with clean shutdown, where the storage engine is ripped from
+ // underneath OperationContexts, which are not "active" (i.e., do not have any locks), but
+ // are just about to delete the recovery unit. See SERVER-16031 for more information. Since
+ // shutting down the WT_CONNECTION will close all WT_SESSIONS, we shouldn't also try to
+ // directly close this session.
+ session->_session = nullptr; // Prevents calling _session->close() in destructor.
+ delete session;
return;
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp
index c1ce6ac0c35..b840815e3a5 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp
@@ -251,6 +251,11 @@ Status WiredTigerUtil::checkTableCreationOptions(const BSONElement& configElem)
ErrorAccumulator eventHandler(&errors);
StringData config = configElem.valueStringData();
+ // Do NOT allow embedded null characters
+ if (config.size() != strlen(config.rawData())) {
+ return {ErrorCodes::FailedToParse, "malformed 'configString' value."};
+ }
+
Status status = wtRCToStatus(
wiredtiger_config_validate(nullptr, &eventHandler, "WT_SESSION.create", config.rawData()));
if (!status.isOK()) {
diff --git a/src/mongo/dbtests/replica_set_monitor_test.cpp b/src/mongo/dbtests/replica_set_monitor_test.cpp
index 1e5a7699bea..a04111c354b 100644
--- a/src/mongo/dbtests/replica_set_monitor_test.cpp
+++ b/src/mongo/dbtests/replica_set_monitor_test.cpp
@@ -26,6 +26,11 @@
* then also delete it in the license file.
*/
+#include "mongo/platform/basic.h"
+
+#include <set>
+#include <vector>
+
#include "mongo/client/connpool.h"
#include "mongo/client/dbclientinterface.h"
#include "mongo/client/dbclient_rs.h"
@@ -35,13 +40,9 @@
#include "mongo/dbtests/mock/mock_replica_set.h"
#include "mongo/unittest/unittest.h"
-#include <set>
-#include <vector>
-
+namespace mongo {
namespace {
-using namespace mongo;
-
using std::map;
using std::vector;
using std::set;
@@ -301,4 +302,5 @@ TEST_F(TwoNodeWithTags, SecDownRetryWithTag) {
ASSERT_EQUALS(secHost, node.toString());
}
+} // namespace
} // namespace mongo
diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp
index f53a2545968..f01c2d88fef 100644
--- a/src/mongo/dbtests/repltests.cpp
+++ b/src/mongo/dbtests/repltests.cpp
@@ -152,37 +152,17 @@ protected:
return count;
}
int opCount() {
- ScopedTransaction transaction(&_txn, MODE_X);
- Lock::GlobalWrite lk(_txn.lockState());
- OldClientContext ctx(&_txn, cllNS());
-
- Database* db = ctx.db();
- Collection* coll = db->getCollection(cllNS());
- if (!coll) {
- WriteUnitOfWork wunit(&_txn);
- coll = db->createCollection(&_txn, cllNS());
- wunit.commit();
- }
-
- int count = 0;
- auto cursor = coll->getCursor(&_txn);
- while (auto record = cursor->next()) {
- ++count;
- }
- return count;
+ return DBDirectClient(&_txn).query(cllNS(), BSONObj())->itcount();
}
void applyAllOperations() {
ScopedTransaction transaction(&_txn, MODE_X);
Lock::GlobalWrite lk(_txn.lockState());
vector<BSONObj> ops;
{
- OldClientContext ctx(&_txn, cllNS());
- Database* db = ctx.db();
- Collection* coll = db->getCollection(cllNS());
-
- auto cursor = coll->getCursor(&_txn);
- while (auto record = cursor->next()) {
- ops.push_back(record->data.releaseToBson().getOwned());
+ DBDirectClient db(&_txn);
+ auto cursor = db.query(cllNS(), BSONObj());
+ while (cursor->more()) {
+ ops.push_back(cursor->nextSafe().getOwned());
}
}
{
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
index b0ebd0d825f..26933d167db 100644
--- a/src/mongo/dbtests/rollbacktests.cpp
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -139,7 +139,7 @@ void dropIndex(OperationContext* txn, const NamespaceString& nss, const string&
}
} // namespace
-template <bool rollback, bool defaultIndexes>
+template <bool rollback, bool defaultIndexes, bool capped>
class CreateCollection {
public:
void run() {
@@ -154,7 +154,8 @@ public:
{
WriteUnitOfWork uow(&txn);
ASSERT(!collectionExists(&ctx, ns));
- ASSERT_OK(userCreateNS(&txn, ctx.db(), ns, BSONObj(), defaultIndexes));
+ auto options = capped ? BSON("capped" << true << "size" << 1000) : BSONObj();
+ ASSERT_OK(userCreateNS(&txn, ctx.db(), ns, options, defaultIndexes));
ASSERT(collectionExists(&ctx, ns));
if (!rollback) {
uow.commit();
@@ -168,7 +169,7 @@ public:
}
};
-template <bool rollback, bool defaultIndexes>
+template <bool rollback, bool defaultIndexes, bool capped>
class DropCollection {
public:
void run() {
@@ -183,7 +184,8 @@ public:
{
WriteUnitOfWork uow(&txn);
ASSERT(!collectionExists(&ctx, ns));
- ASSERT_OK(userCreateNS(&txn, ctx.db(), ns, BSONObj(), defaultIndexes));
+ auto options = capped ? BSON("capped" << true << "size" << 1000) : BSONObj();
+ ASSERT_OK(userCreateNS(&txn, ctx.db(), ns, options, defaultIndexes));
uow.commit();
}
ASSERT(collectionExists(&ctx, ns));
@@ -710,10 +712,21 @@ public:
template <template <bool, bool> class T>
void addAll() {
add<T<false, false>>();
- add<T<true, false>>();
add<T<false, true>>();
+ add<T<true, false>>();
add<T<true, true>>();
}
+ template <template <bool, bool, bool> class T>
+ void addAll() {
+ add<T<false, false, false>>();
+ add<T<false, false, true>>();
+ add<T<false, true, false>>();
+ add<T<false, true, true>>();
+ add<T<true, false, false>>();
+ add<T<true, false, true>>();
+ add<T<true, true, false>>();
+ add<T<true, true, true>>();
+ }
void setupTests() {
addAll<CreateCollection>();
diff --git a/src/mongo/executor/async_secure_stream.cpp b/src/mongo/executor/async_secure_stream.cpp
index d1e4f2bfc23..2d3697561ba 100644
--- a/src/mongo/executor/async_secure_stream.cpp
+++ b/src/mongo/executor/async_secure_stream.cpp
@@ -108,7 +108,7 @@ void AsyncSecureStream::_handleHandshake(std::error_code ec, const std::string&
}
void AsyncSecureStream::cancel() {
- cancelStream(&_stream.lowest_layer(), _connected);
+ cancelStream(&_stream.lowest_layer());
}
bool AsyncSecureStream::isOpen() {
diff --git a/src/mongo/executor/async_stream.cpp b/src/mongo/executor/async_stream.cpp
index 60a805d2753..075a9094dd4 100644
--- a/src/mongo/executor/async_stream.cpp
+++ b/src/mongo/executor/async_stream.cpp
@@ -84,7 +84,7 @@ void AsyncStream::read(asio::mutable_buffer buffer, StreamHandler&& streamHandle
}
void AsyncStream::cancel() {
- cancelStream(&_stream, _connected);
+ cancelStream(&_stream);
}
bool AsyncStream::isOpen() {
diff --git a/src/mongo/executor/async_stream_common.cpp b/src/mongo/executor/async_stream_common.cpp
index f029607b472..a1cd69ff49f 100644
--- a/src/mongo/executor/async_stream_common.cpp
+++ b/src/mongo/executor/async_stream_common.cpp
@@ -39,7 +39,12 @@ namespace executor {
void logCloseFailed(std::error_code ec) {
invariant(ec);
- log() << "failed to close stream: " << ec.message();
+ log() << "Failed to close stream: " << ec.message();
+}
+
+void logCancelFailed(std::error_code ec) {
+ invariant(ec);
+ log() << "Failed to cancel stream: " << ec.message();
}
void logFailureInSetStreamNonBlocking(std::error_code ec) {
diff --git a/src/mongo/executor/async_stream_common.h b/src/mongo/executor/async_stream_common.h
index 6f7bd5eff63..93da9f32786 100644
--- a/src/mongo/executor/async_stream_common.h
+++ b/src/mongo/executor/async_stream_common.h
@@ -80,9 +80,15 @@ void readStream(ASIOStream* stream,
strand->wrap(std::forward<Handler>(handler)));
}
+void logCancelFailed(std::error_code ec);
+
template <typename ASIOStream>
-void cancelStream(ASIOStream* stream, bool connected) {
- stream->cancel();
+void cancelStream(ASIOStream* stream) {
+ std::error_code ec;
+ stream->cancel(ec);
+ if (ec) {
+ logCancelFailed(ec);
+ }
}
void logFailureInSetStreamNonBlocking(std::error_code ec);
diff --git a/src/mongo/executor/async_timer_asio.cpp b/src/mongo/executor/async_timer_asio.cpp
index 293ab3e137a..d0445f053c3 100644
--- a/src/mongo/executor/async_timer_asio.cpp
+++ b/src/mongo/executor/async_timer_asio.cpp
@@ -26,9 +26,14 @@
* it in the license file.
*/
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kASIO
+
+#include "mongo/platform/basic.h"
+
#include "mongo/executor/async_timer_asio.h"
#include "mongo/stdx/memory.h"
+#include "mongo/util/log.h"
namespace mongo {
namespace executor {
@@ -37,7 +42,11 @@ AsyncTimerASIO::AsyncTimerASIO(asio::io_service::strand* strand, Milliseconds ex
: _strand(strand), _timer(_strand->get_io_service(), expiration) {}
void AsyncTimerASIO::cancel() {
- _timer.cancel();
+ std::error_code ec;
+ _timer.cancel(ec);
+ if (ec) {
+ log() << "Failed to cancel timer: " << ec.message();
+ }
}
void AsyncTimerASIO::asyncWait(AsyncTimerInterface::Handler handler) {
diff --git a/src/mongo/executor/connection_pool.cpp b/src/mongo/executor/connection_pool.cpp
index fb88bef8577..ee243789587 100644
--- a/src/mongo/executor/connection_pool.cpp
+++ b/src/mongo/executor/connection_pool.cpp
@@ -166,9 +166,11 @@ private:
State _state;
};
-Milliseconds const ConnectionPool::kDefaultRefreshTimeout = Seconds(20);
-Milliseconds const ConnectionPool::kDefaultRefreshRequirement = Seconds(60);
-Milliseconds const ConnectionPool::kDefaultHostTimeout = Minutes(5);
+const Milliseconds ConnectionPool::kDefaultHostTimeout = Minutes(5);
+size_t const ConnectionPool::kDefaultMaxConns = std::numeric_limits<size_t>::max();
+size_t const ConnectionPool::kDefaultMinConns = 1;
+const Milliseconds ConnectionPool::kDefaultRefreshRequirement = Minutes(1);
+const Milliseconds ConnectionPool::kDefaultRefreshTimeout = Seconds(20);
const Status ConnectionPool::kConnectionStateUnknown =
Status(ErrorCodes::InternalError, "Connection is in an unknown state");
@@ -495,8 +497,15 @@ void ConnectionPool::SpecificPool::spawnConnections(stdx::unique_lock<stdx::mute
// While all of our inflight connections are less than our target
while (_readyPool.size() + _processingPool.size() + _checkedOutPool.size() < target()) {
- // make a new connection and put it in processing
- auto handle = _parent->_factory->makeConnection(hostAndPort, _generation);
+ std::unique_ptr<ConnectionPool::ConnectionInterface> handle;
+ try {
+ // make a new connection and put it in processing
+ handle = _parent->_factory->makeConnection(hostAndPort, _generation);
+ } catch (std::system_error& e) {
+ severe() << "Failed to construct a new connection object: " << e.what();
+ fassertFailed(40336);
+ }
+
auto connPtr = handle.get();
_processingPool[connPtr] = std::move(handle);
diff --git a/src/mongo/executor/connection_pool.h b/src/mongo/executor/connection_pool.h
index a5bcc1c3925..83dbe54d240 100644
--- a/src/mongo/executor/connection_pool.h
+++ b/src/mongo/executor/connection_pool.h
@@ -67,9 +67,11 @@ public:
using GetConnectionCallback = stdx::function<void(StatusWith<ConnectionHandle>)>;
- static const Milliseconds kDefaultRefreshTimeout;
- static const Milliseconds kDefaultRefreshRequirement;
static const Milliseconds kDefaultHostTimeout;
+ static const size_t kDefaultMaxConns;
+ static const size_t kDefaultMinConns;
+ static const Milliseconds kDefaultRefreshRequirement;
+ static const Milliseconds kDefaultRefreshTimeout;
static const Status kConnectionStateUnknown;
@@ -80,14 +82,14 @@ public:
* The minimum number of connections to keep alive while the pool is in
* operation
*/
- size_t minConnections = 1;
+ size_t minConnections = kDefaultMinConns;
/**
* The maximum number of connections to spawn for a host. This includes
* pending connections in setup and connections checked out of the pool
* as well as the obvious live connections in the pool.
*/
- size_t maxConnections = std::numeric_limits<size_t>::max();
+ size_t maxConnections = kDefaultMaxConns;
/**
* Amount of time to wait before timing out a refresh attempt
diff --git a/src/mongo/executor/connection_pool_asio.cpp b/src/mongo/executor/connection_pool_asio.cpp
index 45be3537007..8a7bc826355 100644
--- a/src/mongo/executor/connection_pool_asio.cpp
+++ b/src/mongo/executor/connection_pool_asio.cpp
@@ -25,6 +25,8 @@
* it in the license file.
*/
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kASIO
+
#include "mongo/platform/basic.h"
#include "mongo/executor/connection_pool_asio.h"
@@ -37,6 +39,7 @@
#include "mongo/rpc/legacy_request_builder.h"
#include "mongo/rpc/reply_interface.h"
#include "mongo/stdx/memory.h"
+#include "mongo/util/log.h"
namespace mongo {
namespace executor {
@@ -57,7 +60,13 @@ void ASIOTimer::setTimeout(Milliseconds timeout, TimeoutCallback cb) {
_cb = std::move(cb);
cancelTimeout();
- _impl.expires_after(timeout);
+
+ std::error_code ec;
+ _impl.expires_after(timeout, ec);
+ if (ec) {
+ severe() << "Failed to set connection pool timer: " << ec.message();
+ fassertFailed(40333);
+ }
decltype(_callbackSharedState->id) id;
decltype(_callbackSharedState) sharedState;
@@ -102,7 +111,12 @@ void ASIOTimer::cancelTimeout() {
stdx::lock_guard<stdx::mutex> lk(sharedState->mutex);
if (sharedState->id != id)
return;
- _impl.cancel();
+
+ std::error_code ec;
+ _impl.cancel(ec);
+ if (ec) {
+ log() << "Failed to cancel connection pool timer: " << ec.message();
+ }
});
}
diff --git a/src/mongo/executor/downconvert_find_and_getmore_commands.cpp b/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
index 034c1f133cc..799de9ca11f 100644
--- a/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
+++ b/src/mongo/executor/downconvert_find_and_getmore_commands.cpp
@@ -34,8 +34,6 @@
#include <string>
#include <tuple>
-#include "mongo/base/data_range_cursor.h"
-#include "mongo/base/data_type_validated.h"
#include "mongo/base/status_with.h"
#include "mongo/client/constants.h"
#include "mongo/client/dbclientinterface.h"
@@ -47,7 +45,6 @@
#include "mongo/executor/remote_command_response.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/metadata/server_selection_metadata.h"
-#include "mongo/rpc/object_check.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/net/message.h"
@@ -56,75 +53,17 @@ namespace executor {
namespace {
-StatusWith<std::tuple<CursorId, BSONArray>> getBatchFromReply(std::uint32_t requestId,
- const Message& response) {
- auto header = response.header();
- if (header.getNetworkOp() != mongo::opReply) {
- return {ErrorCodes::ProtocolError,
- str::stream() << "Expected to be decoding an OP_REPLY but got "
- << mongo::networkOpToString(header.getNetworkOp())};
- }
-
- if (header.getResponseTo() != requestId) {
- return {ErrorCodes::ProtocolError,
- str::stream() << "responseTo field of OP_REPLY header with value '"
- << header.getResponseTo() << "' does not match requestId '"
- << requestId << "'"};
- }
-
- if ((header.dataLen() < 0) ||
- (static_cast<std::size_t>(header.dataLen()) > mongo::MaxMessageSizeBytes)) {
- return {ErrorCodes::InvalidLength,
- str::stream() << "Received message has invalid length field with value "
- << header.dataLen()};
- }
-
- QueryResult::View qr = response.header().view2ptr();
-
- auto resultFlags = qr.getResultFlags();
-
- if (resultFlags & ResultFlag_CursorNotFound) {
- return {ErrorCodes::CursorNotFound,
- str::stream() << "Cursor with id '" << qr.getCursorId() << "' not found"};
- }
-
- // Use CDRC directly instead of DocumentRange as DocumentRange has a throwing API.
- ConstDataRangeCursor cdrc{qr.data(), qr.data() + header.dataLen()};
-
- if (resultFlags & ResultFlag_ErrSet) {
- if (qr.getNReturned() != 1) {
- return {ErrorCodes::BadValue,
- str::stream() << "ResultFlag_ErrSet flag set on reply, but nReturned was '"
- << qr.getNReturned() << "' - expected 1"};
- }
- // Convert error document to a Status.
- // Will throw if first document is invalid BSON.
- auto first = cdrc.readAndAdvance<Validated<BSONObj>>();
- if (!first.isOK()) {
- return first.getStatus();
- }
-
- // Convert error document to a status.
- return getStatusFromCommandResult(first.getValue());
- }
-
- Validated<BSONObj> nextObj;
- BSONArrayBuilder batch;
- while (!cdrc.empty() && batch.arrSize() < qr.getNReturned()) {
- auto readStatus = cdrc.readAndAdvance(&nextObj);
- if (!readStatus.isOK()) {
- return readStatus;
- }
- batch.append(nextObj.val);
- }
- if (qr.getNReturned() != batch.arrSize()) {
- return {ErrorCodes::InvalidLength,
- str::stream() << "Count of documents in OP_REPLY message (" << batch.arrSize()
- << ") did not match the value specified in the nReturned field ("
- << qr.getNReturned() << ")"};
- }
-
- return {std::make_tuple(qr.getCursorId(), batch.arr())};
+/**
+ * Returns a non-OK status if 'response' does not report it is a response to the request with id
+ * 'requestId'.
+ */
+Status checkMessageResponseTo(std::uint32_t requestId, const Message* response) {
+ return response->header().getResponseTo() == requestId
+ ? Status::OK()
+ : Status{ErrorCodes::ProtocolError,
+ str::stream() << "responseTo field of OP_REPLY header with value "
+ << response->header().getResponseTo() << " does not match requestId "
+ << requestId};
}
} // namespace
@@ -204,28 +143,21 @@ StatusWith<Message> downconvertFindCommandRequest(const RemoteCommandRequest& re
return {std::move(message)};
}
-StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response) {
- auto swBatch = getBatchFromReply(requestId, response);
- if (!swBatch.isOK()) {
- return swBatch.getStatus();
+StatusWith<RemoteCommandResponse> prepareOpReplyErrorResponse(std::uint32_t requestId,
+ StringData cursorNamespace,
+ Message* response) {
+ auto status = checkMessageResponseTo(requestId, response);
+ if (!status.isOK()) {
+ return status;
}
- BSONArray batch;
- CursorId cursorId;
- std::tie(cursorId, batch) = std::move(swBatch.getValue());
-
BSONObjBuilder result;
- appendCursorResponseObject(cursorId, cursorNamespace, std::move(batch), &result);
- // Using Command::appendCommandStatus would create a circular dep, so it's simpler to just do
- // this.
- result.append("ok", 1.0);
-
- RemoteCommandResponse upconvertedResponse;
- upconvertedResponse.data = result.obj();
+ result.append("ok", 0.0);
+ result.append("code", ErrorCodes::ReceivedOpReplyMessage);
+ result.append("errmsg", "Received an OP_REPLY, unable to parse into a single BSONObj");
+ result.append("ns", cursorNamespace);
- return {std::move(upconvertedResponse)};
+ return {RemoteCommandResponse{std::move(*response), result.obj(), {}, {}}};
}
StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest& request) {
@@ -248,28 +180,5 @@ StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest&
return {std::move(m)};
}
-StatusWith<RemoteCommandResponse> upconvertLegacyGetMoreResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response) {
- auto swBatch = getBatchFromReply(requestId, response);
- if (!swBatch.isOK()) {
- return swBatch.getStatus();
- }
-
- BSONArray batch;
- CursorId cursorId;
-
- std::tie(cursorId, batch) = std::move(swBatch.getValue());
-
- BSONObjBuilder result;
- appendGetMoreResponseObject(cursorId, cursorNamespace, std::move(batch), &result);
- result.append("ok", 1.0);
-
- RemoteCommandResponse resp;
- resp.data = result.obj();
-
- return {std::move(resp)};
-}
-
} // namespace mongo
} // namespace executor
diff --git a/src/mongo/executor/downconvert_find_and_getmore_commands.h b/src/mongo/executor/downconvert_find_and_getmore_commands.h
index dea13150ddd..6dd21318e76 100644
--- a/src/mongo/executor/downconvert_find_and_getmore_commands.h
+++ b/src/mongo/executor/downconvert_find_and_getmore_commands.h
@@ -29,6 +29,9 @@
#include <cstdint>
#include <memory>
+#include "mongo/db/cursor_id.h"
+#include "mongo/util/time_support.h"
+
namespace mongo {
class Message;
@@ -52,13 +55,14 @@ struct RemoteCommandResponse;
StatusWith<Message> downconvertFindCommandRequest(const RemoteCommandRequest& request);
/**
- * Upconverts the OP_REPLY received in response to a legacy OP_QUERY to a semantically equivalent
- * find command response. The 'requestId' parameter is the messageId of the original OP_QUERY, and
- * the 'cursorNamespace' is the full namespace of the collection the query ran on.
+ * We may not be able to fit the entire batch from the OP_REPLY into a single response BSONObj, so
+ * we must defer the parsing to the original requester. This method creates a RemoteCommandResponse
+ * with a response that will signal callers to decode the raw message. The RemoteCommandResponse
+ * returned by this method takes ownership of 'message'.
*/
-StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response);
+StatusWith<RemoteCommandResponse> prepareOpReplyErrorResponse(std::uint32_t requestId,
+ StringData cursorNamespace,
+ Message* response);
/**
* Downconverts a getMore command request to the legacy OP_GET_MORE format. The returned message
@@ -68,14 +72,5 @@ StatusWith<RemoteCommandResponse> upconvertLegacyQueryResponse(std::uint32_t req
*/
StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest& request);
-/**
- * Upconverts the OP_REPLY received in response to a legacy OP_GET_MORE to a semantically equivalent
- * getMore command response. The 'requestId' parameter is the messageId of the original OP_GET_MORE,
- * and the 'curesorNamespace' is the full namespace of the collection the original query ran on.
- */
-StatusWith<RemoteCommandResponse> upconvertLegacyGetMoreResponse(std::uint32_t requestId,
- StringData cursorNamespace,
- const Message& response);
-
} // namespace mongo
} // namespace executor
diff --git a/src/mongo/executor/network_interface_asio.cpp b/src/mongo/executor/network_interface_asio.cpp
index 84028a95194..3900b92f4e5 100644
--- a/src/mongo/executor/network_interface_asio.cpp
+++ b/src/mongo/executor/network_interface_asio.cpp
@@ -113,7 +113,12 @@ void NetworkInterfaceASIO::startup() {
try {
LOG(2) << "The NetworkInterfaceASIO worker thread is spinning up";
asio::io_service::work work(_io_service);
- _io_service.run();
+ std::error_code ec;
+ _io_service.run(ec);
+ if (ec) {
+ severe() << "Failure in _io_service.run(): " << ec.message();
+ fassertFailed(40335);
+ }
} catch (...) {
severe() << "Uncaught exception in NetworkInterfaceASIO IO "
"worker thread of type: " << exceptionToStatus();
@@ -259,10 +264,12 @@ void NetworkInterfaceASIO::startCommand(const TaskExecutor::CallbackHandle& cbHa
// timeout duration - but make no stronger assumption. It is thus possible that
// we have already exceeded the timeout. In this case we timeout the operation
// manually.
- return _completeOperation(op,
- {ErrorCodes::ExceededTimeLimit,
- "Remote command timed out while waiting to get a "
- "connection from the pool."});
+ std::stringstream msg;
+ msg << "Remote command timed out while waiting to get a connection from the "
+ << "pool, took " << getConnectionDuration << ", timeout was set to "
+ << op->_request.timeout;
+ auto rs = ResponseStatus(ErrorCodes::ExceededTimeLimit, msg.str());
+ return _completeOperation(op, rs);
}
// The above conditional guarantees that the adjusted timeout will never underflow.
@@ -270,7 +277,13 @@ void NetworkInterfaceASIO::startCommand(const TaskExecutor::CallbackHandle& cbHa
const auto adjustedTimeout = op->_request.timeout - getConnectionDuration;
const auto requestId = op->_request.id;
- op->_timeoutAlarm = op->_owner->_timerFactory->make(&op->_strand, adjustedTimeout);
+ try {
+ op->_timeoutAlarm =
+ op->_owner->_timerFactory->make(&op->_strand, adjustedTimeout);
+ } catch (std::system_error& e) {
+ severe() << "Failed to construct timer for AsyncOp: " << e.what();
+ fassertFailed(40334);
+ }
std::shared_ptr<AsyncOp::AccessControl> access;
std::size_t generation;
@@ -348,7 +361,15 @@ void NetworkInterfaceASIO::cancelAllCommands() {
void NetworkInterfaceASIO::setAlarm(Date_t when, const stdx::function<void()>& action) {
// "alarm" must stay alive until it expires, hence the shared_ptr.
- auto alarm = std::make_shared<asio::steady_timer>(_io_service, when - now());
+ std::shared_ptr<asio::steady_timer> alarm;
+
+ try {
+ alarm = std::make_shared<asio::steady_timer>(_io_service, when - now());
+ } catch (std::system_error& e) {
+ severe() << "setAlarm() could not construct a timer" << e.what();
+ fassertFailed(40337);
+ }
+
alarm->async_wait([alarm, this, action](std::error_code ec) {
if (!ec) {
return action();
diff --git a/src/mongo/executor/network_interface_asio_command.cpp b/src/mongo/executor/network_interface_asio_command.cpp
index 90fde01055d..88daf7aafe4 100644
--- a/src/mongo/executor/network_interface_asio_command.cpp
+++ b/src/mongo/executor/network_interface_asio_command.cpp
@@ -205,13 +205,10 @@ ResponseStatus NetworkInterfaceASIO::AsyncCommand::response(rpc::Protocol protoc
case CommandType::kRPC: {
return decodeRPC(&received, protocol, now - _start, _target, metadataHook);
}
- case CommandType::kDownConvertedFind: {
- auto ns = DbMessage(_toSend).getns();
- return upconvertLegacyQueryResponse(_toSend.header().getId(), ns, received);
- }
+ case CommandType::kDownConvertedFind:
case CommandType::kDownConvertedGetMore: {
auto ns = DbMessage(_toSend).getns();
- return upconvertLegacyGetMoreResponse(_toSend.header().getId(), ns, received);
+ return prepareOpReplyErrorResponse(_toSend.header().getId(), ns, &received);
}
}
MONGO_UNREACHABLE;
diff --git a/src/mongo/executor/network_interface_asio_integration_test.cpp b/src/mongo/executor/network_interface_asio_integration_test.cpp
index cb85f273764..8de7c71ebc4 100644
--- a/src/mongo/executor/network_interface_asio_integration_test.cpp
+++ b/src/mongo/executor/network_interface_asio_integration_test.cpp
@@ -32,6 +32,7 @@
#include <algorithm>
#include <exception>
+#include <vector>
#include "mongo/client/connection_string.h"
#include "mongo/executor/async_stream_factory.h"
@@ -55,6 +56,8 @@ namespace mongo {
namespace executor {
namespace {
+using StartCommandCB = stdx::function<void(const StatusWith<RemoteCommandResponse>&)>;
+
class NetworkInterfaceASIOIntegrationTest : public mongo::unittest::Test {
public:
void startNet(NetworkInterfaceASIO::Options options = NetworkInterfaceASIO::Options()) {
@@ -87,6 +90,12 @@ public:
return _rng;
}
+ void startCommand(const TaskExecutor::CallbackHandle& cbHandle,
+ RemoteCommandRequest& request,
+ StartCommandCB onFinish) {
+ net().startCommand(cbHandle, request, onFinish);
+ }
+
Deferred<StatusWith<RemoteCommandResponse>> runCommand(
const TaskExecutor::CallbackHandle& cbHandle, const RemoteCommandRequest& request) {
Deferred<StatusWith<RemoteCommandResponse>> deferred;
@@ -170,81 +179,65 @@ public:
using Fixture = NetworkInterfaceASIOIntegrationTest;
using Pool = ThreadPoolInterface;
- Deferred<Status> run(Fixture* fixture, Pool* pool, Milliseconds timeout = Milliseconds(5000)) {
+ void run(Fixture* fixture,
+ StartCommandCB onFinish,
+ Milliseconds timeout = RemoteCommandRequest::kNoTimeout) {
auto cb = makeCallbackHandle();
- auto self = *this;
- auto out =
- fixture->runCommand(cb,
- {unittest::getFixtureConnectionString().getServers()[0],
- "admin",
- _command,
- timeout})
- .then(pool,
- [self](StatusWith<RemoteCommandResponse> resp) -> Status {
- auto status = resp.isOK()
- ? getStatusFromCommandResult(resp.getValue().data)
- : resp.getStatus();
-
- return status == self._expected
- ? Status::OK()
- : Status{ErrorCodes::BadValue,
- str::stream() << "Expected "
- << ErrorCodes::errorString(self._expected)
- << " but got " << status.toString()};
- });
+
+ RemoteCommandRequest request{
+ unittest::getFixtureConnectionString().getServers()[0], "admin", _command, timeout};
+
+ fixture->startCommand(cb, request, onFinish);
+
if (_cancel) {
invariant(fixture->randomNumberGenerator());
sleepmillis(fixture->randomNumberGenerator()->nextInt32(10));
fixture->net().cancelCommand(cb);
}
- return out;
}
- static Deferred<Status> runTimeoutOp(Fixture* fixture, Pool* pool) {
+ static void runTimeoutOp(Fixture* fixture, StartCommandCB onFinish) {
return StressTestOp(BSON("sleep" << 1 << "lock"
<< "none"
<< "secs" << 1),
- ErrorCodes::ExceededTimeLimit,
- false).run(fixture, pool, Milliseconds(100));
+ false).run(fixture, onFinish, Milliseconds(100));
}
- static Deferred<Status> runCompleteOp(Fixture* fixture, Pool* pool) {
+ static void runCompleteOp(Fixture* fixture, StartCommandCB onFinish) {
return StressTestOp(BSON("sleep" << 1 << "lock"
<< "none"
<< "millis" << 100),
- ErrorCodes::OK,
- false).run(fixture, pool);
+ false).run(fixture, onFinish);
}
- static Deferred<Status> runCancelOp(Fixture* fixture, Pool* pool) {
+ static void runCancelOp(Fixture* fixture, StartCommandCB onFinish) {
return StressTestOp(BSON("sleep" << 1 << "lock"
<< "none"
<< "secs" << 10),
- ErrorCodes::CallbackCanceled,
- true).run(fixture, pool);
+ true).run(fixture, onFinish);
}
- static Deferred<Status> runLongOp(Fixture* fixture, Pool* pool) {
+ static void runLongOp(Fixture* fixture, StartCommandCB onFinish) {
return StressTestOp(BSON("sleep" << 1 << "lock"
<< "none"
<< "secs" << 30),
- ErrorCodes::OK,
- false).run(fixture, pool, RemoteCommandRequest::kNoTimeout);
+ false).run(fixture, onFinish);
}
private:
- StressTestOp(const BSONObj& command, ErrorCodes::Error expected, bool cancel)
- : _command(command), _expected(expected), _cancel(cancel) {}
+ StressTestOp(const BSONObj& command, bool cancel) : _command(command), _cancel(cancel) {}
BSONObj _command;
- ErrorCodes::Error _expected;
bool _cancel;
};
TEST_F(NetworkInterfaceASIOIntegrationTest, StressTest) {
+ const std::size_t numOps = 1000;
+ std::vector<Status> testResults(numOps, {ErrorCodes::InternalError, "uninitialized"});
+ ErrorCodes::Error expectedResults[numOps];
+ CountdownLatch cl(numOps);
+
startNet();
- const std::size_t numOps = 10000;
- std::vector<Deferred<Status>> ops;
std::unique_ptr<SecureRandom> seedSource{SecureRandom::create()};
auto seed = seedSource->nextInt64();
@@ -254,51 +247,39 @@ TEST_F(NetworkInterfaceASIOIntegrationTest, StressTest) {
randomNumberGenerator(&rng);
log() << "Starting stress test...";
- ThreadPool::Options threadPoolOpts;
- threadPoolOpts.poolName = "StressTestPool";
- threadPoolOpts.maxThreads = 8;
- ThreadPool pool(threadPoolOpts);
- pool.startup();
-
- auto poolGuard = MakeGuard([&pool] {
- pool.schedule([&pool] { pool.shutdown(); });
- pool.join();
- });
-
- std::generate_n(std::back_inserter(ops),
- numOps,
- [&rng, &pool, this] {
-
- // stagger operations slightly to mitigate connection pool contention
- sleepmillis(rng.nextInt32(10));
-
- auto i = rng.nextCanonicalDouble();
-
- if (i < .3) {
- return StressTestOp::runCancelOp(this, &pool);
- } else if (i < .7) {
- return StressTestOp::runCompleteOp(this, &pool);
- } else if (i < .99) {
- return StressTestOp::runTimeoutOp(this, &pool);
- } else {
- // Just a sprinkling of long ops, to mitigate connection pool contention
- return StressTestOp::runLongOp(this, &pool);
- }
- });
-
- log() << "running ops";
- auto res = helpers::collect(ops, &pool)
- .then(&pool,
- [](std::vector<Status> opResults) -> Status {
- for (const auto& opResult : opResults) {
- if (!opResult.isOK()) {
- return opResult;
- }
- }
- return Status::OK();
- })
- .get();
- ASSERT_OK(res);
+ for (std::size_t i = 0; i < numOps; ++i) {
+ // stagger operations slightly to mitigate connection pool contention
+ sleepmillis(rng.nextInt32(16));
+
+ auto r = rng.nextCanonicalDouble();
+
+ auto cb = [&testResults, &cl, i](const StatusWith<RemoteCommandResponse>& resp) {
+ testResults[i] =
+ resp.isOK() ? getStatusFromCommandResult(resp.getValue().data) : resp.getStatus();
+ cl.countDown();
+ };
+
+ if (r < .3) {
+ expectedResults[i] = ErrorCodes::CallbackCanceled;
+ StressTestOp::runCancelOp(this, cb);
+ } else if (r < .7) {
+ expectedResults[i] = ErrorCodes::OK;
+ StressTestOp::runCompleteOp(this, cb);
+ } else if (r < .99) {
+ expectedResults[i] = ErrorCodes::ExceededTimeLimit;
+ StressTestOp::runTimeoutOp(this, cb);
+ } else {
+ // Just a sprinkling of long ops, to mitigate connection pool contention
+ expectedResults[i] = ErrorCodes::OK;
+ StressTestOp::runLongOp(this, cb);
+ }
+ };
+
+ cl.await();
+
+ for (std::size_t i = 0; i < numOps; ++i) {
+ ASSERT_EQ(testResults[i], expectedResults[i]);
+ }
}
// Hook that intentionally never finishes
diff --git a/src/mongo/executor/network_interface_asio_test_utils.h b/src/mongo/executor/network_interface_asio_test_utils.h
index bfd489514b0..55754050047 100644
--- a/src/mongo/executor/network_interface_asio_test_utils.h
+++ b/src/mongo/executor/network_interface_asio_test_utils.h
@@ -33,6 +33,7 @@
#include <vector>
#include "mongo/executor/task_executor.h"
+#include "mongo/platform/atomic_word.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/future.h"
#include "mongo/stdx/mutex.h"
@@ -113,10 +114,33 @@ private:
return *state->thing;
}
-private:
std::shared_ptr<State> _state = std::make_shared<State>();
};
+class CountdownLatch {
+public:
+ CountdownLatch(uint32_t count) : _count(count) {}
+
+ void countDown() {
+ if (_count.load() == 0) {
+ return;
+ }
+ if (_count.subtractAndFetch(1) == 0) {
+ _cv.notify_all();
+ }
+ }
+
+ void await() {
+ stdx::unique_lock<stdx::mutex> lk(_mtx);
+ _cv.wait(lk, [&] { return _count.load() == 0; });
+ }
+
+private:
+ stdx::condition_variable _cv;
+ stdx::mutex _mtx;
+ AtomicUInt32 _count;
+};
+
namespace helpers {
template <typename T>
diff --git a/src/mongo/executor/network_interface_factory.cpp b/src/mongo/executor/network_interface_factory.cpp
index 8a3f0e8f7cc..2dc7d3f5845 100644
--- a/src/mongo/executor/network_interface_factory.cpp
+++ b/src/mongo/executor/network_interface_factory.cpp
@@ -36,6 +36,7 @@
#include "mongo/executor/async_stream_factory.h"
#include "mongo/executor/async_stream_interface.h"
#include "mongo/executor/async_timer_asio.h"
+#include "mongo/executor/connection_pool.h"
#include "mongo/executor/network_connection_hook.h"
#include "mongo/executor/network_interface_asio.h"
#include "mongo/rpc/metadata/metadata_hook.h"
@@ -52,12 +53,14 @@ std::unique_ptr<NetworkInterface> makeNetworkInterface(std::string instanceName)
std::unique_ptr<NetworkInterface> makeNetworkInterface(
std::string instanceName,
std::unique_ptr<NetworkConnectionHook> hook,
- std::unique_ptr<rpc::EgressMetadataHook> metadataHook) {
+ std::unique_ptr<rpc::EgressMetadataHook> metadataHook,
+ ConnectionPool::Options connPoolOptions) {
NetworkInterfaceASIO::Options options{};
options.instanceName = std::move(instanceName);
options.networkConnectionHook = std::move(hook);
options.metadataHook = std::move(metadataHook);
options.timerFactory = stdx::make_unique<AsyncTimerFactoryASIO>();
+ options.connectionPoolOptions = connPoolOptions;
#ifdef MONGO_CONFIG_SSL
if (SSLManagerInterface* manager = getSSLManager()) {
diff --git a/src/mongo/executor/network_interface_factory.h b/src/mongo/executor/network_interface_factory.h
index 20fe061081a..7aa8a8cdce5 100644
--- a/src/mongo/executor/network_interface_factory.h
+++ b/src/mongo/executor/network_interface_factory.h
@@ -31,6 +31,7 @@
#include <memory>
#include <string>
+#include "mongo/executor/connection_pool.h"
#include "mongo/executor/network_interface.h"
namespace mongo {
@@ -54,7 +55,8 @@ std::unique_ptr<NetworkInterface> makeNetworkInterface(std::string instanceName)
std::unique_ptr<NetworkInterface> makeNetworkInterface(
std::string instanceName,
std::unique_ptr<NetworkConnectionHook> hook,
- std::unique_ptr<rpc::EgressMetadataHook> metadataHook);
+ std::unique_ptr<rpc::EgressMetadataHook> metadataHook,
+ ConnectionPool::Options options = ConnectionPool::Options());
} // namespace executor
} // namespace mongo
diff --git a/src/mongo/s/chunk_manager.cpp b/src/mongo/s/chunk_manager.cpp
index fcb21b5002c..f785e748c54 100644
--- a/src/mongo/s/chunk_manager.cpp
+++ b/src/mongo/s/chunk_manager.cpp
@@ -185,13 +185,11 @@ void ChunkManager::loadExistingRanges(OperationContext* txn, const ChunkManager*
Timer t;
+ log() << "ChunkManager loading chunks for " << _ns << " sequenceNumber: " << _sequenceNumber
+ << " based on: " << (oldManager ? oldManager->getVersion().toString() : "(empty)");
+
bool success = _load(txn, chunkMap, shardIds, &shardVersions, oldManager);
if (success) {
- log() << "ChunkManager: time to load chunks for " << _ns << ": " << t.millis() << "ms"
- << " sequenceNumber: " << _sequenceNumber << " version: " << _version.toString()
- << " based on: "
- << (oldManager ? oldManager->getVersion().toString() : "(empty)");
-
// TODO: Merge into diff code above, so we validate in one place
if (isChunkMapValid(chunkMap)) {
_chunkMap.swap(chunkMap);
@@ -199,15 +197,15 @@ void ChunkManager::loadExistingRanges(OperationContext* txn, const ChunkManager*
_shardVersions.swap(shardVersions);
_chunkRanges.reloadAll(_chunkMap);
+ log() << "ChunkManager load took " << t.millis() << " ms and found version "
+ << _version;
+
return;
}
}
- if (_chunkMap.size() < 10) {
- _printChunks();
- }
-
- warning() << "ChunkManager loaded an invalid config for " << _ns << ", trying again";
+ warning() << "ChunkManager load failed after " << t.millis()
+ << " ms and will be retried up to " << tries << " more times";
sleepmillis(10 * (3 - tries));
}
diff --git a/src/mongo/s/client/shard_registry.cpp b/src/mongo/s/client/shard_registry.cpp
index 9287d7daf8a..11a4d5b5649 100644
--- a/src/mongo/s/client/shard_registry.cpp
+++ b/src/mongo/s/client/shard_registry.cpp
@@ -871,13 +871,13 @@ void ShardRegistry::updateReplSetMonitor(const std::shared_ptr<RemoteCommandTarg
if (ErrorCodes::isNotMasterError(remoteCommandStatus.code()) ||
(remoteCommandStatus == ErrorCodes::InterruptedDueToReplStateChange)) {
- targeter->markHostNotMaster(remoteHost);
+ targeter->markHostNotMaster(remoteHost, remoteCommandStatus);
} else if (ErrorCodes::isNetworkError(remoteCommandStatus.code())) {
- targeter->markHostUnreachable(remoteHost);
+ targeter->markHostUnreachable(remoteHost, remoteCommandStatus);
} else if (remoteCommandStatus == ErrorCodes::NotMasterOrSecondary) {
- targeter->markHostUnreachable(remoteHost);
+ targeter->markHostUnreachable(remoteHost, remoteCommandStatus);
} else if (remoteCommandStatus == ErrorCodes::ExceededTimeLimit) {
- targeter->markHostUnreachable(remoteHost);
+ targeter->markHostUnreachable(remoteHost, remoteCommandStatus);
}
}
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 3a988888202..5400114f85d 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -65,6 +65,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/client/parallel',
+ '$BUILD_DIR/mongo/db/commands/apply_ops_cmd_common',
'$BUILD_DIR/mongo/db/pipeline/pipeline',
'$BUILD_DIR/mongo/db/commands/killcursors_common',
'$BUILD_DIR/mongo/s/coreshard',
diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp
index a48076dc93a..0e1353bd9fc 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -87,9 +87,9 @@ public:
* the command that you are explaining. The auth check is performed recursively
* on the nested command.
*/
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
+ virtual Status checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
if (Object != cmdObj.firstElement().type()) {
return Status(ErrorCodes::BadValue, "explain command requires a nested object");
}
@@ -103,7 +103,7 @@ public:
return Status(ErrorCodes::CommandNotFound, ss);
}
- return commToExplain->checkAuthForCommand(client, dbname, explainObj);
+ return commToExplain->checkAuthForOperation(txn, dbname, explainObj);
}
virtual bool run(OperationContext* txn,
diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp
index 6737fe47674..5d7ea13a58d 100644
--- a/src/mongo/s/commands/commands_public.cpp
+++ b/src/mongo/s/commands/commands_public.cpp
@@ -39,6 +39,7 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/commands.h"
+#include "mongo/db/commands/apply_ops_cmd_common.h"
#include "mongo/db/commands/copydb.h"
#include "mongo/db/commands/rename_collection.h"
#include "mongo/db/lasterror.h"
@@ -1371,11 +1372,11 @@ public:
class ApplyOpsCmd : public PublicGridCommand {
public:
ApplyOpsCmd() : PublicGridCommand("applyOps") {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- // applyOps can do pretty much anything, so require all privileges.
- RoleGraph::generateUniversalPrivileges(out);
+
+ virtual Status checkAuthForOperation(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return checkAuthForApplyOpsCommand(txn, dbname, cmdObj);
}
virtual bool run(OperationContext* txn,
const string& dbName,
diff --git a/src/mongo/s/d_merge.cpp b/src/mongo/s/d_merge.cpp
index d2481310f94..0b07759fc68 100644
--- a/src/mongo/s/d_merge.cpp
+++ b/src/mongo/s/d_merge.cpp
@@ -236,11 +236,7 @@ bool mergeChunks(OperationContext* txn,
//
// Run apply ops command
//
- Status applyOpsStatus = runApplyOpsCmd(txn, chunksToMerge, shardVersion, mergeVersion);
- if (!applyOpsStatus.isOK()) {
- warning() << applyOpsStatus;
- return false;
- }
+ uassertStatusOK(runApplyOpsCmd(txn, chunksToMerge, metadata->getCollVersion(), mergeVersion));
//
// Install merged chunk metadata
@@ -320,7 +316,7 @@ BSONObj buildOpRemoveChunk(const ChunkType& chunkToRemove) {
BSONArray buildOpPrecond(const string& ns,
const string& shardName,
- const ChunkVersion& shardVersion) {
+ const ChunkVersion& collectionVersion) {
BSONArrayBuilder preCond;
BSONObjBuilder condB;
condB.append("ns", ChunkType::ConfigNS);
@@ -329,7 +325,7 @@ BSONArray buildOpPrecond(const string& ns,
<< BSON(ChunkType::DEPRECATED_lastmod() << -1)));
{
BSONObjBuilder resB(condB.subobjStart("res"));
- shardVersion.addToBSON(resB, ChunkType::DEPRECATED_lastmod());
+ collectionVersion.addToBSON(resB, ChunkType::DEPRECATED_lastmod());
resB.done();
}
preCond.append(condB.obj());
@@ -338,7 +334,7 @@ BSONArray buildOpPrecond(const string& ns,
Status runApplyOpsCmd(OperationContext* txn,
const std::vector<ChunkType>& chunksToMerge,
- const ChunkVersion& currShardVersion,
+ const ChunkVersion& collectionVersion,
const ChunkVersion& newMergedVersion) {
BSONArrayBuilder updatesB;
@@ -360,9 +356,11 @@ Status runApplyOpsCmd(OperationContext* txn,
updatesB.append(buildOpRemoveChunk(chunkToMerge));
}
- BSONArray preCond = buildOpPrecond(firstChunk.getNS(), firstChunk.getShard(), currShardVersion);
+ BSONArray preCond =
+ buildOpPrecond(firstChunk.getNS(), firstChunk.getShard(), collectionVersion);
return grid.catalogManager(txn)->applyChunkOpsDeprecated(
txn, updatesB.arr(), preCond, firstChunk.getNS(), newMergedVersion);
}
-}
+
+} // namespace mongo
diff --git a/src/mongo/s/query/async_results_merger.cpp b/src/mongo/s/query/async_results_merger.cpp
index c82de6a3bbe..984c2f85184 100644
--- a/src/mongo/s/query/async_results_merger.cpp
+++ b/src/mongo/s/query/async_results_merger.cpp
@@ -304,6 +304,14 @@ Status AsyncResultsMerger::askForNextBatch_inlock(size_t remoteIndex) {
return Status::OK();
}
+/*
+ * Note: When nextEvent() is called to do retries, only the remotes with retriable errors will
+ * be rescheduled because:
+ *
+ * 1. Other pending remotes still have callback assigned to them.
+ * 2. Remotes that already has some result will have a non-empty buffer.
+ * 3. Remotes that reached maximum retries will be in 'exhausted' state.
+ */
StatusWith<executor::TaskExecutor::EventHandle> AsyncResultsMerger::nextEvent() {
stdx::lock_guard<stdx::mutex> lk(_mutex);
@@ -441,24 +449,29 @@ void AsyncResultsMerger::handleBatchResponse(
}
}
- // If the error is retriable, schedule another request.
+ // If we can still retry the initial cursor establishment, reset the state so it can be
+ // retried the next time nextEvent is called. Never retry getMores to avoid
+ // accidentally skipping results.
if (!remote.cursorId && remote.retryCount < kMaxNumFailedHostRetryAttempts &&
ShardRegistry::kAllRetriableErrors.count(cursorResponseStatus.getStatus().code())) {
+ invariant(remote.docBuffer.empty());
+
LOG(1) << "Initial cursor establishment failed with retriable error and will be retried"
<< causedBy(cursorResponseStatus.getStatus());
++remote.retryCount;
- // Since we potentially updated the targeter that the last host it chose might be
- // faulty, the call below may end up getting a different host.
- remote.status = askForNextBatch_inlock(remoteIndex);
- if (remote.status.isOK()) {
- return;
+ remote.status = Status::OK(); // Reset status so it can be retried.
+
+ // Signal the merger thread to make it retry this remote again.
+ if (_currentEvent.isValid()) {
+ // To prevent ourselves from signalling the event twice,
+ // we set '_currentEvent' as invalid after signalling it.
+ _executor->signalEvent(_currentEvent);
+ _currentEvent = executor::TaskExecutor::EventHandle();
}
- // If we end up here, it means we failed to schedule the retry request, which is a more
- // severe error that should not be retried. Just pass through to the error handling
- // logic below.
+ return;
} else {
remote.status = cursorResponseStatus.getStatus();
}
diff --git a/src/mongo/s/query/async_results_merger.h b/src/mongo/s/query/async_results_merger.h
index d30281aa8f6..4f1d8e7ca58 100644
--- a/src/mongo/s/query/async_results_merger.h
+++ b/src/mongo/s/query/async_results_merger.h
@@ -146,6 +146,12 @@ public:
* Also invalid to call if there is an outstanding event, created by a previous call to this
* function, that has not yet been signaled. If there is an outstanding unsignaled event,
* returns an error.
+ *
+ * Conditions when event can be signaled:
+ * - Finished collecting results from all remotes.
+ * - One of the host failed with a retriable error. In this case, if ready() is false, then
+ * the caller should call nextEvent() to retry the request on the hosts that errored. If
+ * ready() is true, then either the error was not retriable or it has exhausted max retries.
*/
StatusWith<executor::TaskExecutor::EventHandle> nextEvent();
diff --git a/src/mongo/s/query/async_results_merger_test.cpp b/src/mongo/s/query/async_results_merger_test.cpp
index 8f9e04664b1..31cf4228bcf 100644
--- a/src/mongo/s/query/async_results_merger_test.cpp
+++ b/src/mongo/s/query/async_results_merger_test.cpp
@@ -1220,17 +1220,21 @@ TEST_F(AsyncResultsMergerTest, RetryOnNotMasterNoSlaveOkSingleNode) {
makeCursorFromFindCmd(findCmd, {kTestShardIds[0]});
ASSERT_FALSE(arm->ready());
- auto readyEvent = unittest::assertGet(arm->nextEvent());
- ASSERT_FALSE(arm->ready());
// First and second attempts return an error.
+ auto readyEvent = unittest::assertGet(arm->nextEvent());
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
// Third attempt succeeds.
+ readyEvent = unittest::assertGet(arm->nextEvent());
std::vector<CursorResponse> responses;
std::vector<BSONObj> batch = {fromjson("{_id: 1}")};
responses.emplace_back(_nss, CursorId(0), batch);
@@ -1254,15 +1258,25 @@ TEST_F(AsyncResultsMergerTest, RetryOnNotMasterNoSlaveOkAllFailSingleNode) {
// All attempts return an error (one attempt plus three retries)
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::NotMasterNoSlaveOk, "not master and not slave"});
+ executor->waitForEvent(readyEvent);
ASSERT_TRUE(arm->ready());
auto status = arm->nextReady();
@@ -1289,19 +1303,75 @@ TEST_F(AsyncResultsMergerTest, RetryOnHostUnreachableAllowPartialResults) {
// From the second host all attempts return an error (one attempt plus three retries)
scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+ executor->waitForEvent(readyEvent);
ASSERT_FALSE(arm->ready());
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+ executor->waitForEvent(readyEvent);
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ executor->waitForEvent(readyEvent);
ASSERT_TRUE(arm->ready());
- ASSERT_TRUE(arm->ready());
ASSERT_EQ(fromjson("{_id: 1}"), *unittest::assertGet(arm->nextReady()));
+ ASSERT_TRUE(arm->remotesExhausted());
+ ASSERT_TRUE(arm->ready());
+}
+
+TEST_F(AsyncResultsMergerTest, ErrorAtFirstAttemptAtSameTimeShouldEventuallyReturnResults) {
+ BSONObj findCmd = fromjson("{find: 'testcoll', sort: {_id: 1}}");
+ makeCursorFromFindCmd(findCmd, {kTestShardIds[0], kTestShardIds[1]});
+
+ ASSERT_FALSE(arm->ready());
+ auto readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
+
+ // Both hosts return an error which indicates that the request should be retried.
+ scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+ scheduleErrorResponse({ErrorCodes::HostUnreachable, "host unreachable"});
+
+ executor->waitForEvent(readyEvent);
+ ASSERT_FALSE(arm->ready());
+
+ readyEvent = unittest::assertGet(arm->nextEvent());
+ ASSERT_FALSE(arm->ready());
+
+
+ // Return valid data on both hosts.
+ {
+ std::vector<CursorResponse> responses;
+ std::vector<BSONObj> batch = {fromjson("{_id: 1, $sortKey: {'': 1}}")};
+ responses.emplace_back(_nss, CursorId(0), batch);
+ scheduleNetworkResponses(std::move(responses),
+ CursorResponse::ResponseType::InitialResponse);
+ }
+
+ {
+ std::vector<CursorResponse> responses;
+ std::vector<BSONObj> batch = {fromjson("{_id: 2, $sortKey: {'': 2}}}")};
+ responses.emplace_back(_nss, CursorId(0), batch);
+ scheduleNetworkResponses(std::move(responses),
+ CursorResponse::ResponseType::InitialResponse);
+ }
+
+ executor->waitForEvent(readyEvent);
+ ASSERT_TRUE(arm->ready());
+
+ ASSERT_EQ(fromjson("{_id: 1, $sortKey: {'': 1}}}"), *unittest::assertGet(arm->nextReady()));
+ ASSERT_EQ(fromjson("{_id: 2, $sortKey: {'': 2}}}}"), *unittest::assertGet(arm->nextReady()));
ASSERT_TRUE(arm->remotesExhausted());
ASSERT_TRUE(arm->ready());
diff --git a/src/mongo/s/s_only.cpp b/src/mongo/s/s_only.cpp
index 2322aa67d3d..3cf43adb15c 100644
--- a/src/mongo/s/s_only.cpp
+++ b/src/mongo/s/s_only.cpp
@@ -110,7 +110,7 @@ void Command::execCommandClientBasic(OperationContext* txn,
return;
}
- Status status = _checkAuthorization(c, &client, dbname, cmdObj);
+ Status status = checkAuthorization(c, txn, dbname, cmdObj);
if (!status.isOK()) {
appendCommandStatus(result, status);
return;
diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp
index 2200143c5bd..0631c191503 100644
--- a/src/mongo/s/server.cpp
+++ b/src/mongo/s/server.cpp
@@ -141,10 +141,15 @@ void exitCleanly(ExitCode code) {
txn = uniqueTxn.get();
}
- auto cursorManager = grid.getCursorManager();
- cursorManager->shutdown();
- grid.shardRegistry()->shutdown();
- grid.catalogManager(txn)->shutDown(txn);
+ if (auto cursorManager = grid.getCursorManager()) {
+ cursorManager->shutdown();
+ }
+ if (auto shardRegistry = grid.shardRegistry()) {
+ shardRegistry->shutdown();
+ }
+ if (auto catalogManager = grid.catalogManager(txn)) {
+ catalogManager->shutDown(txn);
+ }
}
dbexit(code);
diff --git a/src/mongo/s/sharding_initialization.cpp b/src/mongo/s/sharding_initialization.cpp
index 99079c97c8c..37257afe244 100644
--- a/src/mongo/s/sharding_initialization.cpp
+++ b/src/mongo/s/sharding_initialization.cpp
@@ -39,7 +39,9 @@
#include "mongo/client/syncclusterconnection.h"
#include "mongo/db/audit.h"
#include "mongo/db/server_options.h"
+#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
+#include "mongo/executor/connection_pool.h"
#include "mongo/executor/network_interface_factory.h"
#include "mongo/executor/network_interface_thread_pool.h"
#include "mongo/executor/task_executor.h"
@@ -47,7 +49,6 @@
#include "mongo/executor/thread_pool_task_executor.h"
#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/rpc/metadata/metadata_hook.h"
-#include "mongo/rpc/metadata/config_server_metadata.h"
#include "mongo/s/catalog/forwarding_catalog_manager.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/client/sharding_network_connection_hook.h"
@@ -61,6 +62,16 @@
namespace mongo {
+using executor::ConnectionPool;
+
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(ShardingTaskExecutorPoolHostTimeoutMS, int, -1);
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(ShardingTaskExecutorPoolMaxSize, int, -1);
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(ShardingTaskExecutorPoolMinSize,
+ int,
+ static_cast<int>(ConnectionPool::kDefaultMinConns));
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(ShardingTaskExecutorPoolRefreshRequirementMS, int, -1);
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(ShardingTaskExecutorPoolRefreshTimeoutMS, int, -1);
+
namespace {
using executor::NetworkInterface;
@@ -124,13 +135,15 @@ std::unique_ptr<ThreadPoolTaskExecutor> makeTaskExecutor(std::unique_ptr<Network
stdx::make_unique<NetworkInterfaceThreadPool>(netPtr), std::move(net));
}
-std::unique_ptr<TaskExecutorPool> makeTaskExecutorPool(std::unique_ptr<NetworkInterface> fixedNet) {
+std::unique_ptr<TaskExecutorPool> makeTaskExecutorPool(std::unique_ptr<NetworkInterface> fixedNet,
+ ConnectionPool::Options connPoolOptions) {
std::vector<std::unique_ptr<executor::TaskExecutor>> executors;
for (size_t i = 0; i < TaskExecutorPool::getSuggestedPoolSize(); ++i) {
auto net = executor::makeNetworkInterface(
"NetworkInterfaceASIO-TaskExecutorPool-" + std::to_string(i),
stdx::make_unique<ShardingNetworkConnectionHook>(),
- stdx::make_unique<ShardingEgressMetadataHook>());
+ stdx::make_unique<ShardingEgressMetadataHook>(),
+ connPoolOptions);
auto netPtr = net.get();
auto exec = stdx::make_unique<ThreadPoolTaskExecutor>(
stdx::make_unique<NetworkInterfaceThreadPool>(netPtr), std::move(net));
@@ -157,14 +170,34 @@ Status initializeGlobalShardingState(OperationContext* txn,
[](const HostAndPort& target, const executor::RemoteCommandResponse& isMasterReply) {
return ShardingNetworkConnectionHook::validateHostImpl(target, isMasterReply, true);
});
+
+ // We don't set the ConnectionPool's static const variables to be the default value in
+ // MONGO_EXPORT_STARTUP_SERVER_PARAMETER because it's not guaranteed to be initialized.
+ // The following code is a workaround.
+ ConnectionPool::Options connPoolOptions;
+ connPoolOptions.hostTimeout = (ShardingTaskExecutorPoolHostTimeoutMS != -1)
+ ? Milliseconds(ShardingTaskExecutorPoolHostTimeoutMS)
+ : ConnectionPool::kDefaultHostTimeout;
+ connPoolOptions.maxConnections = (ShardingTaskExecutorPoolMaxSize != -1)
+ ? ShardingTaskExecutorPoolMaxSize
+ : ConnectionPool::kDefaultMaxConns;
+ connPoolOptions.minConnections = ShardingTaskExecutorPoolMinSize;
+ connPoolOptions.refreshRequirement = (ShardingTaskExecutorPoolRefreshRequirementMS != -1)
+ ? Milliseconds(ShardingTaskExecutorPoolRefreshRequirementMS)
+ : ConnectionPool::kDefaultRefreshRequirement;
+ connPoolOptions.refreshTimeout = (ShardingTaskExecutorPoolRefreshTimeoutMS != -1)
+ ? Milliseconds(ShardingTaskExecutorPoolRefreshTimeoutMS)
+ : ConnectionPool::kDefaultRefreshTimeout;
+
auto network =
executor::makeNetworkInterface("NetworkInterfaceASIO-ShardRegistry",
stdx::make_unique<ShardingNetworkConnectionHook>(),
- stdx::make_unique<ShardingEgressMetadataHook>());
+ stdx::make_unique<ShardingEgressMetadataHook>(),
+ connPoolOptions);
auto networkPtr = network.get();
auto shardRegistry(
stdx::make_unique<ShardRegistry>(stdx::make_unique<RemoteCommandTargeterFactoryImpl>(),
- makeTaskExecutorPool(std::move(network)),
+ makeTaskExecutorPool(std::move(network), connPoolOptions),
networkPtr,
makeTaskExecutor(executor::makeNetworkInterface(
"NetworkInterfaceASIO-ShardRegistry-TaskExecutor")),
diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js
index b1f2a94a735..0d1e225a990 100644
--- a/src/mongo/shell/assert.js
+++ b/src/mongo/shell/assert.js
@@ -3,6 +3,9 @@ doassert = function(msg, obj) {
if (typeof(msg) == "function")
msg = msg();
+ if (typeof(msg) == "object")
+ msg = tojson(msg);
+
if (typeof(msg) == "string" && msg.indexOf("assert") == 0)
print(msg);
else
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index c064c292e83..609bbabf1b4 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -727,6 +727,9 @@ int _main(int argc, char* argv[], char** envp) {
cout << "failed to load: " << shellGlobalParams.files[i] << endl;
return -3;
}
+ if (mongo::shell_utils::KillMongoProgramInstances() != EXIT_SUCCESS) {
+ return -3;
+ }
}
if (shellGlobalParams.files.size() == 0 && shellGlobalParams.script.empty())
diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js
index 6b2ec8261f0..6f442f94b05 100644
--- a/src/mongo/shell/mongo.js
+++ b/src/mongo/shell/mongo.js
@@ -280,7 +280,6 @@ Mongo.prototype.writeMode = function() {
if (this.hasWriteCommands()) {
// good with whatever is already set
} else if (this._writeMode == "commands") {
- print("Cannot use commands write mode, degrading to compatibility mode");
this._writeMode = "compatibility";
}
@@ -334,7 +333,6 @@ Mongo.prototype.readMode = function() {
if (hasReadCommands && !this.isReplicaSetConnection()) {
this._readMode = "commands";
} else {
- print("Cannot use 'commands' readMode, degrading to 'legacy' mode");
this._readMode = "legacy";
}
} catch (e) {
diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js
index 8e6d5a05525..fb0807909fb 100644
--- a/src/mongo/shell/replsettest.js
+++ b/src/mongo/shell/replsettest.js
@@ -422,6 +422,26 @@ var ReplSetTest = function(opts) {
};
/**
+ * Blocks until the specified node says it's syncing from the given upstream node.
+ */
+ this.awaitSyncSource = function(node, upstreamNode, timeout) {
+ print("Waiting for node " + node.name + " to start syncing from " + upstreamNode.name);
+ var status = null;
+ assert.soonNoExcept(
+ function() {
+ status = node.getDB("admin").runCommand({replSetGetStatus: 1});
+ for (var j = 0; j < status.members.length; j++) {
+ if (status.members[j].self) {
+ return status.members[j].syncingTo === upstreamNode.host;
+ }
+ }
+ return false;
+ },
+ "Awaiting node " + node + " syncing from " + upstreamNode + ": " + tojson(status),
+ timeout);
+ };
+
+ /**
* Blocks until all nodes agree on who the primary is.
*/
this.awaitNodesAgreeOnPrimary = function(timeout) {
@@ -917,6 +937,12 @@ var ReplSetTest = function(opts) {
printjson(this.nodes);
+ // Clean up after noReplSet to ensure it doesn't effect future restarts.
+ if (options.noReplSet) {
+ this.nodes[n].fullOptions.replSet = defaults.replSet;
+ delete this.nodes[n].fullOptions.noReplSet;
+ }
+
wait = wait || false;
if (!wait.toFixed) {
if (wait)
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index 7f630ae01f4..e9df265f39b 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -952,6 +952,15 @@ var ShardingTest = function(params) {
var numMongos = otherParams.hasOwnProperty('mongos') ? otherParams.mongos : 1;
var numConfigs = otherParams.hasOwnProperty('config') ? otherParams.config : 3;
+ // Default enableBalancer to false.
+ otherParams.enableBalancer =
+ ("enableBalancer" in otherParams) && (otherParams.enableBalancer === true);
+
+ // Let autosplit behavior match that of the balancer if autosplit is not explicitly set.
+ if (!("enableAutoSplit" in otherParams)) {
+ otherParams.enableAutoSplit = otherParams.enableBalancer;
+ }
+
// Allow specifying mixed-type options like this:
// { mongos : [ { noprealloc : "" } ],
// config : [ { smallfiles : "" } ],
@@ -1347,6 +1356,11 @@ var ShardingTest = function(params) {
options.port = options.port || allocatePort();
+ // Disable autosplitting unless it is explicitly turned on.
+ if (!otherParams.enableAutoSplit) {
+ options.noAutoSplit = "";
+ }
+
if (otherParams.useBridge) {
var bridgeOptions =
Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {});
diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp
index 4d5dcffc357..81793143ec0 100644
--- a/src/mongo/shell/shell_utils_launcher.cpp
+++ b/src/mongo/shell/shell_utils_launcher.cpp
@@ -743,7 +743,6 @@ inline void kill_wrapper(ProcessId pid, int sig, int port, const BSONObj& opt) {
int killDb(int port, ProcessId _pid, int signal, const BSONObj& opt) {
ProcessId pid;
- int exitCode = 0;
if (port > 0) {
if (!registry.isPortRegistered(port)) {
log() << "No db started on port: " << port;
@@ -756,32 +755,18 @@ int killDb(int port, ProcessId _pid, int signal, const BSONObj& opt) {
kill_wrapper(pid, signal, port, opt);
- bool processTerminated = false;
- bool killSignalSent = (signal == SIGKILL);
- for (int i = 0; i < 1300; ++i) {
- if (i == 600) {
- log() << "process on port " << port << ", with pid " << pid
- << " not terminated, sending sigkill";
- kill_wrapper(pid, SIGKILL, port, opt);
- killSignalSent = true;
- }
- processTerminated = wait_for_pid(pid, false, &exitCode);
- if (processTerminated) {
- break;
- }
- sleepmillis(100);
- }
- if (!processTerminated) {
- severe() << "failed to terminate process on port " << port << ", with pid " << pid;
- invariant(false);
- }
-
+ int exitCode = EXIT_FAILURE;
+ bool processTerminated = wait_for_pid(pid, true, &exitCode);
registry.deleteProgram(pid);
- if (killSignalSent) {
+ if (signal == SIGKILL) {
sleepmillis(4000); // allow operating system to reclaim resources
}
+ if (!processTerminated) {
+ warning() << "process " << pid << " failed to terminate.";
+ return EXIT_FAILURE;
+ }
return exitCode;
}
@@ -837,12 +822,18 @@ BSONObj StopMongoProgramByPid(const BSONObj& a, void* data) {
return BSON("" << (double)code);
}
-void KillMongoProgramInstances() {
+int KillMongoProgramInstances() {
vector<ProcessId> pids;
registry.getRegisteredPids(pids);
+ int returnCode = EXIT_SUCCESS;
for (auto&& pid : pids) {
- killDb(0, pid, SIGTERM);
+ int port = registry.portForPid(pid);
+ int code = killDb(port != -1 ? port : 0, pid, SIGTERM);
+ if (code != EXIT_SUCCESS) {
+ returnCode = code;
+ }
}
+ return returnCode;
}
MongoProgramScope::~MongoProgramScope() {
diff --git a/src/mongo/shell/shell_utils_launcher.h b/src/mongo/shell/shell_utils_launcher.h
index fd58c1bd614..dcf57c4451d 100644
--- a/src/mongo/shell/shell_utils_launcher.h
+++ b/src/mongo/shell/shell_utils_launcher.h
@@ -52,7 +52,7 @@ struct MongoProgramScope {
MongoProgramScope() {} // Avoid 'unused variable' warning.
~MongoProgramScope();
};
-void KillMongoProgramInstances();
+int KillMongoProgramInstances();
void goingAwaySoon();
void installShellUtilsLauncher(Scope& scope);
diff --git a/src/mongo/unittest/unittest.cpp b/src/mongo/unittest/unittest.cpp
index dfe1ddd2c10..ef523832b1e 100644
--- a/src/mongo/unittest/unittest.cpp
+++ b/src/mongo/unittest/unittest.cpp
@@ -214,6 +214,13 @@ void Test::stopCapturingLogMessages() {
checked_cast<StringVectorAppender*>(_captureAppender.get())->disable();
_isCapturingLogMessages = false;
}
+void Test::printCapturedLogLines() const {
+ log() << "****************************** Captured Lines (start) *****************************";
+ std::for_each(getCapturedLogMessages().begin(),
+ getCapturedLogMessages().end(),
+ [](std::string line) { log() << line; });
+ log() << "****************************** Captured Lines (end) ******************************";
+}
int64_t Test::countLogLinesContaining(const std::string& needle) {
return std::count_if(getCapturedLogMessages().begin(),
diff --git a/src/mongo/unittest/unittest.h b/src/mongo/unittest/unittest.h
index 8953681f428..5ed906b353c 100644
--- a/src/mongo/unittest/unittest.h
+++ b/src/mongo/unittest/unittest.h
@@ -324,6 +324,11 @@ protected:
*/
int64_t countLogLinesContaining(const std::string& needle);
+ /**
+ * Prints the captured log lines.
+ */
+ void printCapturedLogLines() const;
+
private:
/**
* Called on the test object before running the test.
diff --git a/src/mongo/util/heap_profiler.cpp b/src/mongo/util/heap_profiler.cpp
index 288bd441e08..ecfa3bdb885 100644
--- a/src/mongo/util/heap_profiler.cpp
+++ b/src/mongo/util/heap_profiler.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/commands/server_status.h"
#include "mongo/db/server_parameters.h"
#include "mongo/util/log.h"
+#include "mongo/util/stacktrace.h"
#include <gperftools/malloc_hook.h>
#include <third_party/murmurhash3/MurmurHash3.h>
@@ -530,6 +531,9 @@ private:
<< "maxActiveMemory " << maxActiveMemory / MB << " MB; "
<< "objTableSize " << objTableSize / MB << " MB; "
<< "stackTableSize " << stackTableSize / MB << " MB";
+ // print a stack trace to log somap for post-facto symbolization
+ log() << "following stack trace is for heap profiler informational purposes";
+ printStackTrace();
logGeneralStats = false;
}
diff --git a/src/mongo/util/net/hostandport.cpp b/src/mongo/util/net/hostandport.cpp
index ea1d0f93467..890959b123f 100644
--- a/src/mongo/util/net/hostandport.cpp
+++ b/src/mongo/util/net/hostandport.cpp
@@ -170,6 +170,16 @@ std::ostream& operator<<(std::ostream& os, const HostAndPort& hp) {
return os << hp.toString();
}
+template <typename Allocator>
+StringBuilderImpl<Allocator>& operator<<(StringBuilderImpl<Allocator>& os, const HostAndPort& hp) {
+ return os << hp.toString();
+}
+
+template StringBuilderImpl<TrivialAllocator>& operator<<(StringBuilderImpl<TrivialAllocator>&,
+ const HostAndPort&);
+template StringBuilderImpl<StackAllocator>& operator<<(StringBuilderImpl<StackAllocator>&,
+ const HostAndPort&);
+
} // namespace mongo
MONGO_HASH_NAMESPACE_START
diff --git a/src/mongo/util/net/hostandport.h b/src/mongo/util/net/hostandport.h
index 6f7bd46b43c..53209bbaa04 100644
--- a/src/mongo/util/net/hostandport.h
+++ b/src/mongo/util/net/hostandport.h
@@ -34,7 +34,10 @@
#include "mongo/platform/hash_namespace.h"
namespace mongo {
+
class Status;
+template <typename Allocator>
+class StringBuilderImpl;
class StringData;
template <typename T>
class StatusWith;
@@ -124,6 +127,9 @@ private:
std::ostream& operator<<(std::ostream& os, const HostAndPort& hp);
+template <typename Allocator>
+StringBuilderImpl<Allocator>& operator<<(StringBuilderImpl<Allocator>& os, const HostAndPort& hp);
+
} // namespace mongo
MONGO_HASH_NAMESPACE_START
diff --git a/src/third_party/SConscript b/src/third_party/SConscript
index 5ff1ace3b61..13307590e83 100644
--- a/src/third_party/SConscript
+++ b/src/third_party/SConscript
@@ -5,7 +5,7 @@ Import("wiredtiger")
snappySuffix = '-1.1.2'
zlibSuffix = '-1.2.8'
-pcreSuffix = "-8.38"
+pcreSuffix = "-8.39"
mozjsSuffix = '-38'
thirdPartyIncludePathList = [
diff --git a/src/third_party/pcre-8.38/132html b/src/third_party/pcre-8.39/132html
index e00024970a5..e00024970a5 100755
--- a/src/third_party/pcre-8.38/132html
+++ b/src/third_party/pcre-8.39/132html
diff --git a/src/third_party/pcre-8.38/AUTHORS b/src/third_party/pcre-8.39/AUTHORS
index d33723f198a..342417a8a19 100644
--- a/src/third_party/pcre-8.38/AUTHORS
+++ b/src/third_party/pcre-8.39/AUTHORS
@@ -8,7 +8,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2015 University of Cambridge
+Copyright (c) 1997-2016 University of Cambridge
All rights reserved
@@ -19,7 +19,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2015 Zoltan Herczeg
+Copyright(c) 2010-2016 Zoltan Herczeg
All rights reserved.
@@ -30,7 +30,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2015 Zoltan Herczeg
+Copyright(c) 2009-2016 Zoltan Herczeg
All rights reserved.
diff --git a/src/third_party/pcre-8.38/COPYING b/src/third_party/pcre-8.39/COPYING
index 58eed01b61d..58eed01b61d 100644
--- a/src/third_party/pcre-8.38/COPYING
+++ b/src/third_party/pcre-8.39/COPYING
diff --git a/src/third_party/pcre-8.38/ChangeLog b/src/third_party/pcre-8.39/ChangeLog
index 5e5bf188cea..a34f845f8a1 100644
--- a/src/third_party/pcre-8.38/ChangeLog
+++ b/src/third_party/pcre-8.39/ChangeLog
@@ -4,12 +4,104 @@ ChangeLog for PCRE
Note that the PCRE 8.xx series (PCRE1) is now in a bugfix-only state. All
development is happening in the PCRE2 10.xx series.
+Version 8.39 14-June-2016
+-------------------------
+
+1. If PCRE_AUTO_CALLOUT was set on a pattern that had a (?# comment between
+ an item and its qualifier (for example, A(?#comment)?B) pcre_compile()
+ misbehaved. This bug was found by the LLVM fuzzer.
+
+2. Similar to the above, if an isolated \E was present between an item and its
+ qualifier when PCRE_AUTO_CALLOUT was set, pcre_compile() misbehaved. This
+ bug was found by the LLVM fuzzer.
+
+3. Further to 8.38/46, negated classes such as [^[:^ascii:]\d] were also not
+ working correctly in UCP mode.
+
+4. The POSIX wrapper function regexec() crashed if the option REG_STARTEND
+ was set when the pmatch argument was NULL. It now returns REG_INVARG.
+
+5. Allow for up to 32-bit numbers in the ordin() function in pcregrep.
+
+6. An empty \Q\E sequence between an item and its qualifier caused
+ pcre_compile() to misbehave when auto callouts were enabled. This bug was
+ found by the LLVM fuzzer.
+
+7. If a pattern that was compiled with PCRE_EXTENDED started with white
+ space or a #-type comment that was followed by (?-x), which turns off
+ PCRE_EXTENDED, and there was no subsequent (?x) to turn it on again,
+ pcre_compile() assumed that (?-x) applied to the whole pattern and
+ consequently mis-compiled it. This bug was found by the LLVM fuzzer.
+
+8. A call of pcre_copy_named_substring() for a named substring whose number
+ was greater than the space in the ovector could cause a crash.
+
+9. Yet another buffer overflow bug involved duplicate named groups with a
+ group that reset capture numbers (compare 8.38/7 below). Once again, I have
+ just allowed for more memory, even if not needed. (A proper fix is
+ implemented in PCRE2, but it involves a lot of refactoring.)
+
+10. pcre_get_substring_list() crashed if the use of \K in a match caused the
+ start of the match to be earlier than the end.
+
+11. Migrating appropriate PCRE2 JIT improvements to PCRE.
+
+12. A pattern such as /(?<=((?C)0))/, which has a callout inside a lookbehind
+ assertion, caused pcretest to generate incorrect output, and also to read
+ uninitialized memory (detected by ASAN or valgrind).
+
+13. A pattern that included (*ACCEPT) in the middle of a sufficiently deeply
+ nested set of parentheses of sufficient size caused an overflow of the
+ compiling workspace (which was diagnosed, but of course is not desirable).
+
+14. And yet another buffer overflow bug involving duplicate named groups, this
+ time nested, with a nested back reference. Yet again, I have just allowed
+ for more memory, because anything more needs all the refactoring that has
+ been done for PCRE2. An example pattern that provoked this bug is:
+ /((?J)(?'R'(?'R'(?'R'(?'R'(?'R'(?|(\k'R'))))))))/ and the bug was
+ registered as CVE-2016-1283.
+
+15. pcretest went into a loop if global matching was requested with an ovector
+ size less than 2. It now gives an error message. This bug was found by
+ afl-fuzz.
+
+16. An invalid pattern fragment such as (?(?C)0 was not diagnosing an error
+ ("assertion expected") when (?(?C) was not followed by an opening
+ parenthesis.
+
+17. Fixed typo ("&&" for "&") in pcre_study(). Fortunately, this could not
+ actually affect anything, by sheer luck.
+
+18. Applied Chris Wilson's patch (Bugzilla #1681) to CMakeLists.txt for MSVC
+ static compilation.
+
+19. Modified the RunTest script to incorporate a valgrind suppressions file so
+ that certain errors, provoked by the SSE2 instruction set when JIT is used,
+ are ignored.
+
+20. A racing condition is fixed in JIT reported by Mozilla.
+
+21. Minor code refactor to avoid "array subscript is below array bounds"
+ compiler warning.
+
+22. Minor code refactor to avoid "left shift of negative number" warning.
+
+23. Fix typo causing compile error when 16- or 32-bit JIT is compiled without
+ UCP support.
+
+24. Refactor to avoid compiler warnings in pcrecpp.cc.
+
+25. Refactor to fix a typo in pcre_jit_test.c
+
+26. Patch to support compiling pcrecpp.cc with Intel compiler.
+
+
Version 8.38 23-November-2015
-----------------------------
1. If a group that contained a recursive back reference also contained a
forward reference subroutine call followed by a non-forward-reference
- subroutine call, for example /.((?2)(?R)\1)()/, pcre2_compile() failed to
+ subroutine call, for example /.((?2)(?R)\1)()/, pcre_compile() failed to
compile correct code, leading to undefined behaviour or an internally
detected error. This bug was discovered by the LLVM fuzzer.
diff --git a/src/third_party/pcre-8.38/CheckMan b/src/third_party/pcre-8.39/CheckMan
index 480d735481e..480d735481e 100755
--- a/src/third_party/pcre-8.38/CheckMan
+++ b/src/third_party/pcre-8.39/CheckMan
diff --git a/src/third_party/pcre-8.38/CleanTxt b/src/third_party/pcre-8.39/CleanTxt
index 1f42519c8d7..1f42519c8d7 100755
--- a/src/third_party/pcre-8.38/CleanTxt
+++ b/src/third_party/pcre-8.39/CleanTxt
diff --git a/src/third_party/pcre-8.38/Detrail b/src/third_party/pcre-8.39/Detrail
index 1c5c7e9cae1..1c5c7e9cae1 100755
--- a/src/third_party/pcre-8.38/Detrail
+++ b/src/third_party/pcre-8.39/Detrail
diff --git a/src/third_party/pcre-8.38/HACKING b/src/third_party/pcre-8.39/HACKING
index 691b7a14e50..691b7a14e50 100644
--- a/src/third_party/pcre-8.38/HACKING
+++ b/src/third_party/pcre-8.39/HACKING
diff --git a/src/third_party/pcre-8.38/INSTALL b/src/third_party/pcre-8.39/INSTALL
index 2099840756e..2099840756e 100644
--- a/src/third_party/pcre-8.38/INSTALL
+++ b/src/third_party/pcre-8.39/INSTALL
diff --git a/src/third_party/pcre-8.38/LICENCE b/src/third_party/pcre-8.39/LICENCE
index 9f6f98e477f..dd977af971b 100644
--- a/src/third_party/pcre-8.38/LICENCE
+++ b/src/third_party/pcre-8.39/LICENCE
@@ -25,7 +25,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2015 University of Cambridge
+Copyright (c) 1997-2016 University of Cambridge
All rights reserved.
@@ -36,7 +36,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2015 Zoltan Herczeg
+Copyright(c) 2010-2016 Zoltan Herczeg
All rights reserved.
@@ -47,7 +47,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2015 Zoltan Herczeg
+Copyright(c) 2009-2016 Zoltan Herczeg
All rights reserved.
diff --git a/src/third_party/pcre-8.38/NEWS b/src/third_party/pcre-8.39/NEWS
index 7e42dcb3603..0ca1bab2a4c 100644
--- a/src/third_party/pcre-8.38/NEWS
+++ b/src/third_party/pcre-8.39/NEWS
@@ -1,6 +1,15 @@
News about PCRE releases
------------------------
+Release 8.39 14-June-2016
+-------------------------
+
+Some appropriate PCRE2 JIT improvements have been retro-fitted to PCRE1. Apart
+from that, this is another bug-fix release. Note that this library (now called
+PCRE1) is now being maintained for bug fixes only. New projects are advised to
+use the new PCRE2 libraries.
+
+
Release 8.38 23-November-2015
-----------------------------
diff --git a/src/third_party/pcre-8.38/NON-AUTOTOOLS-BUILD b/src/third_party/pcre-8.39/NON-AUTOTOOLS-BUILD
index 3910059106b..3910059106b 100644
--- a/src/third_party/pcre-8.38/NON-AUTOTOOLS-BUILD
+++ b/src/third_party/pcre-8.39/NON-AUTOTOOLS-BUILD
diff --git a/src/third_party/pcre-8.38/NON-UNIX-USE b/src/third_party/pcre-8.39/NON-UNIX-USE
index a25546b6ff5..a25546b6ff5 100644
--- a/src/third_party/pcre-8.38/NON-UNIX-USE
+++ b/src/third_party/pcre-8.39/NON-UNIX-USE
diff --git a/src/third_party/pcre-8.38/PrepareRelease b/src/third_party/pcre-8.39/PrepareRelease
index 9891e08d67f..9891e08d67f 100755
--- a/src/third_party/pcre-8.38/PrepareRelease
+++ b/src/third_party/pcre-8.39/PrepareRelease
diff --git a/src/third_party/pcre-8.38/README b/src/third_party/pcre-8.39/README
index 4887ebf350e..4887ebf350e 100644
--- a/src/third_party/pcre-8.38/README
+++ b/src/third_party/pcre-8.39/README
diff --git a/src/third_party/pcre-8.38/RunGrepTest b/src/third_party/pcre-8.39/RunGrepTest
index a6e93d34892..721ec5184ab 100755
--- a/src/third_party/pcre-8.38/RunGrepTest
+++ b/src/third_party/pcre-8.39/RunGrepTest
@@ -67,6 +67,15 @@ fi
./pcretest -C utf >/dev/null
utf8=$?
+# We need valgrind suppressions when JIT is in use. (This isn't perfect because
+# some tests are run with -no-jit, but as PCRE1 is in maintenance only, I have
+# not bothered about that.)
+
+./pcretest -C jit >/dev/null
+if [ $? -eq 1 -a "$valgrind" != "" ] ; then
+ valgrind="$valgrind --suppressions=./testdata/valgrind-jit.supp"
+fi
+
echo "Testing pcregrep main features"
echo "---------------------------- Test 1 ------------------------------" >testtrygrep
diff --git a/src/third_party/pcre-8.38/RunTest b/src/third_party/pcre-8.39/RunTest
index 67cfbf07cf9..357a739b75f 100755
--- a/src/third_party/pcre-8.38/RunTest
+++ b/src/third_party/pcre-8.39/RunTest
@@ -178,6 +178,7 @@ nojit=
sim=
skip=
valgrind=
+vjs=
# This is in case the caller has set aliases (as I do - PH)
unset cp ls mv rm
@@ -357,6 +358,9 @@ $sim ./pcretest -C jit >/dev/null
jit=$?
if [ $jit -ne 0 -a "$nojit" != "yes" ] ; then
jitopt=-s+
+ if [ "$valgrind" != "" ] ; then
+ vjs="--suppressions=$testdata/valgrind-jit.supp"
+ fi
fi
# If no specific tests were requested, select all. Those that are not
@@ -423,7 +427,7 @@ for bmode in "$test8" "$test16" "$test32"; do
if [ $do1 = yes ] ; then
echo $title1
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput1 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput1 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput1 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -441,7 +445,7 @@ fi
if [ $do2 = yes ] ; then
echo $title2 "(not UTF-$bits)"
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput2 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput2 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput2 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -504,7 +508,7 @@ if [ $do3 = yes ] ; then
if [ "$locale" != "" ] ; then
echo $title3 "(using '$locale' locale)"
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $infile testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $infile testtry
if [ $? = 0 ] ; then
if $cf $outfile testtry >teststdout || \
$cf $outfile2 testtry >teststdout || \
@@ -540,7 +544,7 @@ if [ $do4 = yes ] ; then
echo " Skipped because UTF-$bits support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput4 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput4 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput4 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -560,7 +564,7 @@ if [ $do5 = yes ] ; then
echo " Skipped because UTF-$bits support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput5 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput5 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput5 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -580,7 +584,7 @@ if [ $do6 = yes ] ; then
echo " Skipped because Unicode property support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput6 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput6 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput6 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -602,7 +606,7 @@ if [ $do7 = yes ] ; then
echo " Skipped because Unicode property support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput7 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput7 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput7 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -698,7 +702,7 @@ if [ $do12 = yes ] ; then
if [ $jit -eq 0 -o "$nojit" = "yes" ] ; then
echo " Skipped because JIT is not available or not usable"
else
- $sim $valgrind ./pcretest -q $bmode $testdata/testinput12 testtry
+ $sim $valgrind $vjs ./pcretest -q $bmode $testdata/testinput12 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput12 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -735,7 +739,7 @@ if [ "$do14" = yes ] ; then
cp -f $testdata/saved16 testsaved16
cp -f $testdata/saved32 testsaved32
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput14 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput14 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput14 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -759,7 +763,7 @@ if [ "$do15" = yes ] ; then
echo " Skipped because UTF-$bits support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput15 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput15 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput15 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -783,7 +787,7 @@ if [ $do16 = yes ] ; then
echo " Skipped because Unicode property support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput16 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput16 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput16 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -805,7 +809,7 @@ if [ $do17 = yes ] ; then
echo " Skipped when running 8-bit tests"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput17 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput17 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput17 testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -829,7 +833,7 @@ if [ $do18 = yes ] ; then
echo " Skipped because UTF-$bits support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput18 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput18 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput18-$bits testtry
if [ $? != 0 ] ; then exit 1; fi
@@ -853,7 +857,7 @@ if [ $do19 = yes ] ; then
echo " Skipped because Unicode property support is not available"
else
for opt in "" "-s" $jitopt; do
- $sim $valgrind ./pcretest -q $bmode $opt $testdata/testinput19 testtry
+ $sim $valgrind ${opt:+$vjs} ./pcretest -q $bmode $opt $testdata/testinput19 testtry
if [ $? = 0 ] ; then
$cf $testdata/testoutput19 testtry
if [ $? != 0 ] ; then exit 1; fi
diff --git a/src/third_party/pcre-8.38/RunTest.bat b/src/third_party/pcre-8.39/RunTest.bat
index 35d7f71f9e8..35d7f71f9e8 100644
--- a/src/third_party/pcre-8.38/RunTest.bat
+++ b/src/third_party/pcre-8.39/RunTest.bat
diff --git a/src/third_party/pcre-8.38/SConscript b/src/third_party/pcre-8.39/SConscript
index e7b9a59a550..e7b9a59a550 100644
--- a/src/third_party/pcre-8.38/SConscript
+++ b/src/third_party/pcre-8.39/SConscript
diff --git a/src/third_party/pcre-8.38/ar-lib b/src/third_party/pcre-8.39/ar-lib
index 463b9ec0206..463b9ec0206 100755
--- a/src/third_party/pcre-8.38/ar-lib
+++ b/src/third_party/pcre-8.39/ar-lib
diff --git a/src/third_party/pcre-8.38/build_posix/config.h b/src/third_party/pcre-8.39/build_posix/config.h
index eab6a47ed6b..9cfa5a4d70f 100644
--- a/src/third_party/pcre-8.38/build_posix/config.h
+++ b/src/third_party/pcre-8.39/build_posix/config.h
@@ -221,7 +221,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_NAME "PCRE"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "PCRE 8.38"
+#define PACKAGE_STRING "PCRE 8.39"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "pcre"
@@ -230,7 +230,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "8.38"
+#define PACKAGE_VERSION "8.39"
/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested
parentheses (of any kind) in a pattern. This limits the amount of system
@@ -335,7 +335,7 @@ sure both macros are undefined; an emulation function will then be used. */
/* #undef SUPPORT_VALGRIND */
/* Version number of package */
-#define VERSION "8.38"
+#define VERSION "8.39"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
diff --git a/src/third_party/pcre-8.38/build_solaris/config.h b/src/third_party/pcre-8.39/build_solaris/config.h
index a2d53fe5fc5..a391fd1cee5 100644
--- a/src/third_party/pcre-8.38/build_solaris/config.h
+++ b/src/third_party/pcre-8.39/build_solaris/config.h
@@ -221,7 +221,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_NAME "PCRE"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "PCRE 8.38"
+#define PACKAGE_STRING "PCRE 8.39"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "pcre"
@@ -230,7 +230,7 @@ sure both macros are undefined; an emulation function will then be used. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "8.38"
+#define PACKAGE_VERSION "8.39"
/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested
parentheses (of any kind) in a pattern. This limits the amount of system
@@ -335,7 +335,7 @@ sure both macros are undefined; an emulation function will then be used. */
/* #undef SUPPORT_VALGRIND */
/* Version number of package */
-#define VERSION "8.38"
+#define VERSION "8.39"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
diff --git a/src/third_party/pcre-8.38/build_windows/config.h b/src/third_party/pcre-8.39/build_windows/config.h
index ae1d54bdabd..2e8d4ec7b6a 100644..100755
--- a/src/third_party/pcre-8.38/build_windows/config.h
+++ b/src/third_party/pcre-8.39/build_windows/config.h
@@ -1,57 +1,57 @@
-/* config.h for CMake builds */
-
-/* #undef HAVE_DIRENT_H */
-#define HAVE_SYS_STAT_H 1
-#define HAVE_SYS_TYPES_H 1
-/* #undef HAVE_UNISTD_H */
-#define HAVE_WINDOWS_H 1
-#define HAVE_STDINT_H 1
-#define HAVE_INTTYPES_H 1
-
-/* #undef HAVE_TYPE_TRAITS_H */
-/* #undef HAVE_BITS_TYPE_TRAITS_H */
-
-/* #undef HAVE_BCOPY */
-#define HAVE_MEMMOVE 1
-#define HAVE_STRERROR 1
-#define HAVE_STRTOLL 1
-/* #undef HAVE_STRTOQ */
-#define HAVE__STRTOI64 1
-
-#define PCRE_STATIC 1
-
-#define SUPPORT_PCRE8 1
-/* #undef SUPPORT_PCRE16 */
-/* #undef SUPPORT_PCRE32 */
-/* #undef SUPPORT_JIT */
-/* #undef SUPPORT_PCREGREP_JIT */
-#define SUPPORT_UTF 1
-#define SUPPORT_UCP 1
-/* #undef EBCDIC */
-/* #undef EBCDIC_NL25 */
-/* #undef BSR_ANYCRLF */
-#define NO_RECURSE 1
-
-#define HAVE_LONG_LONG 1
-#define HAVE_UNSIGNED_LONG_LONG 1
-
-/* #undef SUPPORT_LIBBZ2 */
-/* #undef SUPPORT_LIBZ */
-/* #undef SUPPORT_LIBEDIT */
-/* #undef SUPPORT_LIBREADLINE */
-
-/* #undef SUPPORT_VALGRIND */
-/* #undef SUPPORT_GCOV */
-
-#define NEWLINE 10
-#define POSIX_MALLOC_THRESHOLD 10
-#define LINK_SIZE 2
-#define PARENS_NEST_LIMIT 250
-#define MATCH_LIMIT 200000
-#define MATCH_LIMIT_RECURSION 4000
-#define PCREGREP_BUFSIZE 20480
-
-#define MAX_NAME_SIZE 32
-#define MAX_NAME_COUNT 10000
-
-/* end config.h for CMake builds */
+/* config.h for CMake builds */
+
+/* #undef HAVE_DIRENT_H */
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+/* #undef HAVE_UNISTD_H */
+#define HAVE_WINDOWS_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_INTTYPES_H 1
+
+/* #undef HAVE_TYPE_TRAITS_H */
+/* #undef HAVE_BITS_TYPE_TRAITS_H */
+
+/* #undef HAVE_BCOPY */
+#define HAVE_MEMMOVE 1
+#define HAVE_STRERROR 1
+#define HAVE_STRTOLL 1
+/* #undef HAVE_STRTOQ */
+#define HAVE__STRTOI64 1
+
+#define PCRE_STATIC 1
+
+#define SUPPORT_PCRE8 1
+/* #undef SUPPORT_PCRE16 */
+/* #undef SUPPORT_PCRE32 */
+/* #undef SUPPORT_JIT */
+/* #undef SUPPORT_PCREGREP_JIT */
+#define SUPPORT_UTF 1
+#define SUPPORT_UCP 1
+/* #undef EBCDIC */
+/* #undef EBCDIC_NL25 */
+/* #undef BSR_ANYCRLF */
+#define NO_RECURSE 1
+
+#define HAVE_LONG_LONG 1
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* #undef SUPPORT_LIBBZ2 */
+/* #undef SUPPORT_LIBZ */
+/* #undef SUPPORT_LIBEDIT */
+/* #undef SUPPORT_LIBREADLINE */
+
+/* #undef SUPPORT_VALGRIND */
+/* #undef SUPPORT_GCOV */
+
+#define NEWLINE 10
+#define POSIX_MALLOC_THRESHOLD 10
+#define LINK_SIZE 2
+#define PARENS_NEST_LIMIT 250
+#define MATCH_LIMIT 200000
+#define MATCH_LIMIT_RECURSION 4000
+#define PCREGREP_BUFSIZE 20480
+
+#define MAX_NAME_SIZE 32
+#define MAX_NAME_COUNT 10000
+
+/* end config.h for CMake builds */
diff --git a/src/third_party/pcre-8.38/dftables.c b/src/third_party/pcre-8.39/dftables.c
index 1fdc8e0f231..1fdc8e0f231 100644
--- a/src/third_party/pcre-8.38/dftables.c
+++ b/src/third_party/pcre-8.39/dftables.c
diff --git a/src/third_party/pcre-8.38/libpcre.pc.in b/src/third_party/pcre-8.39/libpcre.pc.in
index 0a35da87f85..0a35da87f85 100644
--- a/src/third_party/pcre-8.38/libpcre.pc.in
+++ b/src/third_party/pcre-8.39/libpcre.pc.in
diff --git a/src/third_party/pcre-8.38/libpcre16.pc.in b/src/third_party/pcre-8.39/libpcre16.pc.in
index 080c9dcfe8a..080c9dcfe8a 100644
--- a/src/third_party/pcre-8.38/libpcre16.pc.in
+++ b/src/third_party/pcre-8.39/libpcre16.pc.in
diff --git a/src/third_party/pcre-8.38/libpcre32.pc.in b/src/third_party/pcre-8.39/libpcre32.pc.in
index a3ae0e11fcf..a3ae0e11fcf 100644
--- a/src/third_party/pcre-8.38/libpcre32.pc.in
+++ b/src/third_party/pcre-8.39/libpcre32.pc.in
diff --git a/src/third_party/pcre-8.38/libpcrecpp.pc.in b/src/third_party/pcre-8.39/libpcrecpp.pc.in
index ef006fe47aa..ef006fe47aa 100644
--- a/src/third_party/pcre-8.38/libpcrecpp.pc.in
+++ b/src/third_party/pcre-8.39/libpcrecpp.pc.in
diff --git a/src/third_party/pcre-8.38/libpcreposix.pc.in b/src/third_party/pcre-8.39/libpcreposix.pc.in
index c6c0b0c6c66..c6c0b0c6c66 100644
--- a/src/third_party/pcre-8.38/libpcreposix.pc.in
+++ b/src/third_party/pcre-8.39/libpcreposix.pc.in
diff --git a/src/third_party/pcre-8.38/makevp.bat b/src/third_party/pcre-8.39/makevp.bat
index 5f795487eb9..5f795487eb9 100644
--- a/src/third_party/pcre-8.38/makevp.bat
+++ b/src/third_party/pcre-8.39/makevp.bat
diff --git a/src/third_party/pcre-8.38/makevp_c.txt b/src/third_party/pcre-8.39/makevp_c.txt
index 5648115405d..5648115405d 100644
--- a/src/third_party/pcre-8.38/makevp_c.txt
+++ b/src/third_party/pcre-8.39/makevp_c.txt
diff --git a/src/third_party/pcre-8.38/makevp_l.txt b/src/third_party/pcre-8.39/makevp_l.txt
index 9b071e0f90a..9b071e0f90a 100644
--- a/src/third_party/pcre-8.38/makevp_l.txt
+++ b/src/third_party/pcre-8.39/makevp_l.txt
diff --git a/src/third_party/pcre-8.38/pcre-config.in b/src/third_party/pcre-8.39/pcre-config.in
index ac06a3325bc..ac06a3325bc 100644
--- a/src/third_party/pcre-8.38/pcre-config.in
+++ b/src/third_party/pcre-8.39/pcre-config.in
diff --git a/src/third_party/pcre-8.38/pcre.h b/src/third_party/pcre-8.39/pcre.h
index bf6351f8837..70559700654 100644
--- a/src/third_party/pcre-8.38/pcre.h
+++ b/src/third_party/pcre-8.39/pcre.h
@@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE.
/* The current PCRE version information. */
#define PCRE_MAJOR 8
-#define PCRE_MINOR 38
+#define PCRE_MINOR 39
#define PCRE_PRERELEASE
-#define PCRE_DATE 2015-11-23
+#define PCRE_DATE 2016-06-14
/* When an application links to a PCRE DLL in Windows, the symbols that are
imported have to be identified as such. When building PCRE, the appropriate
diff --git a/src/third_party/pcre-8.38/pcre.h.generic b/src/third_party/pcre-8.39/pcre.h.generic
index bf6351f8837..70559700654 100644
--- a/src/third_party/pcre-8.38/pcre.h.generic
+++ b/src/third_party/pcre-8.39/pcre.h.generic
@@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE.
/* The current PCRE version information. */
#define PCRE_MAJOR 8
-#define PCRE_MINOR 38
+#define PCRE_MINOR 39
#define PCRE_PRERELEASE
-#define PCRE_DATE 2015-11-23
+#define PCRE_DATE 2016-06-14
/* When an application links to a PCRE DLL in Windows, the symbols that are
imported have to be identified as such. When building PCRE, the appropriate
diff --git a/src/third_party/pcre-8.38/pcre.h.in b/src/third_party/pcre-8.39/pcre.h.in
index 667a45ed575..667a45ed575 100644
--- a/src/third_party/pcre-8.38/pcre.h.in
+++ b/src/third_party/pcre-8.39/pcre.h.in
diff --git a/src/third_party/pcre-8.38/pcre16_byte_order.c b/src/third_party/pcre-8.39/pcre16_byte_order.c
index 11d2973a3db..11d2973a3db 100644
--- a/src/third_party/pcre-8.38/pcre16_byte_order.c
+++ b/src/third_party/pcre-8.39/pcre16_byte_order.c
diff --git a/src/third_party/pcre-8.38/pcre16_chartables.c b/src/third_party/pcre-8.39/pcre16_chartables.c
index 7c0ff35f5e0..7c0ff35f5e0 100644
--- a/src/third_party/pcre-8.38/pcre16_chartables.c
+++ b/src/third_party/pcre-8.39/pcre16_chartables.c
diff --git a/src/third_party/pcre-8.38/pcre16_compile.c b/src/third_party/pcre-8.39/pcre16_compile.c
index e499b670877..e499b670877 100644
--- a/src/third_party/pcre-8.38/pcre16_compile.c
+++ b/src/third_party/pcre-8.39/pcre16_compile.c
diff --git a/src/third_party/pcre-8.38/pcre16_config.c b/src/third_party/pcre-8.39/pcre16_config.c
index b52138764f6..b52138764f6 100644
--- a/src/third_party/pcre-8.38/pcre16_config.c
+++ b/src/third_party/pcre-8.39/pcre16_config.c
diff --git a/src/third_party/pcre-8.38/pcre16_dfa_exec.c b/src/third_party/pcre-8.39/pcre16_dfa_exec.c
index 2ba740e972b..2ba740e972b 100644
--- a/src/third_party/pcre-8.38/pcre16_dfa_exec.c
+++ b/src/third_party/pcre-8.39/pcre16_dfa_exec.c
diff --git a/src/third_party/pcre-8.38/pcre16_exec.c b/src/third_party/pcre-8.39/pcre16_exec.c
index 7417b1770c6..7417b1770c6 100644
--- a/src/third_party/pcre-8.38/pcre16_exec.c
+++ b/src/third_party/pcre-8.39/pcre16_exec.c
diff --git a/src/third_party/pcre-8.38/pcre16_fullinfo.c b/src/third_party/pcre-8.39/pcre16_fullinfo.c
index 544dca6ed5c..544dca6ed5c 100644
--- a/src/third_party/pcre-8.38/pcre16_fullinfo.c
+++ b/src/third_party/pcre-8.39/pcre16_fullinfo.c
diff --git a/src/third_party/pcre-8.38/pcre16_get.c b/src/third_party/pcre-8.39/pcre16_get.c
index 3ded08c622c..3ded08c622c 100644
--- a/src/third_party/pcre-8.38/pcre16_get.c
+++ b/src/third_party/pcre-8.39/pcre16_get.c
diff --git a/src/third_party/pcre-8.38/pcre16_globals.c b/src/third_party/pcre-8.39/pcre16_globals.c
index a136b3d8c22..a136b3d8c22 100644
--- a/src/third_party/pcre-8.38/pcre16_globals.c
+++ b/src/third_party/pcre-8.39/pcre16_globals.c
diff --git a/src/third_party/pcre-8.38/pcre16_jit_compile.c b/src/third_party/pcre-8.39/pcre16_jit_compile.c
index ab0cacd7646..ab0cacd7646 100644
--- a/src/third_party/pcre-8.38/pcre16_jit_compile.c
+++ b/src/third_party/pcre-8.39/pcre16_jit_compile.c
diff --git a/src/third_party/pcre-8.38/pcre16_maketables.c b/src/third_party/pcre-8.39/pcre16_maketables.c
index b1cd1c579d6..b1cd1c579d6 100644
--- a/src/third_party/pcre-8.38/pcre16_maketables.c
+++ b/src/third_party/pcre-8.39/pcre16_maketables.c
diff --git a/src/third_party/pcre-8.38/pcre16_newline.c b/src/third_party/pcre-8.39/pcre16_newline.c
index 7fe201400f5..7fe201400f5 100644
--- a/src/third_party/pcre-8.38/pcre16_newline.c
+++ b/src/third_party/pcre-8.39/pcre16_newline.c
diff --git a/src/third_party/pcre-8.38/pcre16_ord2utf16.c b/src/third_party/pcre-8.39/pcre16_ord2utf16.c
index 8e2ce5ea6c5..8e2ce5ea6c5 100644
--- a/src/third_party/pcre-8.38/pcre16_ord2utf16.c
+++ b/src/third_party/pcre-8.39/pcre16_ord2utf16.c
diff --git a/src/third_party/pcre-8.38/pcre16_printint.c b/src/third_party/pcre-8.39/pcre16_printint.c
index 33d8c340200..33d8c340200 100644
--- a/src/third_party/pcre-8.38/pcre16_printint.c
+++ b/src/third_party/pcre-8.39/pcre16_printint.c
diff --git a/src/third_party/pcre-8.38/pcre16_refcount.c b/src/third_party/pcre-8.39/pcre16_refcount.c
index d3d15439737..d3d15439737 100644
--- a/src/third_party/pcre-8.38/pcre16_refcount.c
+++ b/src/third_party/pcre-8.39/pcre16_refcount.c
diff --git a/src/third_party/pcre-8.38/pcre16_string_utils.c b/src/third_party/pcre-8.39/pcre16_string_utils.c
index 382c40799fb..382c40799fb 100644
--- a/src/third_party/pcre-8.38/pcre16_string_utils.c
+++ b/src/third_party/pcre-8.39/pcre16_string_utils.c
diff --git a/src/third_party/pcre-8.38/pcre16_study.c b/src/third_party/pcre-8.39/pcre16_study.c
index f87de081fc4..f87de081fc4 100644
--- a/src/third_party/pcre-8.38/pcre16_study.c
+++ b/src/third_party/pcre-8.39/pcre16_study.c
diff --git a/src/third_party/pcre-8.38/pcre16_tables.c b/src/third_party/pcre-8.39/pcre16_tables.c
index d84297093a4..d84297093a4 100644
--- a/src/third_party/pcre-8.38/pcre16_tables.c
+++ b/src/third_party/pcre-8.39/pcre16_tables.c
diff --git a/src/third_party/pcre-8.38/pcre16_ucd.c b/src/third_party/pcre-8.39/pcre16_ucd.c
index ee23439a013..ee23439a013 100644
--- a/src/third_party/pcre-8.38/pcre16_ucd.c
+++ b/src/third_party/pcre-8.39/pcre16_ucd.c
diff --git a/src/third_party/pcre-8.38/pcre16_utf16_utils.c b/src/third_party/pcre-8.39/pcre16_utf16_utils.c
index 49ced0c0b1c..49ced0c0b1c 100644
--- a/src/third_party/pcre-8.38/pcre16_utf16_utils.c
+++ b/src/third_party/pcre-8.39/pcre16_utf16_utils.c
diff --git a/src/third_party/pcre-8.38/pcre16_valid_utf16.c b/src/third_party/pcre-8.39/pcre16_valid_utf16.c
index 09076539d09..09076539d09 100644
--- a/src/third_party/pcre-8.38/pcre16_valid_utf16.c
+++ b/src/third_party/pcre-8.39/pcre16_valid_utf16.c
diff --git a/src/third_party/pcre-8.38/pcre16_version.c b/src/third_party/pcre-8.39/pcre16_version.c
index e991b1a8cfd..e991b1a8cfd 100644
--- a/src/third_party/pcre-8.38/pcre16_version.c
+++ b/src/third_party/pcre-8.39/pcre16_version.c
diff --git a/src/third_party/pcre-8.38/pcre16_xclass.c b/src/third_party/pcre-8.39/pcre16_xclass.c
index 5aac2a36c68..5aac2a36c68 100644
--- a/src/third_party/pcre-8.38/pcre16_xclass.c
+++ b/src/third_party/pcre-8.39/pcre16_xclass.c
diff --git a/src/third_party/pcre-8.38/pcre32_byte_order.c b/src/third_party/pcre-8.39/pcre32_byte_order.c
index 9cf5362730a..9cf5362730a 100644
--- a/src/third_party/pcre-8.38/pcre32_byte_order.c
+++ b/src/third_party/pcre-8.39/pcre32_byte_order.c
diff --git a/src/third_party/pcre-8.38/pcre32_chartables.c b/src/third_party/pcre-8.39/pcre32_chartables.c
index b5d8c23dbf1..b5d8c23dbf1 100644
--- a/src/third_party/pcre-8.38/pcre32_chartables.c
+++ b/src/third_party/pcre-8.39/pcre32_chartables.c
diff --git a/src/third_party/pcre-8.38/pcre32_compile.c b/src/third_party/pcre-8.39/pcre32_compile.c
index d781eb377e0..d781eb377e0 100644
--- a/src/third_party/pcre-8.38/pcre32_compile.c
+++ b/src/third_party/pcre-8.39/pcre32_compile.c
diff --git a/src/third_party/pcre-8.38/pcre32_config.c b/src/third_party/pcre-8.39/pcre32_config.c
index d63f3e9ea23..d63f3e9ea23 100644
--- a/src/third_party/pcre-8.38/pcre32_config.c
+++ b/src/third_party/pcre-8.39/pcre32_config.c
diff --git a/src/third_party/pcre-8.38/pcre32_dfa_exec.c b/src/third_party/pcre-8.39/pcre32_dfa_exec.c
index b0bfd34f04d..b0bfd34f04d 100644
--- a/src/third_party/pcre-8.38/pcre32_dfa_exec.c
+++ b/src/third_party/pcre-8.39/pcre32_dfa_exec.c
diff --git a/src/third_party/pcre-8.38/pcre32_exec.c b/src/third_party/pcre-8.39/pcre32_exec.c
index 8170ed77d35..8170ed77d35 100644
--- a/src/third_party/pcre-8.38/pcre32_exec.c
+++ b/src/third_party/pcre-8.39/pcre32_exec.c
diff --git a/src/third_party/pcre-8.38/pcre32_fullinfo.c b/src/third_party/pcre-8.39/pcre32_fullinfo.c
index 6ecc5209a08..6ecc5209a08 100644
--- a/src/third_party/pcre-8.38/pcre32_fullinfo.c
+++ b/src/third_party/pcre-8.39/pcre32_fullinfo.c
diff --git a/src/third_party/pcre-8.38/pcre32_get.c b/src/third_party/pcre-8.39/pcre32_get.c
index d35deee0cd4..d35deee0cd4 100644
--- a/src/third_party/pcre-8.38/pcre32_get.c
+++ b/src/third_party/pcre-8.39/pcre32_get.c
diff --git a/src/third_party/pcre-8.38/pcre32_globals.c b/src/third_party/pcre-8.39/pcre32_globals.c
index 32e0914ca6d..32e0914ca6d 100644
--- a/src/third_party/pcre-8.38/pcre32_globals.c
+++ b/src/third_party/pcre-8.39/pcre32_globals.c
diff --git a/src/third_party/pcre-8.38/pcre32_jit_compile.c b/src/third_party/pcre-8.39/pcre32_jit_compile.c
index 2e7c6f97c96..2e7c6f97c96 100644
--- a/src/third_party/pcre-8.38/pcre32_jit_compile.c
+++ b/src/third_party/pcre-8.39/pcre32_jit_compile.c
diff --git a/src/third_party/pcre-8.38/pcre32_maketables.c b/src/third_party/pcre-8.39/pcre32_maketables.c
index 5d1b1c64c96..5d1b1c64c96 100644
--- a/src/third_party/pcre-8.38/pcre32_maketables.c
+++ b/src/third_party/pcre-8.39/pcre32_maketables.c
diff --git a/src/third_party/pcre-8.38/pcre32_newline.c b/src/third_party/pcre-8.39/pcre32_newline.c
index 7f8d5360cdc..7f8d5360cdc 100644
--- a/src/third_party/pcre-8.38/pcre32_newline.c
+++ b/src/third_party/pcre-8.39/pcre32_newline.c
diff --git a/src/third_party/pcre-8.38/pcre32_ord2utf32.c b/src/third_party/pcre-8.39/pcre32_ord2utf32.c
index 606bcb3d7ba..606bcb3d7ba 100644
--- a/src/third_party/pcre-8.38/pcre32_ord2utf32.c
+++ b/src/third_party/pcre-8.39/pcre32_ord2utf32.c
diff --git a/src/third_party/pcre-8.38/pcre32_printint.c b/src/third_party/pcre-8.39/pcre32_printint.c
index f3fd7b25e2c..f3fd7b25e2c 100644
--- a/src/third_party/pcre-8.38/pcre32_printint.c
+++ b/src/third_party/pcre-8.39/pcre32_printint.c
diff --git a/src/third_party/pcre-8.38/pcre32_refcount.c b/src/third_party/pcre-8.39/pcre32_refcount.c
index dbdf432d82a..dbdf432d82a 100644
--- a/src/third_party/pcre-8.38/pcre32_refcount.c
+++ b/src/third_party/pcre-8.39/pcre32_refcount.c
diff --git a/src/third_party/pcre-8.38/pcre32_string_utils.c b/src/third_party/pcre-8.39/pcre32_string_utils.c
index e37b3d4805f..e37b3d4805f 100644
--- a/src/third_party/pcre-8.38/pcre32_string_utils.c
+++ b/src/third_party/pcre-8.39/pcre32_string_utils.c
diff --git a/src/third_party/pcre-8.38/pcre32_study.c b/src/third_party/pcre-8.39/pcre32_study.c
index d3a3afed791..d3a3afed791 100644
--- a/src/third_party/pcre-8.38/pcre32_study.c
+++ b/src/third_party/pcre-8.39/pcre32_study.c
diff --git a/src/third_party/pcre-8.38/pcre32_tables.c b/src/third_party/pcre-8.39/pcre32_tables.c
index 3d94cca33a1..3d94cca33a1 100644
--- a/src/third_party/pcre-8.38/pcre32_tables.c
+++ b/src/third_party/pcre-8.39/pcre32_tables.c
diff --git a/src/third_party/pcre-8.38/pcre32_ucd.c b/src/third_party/pcre-8.39/pcre32_ucd.c
index befe22d3435..befe22d3435 100644
--- a/src/third_party/pcre-8.38/pcre32_ucd.c
+++ b/src/third_party/pcre-8.39/pcre32_ucd.c
diff --git a/src/third_party/pcre-8.38/pcre32_utf32_utils.c b/src/third_party/pcre-8.39/pcre32_utf32_utils.c
index f844e237165..f844e237165 100644
--- a/src/third_party/pcre-8.38/pcre32_utf32_utils.c
+++ b/src/third_party/pcre-8.39/pcre32_utf32_utils.c
diff --git a/src/third_party/pcre-8.38/pcre32_valid_utf32.c b/src/third_party/pcre-8.39/pcre32_valid_utf32.c
index 94cda1a2c4c..94cda1a2c4c 100644
--- a/src/third_party/pcre-8.38/pcre32_valid_utf32.c
+++ b/src/third_party/pcre-8.39/pcre32_valid_utf32.c
diff --git a/src/third_party/pcre-8.38/pcre32_version.c b/src/third_party/pcre-8.39/pcre32_version.c
index fdaad9b0859..fdaad9b0859 100644
--- a/src/third_party/pcre-8.38/pcre32_version.c
+++ b/src/third_party/pcre-8.39/pcre32_version.c
diff --git a/src/third_party/pcre-8.38/pcre32_xclass.c b/src/third_party/pcre-8.39/pcre32_xclass.c
index 5662408ad5f..5662408ad5f 100644
--- a/src/third_party/pcre-8.38/pcre32_xclass.c
+++ b/src/third_party/pcre-8.39/pcre32_xclass.c
diff --git a/src/third_party/pcre-8.38/pcre_byte_order.c b/src/third_party/pcre-8.39/pcre_byte_order.c
index cf5f12b04ea..cf5f12b04ea 100644
--- a/src/third_party/pcre-8.38/pcre_byte_order.c
+++ b/src/third_party/pcre-8.39/pcre_byte_order.c
diff --git a/src/third_party/pcre-8.38/pcre_chartables.c b/src/third_party/pcre-8.39/pcre_chartables.c
index 1e20ec29d05..1e20ec29d05 100644
--- a/src/third_party/pcre-8.38/pcre_chartables.c
+++ b/src/third_party/pcre-8.39/pcre_chartables.c
diff --git a/src/third_party/pcre-8.38/pcre_chartables.c.dist b/src/third_party/pcre-8.39/pcre_chartables.c.dist
index 1e20ec29d05..1e20ec29d05 100644
--- a/src/third_party/pcre-8.38/pcre_chartables.c.dist
+++ b/src/third_party/pcre-8.39/pcre_chartables.c.dist
diff --git a/src/third_party/pcre-8.38/pcre_compile.c b/src/third_party/pcre-8.39/pcre_compile.c
index 4d3b3139dea..7cd39501230 100644
--- a/src/third_party/pcre-8.38/pcre_compile.c
+++ b/src/third_party/pcre-8.39/pcre_compile.c
@@ -6,7 +6,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2014 University of Cambridge
+ Copyright (c) 1997-2016 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -485,7 +485,7 @@ static const char error_texts[] =
"lookbehind assertion is not fixed length\0"
"malformed number or name after (?(\0"
"conditional group contains more than two branches\0"
- "assertion expected after (?(\0"
+ "assertion expected after (?( or (?(?C)\0"
"(?R or (?[+-]digits must be followed by )\0"
/* 30 */
"unknown POSIX class name\0"
@@ -560,6 +560,7 @@ static const char error_texts[] =
/* 85 */
"parentheses are too deeply nested (stack check)\0"
"digits missing in \\x{} or \\o{}\0"
+ "regular expression is too complicated\0"
;
/* Table to identify digits and hex digits. This is used when compiling
@@ -4566,6 +4567,10 @@ for (;; ptr++)
pcre_uint32 ec;
pcre_uchar mcbuffer[8];
+ /* Come here to restart the loop without advancing the pointer. */
+
+ REDO_LOOP:
+
/* Get next character in the pattern */
c = *ptr;
@@ -4591,7 +4596,8 @@ for (;; ptr++)
if (code > cd->start_workspace + cd->workspace_size -
WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */
{
- *errorcodeptr = ERR52;
+ *errorcodeptr = (code >= cd->start_workspace + cd->workspace_size)?
+ ERR52 : ERR87;
goto FAILED;
}
@@ -4645,9 +4651,10 @@ for (;; ptr++)
goto FAILED;
}
- /* If in \Q...\E, check for the end; if not, we have a literal */
+ /* If in \Q...\E, check for the end; if not, we have a literal. Otherwise an
+ isolated \E is ignored. */
- if (inescq && c != CHAR_NULL)
+ if (c != CHAR_NULL)
{
if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E)
{
@@ -4655,7 +4662,7 @@ for (;; ptr++)
ptr++;
continue;
}
- else
+ else if (inescq)
{
if (previous_callout != NULL)
{
@@ -4670,18 +4677,27 @@ for (;; ptr++)
}
goto NORMAL_CHAR;
}
- /* Control does not reach here. */
+
+ /* Check for the start of a \Q...\E sequence. We must do this here rather
+ than later in case it is immediately followed by \E, which turns it into a
+ "do nothing" sequence. */
+
+ if (c == CHAR_BACKSLASH && ptr[1] == CHAR_Q)
+ {
+ inescq = TRUE;
+ ptr++;
+ continue;
+ }
}
- /* In extended mode, skip white space and comments. We need a loop in order
- to check for more white space and more comments after a comment. */
+ /* In extended mode, skip white space and comments. */
if ((options & PCRE_EXTENDED) != 0)
{
- for (;;)
+ const pcre_uchar *wscptr = ptr;
+ while (MAX_255(c) && (cd->ctypes[c] & ctype_space) != 0) c = *(++ptr);
+ if (c == CHAR_NUMBER_SIGN)
{
- while (MAX_255(c) && (cd->ctypes[c] & ctype_space) != 0) c = *(++ptr);
- if (c != CHAR_NUMBER_SIGN) break;
ptr++;
while (*ptr != CHAR_NULL)
{
@@ -4695,8 +4711,29 @@ for (;; ptr++)
if (utf) FORWARDCHAR(ptr);
#endif
}
- c = *ptr; /* Either NULL or the char after a newline */
}
+
+ /* If we skipped any characters, restart the loop. Otherwise, we didn't see
+ a comment. */
+
+ if (ptr > wscptr) goto REDO_LOOP;
+ }
+
+ /* Skip over (?# comments. We need to do this here because we want to know if
+ the next thing is a quantifier, and these comments may come between an item
+ and its quantifier. */
+
+ if (c == CHAR_LEFT_PARENTHESIS && ptr[1] == CHAR_QUESTION_MARK &&
+ ptr[2] == CHAR_NUMBER_SIGN)
+ {
+ ptr += 3;
+ while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++;
+ if (*ptr == CHAR_NULL)
+ {
+ *errorcodeptr = ERR18;
+ goto FAILED;
+ }
+ continue;
}
/* See if the next thing is a quantifier. */
@@ -4820,15 +4857,15 @@ for (;; ptr++)
if (STRNCMP_UC_C8(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0)
{
nestptr = ptr + 7;
- ptr = sub_start_of_word - 1;
- continue;
+ ptr = sub_start_of_word;
+ goto REDO_LOOP;
}
if (STRNCMP_UC_C8(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0)
{
nestptr = ptr + 7;
- ptr = sub_end_of_word - 1;
- continue;
+ ptr = sub_end_of_word;
+ goto REDO_LOOP;
}
/* Handle a real character class. */
@@ -5046,20 +5083,22 @@ for (;; ptr++)
ptr = tempptr + 1;
continue;
- /* For the other POSIX classes (ascii, xdigit) we are going to fall
- through to the non-UCP case and build a bit map for characters with
- code points less than 256. If we are in a negated POSIX class
- within a non-negated overall class, characters with code points
- greater than 255 must all match. In the special case where we have
- not yet generated any xclass data, and this is the final item in
- the overall class, we need do nothing: later on, the opcode
+ /* For the other POSIX classes (ascii, cntrl, xdigit) we are going
+ to fall through to the non-UCP case and build a bit map for
+ characters with code points less than 256. If we are in a negated
+ POSIX class, characters with code points greater than 255 must
+ either all match or all not match. In the special case where we
+ have not yet generated any xclass data, and this is the final item
+ in the overall class, we need do nothing: later on, the opcode
OP_NCLASS will be used to indicate that characters greater than 255
are acceptable. If we have already seen an xclass item or one may
follow (we have to assume that it might if this is not the end of
- the class), explicitly match all wide codepoints. */
+ the class), explicitly list all wide codepoints, which will then
+ either not match or match, depending on whether the class is or is
+ not negated. */
default:
- if (!negate_class && local_negate &&
+ if (local_negate &&
(xclass || tempptr[2] != CHAR_RIGHT_SQUARE_BRACKET))
{
*class_uchardata++ = XCL_RANGE;
@@ -6529,21 +6568,6 @@ for (;; ptr++)
case CHAR_LEFT_PARENTHESIS:
ptr++;
- /* First deal with comments. Putting this code right at the start ensures
- that comments have no bad side effects. */
-
- if (ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN)
- {
- ptr += 2;
- while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++;
- if (*ptr == CHAR_NULL)
- {
- *errorcodeptr = ERR18;
- goto FAILED;
- }
- continue;
- }
-
/* Now deal with various "verbs" that can be introduced by '*'. */
if (ptr[0] == CHAR_ASTERISK && (ptr[1] == ':'
@@ -6604,8 +6628,21 @@ for (;; ptr++)
cd->had_accept = TRUE;
for (oc = cd->open_caps; oc != NULL; oc = oc->next)
{
- *code++ = OP_CLOSE;
- PUT2INC(code, 0, oc->number);
+ if (lengthptr != NULL)
+ {
+#ifdef COMPILE_PCRE8
+ *lengthptr += 1 + IMM2_SIZE;
+#elif defined COMPILE_PCRE16
+ *lengthptr += 2 + IMM2_SIZE;
+#elif defined COMPILE_PCRE32
+ *lengthptr += 4 + IMM2_SIZE;
+#endif
+ }
+ else
+ {
+ *code++ = OP_CLOSE;
+ PUT2INC(code, 0, oc->number);
+ }
}
setverb = *code++ =
(cd->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT;
@@ -6734,6 +6771,15 @@ for (;; ptr++)
for (i = 3;; i++) if (!IS_DIGIT(ptr[i])) break;
if (ptr[i] == CHAR_RIGHT_PARENTHESIS)
tempptr += i + 1;
+
+ /* tempptr should now be pointing to the opening parenthesis of the
+ assertion condition. */
+
+ if (*tempptr != CHAR_LEFT_PARENTHESIS)
+ {
+ *errorcodeptr = ERR28;
+ goto FAILED;
+ }
}
/* For conditions that are assertions, check the syntax, and then exit
@@ -7258,7 +7304,7 @@ for (;; ptr++)
issue is fixed "properly" in PCRE2. As PCRE1 is now in maintenance
only mode, we finesse the bug by allowing more memory always. */
- *lengthptr += 2 + 2*LINK_SIZE;
+ *lengthptr += 4 + 4*LINK_SIZE;
/* It is even worse than that. The current reference may be to an
existing named group with a different number (so apparently not
@@ -7274,7 +7320,12 @@ for (;; ptr++)
so far in order to get the number. If the name is not found, leave
the value of recno as 0 for a forward reference. */
- else
+ /* This patch (removing "else") fixes a problem when a reference is
+ to multiple identically named nested groups from within the nest.
+ Once again, it is not the "proper" fix, and it results in an
+ over-allocation of memory. */
+
+ /* else */
{
ng = cd->named_groups;
for (i = 0; i < cd->names_found; i++, ng++)
@@ -7585,39 +7636,15 @@ for (;; ptr++)
newoptions = (options | set) & (~unset);
/* If the options ended with ')' this is not the start of a nested
- group with option changes, so the options change at this level. If this
- item is right at the start of the pattern, the options can be
- abstracted and made external in the pre-compile phase, and ignored in
- the compile phase. This can be helpful when matching -- for instance in
- caseless checking of required bytes.
-
- If the code pointer is not (cd->start_code + 1 + LINK_SIZE), we are
- definitely *not* at the start of the pattern because something has been
- compiled. In the pre-compile phase, however, the code pointer can have
- that value after the start, because it gets reset as code is discarded
- during the pre-compile. However, this can happen only at top level - if
- we are within parentheses, the starting BRA will still be present. At
- any parenthesis level, the length value can be used to test if anything
- has been compiled at that level. Thus, a test for both these conditions
- is necessary to ensure we correctly detect the start of the pattern in
- both phases.
-
+ group with option changes, so the options change at this level.
If we are not at the pattern start, reset the greedy defaults and the
case value for firstchar and reqchar. */
if (*ptr == CHAR_RIGHT_PARENTHESIS)
{
- if (code == cd->start_code + 1 + LINK_SIZE &&
- (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE))
- {
- cd->external_options = newoptions;
- }
- else
- {
- greedy_default = ((newoptions & PCRE_UNGREEDY) != 0);
- greedy_non_default = greedy_default ^ 1;
- req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS:0;
- }
+ greedy_default = ((newoptions & PCRE_UNGREEDY) != 0);
+ greedy_non_default = greedy_default ^ 1;
+ req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS:0;
/* Change options at this level, and pass them back for use
in subsequent branches. */
@@ -7896,16 +7923,6 @@ for (;; ptr++)
c = ec;
else
{
- if (escape == ESC_Q) /* Handle start of quoted string */
- {
- if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E)
- ptr += 2; /* avoid empty string */
- else inescq = TRUE;
- continue;
- }
-
- if (escape == ESC_E) continue; /* Perl ignores an orphan \E */
-
/* For metasequences that actually match a character, we disable the
setting of a first character if it hasn't already been set. */
diff --git a/src/third_party/pcre-8.38/pcre_config.c b/src/third_party/pcre-8.39/pcre_config.c
index 1cbdd9c960c..1cbdd9c960c 100644
--- a/src/third_party/pcre-8.38/pcre_config.c
+++ b/src/third_party/pcre-8.39/pcre_config.c
diff --git a/src/third_party/pcre-8.38/pcre_dfa_exec.c b/src/third_party/pcre-8.39/pcre_dfa_exec.c
index 170ce6a0016..170ce6a0016 100644
--- a/src/third_party/pcre-8.38/pcre_dfa_exec.c
+++ b/src/third_party/pcre-8.39/pcre_dfa_exec.c
diff --git a/src/third_party/pcre-8.38/pcre_exec.c b/src/third_party/pcre-8.39/pcre_exec.c
index 24b23ca2864..24b23ca2864 100644
--- a/src/third_party/pcre-8.38/pcre_exec.c
+++ b/src/third_party/pcre-8.39/pcre_exec.c
diff --git a/src/third_party/pcre-8.38/pcre_fullinfo.c b/src/third_party/pcre-8.39/pcre_fullinfo.c
index a6c2ece6ca5..a6c2ece6ca5 100644
--- a/src/third_party/pcre-8.38/pcre_fullinfo.c
+++ b/src/third_party/pcre-8.39/pcre_fullinfo.c
diff --git a/src/third_party/pcre-8.38/pcre_get.c b/src/third_party/pcre-8.39/pcre_get.c
index 8094b34bbfb..9475d5e88cd 100644
--- a/src/third_party/pcre-8.38/pcre_get.c
+++ b/src/third_party/pcre-8.39/pcre_get.c
@@ -250,6 +250,7 @@ Arguments:
code the compiled regex
stringname the name of the capturing substring
ovector the vector of matched substrings
+ stringcount number of captured substrings
Returns: the number of the first that is set,
or the number of the last one if none are set,
@@ -258,13 +259,16 @@ Returns: the number of the first that is set,
#if defined COMPILE_PCRE8
static int
-get_first_set(const pcre *code, const char *stringname, int *ovector)
+get_first_set(const pcre *code, const char *stringname, int *ovector,
+ int stringcount)
#elif defined COMPILE_PCRE16
static int
-get_first_set(const pcre16 *code, PCRE_SPTR16 stringname, int *ovector)
+get_first_set(const pcre16 *code, PCRE_SPTR16 stringname, int *ovector,
+ int stringcount)
#elif defined COMPILE_PCRE32
static int
-get_first_set(const pcre32 *code, PCRE_SPTR32 stringname, int *ovector)
+get_first_set(const pcre32 *code, PCRE_SPTR32 stringname, int *ovector,
+ int stringcount)
#endif
{
const REAL_PCRE *re = (const REAL_PCRE *)code;
@@ -295,7 +299,7 @@ if (entrysize <= 0) return entrysize;
for (entry = (pcre_uchar *)first; entry <= (pcre_uchar *)last; entry += entrysize)
{
int n = GET2(entry, 0);
- if (ovector[n*2] >= 0) return n;
+ if (n < stringcount && ovector[n*2] >= 0) return n;
}
return GET2(entry, 0);
}
@@ -402,7 +406,7 @@ pcre32_copy_named_substring(const pcre32 *code, PCRE_SPTR32 subject,
PCRE_UCHAR32 *buffer, int size)
#endif
{
-int n = get_first_set(code, stringname, ovector);
+int n = get_first_set(code, stringname, ovector, stringcount);
if (n <= 0) return n;
#if defined COMPILE_PCRE8
return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size);
@@ -457,7 +461,10 @@ pcre_uchar **stringlist;
pcre_uchar *p;
for (i = 0; i < double_count; i += 2)
- size += sizeof(pcre_uchar *) + IN_UCHARS(ovector[i+1] - ovector[i] + 1);
+ {
+ size += sizeof(pcre_uchar *) + IN_UCHARS(1);
+ if (ovector[i+1] > ovector[i]) size += IN_UCHARS(ovector[i+1] - ovector[i]);
+ }
stringlist = (pcre_uchar **)(PUBL(malloc))(size);
if (stringlist == NULL) return PCRE_ERROR_NOMEMORY;
@@ -473,7 +480,7 @@ p = (pcre_uchar *)(stringlist + stringcount + 1);
for (i = 0; i < double_count; i += 2)
{
- int len = ovector[i+1] - ovector[i];
+ int len = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0;
memcpy(p, subject + ovector[i], IN_UCHARS(len));
*stringlist++ = p;
p += len;
@@ -619,7 +626,7 @@ pcre32_get_named_substring(const pcre32 *code, PCRE_SPTR32 subject,
PCRE_SPTR32 *stringptr)
#endif
{
-int n = get_first_set(code, stringname, ovector);
+int n = get_first_set(code, stringname, ovector, stringcount);
if (n <= 0) return n;
#if defined COMPILE_PCRE8
return pcre_get_substring(subject, ovector, stringcount, n, stringptr);
diff --git a/src/third_party/pcre-8.38/pcre_globals.c b/src/third_party/pcre-8.39/pcre_globals.c
index 0f106aa9013..0f106aa9013 100644
--- a/src/third_party/pcre-8.38/pcre_globals.c
+++ b/src/third_party/pcre-8.39/pcre_globals.c
diff --git a/src/third_party/pcre-8.38/pcre_internal.h b/src/third_party/pcre-8.39/pcre_internal.h
index f7a5ee7aa6f..2923b29f82d 100644
--- a/src/third_party/pcre-8.38/pcre_internal.h
+++ b/src/third_party/pcre-8.39/pcre_internal.h
@@ -7,7 +7,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2014 University of Cambridge
+ Copyright (c) 1997-2016 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -275,7 +275,7 @@ pcre.h(.in) and disable (comment out) this message. */
typedef pcre_uint16 pcre_uchar;
#define UCHAR_SHIFT (1)
-#define IN_UCHARS(x) ((x) << UCHAR_SHIFT)
+#define IN_UCHARS(x) ((x) * 2)
#define MAX_255(c) ((c) <= 255u)
#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default))
@@ -283,7 +283,7 @@ typedef pcre_uint16 pcre_uchar;
typedef pcre_uint32 pcre_uchar;
#define UCHAR_SHIFT (2)
-#define IN_UCHARS(x) ((x) << UCHAR_SHIFT)
+#define IN_UCHARS(x) ((x) * 4)
#define MAX_255(c) ((c) <= 255u)
#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default))
@@ -2289,7 +2289,7 @@ enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9,
ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59,
ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69,
ERR70, ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79,
- ERR80, ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERRCOUNT };
+ ERR80, ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERRCOUNT };
/* JIT compiling modes. The function list is indexed by them. */
diff --git a/src/third_party/pcre-8.38/pcre_jit_compile.c b/src/third_party/pcre-8.39/pcre_jit_compile.c
index 445de0cbefe..4f15a27ac28 100644
--- a/src/third_party/pcre-8.38/pcre_jit_compile.c
+++ b/src/third_party/pcre-8.39/pcre_jit_compile.c
@@ -168,13 +168,13 @@ typedef struct jit_arguments {
pcre_uchar *mark_ptr;
void *callout_data;
/* Everything else after. */
- pcre_uint32 limit_match;
+ sljit_u32 limit_match;
int real_offset_count;
int offset_count;
- pcre_uint8 notbol;
- pcre_uint8 noteol;
- pcre_uint8 notempty;
- pcre_uint8 notempty_atstart;
+ sljit_u8 notbol;
+ sljit_u8 noteol;
+ sljit_u8 notempty;
+ sljit_u8 notempty_atstart;
} jit_arguments;
typedef struct executable_functions {
@@ -183,8 +183,8 @@ typedef struct executable_functions {
sljit_uw executable_sizes[JIT_NUMBER_OF_COMPILE_MODES];
PUBL(jit_callback) callback;
void *userdata;
- pcre_uint32 top_bracket;
- pcre_uint32 limit_match;
+ sljit_u32 top_bracket;
+ sljit_u32 limit_match;
} executable_functions;
typedef struct jump_list {
@@ -277,11 +277,25 @@ typedef struct braminzero_backtrack {
struct sljit_label *matchingpath;
} braminzero_backtrack;
-typedef struct iterator_backtrack {
+typedef struct char_iterator_backtrack {
backtrack_common common;
/* Next iteration. */
struct sljit_label *matchingpath;
-} iterator_backtrack;
+ union {
+ jump_list *backtracks;
+ struct {
+ unsigned int othercasebit;
+ pcre_uchar chr;
+ BOOL enabled;
+ } charpos;
+ } u;
+} char_iterator_backtrack;
+
+typedef struct ref_iterator_backtrack {
+ backtrack_common common;
+ /* Next iteration. */
+ struct sljit_label *matchingpath;
+} ref_iterator_backtrack;
typedef struct recurse_entry {
struct recurse_entry *next;
@@ -321,40 +335,46 @@ typedef struct compiler_common {
/* First byte code. */
pcre_uchar *start;
/* Maps private data offset to each opcode. */
- sljit_si *private_data_ptrs;
+ sljit_s32 *private_data_ptrs;
/* Chain list of read-only data ptrs. */
void *read_only_data_head;
/* Tells whether the capturing bracket is optimized. */
- pcre_uint8 *optimized_cbracket;
+ sljit_u8 *optimized_cbracket;
/* Tells whether the starting offset is a target of then. */
- pcre_uint8 *then_offsets;
+ sljit_u8 *then_offsets;
/* Current position where a THEN must jump. */
then_trap_backtrack *then_trap;
/* Starting offset of private data for capturing brackets. */
- int cbra_ptr;
+ sljit_s32 cbra_ptr;
/* Output vector starting point. Must be divisible by 2. */
- int ovector_start;
+ sljit_s32 ovector_start;
+ /* Points to the starting character of the current match. */
+ sljit_s32 start_ptr;
/* Last known position of the requested byte. */
- int req_char_ptr;
+ sljit_s32 req_char_ptr;
/* Head of the last recursion. */
- int recursive_head_ptr;
- /* First inspected character for partial matching. */
- int start_used_ptr;
+ sljit_s32 recursive_head_ptr;
+ /* First inspected character for partial matching.
+ (Needed for avoiding zero length partial matches.) */
+ sljit_s32 start_used_ptr;
/* Starting pointer for partial soft matches. */
- int hit_start;
- /* End pointer of the first line. */
- int first_line_end;
+ sljit_s32 hit_start;
+ /* Pointer of the match end position. */
+ sljit_s32 match_end_ptr;
/* Points to the marked string. */
- int mark_ptr;
+ sljit_s32 mark_ptr;
/* Recursive control verb management chain. */
- int control_head_ptr;
+ sljit_s32 control_head_ptr;
/* Points to the last matched capture block index. */
- int capture_last_ptr;
- /* Points to the starting position of the current match. */
- int start_ptr;
+ sljit_s32 capture_last_ptr;
+ /* Fast forward skipping byte code pointer. */
+ pcre_uchar *fast_forward_bc_ptr;
+ /* Locals used by fast fail optimization. */
+ sljit_s32 fast_fail_start_ptr;
+ sljit_s32 fast_fail_end_ptr;
/* Flipped and lower case tables. */
- const pcre_uint8 *fcc;
+ const sljit_u8 *fcc;
sljit_sw lcc;
/* Mode can be PCRE_STUDY_JIT_COMPILE and others. */
int mode;
@@ -366,20 +386,20 @@ typedef struct compiler_common {
BOOL has_skip_arg;
/* (*THEN) is found in the pattern. */
BOOL has_then;
- /* Needs to know the start position anytime. */
- BOOL needs_start_ptr;
+ /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */
+ BOOL has_skip_in_assert_back;
/* Currently in recurse or negative assert. */
BOOL local_exit;
/* Currently in a positive assert. */
BOOL positive_assert;
/* Newline control. */
int nltype;
- pcre_uint32 nlmax;
- pcre_uint32 nlmin;
+ sljit_u32 nlmax;
+ sljit_u32 nlmin;
int newline;
int bsr_nltype;
- pcre_uint32 bsr_nlmax;
- pcre_uint32 bsr_nlmin;
+ sljit_u32 bsr_nlmax;
+ sljit_u32 bsr_nlmin;
/* Dollar endonly. */
int endonly;
/* Tables. */
@@ -419,6 +439,7 @@ typedef struct compiler_common {
BOOL utf;
#ifdef SUPPORT_UCP
BOOL use_ucp;
+ jump_list *getucd;
#endif
#ifdef COMPILE_PCRE8
jump_list *utfreadchar;
@@ -426,9 +447,6 @@ typedef struct compiler_common {
jump_list *utfreadtype8;
#endif
#endif /* SUPPORT_UTF */
-#ifdef SUPPORT_UCP
- jump_list *getucd;
-#endif
} compiler_common;
/* For byte_sequence_compare. */
@@ -439,27 +457,27 @@ typedef struct compare_context {
#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
int ucharptr;
union {
- sljit_si asint;
- sljit_uh asushort;
+ sljit_s32 asint;
+ sljit_u16 asushort;
#if defined COMPILE_PCRE8
- sljit_ub asbyte;
- sljit_ub asuchars[4];
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
#elif defined COMPILE_PCRE16
- sljit_uh asuchars[2];
+ sljit_u16 asuchars[2];
#elif defined COMPILE_PCRE32
- sljit_ui asuchars[1];
+ sljit_u32 asuchars[1];
#endif
} c;
union {
- sljit_si asint;
- sljit_uh asushort;
+ sljit_s32 asint;
+ sljit_u16 asushort;
#if defined COMPILE_PCRE8
- sljit_ub asbyte;
- sljit_ub asuchars[4];
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
#elif defined COMPILE_PCRE16
- sljit_uh asuchars[2];
+ sljit_u16 asuchars[2];
#elif defined COMPILE_PCRE32
- sljit_ui asuchars[1];
+ sljit_u32 asuchars[1];
#endif
} oc;
#endif
@@ -501,14 +519,14 @@ the start pointers when the end of the capturing group has not yet reached. */
#define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start])
#if defined COMPILE_PCRE8
-#define MOV_UCHAR SLJIT_MOV_UB
-#define MOVU_UCHAR SLJIT_MOVU_UB
+#define MOV_UCHAR SLJIT_MOV_U8
+#define MOVU_UCHAR SLJIT_MOVU_U8
#elif defined COMPILE_PCRE16
-#define MOV_UCHAR SLJIT_MOV_UH
-#define MOVU_UCHAR SLJIT_MOVU_UH
+#define MOV_UCHAR SLJIT_MOV_U16
+#define MOVU_UCHAR SLJIT_MOVU_U16
#elif defined COMPILE_PCRE32
-#define MOV_UCHAR SLJIT_MOV_UI
-#define MOVU_UCHAR SLJIT_MOVU_UI
+#define MOV_UCHAR SLJIT_MOV_U32
+#define MOVU_UCHAR SLJIT_MOVU_U32
#else
#error Unsupported compiling mode
#endif
@@ -564,11 +582,6 @@ SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS);
return count;
}
-static int ones_in_half_byte[16] = {
- /* 0 */ 0, 1, 1, 2, /* 4 */ 1, 2, 2, 3,
- /* 8 */ 1, 2, 2, 3, /* 12 */ 2, 3, 3, 4
-};
-
/* Functions whose might need modification for all new supported opcodes:
next_opcode
check_opcode_types
@@ -780,6 +793,7 @@ static BOOL check_opcode_types(compiler_common *common, pcre_uchar *cc, pcre_uch
{
int count;
pcre_uchar *slot;
+pcre_uchar *assert_back_end = cc - 1;
/* Calculate important variables (like stack size) and checks whether all opcodes are supported. */
while (cc < ccend)
@@ -850,15 +864,19 @@ while (cc < ccend)
cc += 2 + 2 * LINK_SIZE;
break;
+ case OP_ASSERTBACK:
+ slot = bracketend(cc);
+ if (slot > assert_back_end)
+ assert_back_end = slot;
+ cc += 1 + LINK_SIZE;
+ break;
+
case OP_THEN_ARG:
common->has_then = TRUE;
common->control_head_ptr = 1;
/* Fall through. */
case OP_PRUNE_ARG:
- common->needs_start_ptr = TRUE;
- /* Fall through. */
-
case OP_MARK:
if (common->mark_ptr == 0)
{
@@ -871,17 +889,20 @@ while (cc < ccend)
case OP_THEN:
common->has_then = TRUE;
common->control_head_ptr = 1;
- /* Fall through. */
+ cc += 1;
+ break;
- case OP_PRUNE:
case OP_SKIP:
- common->needs_start_ptr = TRUE;
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
cc += 1;
break;
case OP_SKIP_ARG:
common->control_head_ptr = 1;
common->has_skip_arg = TRUE;
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
cc += 1 + 2 + cc[1];
break;
@@ -895,8 +916,189 @@ while (cc < ccend)
return TRUE;
}
+static BOOL is_accelerated_repeat(pcre_uchar *cc)
+{
+switch(*cc)
+ {
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ return (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI);
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ return TRUE;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
+ case OP_XCLASS:
+ cc += (*cc == OP_XCLASS) ? GET(cc, 1) : (int)(1 + (32 / sizeof(pcre_uchar)));
+#else
+ cc += (1 + (32 / sizeof(pcre_uchar)));
+#endif
+
+ switch(*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ return TRUE;
+ }
+ break;
+ }
+return FALSE;
+}
+
+static SLJIT_INLINE BOOL detect_fast_forward_skip(compiler_common *common, int *private_data_start)
+{
+pcre_uchar *cc = common->start;
+pcre_uchar *end;
+
+/* Skip not repeated brackets. */
+while (TRUE)
+ {
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+ }
+
+ if (*cc != OP_BRA && *cc != OP_CBRA)
+ break;
+
+ end = cc + GET(cc, 1);
+ if (*end != OP_KET || PRIVATE_DATA(end) != 0)
+ return FALSE;
+ if (*cc == OP_CBRA)
+ {
+ if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
+ return FALSE;
+ cc += IMM2_SIZE;
+ }
+ cc += 1 + LINK_SIZE;
+ }
+
+if (is_accelerated_repeat(cc))
+ {
+ common->fast_forward_bc_ptr = cc;
+ common->private_data_ptrs[(cc + 1) - common->start] = *private_data_start;
+ *private_data_start += sizeof(sljit_sw);
+ return TRUE;
+ }
+return FALSE;
+}
+
+static SLJIT_INLINE void detect_fast_fail(compiler_common *common, pcre_uchar *cc, int *private_data_start, sljit_s32 depth)
+{
+ pcre_uchar *next_alt;
+
+ SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA);
+
+ if (*cc == OP_CBRA && common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
+ return;
+
+ next_alt = bracketend(cc) - (1 + LINK_SIZE);
+ if (*next_alt != OP_KET || PRIVATE_DATA(next_alt) != 0)
+ return;
+
+ do
+ {
+ next_alt = cc + GET(cc, 1);
+
+ cc += 1 + LINK_SIZE + ((*cc == OP_CBRA) ? IMM2_SIZE : 0);
+
+ while (TRUE)
+ {
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+ }
+ break;
+ }
+
+ if (depth > 0 && (*cc == OP_BRA || *cc == OP_CBRA))
+ detect_fast_fail(common, cc, private_data_start, depth - 1);
+
+ if (is_accelerated_repeat(cc))
+ {
+ common->private_data_ptrs[(cc + 1) - common->start] = *private_data_start;
+
+ if (common->fast_fail_start_ptr == 0)
+ common->fast_fail_start_ptr = *private_data_start;
+
+ *private_data_start += sizeof(sljit_sw);
+ common->fast_fail_end_ptr = *private_data_start;
+
+ if (*private_data_start > SLJIT_MAX_LOCAL_SIZE)
+ return;
+ }
+
+ cc = next_alt;
+ }
+ while (*cc == OP_ALT);
+}
+
static int get_class_iterator_size(pcre_uchar *cc)
{
+sljit_u32 min;
+sljit_u32 max;
switch(*cc)
{
case OP_CRSTAR:
@@ -911,9 +1113,14 @@ switch(*cc)
case OP_CRRANGE:
case OP_CRMINRANGE:
- if (GET2(cc, 1) == GET2(cc, 1 + IMM2_SIZE))
- return 0;
- return 2;
+ min = GET2(cc, 1);
+ max = GET2(cc, 1 + IMM2_SIZE);
+ if (max == 0)
+ return (*cc == OP_CRRANGE) ? 2 : 1;
+ max -= min;
+ if (max > 2)
+ max = 2;
+ return max;
default:
return 0;
@@ -1186,14 +1393,14 @@ while (cc < ccend)
case OP_CLASS:
case OP_NCLASS:
- size += 1 + 32 / sizeof(pcre_uchar);
space = get_class_iterator_size(cc + size);
+ size = 1 + 32 / sizeof(pcre_uchar);
break;
#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
case OP_XCLASS:
- size = GET(cc, 1);
space = get_class_iterator_size(cc + size);
+ size = GET(cc, 1);
break;
#endif
@@ -1406,6 +1613,7 @@ while (cc < ccend)
case OP_CLASS:
case OP_NCLASS:
case OP_XCLASS:
+ case OP_CALLOUT:
cc = next_opcode(common, cc);
SLJIT_ASSERT(cc != NULL);
@@ -1990,7 +2198,7 @@ if (save)
SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty)));
}
-static SLJIT_INLINE pcre_uchar *set_then_offsets(compiler_common *common, pcre_uchar *cc, pcre_uint8 *current_offset)
+static SLJIT_INLINE pcre_uchar *set_then_offsets(compiler_common *common, pcre_uchar *cc, sljit_u8 *current_offset)
{
pcre_uchar *end = bracketend(cc);
BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT;
@@ -2113,6 +2321,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
/* May destroy all locals and registers except TMP2. */
DEFINE_COMPILER;
+SLJIT_ASSERT(size > 0);
OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
#ifdef DESTROY_REGISTERS
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
@@ -2127,6 +2336,8 @@ add_stub(common, CMP(SLJIT_GREATER, STACK_TOP, 0, STACK_LIMIT, 0));
static SLJIT_INLINE void free_stack(compiler_common *common, int size)
{
DEFINE_COMPILER;
+
+SLJIT_ASSERT(size > 0);
OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
}
@@ -2190,6 +2401,18 @@ else
}
}
+static SLJIT_INLINE void reset_fast_fail(compiler_common *common)
+{
+DEFINE_COMPILER;
+sljit_s32 i;
+
+SLJIT_ASSERT(common->fast_fail_start_ptr < common->fast_fail_end_ptr);
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+for (i = common->fast_fail_start_ptr; i < common->fast_fail_end_ptr; i += sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), i, TMP1, 0);
+}
+
static SLJIT_INLINE void do_reset_match(compiler_common *common, int length)
{
DEFINE_COMPILER;
@@ -2262,7 +2485,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(1), STR_PTR, 0);
OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
if (common->mark_ptr != 0)
OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
-OP1(SLJIT_MOV_SI, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, offset_count));
+OP1(SLJIT_MOV_S32, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, offset_count));
if (common->mark_ptr != 0)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R2, 0);
OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, offsets), SLJIT_IMM, sizeof(int));
@@ -2277,7 +2500,7 @@ OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
-OP1(SLJIT_MOVU_SI, SLJIT_MEM1(SLJIT_R2), sizeof(int), SLJIT_S1, 0);
+OP1(SLJIT_MOVU_S32, SLJIT_MEM1(SLJIT_R2), sizeof(int), SLJIT_S1, 0);
OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, loop);
JUMPHERE(early_quit);
@@ -2310,7 +2533,7 @@ SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0
OP1(SLJIT_MOV, SLJIT_R1, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_PARTIAL);
-OP1(SLJIT_MOV_SI, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, real_offset_count));
+OP1(SLJIT_MOV_S32, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, real_offset_count));
CMPTO(SLJIT_SIG_LESS, SLJIT_R2, 0, SLJIT_IMM, 2, quit);
/* Store match begin and end. */
@@ -2322,7 +2545,7 @@ OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mode == JIT_PARTIAL_HA
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_R1), 2 * sizeof(int), SLJIT_R2, 0);
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(SLJIT_R1), 2 * sizeof(int), SLJIT_R2, 0);
JUMPHERE(jump);
OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mode == JIT_PARTIAL_HARD_COMPILE ? common->start_used_ptr : common->hit_start);
@@ -2330,13 +2553,13 @@ OP2(SLJIT_SUB, SLJIT_S1, 0, STR_END, 0, SLJIT_S0, 0);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_R1), sizeof(int), SLJIT_S1, 0);
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(SLJIT_R1), sizeof(int), SLJIT_S1, 0);
OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S0, 0);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R2, 0);
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R2, 0);
JUMPTO(SLJIT_JUMP, quit);
}
@@ -2573,7 +2796,7 @@ else
JUMPHERE(jump);
}
-static void peek_char(compiler_common *common, pcre_uint32 max)
+static void peek_char(compiler_common *common, sljit_u32 max)
{
/* Reads the character into TMP1, keeps STR_PTR.
Does not check STR_END. TMP2 Destroyed. */
@@ -2618,12 +2841,12 @@ if (common->utf)
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
-static BOOL is_char7_bitset(const pcre_uint8 *bitset, BOOL nclass)
+static BOOL is_char7_bitset(const sljit_u8 *bitset, BOOL nclass)
{
/* Tells whether the character codes below 128 are enough
to determine a match. */
-const pcre_uint8 value = nclass ? 0xff : 0;
-const pcre_uint8 *end = bitset + 32;
+const sljit_u8 value = nclass ? 0xff : 0;
+const sljit_u8 *end = bitset + 32;
bitset += 16;
do
@@ -2648,12 +2871,12 @@ SLJIT_ASSERT(common->utf);
OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
if (full_read)
{
jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xc0);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
JUMPHERE(jump);
}
@@ -2661,7 +2884,7 @@ if (full_read)
#endif /* SUPPORT_UTF && COMPILE_PCRE8 */
-static void read_char_range(compiler_common *common, pcre_uint32 min, pcre_uint32 max, BOOL update_str_ptr)
+static void read_char_range(compiler_common *common, sljit_u32 min, sljit_u32 max, BOOL update_str_ptr)
{
/* Reads the precise value of a character into TMP1, if the character is
between min and max (c >= min && c <= max). Otherwise it returns with a value
@@ -2692,7 +2915,7 @@ if (common->utf)
{
OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xf0);
if (update_str_ptr)
- OP1(SLJIT_MOV_UB, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
@@ -2716,7 +2939,7 @@ if (common->utf)
{
OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xe0);
if (update_str_ptr)
- OP1(SLJIT_MOV_UB, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0xf);
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
@@ -2736,7 +2959,7 @@ if (common->utf)
add_jump(compiler, (max < 0x10000) ? &common->utfreadchar16 : &common->utfreadchar, JUMP(SLJIT_FAST_CALL));
else if (max < 128)
{
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
}
else
@@ -2745,7 +2968,7 @@ if (common->utf)
if (!update_str_ptr)
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
else
- OP1(SLJIT_MOV_UB, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
@@ -2815,7 +3038,7 @@ if (common->utf)
{
/* This can be an extra read in some situations, but hopefully
it is needed in most cases. */
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xc0);
if (!update_str_ptr)
{
@@ -2827,7 +3050,7 @@ if (common->utf)
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
JUMPHERE(jump2);
}
else
@@ -2842,7 +3065,7 @@ if (common->utf)
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
jump = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
#endif
-OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
#if !defined COMPILE_PCRE8
JUMPHERE(jump);
#endif
@@ -3034,7 +3257,7 @@ compare = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x3);
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0);
-OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
JUMPHERE(compare);
@@ -3043,7 +3266,7 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
/* We only have types for characters less than 256. */
JUMPHERE(jump);
-OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
@@ -3069,26 +3292,26 @@ SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8);
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
-OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
-OP1(SLJIT_MOV_UH, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
-OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
#endif
-static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, BOOL firstline)
+static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf)
{
DEFINE_COMPILER;
struct sljit_label *mainloop;
struct sljit_label *newlinelabel = NULL;
struct sljit_jump *start;
struct sljit_jump *end = NULL;
-struct sljit_jump *nl = NULL;
+struct sljit_jump *end2 = NULL;
#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
struct sljit_jump *singlechar;
#endif
@@ -3096,14 +3319,13 @@ jump_list *newline = NULL;
BOOL newlinecheck = FALSE;
BOOL readuchar = FALSE;
-if (!(hascrorlf || firstline) && (common->nltype == NLTYPE_ANY ||
- common->nltype == NLTYPE_ANYCRLF || common->newline > 255))
+if (!(hascrorlf || (common->match_end_ptr != 0)) &&
+ (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255))
newlinecheck = TRUE;
-if (firstline)
+if (common->match_end_ptr != 0)
{
/* Search for the end of the first line. */
- SLJIT_ASSERT(common->first_line_end != 0);
OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
if (common->nltype == NLTYPE_FIXED && common->newline > 255)
@@ -3116,19 +3338,19 @@ if (firstline)
CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop);
CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop);
JUMPHERE(end);
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
else
{
end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
mainloop = LABEL();
/* Continual stores does not cause data dependency. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
read_char_range(common, common->nlmin, common->nlmax, TRUE);
check_newlinechar(common, common->nltype, &newline, TRUE);
CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop);
JUMPHERE(end);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->first_line_end, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
set_jumps(newline, LABEL());
}
@@ -3149,7 +3371,7 @@ if (newlinecheck)
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
- nl = JUMP(SLJIT_JUMP);
+ end2 = JUMP(SLJIT_JUMP);
}
mainloop = LABEL();
@@ -3172,7 +3394,7 @@ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
if (common->utf)
{
singlechar = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
JUMPHERE(singlechar);
}
@@ -3194,51 +3416,52 @@ JUMPHERE(start);
if (newlinecheck)
{
JUMPHERE(end);
- JUMPHERE(nl);
+ JUMPHERE(end2);
}
return mainloop;
}
#define MAX_N_CHARS 16
-#define MAX_N_BYTES 8
+#define MAX_DIFF_CHARS 6
-static SLJIT_INLINE void add_prefix_byte(pcre_uint8 byte, pcre_uint8 *bytes)
+static SLJIT_INLINE void add_prefix_char(pcre_uchar chr, pcre_uchar *chars)
{
-pcre_uint8 len = bytes[0];
-int i;
+pcre_uchar i, len;
+len = chars[0];
if (len == 255)
return;
if (len == 0)
{
- bytes[0] = 1;
- bytes[1] = byte;
+ chars[0] = 1;
+ chars[1] = chr;
return;
}
for (i = len; i > 0; i--)
- if (bytes[i] == byte)
+ if (chars[i] == chr)
return;
-if (len >= MAX_N_BYTES - 1)
+if (len >= MAX_DIFF_CHARS - 1)
{
- bytes[0] = 255;
+ chars[0] = 255;
return;
}
len++;
-bytes[len] = byte;
-bytes[0] = len;
+chars[len] = chr;
+chars[0] = len;
}
-static int scan_prefix(compiler_common *common, pcre_uchar *cc, pcre_uint32 *chars, pcre_uint8 *bytes, int max_chars, pcre_uint32 *rec_count)
+static int scan_prefix(compiler_common *common, pcre_uchar *cc, pcre_uchar *chars, int max_chars, sljit_u32 *rec_count)
{
/* Recursive function, which scans prefix literals. */
-BOOL last, any, caseless;
+BOOL last, any, class, caseless;
int len, repeat, len_save, consumed = 0;
-pcre_uint32 chr, mask;
+sljit_u32 chr; /* Any unicode character. */
+sljit_u8 *bytes, *bytes_end, byte;
pcre_uchar *alternative, *cc_save, *oc;
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
pcre_uchar othercase[8];
@@ -3257,6 +3480,7 @@ while (TRUE)
last = TRUE;
any = FALSE;
+ class = FALSE;
caseless = FALSE;
switch (*cc)
@@ -3320,7 +3544,7 @@ while (TRUE)
#ifdef SUPPORT_UTF
if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc);
#endif
- max_chars = scan_prefix(common, cc + len, chars, bytes, max_chars, rec_count);
+ max_chars = scan_prefix(common, cc + len, chars, max_chars, rec_count);
if (max_chars == 0)
return consumed;
last = FALSE;
@@ -3343,7 +3567,7 @@ while (TRUE)
alternative = cc + GET(cc, 1);
while (*alternative == OP_ALT)
{
- max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, bytes, max_chars, rec_count);
+ max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, max_chars, rec_count);
if (max_chars == 0)
return consumed;
alternative += GET(alternative, 1);
@@ -3356,18 +3580,17 @@ while (TRUE)
case OP_CLASS:
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && !is_char7_bitset((const pcre_uint8 *)(cc + 1), FALSE)) return consumed;
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)(cc + 1), FALSE))
+ return consumed;
#endif
- any = TRUE;
- cc += 1 + 32 / sizeof(pcre_uchar);
+ class = TRUE;
break;
case OP_NCLASS:
#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
if (common->utf) return consumed;
#endif
- any = TRUE;
- cc += 1 + 32 / sizeof(pcre_uchar);
+ class = TRUE;
break;
#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
@@ -3382,7 +3605,7 @@ while (TRUE)
case OP_DIGIT:
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && !is_char7_bitset((const pcre_uint8 *)common->ctypes - cbit_length + cbit_digit, FALSE))
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_digit, FALSE))
return consumed;
#endif
any = TRUE;
@@ -3391,7 +3614,7 @@ while (TRUE)
case OP_WHITESPACE:
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && !is_char7_bitset((const pcre_uint8 *)common->ctypes - cbit_length + cbit_space, FALSE))
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_space, FALSE))
return consumed;
#endif
any = TRUE;
@@ -3400,7 +3623,7 @@ while (TRUE)
case OP_WORDCHAR:
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && !is_char7_bitset((const pcre_uint8 *)common->ctypes - cbit_length + cbit_word, FALSE))
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_word, FALSE))
return consumed;
#endif
any = TRUE;
@@ -3423,10 +3646,10 @@ while (TRUE)
cc++;
break;
-#ifdef SUPPORT_UCP
+#ifdef SUPPORT_UTF
case OP_NOTPROP:
case OP_PROP:
-#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+#ifndef COMPILE_PCRE32
if (common->utf) return consumed;
#endif
any = TRUE;
@@ -3455,30 +3678,114 @@ while (TRUE)
if (any)
{
-#if defined COMPILE_PCRE8
- mask = 0xff;
-#elif defined COMPILE_PCRE16
- mask = 0xffff;
-#elif defined COMPILE_PCRE32
- mask = 0xffffffff;
-#else
- SLJIT_ASSERT_STOP();
-#endif
+ do
+ {
+ chars[0] = 255;
+
+ consumed++;
+ if (--max_chars == 0)
+ return consumed;
+ chars += MAX_DIFF_CHARS;
+ }
+ while (--repeat > 0);
+
+ repeat = 1;
+ continue;
+ }
+
+ if (class)
+ {
+ bytes = (sljit_u8*) (cc + 1);
+ cc += 1 + 32 / sizeof(pcre_uchar);
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ max_chars = scan_prefix(common, cc + 1, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ break;
+
+ default:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ repeat = GET2(cc, 1);
+ if (repeat <= 0)
+ return consumed;
+ break;
+ }
do
{
- chars[0] = mask;
- chars[1] = mask;
- bytes[0] = 255;
+ if (bytes[31] & 0x80)
+ chars[0] = 255;
+ else if (chars[0] != 255)
+ {
+ bytes_end = bytes + 32;
+ chr = 0;
+ do
+ {
+ byte = *bytes++;
+ SLJIT_ASSERT((chr & 0x7) == 0);
+ if (byte == 0)
+ chr += 8;
+ else
+ {
+ do
+ {
+ if ((byte & 0x1) != 0)
+ add_prefix_char(chr, chars);
+ byte >>= 1;
+ chr++;
+ }
+ while (byte != 0);
+ chr = (chr + 7) & ~7;
+ }
+ }
+ while (chars[0] != 255 && bytes < bytes_end);
+ bytes = bytes_end - 32;
+ }
consumed++;
if (--max_chars == 0)
return consumed;
- chars += 2;
- bytes += MAX_N_BYTES;
+ chars += MAX_DIFF_CHARS;
}
while (--repeat > 0);
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ return consumed;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ if (GET2(cc, 1) != GET2(cc, 1 + IMM2_SIZE))
+ return consumed;
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+ }
+
repeat = 1;
continue;
}
@@ -3505,7 +3812,10 @@ while (TRUE)
}
}
else
+ {
caseless = FALSE;
+ othercase[0] = 0; /* Stops compiler warning - PH */
+ }
len_save = len;
cc_save = cc;
@@ -3515,43 +3825,16 @@ while (TRUE)
do
{
chr = *cc;
-#ifdef COMPILE_PCRE32
- if (SLJIT_UNLIKELY(chr == NOTACHAR))
- return consumed;
-#endif
- add_prefix_byte((pcre_uint8)chr, bytes);
+ add_prefix_char(*cc, chars);
- mask = 0;
if (caseless)
- {
- add_prefix_byte((pcre_uint8)*oc, bytes);
- mask = *cc ^ *oc;
- chr |= mask;
- }
-
-#ifdef COMPILE_PCRE32
- if (chars[0] == NOTACHAR && chars[1] == 0)
-#else
- if (chars[0] == NOTACHAR)
-#endif
- {
- chars[0] = chr;
- chars[1] = mask;
- }
- else
- {
- mask |= chars[0] ^ chr;
- chr |= mask;
- chars[0] = chr;
- chars[1] |= mask;
- }
+ add_prefix_char(*oc, chars);
len--;
consumed++;
if (--max_chars == 0)
return consumed;
- chars += 2;
- bytes += MAX_N_BYTES;
+ chars += MAX_DIFF_CHARS;
cc++;
oc++;
}
@@ -3570,163 +3853,576 @@ while (TRUE)
}
}
-static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common, BOOL firstline)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+static sljit_s32 character_to_int32(pcre_uchar chr)
+{
+sljit_s32 value = (sljit_s32)chr;
+#if defined COMPILE_PCRE8
+#define SSE2_COMPARE_TYPE_INDEX 0
+return (value << 24) | (value << 16) | (value << 8) | value;
+#elif defined COMPILE_PCRE16
+#define SSE2_COMPARE_TYPE_INDEX 1
+return (value << 16) | value;
+#elif defined COMPILE_PCRE32
+#define SSE2_COMPARE_TYPE_INDEX 2
+return value;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+static SLJIT_INLINE void fast_forward_first_char2_sse2(compiler_common *common, pcre_uchar char1, pcre_uchar char2)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *quit[3];
+struct sljit_jump *nomatch;
+sljit_u8 instruction[8];
+sljit_s32 tmp1_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR);
+BOOL load_twice = FALSE;
+pcre_uchar bit;
+
+bit = char1 ^ char2;
+if (!is_powerof2(bit))
+ bit = 0;
+
+if ((char1 != char2) && bit == 0)
+ load_twice = TRUE;
+
+quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+/* First part (unaligned start) */
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
+
+SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1);
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+instruction[3] = 0xc0 | (2 << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1 != char2)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
+
+ /* MOVD xmm, r/m32 */
+ instruction[3] = 0xc0 | (3 << 3) | tmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+instruction[2] = 0x70;
+instruction[3] = 0xc0 | (2 << 3) | 2;
+instruction[4] = 0;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1 != char2)
+ {
+ /* PSHUFD xmm1, xmm2/m128, imm8 */
+ instruction[3] = 0xc0 | (3 << 3) | 3;
+ instruction[4] = 0;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 0xf);
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+
+/* MOVDQA xmm1, xmm2/m128 */
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+if (str_ptr_ind < 8)
+ {
+ instruction[2] = 0x6f;
+ instruction[3] = (0 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ if (load_twice)
+ {
+ instruction[3] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+ }
+else
+ {
+ instruction[1] = 0x41;
+ instruction[2] = 0x0f;
+ instruction[3] = 0x6f;
+ instruction[4] = (0 << 3) | (str_ptr_ind & 0x7);
+ sljit_emit_op_custom(compiler, instruction, 5);
+
+ if (load_twice)
+ {
+ instruction[4] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+ instruction[1] = 0x0f;
+ }
+
+#else
+
+instruction[2] = 0x6f;
+instruction[3] = (0 << 3) | str_ptr_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ instruction[3] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+#endif
+
+if (bit != 0)
+ {
+ /* POR xmm1, xmm2/m128 */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (0 << 3) | 3;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PCMPEQB/W/D xmm1, xmm2/m128 */
+instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+instruction[3] = 0xc0 | (0 << 3) | 2;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ instruction[3] = 0xc0 | (1 << 3) | 3;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PMOVMSKB reg, xmm */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, TMP2, 0);
+ instruction[3] = 0xc0 | (tmp2_ind << 3) | 1;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, TMP3, 0);
+ }
+
+OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+nomatch = JUMP(SLJIT_ZERO);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+quit[1] = JUMP(SLJIT_JUMP);
+
+JUMPHERE(nomatch);
+
+start = LABEL();
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+quit[2] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+/* Second part (aligned) */
+
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+
+/* MOVDQA xmm1, xmm2/m128 */
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+if (str_ptr_ind < 8)
+ {
+ instruction[2] = 0x6f;
+ instruction[3] = (0 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ if (load_twice)
+ {
+ instruction[3] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+ }
+else
+ {
+ instruction[1] = 0x41;
+ instruction[2] = 0x0f;
+ instruction[3] = 0x6f;
+ instruction[4] = (0 << 3) | (str_ptr_ind & 0x7);
+ sljit_emit_op_custom(compiler, instruction, 5);
+
+ if (load_twice)
+ {
+ instruction[4] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+ instruction[1] = 0x0f;
+ }
+
+#else
+
+instruction[2] = 0x6f;
+instruction[3] = (0 << 3) | str_ptr_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ instruction[3] = (1 << 3) | str_ptr_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+#endif
+
+if (bit != 0)
+ {
+ /* POR xmm1, xmm2/m128 */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (0 << 3) | 3;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PCMPEQB/W/D xmm1, xmm2/m128 */
+instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+instruction[3] = 0xc0 | (0 << 3) | 2;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ instruction[3] = 0xc0 | (1 << 3) | 3;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PMOVMSKB reg, xmm */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (load_twice)
+ {
+ instruction[3] = 0xc0 | (tmp2_ind << 3) | 1;
+ sljit_emit_op_custom(compiler, instruction, 4);
+
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ }
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+JUMPTO(SLJIT_ZERO, start);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+start = LABEL();
+SET_LABEL(quit[0], start);
+SET_LABEL(quit[1], start);
+SET_LABEL(quit[2], start);
+}
+
+#undef SSE2_COMPARE_TYPE_INDEX
+
+#endif
+
+static void fast_forward_first_char2(compiler_common *common, pcre_uchar char1, pcre_uchar char2, sljit_s32 offset)
{
DEFINE_COMPILER;
struct sljit_label *start;
struct sljit_jump *quit;
-pcre_uint32 chars[MAX_N_CHARS * 2];
-pcre_uint8 bytes[MAX_N_CHARS * MAX_N_BYTES];
-pcre_uint8 ones[MAX_N_CHARS];
-int offsets[3];
-pcre_uint32 mask;
-pcre_uint8 *byte_set, *byte_set_end;
-int i, max, from;
-int range_right = -1, range_len = 3 - 1;
-sljit_ub *update_table = NULL;
-BOOL in_range;
-pcre_uint32 rec_count;
+struct sljit_jump *found;
+pcre_uchar mask;
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+struct sljit_label *utf_start = NULL;
+struct sljit_jump *utf_quit = NULL;
+#endif
+BOOL has_match_end = (common->match_end_ptr != 0);
-for (i = 0; i < MAX_N_CHARS; i++)
+if (offset > 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+
+if (has_match_end)
{
- chars[i << 1] = NOTACHAR;
- chars[(i << 1) + 1] = 0;
- bytes[i * MAX_N_BYTES] = 0;
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+
+ OP2(SLJIT_ADD, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, SLJIT_IMM, IN_UCHARS(offset + 1));
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+ if (sljit_x86_is_cmov_available())
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0);
+ sljit_x86_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0);
+ }
+#endif
+ {
+ quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP3, 0);
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ JUMPHERE(quit);
+ }
}
-rec_count = 10000;
-max = scan_prefix(common, common->start, chars, bytes, MAX_N_CHARS, &rec_count);
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+if (common->utf && offset > 0)
+ utf_start = LABEL();
+#endif
-if (max <= 1)
- return FALSE;
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
-for (i = 0; i < max; i++)
+/* SSE2 accelerated first character search. */
+
+if (sljit_x86_is_sse2_available())
{
- mask = chars[(i << 1) + 1];
- ones[i] = ones_in_half_byte[mask & 0xf];
- mask >>= 4;
- while (mask != 0)
+ fast_forward_first_char2_sse2(common, char1, char2);
+
+ SLJIT_ASSERT(common->mode == JIT_COMPILE || offset == 0);
+ if (common->mode == JIT_COMPILE)
{
- ones[i] += ones_in_half_byte[mask & 0xf];
- mask >>= 4;
+ /* In complete mode, we don't need to run a match when STR_PTR == STR_END. */
+ SLJIT_ASSERT(common->forced_quit_label == NULL);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH);
+ add_jump(compiler, &common->forced_quit, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+ if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == JIT_COMPILE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if defined COMPILE_PCRE8
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start);
+#elif defined COMPILE_PCRE16
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start);
+#else
+#error "Unknown code width"
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+#endif
+
+ if (offset > 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
}
+ else if (sljit_x86_is_cmov_available())
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0);
+ sljit_x86_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
+ }
+ else
+ {
+ quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
+ JUMPHERE(quit);
+ }
+
+ if (has_match_end)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ return;
}
+#endif
+
+quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+start = LABEL();
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+
+if (char1 == char2)
+ found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1);
+else
+ {
+ mask = char1 ^ char2;
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask);
+ }
+ else
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ found = JUMP(SLJIT_NOT_ZERO);
+ }
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, start);
+
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+if (common->utf && offset > 0)
+ utf_quit = JUMP(SLJIT_JUMP);
+#endif
+
+JUMPHERE(found);
+
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+if (common->utf && offset > 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if defined COMPILE_PCRE8
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, utf_start);
+#elif defined COMPILE_PCRE16
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, utf_start);
+#else
+#error "Unknown code width"
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(utf_quit);
+ }
+#endif
+
+JUMPHERE(quit);
+
+if (has_match_end)
+ {
+ quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ if (offset > 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+ JUMPHERE(quit);
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ }
+
+if (offset > 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+}
+
+static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *quit;
+struct sljit_jump *match;
+/* bytes[0] represent the number of characters between 0
+and MAX_N_BYTES - 1, 255 represents any character. */
+pcre_uchar chars[MAX_N_CHARS * MAX_DIFF_CHARS];
+sljit_s32 offset;
+pcre_uchar mask;
+pcre_uchar *char_set, *char_set_end;
+int i, max, from;
+int range_right = -1, range_len;
+sljit_u8 *update_table = NULL;
+BOOL in_range;
+sljit_u32 rec_count;
+
+for (i = 0; i < MAX_N_CHARS; i++)
+ chars[i * MAX_DIFF_CHARS] = 0;
+
+rec_count = 10000;
+max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count);
+
+if (max < 1)
+ return FALSE;
+
in_range = FALSE;
-from = 0; /* Prevent compiler "uninitialized" warning */
+/* Prevent compiler "uninitialized" warning */
+from = 0;
+range_len = 4 /* minimum length */ - 1;
for (i = 0; i <= max; i++)
{
- if (in_range && (i - from) > range_len && (bytes[(i - 1) * MAX_N_BYTES] <= 4))
+ if (in_range && (i - from) > range_len && (chars[(i - 1) * MAX_DIFF_CHARS] < 255))
{
range_len = i - from;
range_right = i - 1;
}
- if (i < max && bytes[i * MAX_N_BYTES] < 255)
+ if (i < max && chars[i * MAX_DIFF_CHARS] < 255)
{
+ SLJIT_ASSERT(chars[i * MAX_DIFF_CHARS] > 0);
if (!in_range)
{
in_range = TRUE;
from = i;
}
}
- else if (in_range)
+ else
in_range = FALSE;
}
if (range_right >= 0)
{
- update_table = (sljit_ub *)allocate_read_only_data(common, 256);
+ update_table = (sljit_u8 *)allocate_read_only_data(common, 256);
if (update_table == NULL)
return TRUE;
memset(update_table, IN_UCHARS(range_len), 256);
for (i = 0; i < range_len; i++)
{
- byte_set = bytes + ((range_right - i) * MAX_N_BYTES);
- SLJIT_ASSERT(byte_set[0] > 0 && byte_set[0] < 255);
- byte_set_end = byte_set + byte_set[0];
- byte_set++;
- while (byte_set <= byte_set_end)
+ char_set = chars + ((range_right - i) * MAX_DIFF_CHARS);
+ SLJIT_ASSERT(char_set[0] > 0 && char_set[0] < 255);
+ char_set_end = char_set + char_set[0];
+ char_set++;
+ while (char_set <= char_set_end)
{
- if (update_table[*byte_set] > IN_UCHARS(i))
- update_table[*byte_set] = IN_UCHARS(i);
- byte_set++;
+ if (update_table[(*char_set) & 0xff] > IN_UCHARS(i))
+ update_table[(*char_set) & 0xff] = IN_UCHARS(i);
+ char_set++;
}
}
}
-offsets[0] = -1;
+offset = -1;
/* Scan forward. */
for (i = 0; i < max; i++)
- if (ones[i] <= 2) {
- offsets[0] = i;
- break;
- }
-
-if (offsets[0] < 0 && range_right < 0)
- return FALSE;
-
-if (offsets[0] >= 0)
{
- /* Scan backward. */
- offsets[1] = -1;
- for (i = max - 1; i > offsets[0]; i--)
- if (ones[i] <= 2 && i != range_right)
- {
- offsets[1] = i;
- break;
- }
-
- /* This case is handled better by fast_forward_first_char. */
- if (offsets[1] == -1 && offsets[0] == 0 && range_right < 0)
- return FALSE;
-
- offsets[2] = -1;
- /* We only search for a middle character if there is no range check. */
- if (offsets[1] >= 0 && range_right == -1)
+ if (offset == -1)
{
- /* Scan from middle. */
- for (i = (offsets[0] + offsets[1]) / 2 + 1; i < offsets[1]; i++)
- if (ones[i] <= 2)
+ if (chars[i * MAX_DIFF_CHARS] <= 2)
+ offset = i;
+ }
+ else if (chars[offset * MAX_DIFF_CHARS] == 2 && chars[i * MAX_DIFF_CHARS] <= 2)
+ {
+ if (chars[i * MAX_DIFF_CHARS] == 1)
+ offset = i;
+ else
+ {
+ mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2];
+ if (!is_powerof2(mask))
{
- offsets[2] = i;
- break;
+ mask = chars[i * MAX_DIFF_CHARS + 1] ^ chars[i * MAX_DIFF_CHARS + 2];
+ if (is_powerof2(mask))
+ offset = i;
}
-
- if (offsets[2] == -1)
- {
- for (i = (offsets[0] + offsets[1]) / 2; i > offsets[0]; i--)
- if (ones[i] <= 2)
- {
- offsets[2] = i;
- break;
- }
}
}
+ }
- SLJIT_ASSERT(offsets[1] == -1 || (offsets[0] < offsets[1]));
- SLJIT_ASSERT(offsets[2] == -1 || (offsets[0] < offsets[2] && offsets[1] > offsets[2]));
-
- chars[0] = chars[offsets[0] << 1];
- chars[1] = chars[(offsets[0] << 1) + 1];
- if (offsets[2] >= 0)
- {
- chars[2] = chars[offsets[2] << 1];
- chars[3] = chars[(offsets[2] << 1) + 1];
- }
- if (offsets[1] >= 0)
- {
- chars[4] = chars[offsets[1] << 1];
- chars[5] = chars[(offsets[1] << 1) + 1];
- }
+if (range_right < 0)
+ {
+ if (offset < 0)
+ return FALSE;
+ SLJIT_ASSERT(chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2);
+ /* Works regardless the value is 1 or 2. */
+ mask = chars[offset * MAX_DIFF_CHARS + chars[offset * MAX_DIFF_CHARS]];
+ fast_forward_first_char2(common, chars[offset * MAX_DIFF_CHARS + 1], mask, offset);
+ return TRUE;
}
+if (range_right == offset)
+ offset = -1;
+
+SLJIT_ASSERT(offset == -1 || (chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2));
+
max -= 1;
-if (firstline)
+SLJIT_ASSERT(max > 0);
+if (common->match_end_ptr != 0)
{
- SLJIT_ASSERT(common->first_line_end != 0);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP1, 0);
@@ -3736,68 +4432,86 @@ if (firstline)
else
OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+SLJIT_ASSERT(range_right >= 0);
+
#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
-if (range_right >= 0)
- OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table);
+OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table);
#endif
start = LABEL();
quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
-SLJIT_ASSERT(range_right >= 0 || offsets[0] >= 0);
-
-if (range_right >= 0)
- {
#if defined COMPILE_PCRE8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right));
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right));
#else
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1);
#endif
#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0);
#else
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table);
#endif
- OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start);
- }
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start);
-if (offsets[0] >= 0)
+if (offset >= 0)
{
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[0]));
- if (offsets[1] >= 0)
- OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[1]));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset));
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- if (chars[1] != 0)
- OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[1]);
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[0], start);
- if (offsets[2] >= 0)
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offsets[2] - 1));
-
- if (offsets[1] >= 0)
+ if (chars[offset * MAX_DIFF_CHARS] == 1)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1], start);
+ else
{
- if (chars[5] != 0)
- OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, chars[5]);
- CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, chars[4], start);
+ mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2];
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1] | mask, start);
+ }
+ else
+ {
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1]);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 2], start);
+ JUMPHERE(match);
+ }
}
+ }
- if (offsets[2] >= 0)
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+if (common->utf && offset != 0)
+ {
+ if (offset < 0)
{
- if (chars[3] != 0)
- OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, chars[3]);
- CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[2], start);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ else
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+#if defined COMPILE_PCRE8
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, start);
+#elif defined COMPILE_PCRE16
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00, start);
+#else
+#error "Unknown code width"
+#endif
+ if (offset < 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
+#endif
+
+if (offset >= 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
JUMPHERE(quit);
-if (firstline)
+if (common->match_end_ptr != 0)
{
if (range_right >= 0)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
if (range_right >= 0)
{
@@ -3812,66 +4526,26 @@ return TRUE;
}
#undef MAX_N_CHARS
-#undef MAX_N_BYTES
+#undef MAX_DIFF_CHARS
-static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, pcre_uchar first_char, BOOL caseless, BOOL firstline)
+static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, pcre_uchar first_char, BOOL caseless)
{
-DEFINE_COMPILER;
-struct sljit_label *start;
-struct sljit_jump *quit;
-struct sljit_jump *found;
-pcre_uchar oc, bit;
-
-if (firstline)
- {
- SLJIT_ASSERT(common->first_line_end != 0);
- OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
- }
-
-start = LABEL();
-quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
-OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+pcre_uchar oc;
oc = first_char;
if (caseless)
{
oc = TABLE_GET(first_char, common->fcc, first_char);
-#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8)
+#if defined SUPPORT_UCP && !defined COMPILE_PCRE8
if (first_char > 127 && common->utf)
oc = UCD_OTHERCASE(first_char);
#endif
}
-if (first_char == oc)
- found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, first_char);
-else
- {
- bit = first_char ^ oc;
- if (is_powerof2(bit))
- {
- OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, bit);
- found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, first_char | bit);
- }
- else
- {
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, first_char);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- found = JUMP(SLJIT_NOT_ZERO);
- }
- }
-OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
-JUMPTO(SLJIT_JUMP, start);
-JUMPHERE(found);
-JUMPHERE(quit);
-
-if (firstline)
- OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+fast_forward_first_char2(common, first_char, oc, 0);
}
-static SLJIT_INLINE void fast_forward_newline(compiler_common *common, BOOL firstline)
+static SLJIT_INLINE void fast_forward_newline(compiler_common *common)
{
DEFINE_COMPILER;
struct sljit_label *loop;
@@ -3882,11 +4556,10 @@ struct sljit_jump *foundcr = NULL;
struct sljit_jump *notfoundnl;
jump_list *newline = NULL;
-if (firstline)
+if (common->match_end_ptr != 0)
{
- SLJIT_ASSERT(common->first_line_end != 0);
OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
+ OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
}
if (common->nltype == NLTYPE_FIXED && common->newline > 255)
@@ -3917,7 +4590,7 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
JUMPHERE(firstchar);
JUMPHERE(lastchar);
- if (firstline)
+ if (common->match_end_ptr != 0)
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
return;
}
@@ -3955,13 +4628,13 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
JUMPHERE(lastchar);
JUMPHERE(firstchar);
-if (firstline)
+if (common->match_end_ptr != 0)
OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
}
-static BOOL check_class_ranges(compiler_common *common, const pcre_uint8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
+static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
-static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, pcre_uint8 *start_bits, BOOL firstline)
+static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_u8 *start_bits)
{
DEFINE_COMPILER;
struct sljit_label *start;
@@ -3972,11 +4645,10 @@ jump_list *matches = NULL;
struct sljit_jump *jump;
#endif
-if (firstline)
+if (common->match_end_ptr != 0)
{
- SLJIT_ASSERT(common->first_line_end != 0);
OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
+ OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
}
start = LABEL();
@@ -3996,7 +4668,7 @@ if (!check_class_ranges(common, start_bits, (start_bits[31] & 0x80) != 0, TRUE,
#endif
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
found = JUMP(SLJIT_NOT_ZERO);
@@ -4012,7 +4684,7 @@ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
if (common->utf)
{
CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
}
#elif defined COMPILE_PCRE16
@@ -4034,7 +4706,7 @@ if (matches != NULL)
set_jumps(matches, LABEL());
JUMPHERE(quit);
-if (firstline)
+if (common->match_end_ptr != 0)
OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0);
}
@@ -4047,7 +4719,7 @@ struct sljit_jump *alreadyfound;
struct sljit_jump *found;
struct sljit_jump *foundoc = NULL;
struct sljit_jump *notfound;
-pcre_uint32 oc, bit;
+sljit_u32 oc, bit;
SLJIT_ASSERT(common->req_char_ptr != 0);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr);
@@ -4184,7 +4856,7 @@ else
if (common->utf)
jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
#endif /* COMPILE_PCRE8 */
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0);
@@ -4229,7 +4901,7 @@ else
if (common->utf)
jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
#endif
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes);
OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */);
OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
#ifndef COMPILE_PCRE8
@@ -4245,11 +4917,12 @@ OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOC
sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
}
-static BOOL check_class_ranges(compiler_common *common, const pcre_uint8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
{
+/* May destroy TMP1. */
DEFINE_COMPILER;
int ranges[MAX_RANGE_SIZE];
-pcre_uint8 bit, cbit, all;
+sljit_u8 bit, cbit, all;
int i, byte, length = 0;
bit = bits[0] & 0x1;
@@ -4544,12 +5217,12 @@ OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
#ifndef COMPILE_PCRE8
jump = CMP(SLJIT_GREATER, CHAR1, 0, SLJIT_IMM, 255);
#endif
-OP1(SLJIT_MOV_UB, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0);
+OP1(SLJIT_MOV_U8, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0);
#ifndef COMPILE_PCRE8
JUMPHERE(jump);
jump = CMP(SLJIT_GREATER, CHAR2, 0, SLJIT_IMM, 255);
#endif
-OP1(SLJIT_MOV_UB, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0);
+OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0);
#ifndef COMPILE_PCRE8
JUMPHERE(jump);
#endif
@@ -4574,11 +5247,11 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
static const pcre_uchar * SLJIT_CALL do_utf_caselesscmp(pcre_uchar *src1, jit_arguments *args, pcre_uchar *end1)
{
/* This function would be ineffective to do in JIT level. */
-pcre_uint32 c1, c2;
+sljit_u32 c1, c2;
const pcre_uchar *src2 = args->uchar_ptr;
const pcre_uchar *end2 = args->end;
const ucd_record *ur;
-const pcre_uint32 *pp;
+const sljit_u32 *pp;
while (src1 < end1)
{
@@ -4638,16 +5311,16 @@ if (context->sourcereg == -1)
#if defined COMPILE_PCRE8
#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
if (context->length >= 4)
- OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
else if (context->length >= 2)
- OP1(SLJIT_MOV_UH, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
else
#endif
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
#elif defined COMPILE_PCRE16
#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
if (context->length >= 4)
- OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
else
#endif
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
@@ -4689,12 +5362,12 @@ do
#endif
{
if (context->length >= 4)
- OP1(SLJIT_MOV_SI, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_S32, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
else if (context->length >= 2)
- OP1(SLJIT_MOV_UH, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_U16, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
#if defined COMPILE_PCRE8
else if (context->length >= 1)
- OP1(SLJIT_MOV_UB, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ OP1(SLJIT_MOV_U8, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
#endif /* COMPILE_PCRE8 */
context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1;
@@ -4777,6 +5450,8 @@ return cc;
} \
charoffset = (value);
+static pcre_uchar *compile_char1_matchingpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **backtracks, BOOL check_str_ptr);
+
static void compile_xclass_matchingpath(compiler_common *common, pcre_uchar *cc, jump_list **backtracks)
{
DEFINE_COMPILER;
@@ -4793,8 +5468,8 @@ BOOL utf = common->utf;
#ifdef SUPPORT_UCP
BOOL needstype = FALSE, needsscript = FALSE, needschar = FALSE;
BOOL charsaved = FALSE;
-int typereg = TMP1, scriptreg = TMP1;
-const pcre_uint32 *other_cases;
+int typereg = TMP1;
+const sljit_u32 *other_cases;
sljit_uw typeoffset;
#endif
@@ -4856,6 +5531,14 @@ while (*cc != XCL_END)
switch(*cc)
{
case PT_ANY:
+ /* Any either accepts everything or ignored. */
+ if (cc[-1] == XCL_PROP)
+ {
+ compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE);
+ if (list == backtracks)
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ return;
+ }
break;
case PT_LAMP:
@@ -4892,10 +5575,10 @@ while (*cc != XCL_END)
}
#endif
}
+SLJIT_ASSERT(compares > 0);
/* We are not necessary in utf mode even in 8 bit mode. */
cc = ccbegin;
-detect_partial_match(common, backtracks);
read_char_range(common, min, max, (cc[-1] & XCL_NOT) != 0);
if ((cc[-1] & XCL_HASPROP) == 0)
@@ -4903,11 +5586,11 @@ if ((cc[-1] & XCL_HASPROP) == 0)
if ((cc[-1] & XCL_MAP) != 0)
{
jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
- if (!check_class_ranges(common, (const pcre_uint8 *)cc, (((const pcre_uint8 *)cc)[31] & 0x80) != 0, TRUE, &found))
+ if (!check_class_ranges(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found))
{
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO));
@@ -4926,11 +5609,11 @@ if ((cc[-1] & XCL_HASPROP) == 0)
}
else if ((cc[-1] & XCL_MAP) != 0)
{
- OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
#ifdef SUPPORT_UCP
charsaved = TRUE;
#endif
- if (!check_class_ranges(common, (const pcre_uint8 *)cc, FALSE, TRUE, list))
+ if (!check_class_ranges(common, (const sljit_u8 *)cc, FALSE, TRUE, list))
{
#ifdef COMPILE_PCRE8
jump = NULL;
@@ -4940,7 +5623,7 @@ else if ((cc[-1] & XCL_MAP) != 0)
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO));
@@ -4951,45 +5634,82 @@ else if ((cc[-1] & XCL_MAP) != 0)
JUMPHERE(jump);
}
- OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
cc += 32 / sizeof(pcre_uchar);
}
#ifdef SUPPORT_UCP
-/* Simple register allocation. TMP1 is preferred if possible. */
if (needstype || needsscript)
{
if (needschar && !charsaved)
- OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
- add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
- if (needschar)
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+
+ OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+ OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+
+ /* Before anything else, we deal with scripts. */
+ if (needsscript)
{
- if (needstype)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3);
+
+ ccbegin = cc;
+
+ while (*cc != XCL_END)
{
- OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
- typereg = RETURN_ADDR;
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_SC)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]);
+ add_jump(compiler, compares > 0 ? list : backtracks, jump);
+ }
+ cc += 2;
+ }
}
- if (needsscript)
- scriptreg = TMP3;
- OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
+ cc = ccbegin;
}
- else if (needstype && needsscript)
- scriptreg = TMP3;
- /* In all other cases only one of them was specified, and that can goes to TMP1. */
- if (needsscript)
+ if (needschar)
{
- if (scriptreg == TMP1)
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+ }
+
+ if (needstype)
+ {
+ if (!needschar)
{
- OP1(SLJIT_MOV, scriptreg, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
- OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM2(scriptreg, TMP2), 3);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3);
}
else
{
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3);
- OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
- OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+ typereg = RETURN_ADDR;
}
}
}
@@ -5061,20 +5781,15 @@ while (*cc != XCL_END)
#ifdef SUPPORT_UCP
else
{
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
if (*cc == XCL_NOTPROP)
invertcmp ^= 0x1;
cc++;
switch(*cc)
{
case PT_ANY:
- if (list != backtracks)
- {
- if ((cc[-1] == XCL_NOTPROP && compares > 0) || (cc[-1] == XCL_PROP && compares == 0))
- continue;
- }
- else if (cc[-1] == XCL_NOTPROP)
- continue;
- jump = JUMP(SLJIT_JUMP);
+ if (!invertcmp)
+ jump = JUMP(SLJIT_JUMP);
break;
case PT_LAMP:
@@ -5098,7 +5813,8 @@ while (*cc != XCL_END)
break;
case PT_SC:
- jump = CMP(SLJIT_EQUAL ^ invertcmp, scriptreg, 0, SLJIT_IMM, (int)cc[1]);
+ compares++;
+ /* Do nothing. */
break;
case PT_SPACE:
@@ -5264,6 +5980,10 @@ while (*cc != XCL_END)
OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
+
+ default:
+ SLJIT_ASSERT_STOP();
+ break;
}
cc += 2;
}
@@ -5282,19 +6002,13 @@ if (found != NULL)
#endif
-static pcre_uchar *compile_char1_matchingpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **backtracks)
+static pcre_uchar *compile_simple_assertion_matchingpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **backtracks)
{
DEFINE_COMPILER;
int length;
-unsigned int c, oc, bit;
-compare_context context;
struct sljit_jump *jump[4];
-jump_list *end_list;
#ifdef SUPPORT_UTF
struct sljit_label *label;
-#ifdef SUPPORT_UCP
-pcre_uchar propdata[5];
-#endif
#endif /* SUPPORT_UTF */
switch(type)
@@ -5317,12 +6031,220 @@ switch(type)
add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
+ case OP_EODN:
+ /* Requires rather complex checks. */
+ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == JIT_COMPILE)
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS);
+ OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else if (common->nltype == NLTYPE_FIXED)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
+ }
+ else
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ jump[2] = JUMP(SLJIT_GREATER);
+ add_jump(compiler, backtracks, JUMP(SLJIT_LESS));
+ /* Equal. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[1]);
+ if (common->nltype == NLTYPE_ANYCRLF)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
+ read_char_range(common, common->nlmin, common->nlmax, TRUE);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+ add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+ }
+ JUMPHERE(jump[2]);
+ JUMPHERE(jump[3]);
+ }
+ JUMPHERE(jump[0]);
+ check_partial(common, FALSE);
+ return cc;
+
+ case OP_EOD:
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ check_partial(common, FALSE);
+ return cc;
+
+ case OP_DOLL:
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+
+ if (!common->endonly)
+ compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks);
+ else
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ check_partial(common, FALSE);
+ }
+ return cc;
+
+ case OP_DOLLM:
+ jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ check_partial(common, FALSE);
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == JIT_COMPILE)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
+ /* STR_PTR = STR_END - IN_UCHARS(1) */
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ peek_char(common, common->nlmax);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_CIRC:
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ return cc;
+
+ case OP_CIRCM:
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+ jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, TMP1, 0));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ skip_char_back(common);
+ read_char_range(common, common->nlmin, common->nlmax, TRUE);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_REVERSE:
+ length = GET(cc, 0);
+ if (length == 0)
+ return cc + LINK_SIZE;
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+#ifdef SUPPORT_UTF
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, length);
+ label = LABEL();
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0));
+ skip_char_back(common);
+ OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+#endif
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, TMP1, 0));
+ }
+ check_start_used_ptr(common);
+ return cc + LINK_SIZE;
+ }
+SLJIT_ASSERT_STOP();
+return cc;
+}
+
+static pcre_uchar *compile_char1_matchingpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **backtracks, BOOL check_str_ptr)
+{
+DEFINE_COMPILER;
+int length;
+unsigned int c, oc, bit;
+compare_context context;
+struct sljit_jump *jump[3];
+jump_list *end_list;
+#ifdef SUPPORT_UTF
+struct sljit_label *label;
+#ifdef SUPPORT_UCP
+pcre_uchar propdata[5];
+#endif
+#endif /* SUPPORT_UTF */
+
+switch(type)
+ {
case OP_NOT_DIGIT:
case OP_DIGIT:
/* Digits are usually 0-9, so it is worth to optimize them. */
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && is_char7_bitset((const pcre_uint8*)common->ctypes - cbit_length + cbit_digit, FALSE))
+ if (common->utf && is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_digit, FALSE))
read_char7_type(common, type == OP_NOT_DIGIT);
else
#endif
@@ -5334,9 +6256,10 @@ switch(type)
case OP_NOT_WHITESPACE:
case OP_WHITESPACE:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && is_char7_bitset((const pcre_uint8*)common->ctypes - cbit_length + cbit_space, FALSE))
+ if (common->utf && is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_space, FALSE))
read_char7_type(common, type == OP_NOT_WHITESPACE);
else
#endif
@@ -5347,9 +6270,10 @@ switch(type)
case OP_NOT_WORDCHAR:
case OP_WORDCHAR:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- if (common->utf && is_char7_bitset((const pcre_uint8*)common->ctypes - cbit_length + cbit_word, FALSE))
+ if (common->utf && is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_word, FALSE))
read_char7_type(common, type == OP_NOT_WORDCHAR);
else
#endif
@@ -5359,7 +6283,8 @@ switch(type)
return cc;
case OP_ANY:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
read_char_range(common, common->nlmin, common->nlmax, TRUE);
if (common->nltype == NLTYPE_FIXED && common->newline > 255)
{
@@ -5380,7 +6305,8 @@ switch(type)
return cc;
case OP_ALLANY:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#ifdef SUPPORT_UTF
if (common->utf)
{
@@ -5389,7 +6315,7 @@ switch(type)
#if defined COMPILE_PCRE8 || defined COMPILE_PCRE16
#if defined COMPILE_PCRE8
jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
#elif defined COMPILE_PCRE16
jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
@@ -5408,7 +6334,8 @@ switch(type)
return cc;
case OP_ANYBYTE:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
return cc;
@@ -5421,13 +6348,16 @@ switch(type)
propdata[2] = cc[0];
propdata[3] = cc[1];
propdata[4] = XCL_END;
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
compile_xclass_matchingpath(common, propdata, backtracks);
return cc + 2;
#endif
#endif
case OP_ANYNL:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
read_char_range(common, common->bsr_nlmin, common->bsr_nlmax, FALSE);
jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
/* We don't need to handle soft partial matching case. */
@@ -5449,7 +6379,8 @@ switch(type)
case OP_NOT_HSPACE:
case OP_HSPACE:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
read_char_range(common, 0x9, 0x3000, type == OP_NOT_HSPACE);
add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL));
add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
@@ -5457,7 +6388,8 @@ switch(type)
case OP_NOT_VSPACE:
case OP_VSPACE:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
read_char_range(common, 0xa, 0x2029, type == OP_NOT_VSPACE);
add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL));
add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
@@ -5465,13 +6397,14 @@ switch(type)
#ifdef SUPPORT_UCP
case OP_EXTUNI:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
read_char(common);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop));
/* Optimize register allocation: use a real register. */
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
- OP1(SLJIT_MOV_UB, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3);
+ OP1(SLJIT_MOV_U8, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3);
label = LABEL();
jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
@@ -5479,10 +6412,10 @@ switch(type)
read_char(common);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop));
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3);
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3);
OP2(SLJIT_SHL, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2);
- OP1(SLJIT_MOV_UI, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable));
+ OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable));
OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
@@ -5502,173 +6435,14 @@ switch(type)
return cc;
#endif
- case OP_EODN:
- /* Requires rather complex checks. */
- jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
- if (common->nltype == NLTYPE_FIXED && common->newline > 255)
- {
- OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
- if (common->mode == JIT_COMPILE)
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
- else
- {
- jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
- add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
- check_partial(common, TRUE);
- add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
- JUMPHERE(jump[1]);
- }
- OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
- }
- else if (common->nltype == NLTYPE_FIXED)
- {
- OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
- }
- else
- {
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
- jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
- OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
- jump[2] = JUMP(SLJIT_GREATER);
- add_jump(compiler, backtracks, JUMP(SLJIT_LESS));
- /* Equal. */
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
- jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
- add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
-
- JUMPHERE(jump[1]);
- if (common->nltype == NLTYPE_ANYCRLF)
- {
- OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
- add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, STR_END, 0));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
- }
- else
- {
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
- read_char_range(common, common->nlmin, common->nlmax, TRUE);
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
- add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
- add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
- }
- JUMPHERE(jump[2]);
- JUMPHERE(jump[3]);
- }
- JUMPHERE(jump[0]);
- check_partial(common, FALSE);
- return cc;
-
- case OP_EOD:
- add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
- check_partial(common, FALSE);
- return cc;
-
- case OP_CIRC:
- OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
- add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
- return cc;
-
- case OP_CIRCM:
- OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
- jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
- jump[0] = JUMP(SLJIT_JUMP);
- JUMPHERE(jump[1]);
-
- add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
- if (common->nltype == NLTYPE_FIXED && common->newline > 255)
- {
- OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, TMP1, 0));
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
- OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
- }
- else
- {
- skip_char_back(common);
- read_char_range(common, common->nlmin, common->nlmax, TRUE);
- check_newlinechar(common, common->nltype, backtracks, FALSE);
- }
- JUMPHERE(jump[0]);
- return cc;
-
- case OP_DOLL:
- OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
-
- if (!common->endonly)
- compile_char1_matchingpath(common, OP_EODN, cc, backtracks);
- else
- {
- add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
- check_partial(common, FALSE);
- }
- return cc;
-
- case OP_DOLLM:
- jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
- OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
- check_partial(common, FALSE);
- jump[0] = JUMP(SLJIT_JUMP);
- JUMPHERE(jump[1]);
-
- if (common->nltype == NLTYPE_FIXED && common->newline > 255)
- {
- OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
- if (common->mode == JIT_COMPILE)
- add_jump(compiler, backtracks, CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0));
- else
- {
- jump[1] = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
- /* STR_PTR = STR_END - IN_UCHARS(1) */
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
- check_partial(common, TRUE);
- add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
- JUMPHERE(jump[1]);
- }
-
- OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
- add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
- }
- else
- {
- peek_char(common, common->nlmax);
- check_newlinechar(common, common->nltype, backtracks, FALSE);
- }
- JUMPHERE(jump[0]);
- return cc;
-
case OP_CHAR:
case OP_CHARI:
length = 1;
#ifdef SUPPORT_UTF
if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc);
#endif
- if (common->mode == JIT_COMPILE && (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0))
+ if (common->mode == JIT_COMPILE && check_str_ptr
+ && (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0))
{
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
@@ -5681,7 +6455,8 @@ switch(type)
return byte_sequence_compare(common, type == OP_CHARI, cc, &context, backtracks);
}
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#ifdef SUPPORT_UTF
if (common->utf)
{
@@ -5713,7 +6488,8 @@ switch(type)
case OP_NOT:
case OP_NOTI:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
length = 1;
#ifdef SUPPORT_UTF
if (common->utf)
@@ -5722,7 +6498,7 @@ switch(type)
c = *cc;
if (c < 128)
{
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
if (type == OP_NOT || !char_has_othercase(common, cc))
add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
else
@@ -5774,16 +6550,17 @@ switch(type)
case OP_CLASS:
case OP_NCLASS:
- detect_partial_match(common, backtracks);
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
- bit = (common->utf && is_char7_bitset((const pcre_uint8 *)cc, type == OP_NCLASS)) ? 127 : 255;
+ bit = (common->utf && is_char7_bitset((const sljit_u8 *)cc, type == OP_NCLASS)) ? 127 : 255;
read_char_range(common, 0, bit, type == OP_NCLASS);
#else
read_char_range(common, 0, 255, type == OP_NCLASS);
#endif
- if (check_class_ranges(common, (const pcre_uint8 *)cc, type == OP_NCLASS, FALSE, backtracks))
+ if (check_class_ranges(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks))
return cc + 32 / sizeof(pcre_uchar);
#if defined SUPPORT_UTF && defined COMPILE_PCRE8
@@ -5808,7 +6585,7 @@ switch(type)
OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
- OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
@@ -5817,40 +6594,15 @@ switch(type)
if (jump[0] != NULL)
JUMPHERE(jump[0]);
#endif
-
return cc + 32 / sizeof(pcre_uchar);
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
case OP_XCLASS:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
compile_xclass_matchingpath(common, cc + LINK_SIZE, backtracks);
return cc + GET(cc, 0) - 1;
#endif
-
- case OP_REVERSE:
- length = GET(cc, 0);
- if (length == 0)
- return cc + LINK_SIZE;
- OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-#ifdef SUPPORT_UTF
- if (common->utf)
- {
- OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, length);
- label = LABEL();
- add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0));
- skip_char_back(common);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, label);
- }
- else
-#endif
- {
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
- OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
- add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, TMP1, 0));
- }
- check_start_used_ptr(common);
- return cc + LINK_SIZE;
}
SLJIT_ASSERT_STOP();
return cc;
@@ -5919,7 +6671,7 @@ if (context.length > 0)
}
/* A non-fixed length character will be checked if length == 0. */
-return compile_char1_matchingpath(common, *cc, cc + 1, backtracks);
+return compile_char1_matchingpath(common, *cc, cc + 1, backtracks, TRUE);
}
/* Forward definitions. */
@@ -6094,7 +6846,7 @@ pcre_uchar *ccbegin = cc;
int min = 0, max = 0;
BOOL minimize;
-PUSH_BACKTRACK(sizeof(iterator_backtrack), cc, NULL);
+PUSH_BACKTRACK(sizeof(ref_iterator_backtrack), cc, NULL);
if (ref)
offset = GET2(cc, 1) << 1;
@@ -6214,7 +6966,7 @@ if (!minimize)
}
JUMPHERE(zerolength);
- BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+ BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
count_match(common);
return cc;
@@ -6260,7 +7012,7 @@ else
}
}
-BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
if (max > 0)
add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max));
@@ -6274,7 +7026,7 @@ if (min > 1)
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(iterator_backtrack)->matchingpath);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(ref_iterator_backtrack)->matchingpath);
}
else if (max > 0)
OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1);
@@ -6419,8 +7171,8 @@ allocate_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw));
SLJIT_ASSERT(common->capture_last_ptr != 0);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, cc[1]);
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0);
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, cc[1]);
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0);
/* These pointer sized fields temporarly stores internal variables. */
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
@@ -6429,8 +7181,8 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(subject), TMP2, 0);
if (common->mark_ptr != 0)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr));
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 2));
-OP1(SLJIT_MOV_SI, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 2 + LINK_SIZE));
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 2));
+OP1(SLJIT_MOV_S32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 2 + LINK_SIZE));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0);
/* Needed to save important temporary registers. */
@@ -6438,7 +7190,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE);
GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START);
sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout));
-OP1(SLJIT_MOV_SI, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0);
+OP1(SLJIT_MOV_S32, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0);
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw));
@@ -6455,6 +7207,32 @@ return cc + 2 + 2 * LINK_SIZE;
#undef CALLOUT_ARG_SIZE
#undef CALLOUT_ARG_OFFSET
+static SLJIT_INLINE BOOL assert_needs_str_ptr_saving(pcre_uchar *cc)
+{
+while (TRUE)
+ {
+ switch (*cc)
+ {
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CALLOUT:
+ case OP_ALT:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_KET:
+ return FALSE;
+
+ default:
+ return TRUE;
+ }
+ }
+}
+
static pcre_uchar *compile_assert_matchingpath(compiler_common *common, pcre_uchar *cc, assert_backtrack *backtrack, BOOL conditional)
{
DEFINE_COMPILER;
@@ -6511,15 +7289,28 @@ if (bra == OP_BRAMINZERO)
if (framesize < 0)
{
- extrasize = needs_control_head ? 2 : 1;
+ extrasize = 1;
+ if (bra == OP_BRA && !assert_needs_str_ptr_saving(ccbegin + 1 + LINK_SIZE))
+ extrasize = 0;
+
+ if (needs_control_head)
+ extrasize++;
+
if (framesize == no_frame)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
- allocate_stack(common, extrasize);
+
+ if (extrasize > 0)
+ allocate_stack(common, extrasize);
+
if (needs_control_head)
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
if (needs_control_head)
{
+ SLJIT_ASSERT(extrasize == 2);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
}
@@ -6528,12 +7319,14 @@ else
{
extrasize = needs_control_head ? 3 : 2;
allocate_stack(common, framesize + extrasize);
+
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
if (needs_control_head)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
if (needs_control_head)
{
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0);
@@ -6542,6 +7335,7 @@ else
}
else
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+
init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE);
}
@@ -6565,7 +7359,7 @@ while (1)
altbacktrack.top = NULL;
altbacktrack.topbacktracks = NULL;
- if (*ccbegin == OP_ALT)
+ if (*ccbegin == OP_ALT && extrasize > 0)
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
altbacktrack.cc = ccbegin;
@@ -6594,8 +7388,9 @@ while (1)
{
if (framesize == no_frame)
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- else
+ else if (extrasize > 0)
free_stack(common, extrasize);
+
if (needs_control_head)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0);
}
@@ -6621,7 +7416,10 @@ while (1)
{
/* We know that STR_PTR was stored on the top of the stack. */
if (conditional)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0);
+ {
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0);
+ }
else if (bra == OP_BRAZERO)
{
if (framesize < 0)
@@ -6698,7 +7496,7 @@ if (needs_control_head)
if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
{
/* Assert is failed. */
- if (conditional || bra == OP_BRAZERO)
+ if ((conditional && extrasize > 0) || bra == OP_BRAZERO)
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
if (framesize < 0)
@@ -6710,7 +7508,7 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
free_stack(common, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
- else
+ else if (extrasize > 0)
free_stack(common, extrasize);
}
else
@@ -6735,7 +7533,9 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
if (framesize < 0)
{
/* We know that STR_PTR was stored on the top of the stack. */
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+
/* Keep the STR_PTR on the top of the stack. */
if (bra == OP_BRAZERO)
{
@@ -6798,14 +7598,16 @@ else
/* AssertNot is successful. */
if (framesize < 0)
{
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
if (bra != OP_BRA)
{
if (extrasize == 2)
free_stack(common, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
- else
+ else if (extrasize > 0)
free_stack(common, extrasize);
}
else
@@ -6867,7 +7669,9 @@ if (framesize < 0)
stacksize = needs_control_head ? 1 : 0;
if (ket != OP_KET || has_alternatives)
stacksize++;
- free_stack(common, stacksize);
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
}
if (needs_control_head)
@@ -7513,9 +8317,13 @@ while (*cc == OP_ALT)
cc += GET(cc, 1);
cc += 1 + LINK_SIZE;
-/* Temporarily encoding the needs_control_head in framesize. */
if (opcode == OP_ONCE)
+ {
+ /* We temporarily encode the needs_control_head in the lowest bit.
+ Note: on the target architectures of SLJIT the ((x << 1) >> 1) returns
+ the same value for small signed numbers (including negative numbers). */
BACKTRACK_AS(bracket_backtrack)->u.framesize = (BACKTRACK_AS(bracket_backtrack)->u.framesize << 1) | (needs_control_head ? 1 : 0);
+ }
return cc + repeat_length;
}
@@ -7802,11 +8610,13 @@ count_match(common);
return cc + 1 + LINK_SIZE;
}
-static SLJIT_INLINE pcre_uchar *get_iterator_parameters(compiler_common *common, pcre_uchar *cc, pcre_uchar *opcode, pcre_uchar *type, int *max, int *min, pcre_uchar **end)
+static SLJIT_INLINE pcre_uchar *get_iterator_parameters(compiler_common *common, pcre_uchar *cc, pcre_uchar *opcode, pcre_uchar *type, sljit_u32 *max, sljit_u32 *exact, pcre_uchar **end)
{
int class_len;
*opcode = *cc;
+*exact = 0;
+
if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO)
{
cc++;
@@ -7834,7 +8644,7 @@ else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO)
{
cc++;
*opcode -= OP_TYPESTAR - OP_STAR;
- *type = 0;
+ *type = OP_END;
}
else
{
@@ -7843,60 +8653,105 @@ else
cc++;
class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(pcre_uchar))) : GET(cc, 0);
*opcode = cc[class_len - 1];
+
if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY)
{
*opcode -= OP_CRSTAR - OP_STAR;
- if (end != NULL)
- *end = cc + class_len;
+ *end = cc + class_len;
+
+ if (*opcode == OP_PLUS || *opcode == OP_MINPLUS)
+ {
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ }
}
else if (*opcode >= OP_CRPOSSTAR && *opcode <= OP_CRPOSQUERY)
{
*opcode -= OP_CRPOSSTAR - OP_POSSTAR;
- if (end != NULL)
- *end = cc + class_len;
+ *end = cc + class_len;
+
+ if (*opcode == OP_POSPLUS)
+ {
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ }
}
else
{
SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE || *opcode == OP_CRPOSRANGE);
*max = GET2(cc, (class_len + IMM2_SIZE));
- *min = GET2(cc, class_len);
+ *exact = GET2(cc, class_len);
- if (*min == 0)
+ if (*max == 0)
{
- SLJIT_ASSERT(*max != 0);
- *opcode = (*opcode == OP_CRRANGE) ? OP_UPTO : (*opcode == OP_CRMINRANGE ? OP_MINUPTO : OP_POSUPTO);
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSSTAR;
+ else
+ *opcode -= OP_CRRANGE - OP_STAR;
}
- if (*max == *min)
- *opcode = OP_EXACT;
-
- if (end != NULL)
- *end = cc + class_len + 2 * IMM2_SIZE;
+ else
+ {
+ *max -= *exact;
+ if (*max == 0)
+ *opcode = OP_EXACT;
+ else if (*max == 1)
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSQUERY;
+ else
+ *opcode -= OP_CRRANGE - OP_QUERY;
+ }
+ else
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSUPTO;
+ else
+ *opcode -= OP_CRRANGE - OP_UPTO;
+ }
+ }
+ *end = cc + class_len + 2 * IMM2_SIZE;
}
return cc;
}
-if (*opcode == OP_UPTO || *opcode == OP_MINUPTO || *opcode == OP_EXACT || *opcode == OP_POSUPTO)
+switch(*opcode)
{
+ case OP_EXACT:
+ *exact = GET2(cc, 0);
+ cc += IMM2_SIZE;
+ break;
+
+ case OP_PLUS:
+ case OP_MINPLUS:
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ break;
+
+ case OP_POSPLUS:
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ break;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
*max = GET2(cc, 0);
cc += IMM2_SIZE;
+ break;
}
-if (*type == 0)
+if (*type == OP_END)
{
*type = *cc;
- if (end != NULL)
- *end = next_opcode(common, cc);
+ *end = next_opcode(common, cc);
cc++;
return cc;
}
-if (end != NULL)
- {
- *end = cc + 1;
+*end = cc + 1;
#ifdef SUPPORT_UTF
- if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc);
+if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc);
#endif
- }
return cc;
}
@@ -7906,9 +8761,15 @@ DEFINE_COMPILER;
backtrack_common *backtrack;
pcre_uchar opcode;
pcre_uchar type;
-int max = -1, min = -1;
+sljit_u32 max = 0, exact;
+BOOL fast_fail;
+sljit_s32 fast_str_ptr;
+BOOL charpos_enabled;
+pcre_uchar charpos_char;
+unsigned int charpos_othercasebit;
pcre_uchar *end;
-jump_list *nomatch = NULL;
+jump_list *no_match = NULL;
+jump_list *no_char1_match = NULL;
struct sljit_jump *jump = NULL;
struct sljit_label *label;
int private_data_ptr = PRIVATE_DATA(cc);
@@ -7917,83 +8778,92 @@ int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw);
int tmp_base, tmp_offset;
-PUSH_BACKTRACK(sizeof(iterator_backtrack), cc, NULL);
+PUSH_BACKTRACK(sizeof(char_iterator_backtrack), cc, NULL);
-cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &min, &end);
+fast_str_ptr = PRIVATE_DATA(cc + 1);
+fast_fail = TRUE;
-switch(type)
+SLJIT_ASSERT(common->fast_forward_bc_ptr == NULL || fast_str_ptr == 0 || cc == common->fast_forward_bc_ptr);
+
+if (cc == common->fast_forward_bc_ptr)
+ fast_fail = FALSE;
+else if (common->fast_fail_start_ptr == 0)
+ fast_str_ptr = 0;
+
+SLJIT_ASSERT(common->fast_forward_bc_ptr != NULL || fast_str_ptr == 0
+ || (fast_str_ptr >= common->fast_fail_start_ptr && fast_str_ptr <= common->fast_fail_end_ptr));
+
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
+
+if (type != OP_EXTUNI)
{
- case OP_NOT_DIGIT:
- case OP_DIGIT:
- case OP_NOT_WHITESPACE:
- case OP_WHITESPACE:
- case OP_NOT_WORDCHAR:
- case OP_WORDCHAR:
- case OP_ANY:
- case OP_ALLANY:
- case OP_ANYBYTE:
- case OP_ANYNL:
- case OP_NOT_HSPACE:
- case OP_HSPACE:
- case OP_NOT_VSPACE:
- case OP_VSPACE:
- case OP_CHAR:
- case OP_CHARI:
- case OP_NOT:
- case OP_NOTI:
- case OP_CLASS:
- case OP_NCLASS:
tmp_base = TMP3;
tmp_offset = 0;
- break;
-
- default:
- SLJIT_ASSERT_STOP();
- /* Fall through. */
-
- case OP_EXTUNI:
- case OP_XCLASS:
- case OP_NOTPROP:
- case OP_PROP:
+ }
+else
+ {
tmp_base = SLJIT_MEM1(SLJIT_SP);
tmp_offset = POSSESSIVE0;
- break;
}
+if (fast_fail && fast_str_ptr != 0)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), fast_str_ptr));
+
+/* Handle fixed part first. */
+if (exact > 1)
+ {
+ SLJIT_ASSERT(fast_str_ptr == 0);
+ if (common->mode == JIT_COMPILE
+#ifdef SUPPORT_UTF
+ && !common->utf
+#endif
+ )
+ {
+ OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(exact));
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER, TMP1, 0, STR_END, 0));
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ }
+else if (exact == 1)
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+
switch(opcode)
{
case OP_STAR:
- case OP_PLUS:
case OP_UPTO:
- case OP_CRRANGE:
+ SLJIT_ASSERT(fast_str_ptr == 0 || opcode == OP_STAR);
+
if (type == OP_ANYNL || type == OP_EXTUNI)
{
SLJIT_ASSERT(private_data_ptr == 0);
- if (opcode == OP_STAR || opcode == OP_UPTO)
- {
- allocate_stack(common, 2);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
- }
- else
- {
- allocate_stack(common, 1);
- OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
- }
+ SLJIT_ASSERT(fast_str_ptr == 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
- if (opcode == OP_UPTO || opcode == OP_CRRANGE)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, 0);
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, max);
label = LABEL();
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
- if (opcode == OP_UPTO || opcode == OP_CRRANGE)
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ if (opcode == OP_UPTO)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
- OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
- if (opcode == OP_CRRANGE && min > 0)
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, label);
- if (opcode == OP_UPTO || (opcode == OP_CRRANGE && max > 0))
- jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max);
+ OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ jump = JUMP(SLJIT_ZERO);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
}
@@ -8006,134 +8876,268 @@ switch(opcode)
}
else
{
- if (opcode == OP_PLUS)
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
- if (private_data_ptr == 0)
- allocate_stack(common, 2);
- OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- if (opcode <= OP_PLUS)
+ charpos_enabled = FALSE;
+ charpos_char = 0;
+ charpos_othercasebit = 0;
+
+ if ((type != OP_CHAR && type != OP_CHARI) && (*end == OP_CHAR || *end == OP_CHARI))
+ {
+ charpos_enabled = TRUE;
+#ifdef SUPPORT_UTF
+ charpos_enabled = !common->utf || !HAS_EXTRALEN(end[1]);
+#endif
+ if (charpos_enabled && *end == OP_CHARI && char_has_othercase(common, end + 1))
+ {
+ charpos_othercasebit = char_get_othercase_bit(common, end + 1);
+ if (charpos_othercasebit == 0)
+ charpos_enabled = FALSE;
+ }
+
+ if (charpos_enabled)
+ {
+ charpos_char = end[1];
+ /* Consumpe the OP_CHAR opcode. */
+ end += 2;
+#if defined COMPILE_PCRE8
+ SLJIT_ASSERT((charpos_othercasebit >> 8) == 0);
+#elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
+ SLJIT_ASSERT((charpos_othercasebit >> 9) == 0);
+ if ((charpos_othercasebit & 0x100) != 0)
+ charpos_othercasebit = (charpos_othercasebit & 0xff) << 8;
+#endif
+ if (charpos_othercasebit != 0)
+ charpos_char |= charpos_othercasebit;
+
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.enabled = TRUE;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.chr = charpos_char;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.othercasebit = charpos_othercasebit;
+ }
+ }
+
+ if (charpos_enabled)
+ {
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max + 1);
+
+ /* Search the first instance of charpos_char. */
+ jump = JUMP(SLJIT_JUMP);
+ label = LABEL();
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO));
+ }
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+
+ detect_partial_match(common, &backtrack->topbacktracks);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
- else
- OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, 1);
- label = LABEL();
- compile_char1_matchingpath(common, type, cc, &nomatch);
- OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- if (opcode <= OP_PLUS)
- JUMPTO(SLJIT_JUMP, label);
- else if (opcode == OP_CRRANGE && max == 0)
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ }
+
+ /* Search the last instance of charpos_char. */
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
+ detect_partial_match(common, &no_match);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+ if (opcode == OP_STAR)
+ {
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+ else if (common->utf)
{
- OP2(SLJIT_ADD, base, offset1, base, offset1, SLJIT_IMM, 1);
- JUMPTO(SLJIT_JUMP, label);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
}
+#endif
else
{
- OP1(SLJIT_MOV, TMP1, 0, base, offset1);
- OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
- OP1(SLJIT_MOV, base, offset1, TMP1, 0);
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, max + 1, label);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ label = LABEL();
+ detect_partial_match(common, &no_match);
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+ else
+ JUMPTO(SLJIT_JUMP, label);
+
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
}
- set_jumps(nomatch, LABEL());
- if (opcode == OP_CRRANGE)
- add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS, base, offset1, SLJIT_IMM, min + 1));
- OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
}
- BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
break;
case OP_MINSTAR:
- case OP_MINPLUS:
- if (opcode == OP_MINPLUS)
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
if (private_data_ptr == 0)
allocate_stack(common, 1);
OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
break;
case OP_MINUPTO:
- case OP_CRMINRANGE:
+ SLJIT_ASSERT(fast_str_ptr == 0);
if (private_data_ptr == 0)
allocate_stack(common, 2);
OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, 1);
- if (opcode == OP_CRMINRANGE)
- add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP));
- BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+ OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, max + 1);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
break;
case OP_QUERY:
case OP_MINQUERY:
+ SLJIT_ASSERT(fast_str_ptr == 0);
if (private_data_ptr == 0)
allocate_stack(common, 1);
OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
if (opcode == OP_QUERY)
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
- BACKTRACK_AS(iterator_backtrack)->matchingpath = LABEL();
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
break;
case OP_EXACT:
- OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
- label = LABEL();
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, label);
break;
case OP_POSSTAR:
- case OP_POSPLUS:
- case OP_POSUPTO:
- if (opcode == OP_POSPLUS)
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
- if (opcode == OP_POSUPTO)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_IMM, max);
- OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- label = LABEL();
- compile_char1_matchingpath(common, type, cc, &nomatch);
- OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- if (opcode != OP_POSUPTO)
- JUMPTO(SLJIT_JUMP, label);
- else
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+ if (common->utf)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, label);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
+ break;
}
- set_jumps(nomatch, LABEL());
- OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
- break;
-
- case OP_POSQUERY:
- OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- compile_char1_matchingpath(common, type, cc, &nomatch);
- OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- set_jumps(nomatch, LABEL());
- OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+#endif
+ label = LABEL();
+ detect_partial_match(common, &no_match);
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ JUMPTO(SLJIT_JUMP, label);
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ if (fast_str_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), fast_str_ptr, STR_PTR, 0);
break;
- case OP_CRPOSRANGE:
- /* Combination of OP_EXACT and OP_POSSTAR or OP_POSUPTO */
- OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, min);
+ case OP_POSUPTO:
+ SLJIT_ASSERT(fast_str_ptr == 0);
+#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
+ break;
+ }
+#endif
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
label = LABEL();
- compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks);
+ detect_partial_match(common, &no_match);
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ break;
- if (max != 0)
- {
- SLJIT_ASSERT(max - min > 0);
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_IMM, max - min);
- }
+ case OP_POSQUERY:
+ SLJIT_ASSERT(fast_str_ptr == 0);
OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- label = LABEL();
- compile_char1_matchingpath(common, type, cc, &nomatch);
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
- if (max == 0)
- JUMPTO(SLJIT_JUMP, label);
- else
- {
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, SLJIT_IMM, 1);
- JUMPTO(SLJIT_NOT_ZERO, label);
- }
- set_jumps(nomatch, LABEL());
+ set_jumps(no_match, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
break;
@@ -8174,9 +9178,9 @@ if (common->accept_label == NULL)
else
CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
-OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty));
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty));
add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
-OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart));
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart));
if (common->accept_label == NULL)
add_jump(compiler, &common->accept, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
else
@@ -8298,6 +9302,16 @@ while (cc < ccend)
case OP_SOM:
case OP_NOT_WORD_BOUNDARY:
case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_REVERSE:
+ cc = compile_simple_assertion_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ break;
+
case OP_NOT_DIGIT:
case OP_DIGIT:
case OP_NOT_WHITESPACE:
@@ -8315,16 +9329,9 @@ while (cc < ccend)
case OP_NOT_VSPACE:
case OP_VSPACE:
case OP_EXTUNI:
- case OP_EODN:
- case OP_EOD:
- case OP_CIRC:
- case OP_CIRCM:
- case OP_DOLL:
- case OP_DOLLM:
case OP_NOT:
case OP_NOTI:
- case OP_REVERSE:
- cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
break;
case OP_SET_SOM:
@@ -8341,7 +9348,7 @@ while (cc < ccend)
if (common->mode == JIT_COMPILE)
cc = compile_charn_matchingpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
else
- cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
break;
case OP_STAR:
@@ -8417,7 +9424,7 @@ while (cc < ccend)
if (cc[1 + (32 / sizeof(pcre_uchar))] >= OP_CRSTAR && cc[1 + (32 / sizeof(pcre_uchar))] <= OP_CRPOSRANGE)
cc = compile_iterator_matchingpath(common, cc, parent);
else
- cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
break;
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
@@ -8425,7 +9432,7 @@ while (cc < ccend)
if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRPOSRANGE)
cc = compile_iterator_matchingpath(common, cc, parent);
else
- cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
break;
#endif
@@ -8601,95 +9608,82 @@ DEFINE_COMPILER;
pcre_uchar *cc = current->cc;
pcre_uchar opcode;
pcre_uchar type;
-int max = -1, min = -1;
+sljit_u32 max = 0, exact;
struct sljit_label *label = NULL;
struct sljit_jump *jump = NULL;
jump_list *jumplist = NULL;
+pcre_uchar *end;
int private_data_ptr = PRIVATE_DATA(cc);
int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP);
int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + (int)sizeof(sljit_sw);
-cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &min, NULL);
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
switch(opcode)
{
case OP_STAR:
- case OP_PLUS:
case OP_UPTO:
- case OP_CRRANGE:
if (type == OP_ANYNL || type == OP_EXTUNI)
{
SLJIT_ASSERT(private_data_ptr == 0);
- set_jumps(current->topbacktracks, LABEL());
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
free_stack(common, 1);
- CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
}
else
{
- if (opcode == OP_UPTO)
- min = 0;
- if (opcode <= OP_PLUS)
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.enabled)
{
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, base, offset1);
+ OP1(SLJIT_MOV, TMP2, 0, base, offset1);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+ label = LABEL();
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.chr, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ skip_char_back(common);
+ CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP2, 0, label);
}
else
{
- OP1(SLJIT_MOV, TMP1, 0, base, offset1);
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- jump = CMP(SLJIT_LESS_EQUAL, TMP1, 0, SLJIT_IMM, min + 1);
- OP2(SLJIT_SUB, base, offset1, TMP1, 0, SLJIT_IMM, 1);
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, base, offset1);
+ skip_char_back(common);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
}
- skip_char_back(common);
- OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath);
- if (opcode == OP_CRRANGE)
- set_jumps(current->topbacktracks, LABEL());
JUMPHERE(jump);
if (private_data_ptr == 0)
free_stack(common, 2);
- if (opcode == OP_PLUS)
- set_jumps(current->topbacktracks, LABEL());
}
break;
case OP_MINSTAR:
- case OP_MINPLUS:
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- compile_char1_matchingpath(common, type, cc, &jumplist);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
set_jumps(jumplist, LABEL());
if (private_data_ptr == 0)
free_stack(common, 1);
- if (opcode == OP_MINPLUS)
- set_jumps(current->topbacktracks, LABEL());
break;
case OP_MINUPTO:
- case OP_CRMINRANGE:
- if (opcode == OP_CRMINRANGE)
- {
- label = LABEL();
- set_jumps(current->topbacktracks, label);
- }
+ OP1(SLJIT_MOV, TMP1, 0, base, offset1);
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- compile_char1_matchingpath(common, type, cc, &jumplist);
+ OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO));
- OP1(SLJIT_MOV, TMP1, 0, base, offset1);
- OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
- OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP1(SLJIT_MOV, base, offset1, TMP1, 0);
-
- if (opcode == OP_CRMINRANGE)
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min + 1, label);
-
- if (opcode == OP_CRMINRANGE && max == 0)
- JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath);
- else
- CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, max + 2, CURRENT_AS(iterator_backtrack)->matchingpath);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
set_jumps(jumplist, LABEL());
if (private_data_ptr == 0)
@@ -8699,12 +9693,12 @@ switch(opcode)
case OP_QUERY:
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
- CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
jump = JUMP(SLJIT_JUMP);
- set_jumps(current->topbacktracks, LABEL());
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
- JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
JUMPHERE(jump);
if (private_data_ptr == 0)
free_stack(common, 1);
@@ -8714,8 +9708,8 @@ switch(opcode)
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
jump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
- compile_char1_matchingpath(common, type, cc, &jumplist);
- JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_backtrack)->matchingpath);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
set_jumps(jumplist, LABEL());
JUMPHERE(jump);
if (private_data_ptr == 0)
@@ -8723,11 +9717,6 @@ switch(opcode)
break;
case OP_EXACT:
- case OP_POSPLUS:
- case OP_CRPOSRANGE:
- set_jumps(current->topbacktracks, LABEL());
- break;
-
case OP_POSSTAR:
case OP_POSQUERY:
case OP_POSUPTO:
@@ -8737,6 +9726,8 @@ switch(opcode)
SLJIT_ASSERT_STOP();
break;
}
+
+set_jumps(current->topbacktracks, LABEL());
}
static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
@@ -8754,12 +9745,12 @@ if ((type & 0x1) == 0)
set_jumps(current->topbacktracks, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
free_stack(common, 1);
- CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
return;
}
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
-CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_backtrack)->matchingpath);
+CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
set_jumps(current->topbacktracks, LABEL());
free_stack(common, ref ? 2 : 3);
}
@@ -9258,7 +10249,9 @@ else if (opcode == OP_ONCE)
/* The STR_PTR must be released. */
stacksize++;
}
- free_stack(common, stacksize);
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
JUMPHERE(once);
/* Restore previous private_data_ptr */
@@ -9688,8 +10681,8 @@ common->currententry->entry = LABEL();
set_jumps(common->currententry->calls, common->currententry->entry);
sljit_emit_fast_enter(compiler, TMP2, 0);
-allocate_stack(common, private_data_size + framesize + alternativesize);
count_match(common);
+allocate_stack(common, private_data_size + framesize + alternativesize);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0);
copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head);
if (needs_control_head)
@@ -9795,7 +10788,7 @@ struct sljit_compiler *compiler;
backtrack_common rootbacktrack;
compiler_common common_data;
compiler_common *common = &common_data;
-const pcre_uint8 *tables = re->tables;
+const sljit_u8 *tables = re->tables;
pcre_study_data *study;
int private_data_size;
pcre_uchar *ccend;
@@ -9907,7 +10900,7 @@ ccend = bracketend(common->start);
/* Calculate the local space size on the stack. */
common->ovector_start = LIMIT_MATCH + sizeof(sljit_sw);
-common->optimized_cbracket = (pcre_uint8 *)SLJIT_MALLOC(re->top_bracket + 1, compiler->allocator_data);
+common->optimized_cbracket = (sljit_u8 *)SLJIT_MALLOC(re->top_bracket + 1, compiler->allocator_data);
if (!common->optimized_cbracket)
return;
#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 1
@@ -9942,15 +10935,10 @@ if (mode != JIT_COMPILE)
common->hit_start = common->ovector_start;
common->ovector_start += 2 * sizeof(sljit_sw);
}
- else
- {
- SLJIT_ASSERT(mode == JIT_PARTIAL_HARD_COMPILE);
- common->needs_start_ptr = TRUE;
- }
}
if ((re->options & PCRE_FIRSTLINE) != 0)
{
- common->first_line_end = common->ovector_start;
+ common->match_end_ptr = common->ovector_start;
common->ovector_start += sizeof(sljit_sw);
}
#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
@@ -9961,14 +10949,12 @@ if (common->control_head_ptr != 0)
common->control_head_ptr = common->ovector_start;
common->ovector_start += sizeof(sljit_sw);
}
-if (common->needs_start_ptr && common->has_set_som)
+if (common->has_set_som)
{
/* Saving the real start pointer is necessary. */
common->start_ptr = common->ovector_start;
common->ovector_start += sizeof(sljit_sw);
}
-else
- common->needs_start_ptr = FALSE;
/* Aligning ovector to even number of sljit words. */
if ((common->ovector_start & sizeof(sljit_sw)) != 0)
@@ -9985,16 +10971,24 @@ SLJIT_ASSERT(!(common->req_char_ptr != 0 && common->start_used_ptr != 0));
common->cbra_ptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_sw);
total_length = ccend - common->start;
-common->private_data_ptrs = (sljit_si *)SLJIT_MALLOC(total_length * (sizeof(sljit_si) + (common->has_then ? 1 : 0)), compiler->allocator_data);
+common->private_data_ptrs = (sljit_s32 *)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), compiler->allocator_data);
if (!common->private_data_ptrs)
{
SLJIT_FREE(common->optimized_cbracket, compiler->allocator_data);
return;
}
-memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_si));
+memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_s32));
private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw);
set_private_data_ptrs(common, &private_data_size, ccend);
+if ((re->options & PCRE_ANCHORED) == 0 && (re->options & PCRE_NO_START_OPTIMIZE) == 0)
+ {
+ if (!detect_fast_forward_skip(common, &private_data_size) && !common->has_skip_in_assert_back)
+ detect_fast_fail(common, common->start, &private_data_size, 4);
+ }
+
+SLJIT_ASSERT(common->fast_fail_start_ptr <= common->fast_fail_end_ptr);
+
if (private_data_size > SLJIT_MAX_LOCAL_SIZE)
{
SLJIT_FREE(common->private_data_ptrs, compiler->allocator_data);
@@ -10004,7 +10998,7 @@ if (private_data_size > SLJIT_MAX_LOCAL_SIZE)
if (common->has_then)
{
- common->then_offsets = (pcre_uint8 *)(common->private_data_ptrs + total_length);
+ common->then_offsets = (sljit_u8 *)(common->private_data_ptrs + total_length);
memset(common->then_offsets, 0, total_length);
set_then_offsets(common, common->start, NULL);
}
@@ -10031,12 +11025,15 @@ OP1(SLJIT_MOV, TMP1, 0, SLJIT_S0, 0);
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end));
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
-OP1(SLJIT_MOV_UI, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match));
+OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match));
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base));
OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit));
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0);
+if (common->fast_fail_start_ptr < common->fast_fail_end_ptr)
+ reset_fast_fail(common);
+
if (mode == JIT_PARTIAL_SOFT_COMPILE)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1);
if (common->mark_ptr != 0)
@@ -10047,19 +11044,19 @@ if (common->control_head_ptr != 0)
/* Main part of the matching */
if ((re->options & PCRE_ANCHORED) == 0)
{
- mainloop_label = mainloop_entry(common, (re->flags & PCRE_HASCRORLF) != 0, (re->options & PCRE_FIRSTLINE) != 0);
+ mainloop_label = mainloop_entry(common, (re->flags & PCRE_HASCRORLF) != 0);
continue_match_label = LABEL();
/* Forward search if possible. */
if ((re->options & PCRE_NO_START_OPTIMIZE) == 0)
{
- if (mode == JIT_COMPILE && fast_forward_first_n_chars(common, (re->options & PCRE_FIRSTLINE) != 0))
+ if (mode == JIT_COMPILE && fast_forward_first_n_chars(common))
;
else if ((re->flags & PCRE_FIRSTSET) != 0)
- fast_forward_first_char(common, (pcre_uchar)re->first_char, (re->flags & PCRE_FCH_CASELESS) != 0, (re->options & PCRE_FIRSTLINE) != 0);
+ fast_forward_first_char(common, (pcre_uchar)re->first_char, (re->flags & PCRE_FCH_CASELESS) != 0);
else if ((re->flags & PCRE_STARTLINE) != 0)
- fast_forward_newline(common, (re->options & PCRE_FIRSTLINE) != 0);
+ fast_forward_newline(common);
else if (study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0)
- fast_forward_start_bits(common, study->start_bits, (re->options & PCRE_FIRSTLINE) != 0);
+ fast_forward_start_bits(common, study->start_bits);
}
}
else
@@ -10080,14 +11077,11 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0);
OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH);
if (common->capture_last_ptr != 0)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, -1);
+if (common->fast_forward_bc_ptr != NULL)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1), STR_PTR, 0);
-if (common->needs_start_ptr)
- {
- SLJIT_ASSERT(common->start_ptr != OVECTOR(0));
+if (common->start_ptr != OVECTOR(0))
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_ptr, STR_PTR, 0);
- }
-else
- SLJIT_ASSERT(common->start_ptr == OVECTOR(0));
/* Copy the beginning of the string. */
if (mode == JIT_PARTIAL_SOFT_COMPILE)
@@ -10166,11 +11160,12 @@ if (mode == JIT_PARTIAL_SOFT_COMPILE)
/* Check we have remaining characters. */
if ((re->options & PCRE_ANCHORED) == 0 && (re->options & PCRE_FIRSTLINE) != 0)
{
- SLJIT_ASSERT(common->first_line_end != 0);
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->first_line_end);
+ SLJIT_ASSERT(common->match_end_ptr != 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
}
-OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP),
+ (common->fast_forward_bc_ptr != NULL) ? (PRIVATE_DATA(common->fast_forward_bc_ptr + 1)) : common->start_ptr);
if ((re->options & PCRE_ANCHORED) == 0)
{
@@ -10181,12 +11176,7 @@ if ((re->options & PCRE_ANCHORED) == 0)
/* There cannot be more newlines here. */
}
else
- {
- if ((re->options & PCRE_FIRSTLINE) == 0)
- CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop_label);
- else
- CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, mainloop_label);
- }
+ CMPTO(SLJIT_LESS, STR_PTR, 0, ((re->options & PCRE_FIRSTLINE) == 0) ? STR_END : TMP1, 0, mainloop_label);
}
/* No more remaining characters. */
@@ -10205,15 +11195,18 @@ if (common->might_be_empty)
{
JUMPHERE(empty_match);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty));
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty));
CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_backtrack_label);
- OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart));
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart));
CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_found_label);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label);
JUMPTO(SLJIT_JUMP, empty_match_backtrack_label);
}
+common->fast_forward_bc_ptr = NULL;
+common->fast_fail_start_ptr = 0;
+common->fast_fail_end_ptr = 0;
common->currententry = common->entries;
common->local_exit = TRUE;
quit_label = common->quit_label;
@@ -10395,7 +11388,7 @@ union {
void *executable_func;
jit_function call_executable_func;
} convert_executable_func;
-pcre_uint8 local_space[MACHINE_STACK_SIZE];
+sljit_u8 local_space[MACHINE_STACK_SIZE];
struct sljit_stack local_stack;
local_stack.top = (sljit_sw)&local_space;
@@ -10435,7 +11428,7 @@ arguments.begin = subject;
arguments.end = subject + length;
arguments.mark_ptr = NULL;
/* JIT decreases this value less frequently than the interpreter. */
-arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (pcre_uint32)(extra_data->match_limit);
+arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (sljit_u32)(extra_data->match_limit);
if (functions->limit_match != 0 && functions->limit_match < arguments.limit_match)
arguments.limit_match = functions->limit_match;
arguments.notbol = (options & PCRE_NOTBOL) != 0;
@@ -10528,7 +11521,7 @@ arguments.begin = subject_ptr;
arguments.end = subject_ptr + length;
arguments.mark_ptr = NULL;
/* JIT decreases this value less frequently than the interpreter. */
-arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (pcre_uint32)(extra_data->match_limit);
+arguments.limit_match = ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) ? MATCH_LIMIT : (sljit_u32)(extra_data->match_limit);
if (functions->limit_match != 0 && functions->limit_match < arguments.limit_match)
arguments.limit_match = functions->limit_match;
arguments.notbol = (options & PCRE_NOTBOL) != 0;
diff --git a/src/third_party/pcre-8.38/pcre_jit_test.c b/src/third_party/pcre-8.39/pcre_jit_test.c
index 17378957d62..9b61ec000fa 100644
--- a/src/third_party/pcre-8.38/pcre_jit_test.c
+++ b/src/third_party/pcre-8.39/pcre_jit_test.c
@@ -242,13 +242,17 @@ static struct regression_test_case regression_test_cases[] = {
{ MA, 0, "a\\z", "aaa" },
{ MA, 0 | F_NOMATCH, "a\\z", "aab" },
- /* Brackets. */
+ /* Brackets and alternatives. */
{ MUA, 0, "(ab|bb|cd)", "bacde" },
{ MUA, 0, "(?:ab|a)(bc|c)", "ababc" },
{ MUA, 0, "((ab|(cc))|(bb)|(?:cd|efg))", "abac" },
{ CMUA, 0, "((aB|(Cc))|(bB)|(?:cd|EFg))", "AcCe" },
{ MUA, 0, "((ab|(cc))|(bb)|(?:cd|ebg))", "acebebg" },
{ MUA, 0, "(?:(a)|(?:b))(cc|(?:d|e))(a|b)k", "accabdbbccbk" },
+ { MUA, 0, "\xc7\x82|\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" },
+ { MUA, 0, "=\xc7\x82|#\xc6\x82", "\xf1\x83\x82\x82=\xc7\x82\xc7\x83" },
+ { MUA, 0, "\xc7\x82\xc7\x83|\xc6\x82\xc6\x82", "\xf1\x83\x82\x82\xc7\x82\xc7\x83" },
+ { MUA, 0, "\xc6\x82\xc6\x82|\xc7\x83\xc7\x83|\xc8\x84\xc8\x84", "\xf1\x83\x82\x82\xc8\x84\xc8\x84" },
/* Greedy and non-greedy ? operators. */
{ MUA, 0, "(?:a)?a", "laab" },
@@ -318,6 +322,14 @@ static struct regression_test_case regression_test_cases[] = {
{ CMUA, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" },
{ MUA, 0, "[^\xe1\xbd\xb8][^\xc3\xa9]", "\xe1\xbd\xb8\xe1\xbf\xb8\xc3\xa9\xc3\x89#" },
{ MUA, 0, "[^\xe1\xbd\xb8]{3,}?", "##\xe1\xbd\xb8#\xe1\xbd\xb8#\xc3\x89#\xe1\xbd\xb8" },
+ { MUA, 0, "\\d+123", "987654321,01234" },
+ { MUA, 0, "abcd*|\\w+xy", "aaaaa,abxyz" },
+ { MUA, 0, "(?:abc|((?:amc|\\b\\w*xy)))", "aaaaa,abxyz" },
+ { MUA, 0, "a(?R)|([a-z]++)#", ".abcd.abcd#."},
+ { MUA, 0, "a(?R)|([a-z]++)#", ".abcd.mbcd#."},
+ { MUA, 0, ".[ab]*.", "xx" },
+ { MUA, 0, ".[ab]*a", "xxa" },
+ { MUA, 0, ".[ab]?.", "xx" },
/* Bracket repeats with limit. */
{ MUA, 0, "(?:(ab){2}){5}M", "abababababababababababM" },
@@ -574,6 +586,16 @@ static struct regression_test_case regression_test_cases[] = {
{ MUA, 0, "(?:(?=.)??[a-c])+m", "abacdcbacacdcaccam" },
{ MUA, 0, "((?!a)?(?!([^a]))?)+$", "acbab" },
{ MUA, 0, "((?!a)?\?(?!([^a]))?\?)+$", "acbab" },
+ { MUA, 0, "a(?=(?C)\\B)b", "ab" },
+ { MUA, 0, "a(?!(?C)\\B)bb|ab", "abb" },
+ { MUA, 0, "a(?=\\b|(?C)\\B)b", "ab" },
+ { MUA, 0, "a(?!\\b|(?C)\\B)bb|ab", "abb" },
+ { MUA, 0, "c(?(?=(?C)\\B)ab|a)", "cab" },
+ { MUA, 0, "c(?(?!(?C)\\B)ab|a)", "cab" },
+ { MUA, 0, "c(?(?=\\b|(?C)\\B)ab|a)", "cab" },
+ { MUA, 0, "c(?(?!\\b|(?C)\\B)ab|a)", "cab" },
+ { MUA, 0, "a(?=)b", "ab" },
+ { MUA, 0 | F_NOMATCH, "a(?!)b", "ab" },
/* Not empty, ACCEPT, FAIL */
{ MUA | PCRE_NOTEMPTY, 0 | F_NOMATCH, "a*", "bcx" },
@@ -664,6 +686,7 @@ static struct regression_test_case regression_test_cases[] = {
{ PCRE_MULTILINE | PCRE_UTF8 | PCRE_NEWLINE_CRLF | PCRE_FIRSTLINE, 1, ".", "\r\n" },
{ PCRE_FIRSTLINE | PCRE_NEWLINE_LF | PCRE_DOTALL, 0 | F_NOMATCH, "ab.", "ab" },
{ MUA | PCRE_FIRSTLINE, 1 | F_NOMATCH, "^[a-d0-9]", "\nxx\nd" },
+ { PCRE_NEWLINE_ANY | PCRE_FIRSTLINE | PCRE_DOTALL, 0, "....a", "012\n0a" },
/* Recurse. */
{ MUA, 0, "(a)(?1)", "aa" },
@@ -798,6 +821,9 @@ static struct regression_test_case regression_test_cases[] = {
/* (*SKIP) verb. */
{ MUA, 0 | F_NOMATCH, "(?=a(*SKIP)b)ab|ad", "ad" },
+ { MUA, 0, "(\\w+(*SKIP)#)", "abcd,xyz#," },
+ { MUA, 0, "\\w+(*SKIP)#|mm", "abcd,xyz#," },
+ { MUA, 0 | F_NOMATCH, "b+(?<=(*SKIP)#c)|b+", "#bbb" },
/* (*THEN) verb. */
{ MUA, 0, "((?:a(*THEN)|aab)(*THEN)c|a+)+m", "aabcaabcaabcaabcnacm" },
@@ -1534,10 +1560,10 @@ static int regression_tests(void)
is_successful = 0;
}
#endif
-#if defined SUPPORT_PCRE16 && defined SUPPORT_PCRE16
- if (ovector16_1[i] != ovector16_2[i] || ovector16_1[i] != ovector16_1[i] || ovector16_1[i] != ovector16_2[i]) {
- printf("\n16 and 16 bit: Ovector[%d] value differs(J16:%d,I16:%d,J32:%d,I32:%d): [%d] '%s' @ '%s' \n",
- i, ovector16_1[i], ovector16_2[i], ovector16_1[i], ovector16_2[i],
+#if defined SUPPORT_PCRE16 && defined SUPPORT_PCRE32
+ if (ovector16_1[i] != ovector16_2[i] || ovector16_1[i] != ovector32_1[i] || ovector16_1[i] != ovector32_2[i]) {
+ printf("\n16 and 32 bit: Ovector[%d] value differs(J16:%d,I16:%d,J32:%d,I32:%d): [%d] '%s' @ '%s' \n",
+ i, ovector16_1[i], ovector16_2[i], ovector32_1[i], ovector32_2[i],
total, current->pattern, current->input);
is_successful = 0;
}
diff --git a/src/third_party/pcre-8.38/pcre_maketables.c b/src/third_party/pcre-8.39/pcre_maketables.c
index a44a6eaa905..a44a6eaa905 100644
--- a/src/third_party/pcre-8.38/pcre_maketables.c
+++ b/src/third_party/pcre-8.39/pcre_maketables.c
diff --git a/src/third_party/pcre-8.38/pcre_newline.c b/src/third_party/pcre-8.39/pcre_newline.c
index b8f5a4de19c..b8f5a4de19c 100644
--- a/src/third_party/pcre-8.38/pcre_newline.c
+++ b/src/third_party/pcre-8.39/pcre_newline.c
diff --git a/src/third_party/pcre-8.38/pcre_ord2utf8.c b/src/third_party/pcre-8.39/pcre_ord2utf8.c
index 95f1beb963e..95f1beb963e 100644
--- a/src/third_party/pcre-8.38/pcre_ord2utf8.c
+++ b/src/third_party/pcre-8.39/pcre_ord2utf8.c
diff --git a/src/third_party/pcre-8.38/pcre_printint.c b/src/third_party/pcre-8.39/pcre_printint.c
index 60dcb55efbf..60dcb55efbf 100644
--- a/src/third_party/pcre-8.38/pcre_printint.c
+++ b/src/third_party/pcre-8.39/pcre_printint.c
diff --git a/src/third_party/pcre-8.38/pcre_refcount.c b/src/third_party/pcre-8.39/pcre_refcount.c
index 79efa90f216..79efa90f216 100644
--- a/src/third_party/pcre-8.38/pcre_refcount.c
+++ b/src/third_party/pcre-8.39/pcre_refcount.c
diff --git a/src/third_party/pcre-8.38/pcre_scanner.cc b/src/third_party/pcre-8.39/pcre_scanner.cc
index 6be2be6829b..6be2be6829b 100644
--- a/src/third_party/pcre-8.38/pcre_scanner.cc
+++ b/src/third_party/pcre-8.39/pcre_scanner.cc
diff --git a/src/third_party/pcre-8.38/pcre_scanner.h b/src/third_party/pcre-8.39/pcre_scanner.h
index 5617e4515cb..5617e4515cb 100644
--- a/src/third_party/pcre-8.38/pcre_scanner.h
+++ b/src/third_party/pcre-8.39/pcre_scanner.h
diff --git a/src/third_party/pcre-8.38/pcre_scanner_unittest.cc b/src/third_party/pcre-8.39/pcre_scanner_unittest.cc
index c00312c4f63..c00312c4f63 100644
--- a/src/third_party/pcre-8.38/pcre_scanner_unittest.cc
+++ b/src/third_party/pcre-8.39/pcre_scanner_unittest.cc
diff --git a/src/third_party/pcre-8.38/pcre_string_utils.c b/src/third_party/pcre-8.39/pcre_string_utils.c
index 25eacc85073..25eacc85073 100644
--- a/src/third_party/pcre-8.38/pcre_string_utils.c
+++ b/src/third_party/pcre-8.39/pcre_string_utils.c
diff --git a/src/third_party/pcre-8.38/pcre_stringpiece.cc b/src/third_party/pcre-8.39/pcre_stringpiece.cc
index 67c0f1fc0e5..67c0f1fc0e5 100644
--- a/src/third_party/pcre-8.38/pcre_stringpiece.cc
+++ b/src/third_party/pcre-8.39/pcre_stringpiece.cc
diff --git a/src/third_party/pcre-8.38/pcre_stringpiece.h b/src/third_party/pcre-8.39/pcre_stringpiece.h
index cc3dc42963a..cc3dc42963a 100644
--- a/src/third_party/pcre-8.38/pcre_stringpiece.h
+++ b/src/third_party/pcre-8.39/pcre_stringpiece.h
diff --git a/src/third_party/pcre-8.38/pcre_stringpiece.h.in b/src/third_party/pcre-8.39/pcre_stringpiece.h.in
index eb25826b453..eb25826b453 100644
--- a/src/third_party/pcre-8.38/pcre_stringpiece.h.in
+++ b/src/third_party/pcre-8.39/pcre_stringpiece.h.in
diff --git a/src/third_party/pcre-8.38/pcre_stringpiece_unittest.cc b/src/third_party/pcre-8.39/pcre_stringpiece_unittest.cc
index 1c4759da3b0..1c4759da3b0 100644
--- a/src/third_party/pcre-8.38/pcre_stringpiece_unittest.cc
+++ b/src/third_party/pcre-8.39/pcre_stringpiece_unittest.cc
diff --git a/src/third_party/pcre-8.38/pcre_study.c b/src/third_party/pcre-8.39/pcre_study.c
index 7fd0ba0b3d8..d9d4960d84e 100644
--- a/src/third_party/pcre-8.38/pcre_study.c
+++ b/src/third_party/pcre-8.39/pcre_study.c
@@ -1371,7 +1371,7 @@ do
for (c = 0; c < 16; c++) start_bits[c] |= map[c];
for (c = 128; c < 256; c++)
{
- if ((map[c/8] && (1 << (c&7))) != 0)
+ if ((map[c/8] & (1 << (c&7))) != 0)
{
int d = (c >> 6) | 0xc0; /* Set bit for this starter */
start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */
diff --git a/src/third_party/pcre-8.38/pcre_tables.c b/src/third_party/pcre-8.39/pcre_tables.c
index 4960af57c4d..4960af57c4d 100644
--- a/src/third_party/pcre-8.38/pcre_tables.c
+++ b/src/third_party/pcre-8.39/pcre_tables.c
diff --git a/src/third_party/pcre-8.38/pcre_ucd.c b/src/third_party/pcre-8.39/pcre_ucd.c
index 69c4fd42c34..69c4fd42c34 100644
--- a/src/third_party/pcre-8.38/pcre_ucd.c
+++ b/src/third_party/pcre-8.39/pcre_ucd.c
diff --git a/src/third_party/pcre-8.38/pcre_valid_utf8.c b/src/third_party/pcre-8.39/pcre_valid_utf8.c
index 3b0f6464a35..3b0f6464a35 100644
--- a/src/third_party/pcre-8.38/pcre_valid_utf8.c
+++ b/src/third_party/pcre-8.39/pcre_valid_utf8.c
diff --git a/src/third_party/pcre-8.38/pcre_version.c b/src/third_party/pcre-8.39/pcre_version.c
index ae86ff28bc8..ae86ff28bc8 100644
--- a/src/third_party/pcre-8.38/pcre_version.c
+++ b/src/third_party/pcre-8.39/pcre_version.c
diff --git a/src/third_party/pcre-8.38/pcre_xclass.c b/src/third_party/pcre-8.39/pcre_xclass.c
index ef759a589a6..ef759a589a6 100644
--- a/src/third_party/pcre-8.38/pcre_xclass.c
+++ b/src/third_party/pcre-8.39/pcre_xclass.c
diff --git a/src/third_party/pcre-8.38/pcrecpp.cc b/src/third_party/pcre-8.39/pcrecpp.cc
index c595cbcab5c..d09c9abc516 100644
--- a/src/third_party/pcre-8.38/pcrecpp.cc
+++ b/src/third_party/pcre-8.39/pcrecpp.cc
@@ -66,7 +66,7 @@ Arg RE::no_arg((void*)NULL);
// inclusive test if we ever needed it. (Note that not only the
// __attribute__ syntax, but also __USER_LABEL_PREFIX__, are
// gnu-specific.)
-#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__)
+#if defined(__GNUC__) && __GNUC__ >= 3 && defined(__ELF__) && !defined(__INTEL_COMPILER)
# define ULP_AS_STRING(x) ULP_AS_STRING_INTERNAL(x)
# define ULP_AS_STRING_INTERNAL(x) #x
# define USER_LABEL_PREFIX_STR ULP_AS_STRING(__USER_LABEL_PREFIX__)
@@ -168,22 +168,22 @@ bool RE::FullMatch(const StringPiece& text,
const Arg& ptr16) const {
const Arg* args[kMaxArgs];
int n = 0;
- if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
- if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
- if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
- if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
- if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
- if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
- if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
- if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
- if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
- if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
- if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
- if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
- if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
- if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
- if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
- if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
+ if (&ptr1 == &no_arg) { goto done; } args[n++] = &ptr1;
+ if (&ptr2 == &no_arg) { goto done; } args[n++] = &ptr2;
+ if (&ptr3 == &no_arg) { goto done; } args[n++] = &ptr3;
+ if (&ptr4 == &no_arg) { goto done; } args[n++] = &ptr4;
+ if (&ptr5 == &no_arg) { goto done; } args[n++] = &ptr5;
+ if (&ptr6 == &no_arg) { goto done; } args[n++] = &ptr6;
+ if (&ptr7 == &no_arg) { goto done; } args[n++] = &ptr7;
+ if (&ptr8 == &no_arg) { goto done; } args[n++] = &ptr8;
+ if (&ptr9 == &no_arg) { goto done; } args[n++] = &ptr9;
+ if (&ptr10 == &no_arg) { goto done; } args[n++] = &ptr10;
+ if (&ptr11 == &no_arg) { goto done; } args[n++] = &ptr11;
+ if (&ptr12 == &no_arg) { goto done; } args[n++] = &ptr12;
+ if (&ptr13 == &no_arg) { goto done; } args[n++] = &ptr13;
+ if (&ptr14 == &no_arg) { goto done; } args[n++] = &ptr14;
+ if (&ptr15 == &no_arg) { goto done; } args[n++] = &ptr15;
+ if (&ptr16 == &no_arg) { goto done; } args[n++] = &ptr16;
done:
int consumed;
@@ -210,22 +210,22 @@ bool RE::PartialMatch(const StringPiece& text,
const Arg& ptr16) const {
const Arg* args[kMaxArgs];
int n = 0;
- if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
- if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
- if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
- if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
- if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
- if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
- if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
- if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
- if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
- if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
- if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
- if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
- if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
- if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
- if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
- if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
+ if (&ptr1 == &no_arg) { goto done; } args[n++] = &ptr1;
+ if (&ptr2 == &no_arg) { goto done; } args[n++] = &ptr2;
+ if (&ptr3 == &no_arg) { goto done; } args[n++] = &ptr3;
+ if (&ptr4 == &no_arg) { goto done; } args[n++] = &ptr4;
+ if (&ptr5 == &no_arg) { goto done; } args[n++] = &ptr5;
+ if (&ptr6 == &no_arg) { goto done; } args[n++] = &ptr6;
+ if (&ptr7 == &no_arg) { goto done; } args[n++] = &ptr7;
+ if (&ptr8 == &no_arg) { goto done; } args[n++] = &ptr8;
+ if (&ptr9 == &no_arg) { goto done; } args[n++] = &ptr9;
+ if (&ptr10 == &no_arg) { goto done; } args[n++] = &ptr10;
+ if (&ptr11 == &no_arg) { goto done; } args[n++] = &ptr11;
+ if (&ptr12 == &no_arg) { goto done; } args[n++] = &ptr12;
+ if (&ptr13 == &no_arg) { goto done; } args[n++] = &ptr13;
+ if (&ptr14 == &no_arg) { goto done; } args[n++] = &ptr14;
+ if (&ptr15 == &no_arg) { goto done; } args[n++] = &ptr15;
+ if (&ptr16 == &no_arg) { goto done; } args[n++] = &ptr16;
done:
int consumed;
@@ -252,22 +252,22 @@ bool RE::Consume(StringPiece* input,
const Arg& ptr16) const {
const Arg* args[kMaxArgs];
int n = 0;
- if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
- if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
- if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
- if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
- if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
- if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
- if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
- if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
- if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
- if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
- if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
- if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
- if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
- if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
- if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
- if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
+ if (&ptr1 == &no_arg) { goto done; } args[n++] = &ptr1;
+ if (&ptr2 == &no_arg) { goto done; } args[n++] = &ptr2;
+ if (&ptr3 == &no_arg) { goto done; } args[n++] = &ptr3;
+ if (&ptr4 == &no_arg) { goto done; } args[n++] = &ptr4;
+ if (&ptr5 == &no_arg) { goto done; } args[n++] = &ptr5;
+ if (&ptr6 == &no_arg) { goto done; } args[n++] = &ptr6;
+ if (&ptr7 == &no_arg) { goto done; } args[n++] = &ptr7;
+ if (&ptr8 == &no_arg) { goto done; } args[n++] = &ptr8;
+ if (&ptr9 == &no_arg) { goto done; } args[n++] = &ptr9;
+ if (&ptr10 == &no_arg) { goto done; } args[n++] = &ptr10;
+ if (&ptr11 == &no_arg) { goto done; } args[n++] = &ptr11;
+ if (&ptr12 == &no_arg) { goto done; } args[n++] = &ptr12;
+ if (&ptr13 == &no_arg) { goto done; } args[n++] = &ptr13;
+ if (&ptr14 == &no_arg) { goto done; } args[n++] = &ptr14;
+ if (&ptr15 == &no_arg) { goto done; } args[n++] = &ptr15;
+ if (&ptr16 == &no_arg) { goto done; } args[n++] = &ptr16;
done:
int consumed;
@@ -300,22 +300,22 @@ bool RE::FindAndConsume(StringPiece* input,
const Arg& ptr16) const {
const Arg* args[kMaxArgs];
int n = 0;
- if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1;
- if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2;
- if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3;
- if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4;
- if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5;
- if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6;
- if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7;
- if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8;
- if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9;
- if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10;
- if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11;
- if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12;
- if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13;
- if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14;
- if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15;
- if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16;
+ if (&ptr1 == &no_arg) { goto done; } args[n++] = &ptr1;
+ if (&ptr2 == &no_arg) { goto done; } args[n++] = &ptr2;
+ if (&ptr3 == &no_arg) { goto done; } args[n++] = &ptr3;
+ if (&ptr4 == &no_arg) { goto done; } args[n++] = &ptr4;
+ if (&ptr5 == &no_arg) { goto done; } args[n++] = &ptr5;
+ if (&ptr6 == &no_arg) { goto done; } args[n++] = &ptr6;
+ if (&ptr7 == &no_arg) { goto done; } args[n++] = &ptr7;
+ if (&ptr8 == &no_arg) { goto done; } args[n++] = &ptr8;
+ if (&ptr9 == &no_arg) { goto done; } args[n++] = &ptr9;
+ if (&ptr10 == &no_arg) { goto done; } args[n++] = &ptr10;
+ if (&ptr11 == &no_arg) { goto done; } args[n++] = &ptr11;
+ if (&ptr12 == &no_arg) { goto done; } args[n++] = &ptr12;
+ if (&ptr13 == &no_arg) { goto done; } args[n++] = &ptr13;
+ if (&ptr14 == &no_arg) { goto done; } args[n++] = &ptr14;
+ if (&ptr15 == &no_arg) { goto done; } args[n++] = &ptr15;
+ if (&ptr16 == &no_arg) { goto done; } args[n++] = &ptr16;
done:
int consumed;
diff --git a/src/third_party/pcre-8.38/pcrecpp.h b/src/third_party/pcre-8.39/pcrecpp.h
index 3e594b0d439..3e594b0d439 100644
--- a/src/third_party/pcre-8.38/pcrecpp.h
+++ b/src/third_party/pcre-8.39/pcrecpp.h
diff --git a/src/third_party/pcre-8.38/pcrecpp_internal.h b/src/third_party/pcre-8.39/pcrecpp_internal.h
index 827f9e04e2a..827f9e04e2a 100644
--- a/src/third_party/pcre-8.38/pcrecpp_internal.h
+++ b/src/third_party/pcre-8.39/pcrecpp_internal.h
diff --git a/src/third_party/pcre-8.38/pcrecpp_unittest.cc b/src/third_party/pcre-8.39/pcrecpp_unittest.cc
index 92cae8fbea5..92cae8fbea5 100644
--- a/src/third_party/pcre-8.38/pcrecpp_unittest.cc
+++ b/src/third_party/pcre-8.39/pcrecpp_unittest.cc
diff --git a/src/third_party/pcre-8.38/pcrecpparg.h b/src/third_party/pcre-8.39/pcrecpparg.h
index b4f9c3f4989..b4f9c3f4989 100644
--- a/src/third_party/pcre-8.38/pcrecpparg.h
+++ b/src/third_party/pcre-8.39/pcrecpparg.h
diff --git a/src/third_party/pcre-8.38/pcrecpparg.h.in b/src/third_party/pcre-8.39/pcrecpparg.h.in
index 61bcab5402c..61bcab5402c 100644
--- a/src/third_party/pcre-8.38/pcrecpparg.h.in
+++ b/src/third_party/pcre-8.39/pcrecpparg.h.in
diff --git a/src/third_party/pcre-8.38/pcredemo.c b/src/third_party/pcre-8.39/pcredemo.c
index 946aba45cdc..946aba45cdc 100644
--- a/src/third_party/pcre-8.38/pcredemo.c
+++ b/src/third_party/pcre-8.39/pcredemo.c
diff --git a/src/third_party/pcre-8.38/pcregexp.pas b/src/third_party/pcre-8.39/pcregexp.pas
index bb2b3da8f3d..bb2b3da8f3d 100644
--- a/src/third_party/pcre-8.38/pcregexp.pas
+++ b/src/third_party/pcre-8.39/pcregexp.pas
diff --git a/src/third_party/pcre-8.38/pcregrep.c b/src/third_party/pcre-8.39/pcregrep.c
index 64986b016e6..cd53c648da2 100644
--- a/src/third_party/pcre-8.38/pcregrep.c
+++ b/src/third_party/pcre-8.39/pcregrep.c
@@ -2437,7 +2437,7 @@ return options;
static char *
ordin(int n)
{
-static char buffer[8];
+static char buffer[14];
char *p = buffer;
sprintf(p, "%d", n);
while (*p != 0) p++;
diff --git a/src/third_party/pcre-8.38/pcreposix.c b/src/third_party/pcre-8.39/pcreposix.c
index f024423b634..cf75588c40d 100644
--- a/src/third_party/pcre-8.38/pcreposix.c
+++ b/src/third_party/pcre-8.39/pcreposix.c
@@ -6,7 +6,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2014 University of Cambridge
+ Copyright (c) 1997-2016 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -173,7 +173,8 @@ static const int eint[] = {
REG_BADPAT, /* group name must start with a non-digit */
/* 85 */
REG_BADPAT, /* parentheses too deeply nested (stack check) */
- REG_BADPAT /* missing digits in \x{} or \o{} */
+ REG_BADPAT, /* missing digits in \x{} or \o{} */
+ REG_BADPAT /* pattern too complicated */
};
/* Table of texts corresponding to POSIX error codes */
@@ -364,6 +365,7 @@ start location rather than being passed as a PCRE "starting offset". */
if ((eflags & REG_STARTEND) != 0)
{
+ if (pmatch == NULL) return REG_INVARG;
so = pmatch[0].rm_so;
eo = pmatch[0].rm_eo;
}
diff --git a/src/third_party/pcre-8.38/pcreposix.h b/src/third_party/pcre-8.39/pcreposix.h
index c77c0b0523c..c77c0b0523c 100644
--- a/src/third_party/pcre-8.38/pcreposix.h
+++ b/src/third_party/pcre-8.39/pcreposix.h
diff --git a/src/third_party/pcre-8.38/pcretest.c b/src/third_party/pcre-8.39/pcretest.c
index 488e419462e..78ef5177df7 100644
--- a/src/third_party/pcre-8.38/pcretest.c
+++ b/src/third_party/pcre-8.39/pcretest.c
@@ -2250,7 +2250,7 @@ data is not zero. */
static int callout(pcre_callout_block *cb)
{
FILE *f = (first_callout | callout_extra)? outfile : NULL;
-int i, pre_start, post_start, subject_length;
+int i, current_position, pre_start, post_start, subject_length;
if (callout_extra)
{
@@ -2280,14 +2280,19 @@ printed lengths of the substrings. */
if (f != NULL) fprintf(f, "--->");
+/* If a lookbehind is involved, the current position may be earlier than the
+match start. If so, use the match start instead. */
+
+current_position = (cb->current_position >= cb->start_match)?
+ cb->current_position : cb->start_match;
+
PCHARS(pre_start, cb->subject, 0, cb->start_match, f);
PCHARS(post_start, cb->subject, cb->start_match,
- cb->current_position - cb->start_match, f);
+ current_position - cb->start_match, f);
PCHARS(subject_length, cb->subject, 0, cb->subject_length, NULL);
-PCHARSV(cb->subject, cb->current_position,
- cb->subject_length - cb->current_position, f);
+PCHARSV(cb->subject, current_position, cb->subject_length - current_position, f);
if (f != NULL) fprintf(f, "\n");
@@ -5612,6 +5617,12 @@ while (!done)
break;
}
+ if (use_size_offsets < 2)
+ {
+ fprintf(outfile, "Cannot do global matching with an ovector size < 2\n");
+ break;
+ }
+
/* If we have matched an empty string, first check to see if we are at
the end of the subject. If so, the /g loop is over. Otherwise, mimic what
Perl's /g options does. This turns out to be rather cunning. First we set
@@ -5740,3 +5751,4 @@ return yield;
}
/* End of pcretest.c */
+
diff --git a/src/third_party/pcre-8.38/perltest.pl b/src/third_party/pcre-8.39/perltest.pl
index 29b808b5293..29b808b5293 100755
--- a/src/third_party/pcre-8.38/perltest.pl
+++ b/src/third_party/pcre-8.39/perltest.pl
diff --git a/src/third_party/pcre-8.38/ucp.h b/src/third_party/pcre-8.39/ucp.h
index 2fa00296e42..2fa00296e42 100644
--- a/src/third_party/pcre-8.38/ucp.h
+++ b/src/third_party/pcre-8.39/ucp.h
diff --git a/src/third_party/scripts/pcre_get_sources.sh b/src/third_party/scripts/pcre_get_sources.sh
index e4be2525378..da73652ef66 100644
--- a/src/third_party/scripts/pcre_get_sources.sh
+++ b/src/third_party/scripts/pcre_get_sources.sh
@@ -8,13 +8,22 @@
# 2. Run on Solaris
# 3. Run on Windows
#
-VERSION=8.38
+set -euo pipefail
+IFS=$'\n\t'
+
+if [ "$#" -ne 0 ]; then
+ echo "This script does not take any arguments"
+ exit 1
+fi
+
+VERSION=8.39
NAME=pcre
TARBALL=$NAME-$VERSION.tar.gz
TARBALL_DIR=$NAME-$VERSION
-TEMP_DIR=/tmp/temp-$NAME-$VERSION
-DEST_DIR=`git rev-parse --show-toplevel`/src/third_party/$NAME-$VERSION
-UNAME=`uname | tr A-Z a-z`
+TEMP_DIR=$(mktemp -d /tmp/pcre.XXXXXX)
+trap "rm -rf $TEMP_DIR" EXIT
+DEST_DIR=$(git rev-parse --show-toplevel)/src/third_party/$NAME-$VERSION
+UNAME=$(uname | tr A-Z a-z)
if [ $UNAME == "sunos" ]; then
TARGET_UNAME=solaris
@@ -27,8 +36,7 @@ fi
echo TARGET_UNAME: $TARGET_UNAME
if [ ! -f $TARBALL ]; then
- echo "Get tarball"
- wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/$TARBALL
+ curl -O ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/$TARBALL
fi
tar -zxvf $TARBALL
@@ -41,13 +49,28 @@ cd $TEMP_DIR
if [ $TARGET_UNAME != "windows" ]; then
# Do a shallow copy, it is all we need
- cp $TEMP_DIR/* $DEST_DIR
+ for file_copy in $(find $TEMP_DIR -maxdepth 1 -type f); do
+ echo copying $file_copy $DEST_DIR
+ cp $file_copy $DEST_DIR
+ done
+
rm -f $DEST_DIR/Makefile* $DEST_DIR/config* $DEST_DIR/*sh
rm -f $DEST_DIR/compile* $DEST_DIR/depcomp $DEST_DIR/libtool
rm -f $DEST_DIR/test-driver $DEST_DIR/*.m4 $DEST_DIR/missing
echo "Generating Config.h and other files"
- ./configure --disable-jit --with-posix-malloc-threshold=10 --with-match-limit-recursion=4000 --disable-stack-for-recursion --with-link-size=2 -enable-newline-is-lf --with-match-limit=200000 --with-parens-nest-limit=250 --enable-utf --enable-unicode-properties --enable-shared=no
+ ./configure \
+ --disable-jit \
+ --with-posix-malloc-threshold=10 \
+ --with-match-limit-recursion=4000 \
+ --disable-stack-for-recursion \
+ --with-link-size=2 \
+ -enable-newline-is-lf \
+ --with-match-limit=200000 \
+ --with-parens-nest-limit=250 \
+ --enable-utf \
+ --enable-unicode-properties \
+ --enable-shared=no
# We need to make it to get pcre_chartables.c
make
@@ -57,16 +80,21 @@ if [ $TARGET_UNAME != "windows" ]; then
cp $TEMP_DIR/pcre_chartables.c $DEST_DIR
cp $TEMP_DIR/pcre_stringpiece.h $DEST_DIR
cp $TEMP_DIR/pcrecpparg.h $DEST_DIR
-
- # Copy over config.h
- mkdir $DEST_DIR/build_$TARGET_UNAME
- cp $TEMP_DIR/config.h $DEST_DIR/build_$TARGET_UNAME
else
- /cygdrive/c/Program\ Files\ \(x86\)/CMake/bin/cmake.exe -DPCRE_SUPPORT_PCREGREP_JIT:BOOL="0" -DPCRE_BUILD_TESTS:BOOL="0" -DPCRE_POSIX_MALLOC_THRESHOLD:STRING="10" -DPCRE_MATCH_LIMIT_RECURSION:STRING="4000" -DPCRE_NO_RECURSE:BOOL="1" -DPCRE_LINK_SIZE:STRING="2" -DPCRE_NEWLINE:STRING="LF" -DPCRE_MATCH_LIMIT:STRING="200000" -DPCRE_PARENS_NEST_LIMIT:STRING="250" -DPCRE_SUPPORT_UTF:BOOL="1" -DPCRE_SUPPORT_UNICODE_PROPERTIES:BOOL="1"
+ /cygdrive/c/cmake/bin/cmake.exe \
+ -DPCRE_SUPPORT_PCREGREP_JIT:BOOL="0" \
+ -DPCRE_BUILD_TESTS:BOOL="0" \
+ -DPCRE_POSIX_MALLOC_THRESHOLD:STRING="10" \
+ -DPCRE_MATCH_LIMIT_RECURSION:STRING="4000" \
+ -DPCRE_NO_RECURSE:BOOL="1" \
+ -DPCRE_LINK_SIZE:STRING="2" \
+ -DPCRE_NEWLINE:STRING="LF" \
+ -DPCRE_MATCH_LIMIT:STRING="200000" \
+ -DPCRE_PARENS_NEST_LIMIT:STRING="250" \
+ -DPCRE_SUPPORT_UTF:BOOL="1" \
+ -DPCRE_SUPPORT_UNICODE_PROPERTIES:BOOL="1"
fi
# Copy over config.h
-mkdir $DEST_DIR/build_$TARGET_UNAME
+mkdir $DEST_DIR/build_$TARGET_UNAME || true
cp $TEMP_DIR/config.h $DEST_DIR/build_$TARGET_UNAME
-
-echo "Done"
diff --git a/src/third_party/wiredtiger/.gitignore b/src/third_party/wiredtiger/.gitignore
index e69de29bb2d..bef47daabf9 100644
--- a/src/third_party/wiredtiger/.gitignore
+++ b/src/third_party/wiredtiger/.gitignore
@@ -0,0 +1,123 @@
+# Editor files
+*.swp
+
+# Build directories
+.deps/
+.libs/
+
+# Build files
+*.dll
+*.exe
+*.exp
+*.la
+*.lib
+*.lo
+*.o
+*.obj
+*.pdb
+*.pyc
+.sconf_temp
+.sconsign.dblite
+.dirstamp
+/Makefile.am
+/aclocal.m4
+/build_posix/aclocal/libtool.m4
+/build_posix/aclocal/ltoptions.m4
+/build_posix/aclocal/ltsugar.m4
+/build_posix/aclocal/ltversion.m4
+/build_posix/aclocal/lt~obsolete.m4
+/build_posix/config.hin
+/build_posix/gnu-support/
+/config.log
+/config.status
+/configure
+/configure.ac
+/libtool/
+/stamp-h1
+/wiredtiger.h
+/wiredtiger.pc
+/wiredtiger_config.h
+/wiredtiger_ext.h
+/wt
+Makefile
+Makefile.in
+build_posix/config.log
+build_posix/config.status
+build_posix/libtool
+build_posix/stamp-h1
+build_posix/wiredtiger.h
+build_posix/wiredtiger.pc
+build_posix/wiredtiger_config.h
+build_posix/wiredtiger_ext.h
+build_posix/wt
+tags
+
+# Documentation
+/docs/
+/src/docs/changelog.md
+/src/docs/doxygen.log
+
+# Tests
+WT_HOME/
+WT_TEST/
+
+# Python
+/lang/python/_wiredtiger.so
+/lang/python/wiredtiger/__init__.py
+/lang/python/wiredtiger/_wiredtiger.pyd
+/lang/python/wiredtiger.py
+/lang/python/wiredtiger_wrap.c
+_wiredtiger.pyd
+
+# Binaries
+**/bench/wtperf/wtperf
+**/examples/c/ex_access
+**/examples/c/ex_all
+**/examples/c/ex_async
+**/examples/c/ex_backup
+**/examples/c/ex_call_center
+**/examples/c/ex_config_parse
+**/examples/c/ex_cursor
+**/examples/c/ex_data_source
+**/examples/c/ex_encrypt
+**/examples/c/ex_event_handler
+**/examples/c/ex_extending
+**/examples/c/ex_extractor
+**/examples/c/ex_file_system
+**/examples/c/ex_hello
+**/examples/c/ex_log
+**/examples/c/ex_pack
+**/examples/c/ex_process
+**/examples/c/ex_schema
+**/examples/c/ex_scope
+**/examples/c/ex_stat
+**/examples/c/ex_sync
+**/examples/c/ex_thread
+**/test/bloom/t
+**/test/checkpoint/t
+**/test/csuite/test_wt1965_col_efficiency
+**/test/csuite/test_wt2246_col_append
+**/test/csuite/test_wt2323_join_visibility
+**/test/csuite/test_wt2403_lsm_workload
+**/test/csuite/test_wt2447_join_main_table
+**/test/csuite/test_wt2535_insert_race
+**/test/csuite/test_wt2592_join_schema
+**/test/csuite/test_wt2695_checksum
+**/test/csuite/test_wt2719_reconfig
+**/test/csuite/test_wt2834_join_bloom_fix
+**/test/csuite/test_wt2853_perf
+**/test/cursor_order/cursor_order
+**/test/fops/t
+**/test/format/s_dumpcmp
+**/test/format/t
+**/test/huge/t
+**/test/manydbs/t
+**/test/packing/intpack-test
+**/test/packing/intpack-test2
+**/test/packing/intpack-test3
+**/test/packing/packing-test
+**/test/readonly/t
+**/test/recovery/random-abort
+**/test/recovery/truncated-log
+**/test/salvage/t
+**/test/thread/t
diff --git a/src/third_party/wiredtiger/bench/wtperf/config.c b/src/third_party/wiredtiger/bench/wtperf/config.c
index 4c7b17f102a..5b14a4cdf68 100644
--- a/src/third_party/wiredtiger/bench/wtperf/config.c
+++ b/src/third_party/wiredtiger/bench/wtperf/config.c
@@ -28,15 +28,19 @@
#include "wtperf.h"
-/* All options changeable on command line using -o or -O are listed here. */
-static CONFIG_OPT config_opts[] = {
+static CONFIG_OPT config_opts_desc[] = { /* Option descriptions */
#define OPT_DEFINE_DESC
#include "wtperf_opt.i"
#undef OPT_DEFINE_DESC
};
-static int config_opt(CONFIG *, WT_CONFIG_ITEM *, WT_CONFIG_ITEM *);
-static void config_opt_usage(void);
+static CONFIG_OPTS config_opts_default = { /* Option defaults */
+#define OPT_DEFINE_DEFAULT
+#include "wtperf_opt.i"
+#undef OPT_DEFINE_DEFAULT
+
+ { NULL, NULL } /* config_head */
+};
/*
* STRING_MATCH --
@@ -47,6 +51,72 @@ static void config_opt_usage(void);
(strncmp(str, bytes, len) == 0 && (str)[(len)] == '\0')
/*
+ * config_opt_init --
+ * Initialize the global configuration options.
+ */
+void
+config_opt_init(CONFIG_OPTS **retp)
+{
+ CONFIG_OPT *desc;
+ CONFIG_OPTS *opts;
+ size_t i;
+ char **strp;
+ void *valueloc;
+
+ opts = dmalloc(sizeof(CONFIG_OPTS));
+ *opts = config_opts_default;
+
+ TAILQ_INIT(&opts->config_head);
+
+ /*
+ * Option strings come-and-go as we configure them, so allocate copies
+ * of the default strings now so that we can always free the string as
+ * we allocate new versions.
+ */
+ for (i = 0, desc = config_opts_desc;
+ i < WT_ELEMENTS(config_opts_desc); i++, ++desc)
+ if (desc->type == CONFIG_STRING_TYPE ||
+ desc->type == STRING_TYPE) {
+ valueloc = ((uint8_t *)opts + desc->offset);
+ strp = (char **)valueloc;
+ *strp = dstrdup(*strp);
+ }
+
+ *retp = opts;
+}
+
+/*
+ * config_opt_cleanup --
+ * Clean up the global configuration options.
+ */
+void
+config_opt_cleanup(CONFIG_OPTS *opts)
+{
+ CONFIG_OPT *desc;
+ CONFIG_QUEUE_ENTRY *config_line;
+ size_t i;
+ char **strp;
+ void *valueloc;
+
+ for (i = 0, desc = config_opts_desc;
+ i < WT_ELEMENTS(config_opts_desc); i++, ++desc)
+ if (desc->type == CONFIG_STRING_TYPE ||
+ desc->type == STRING_TYPE) {
+ valueloc = ((uint8_t *)opts + desc->offset);
+ strp = (char **)valueloc;
+ free(*strp);
+ }
+
+ while ((config_line = TAILQ_FIRST(&opts->config_head)) != NULL) {
+ TAILQ_REMOVE(&opts->config_head, config_line, q);
+ free(config_line->string);
+ free(config_line);
+ }
+
+ free(opts);
+}
+
+/*
* config_unescape --
* Modify a string in place, replacing any backslash escape sequences.
* The modified string is always shorter.
@@ -94,168 +164,11 @@ config_unescape(char *orig)
}
/*
- * config_copy --
- * CONFIG structure initialization, based on a source configuration.
- */
-void
-config_copy(CONFIG *dest, const CONFIG *src)
-{
- CONFIG_QUEUE_ENTRY *conf_line, *tmp_line;
- size_t i;
- char *newstr, **pstr;
-
- memcpy(dest, src, sizeof(CONFIG));
-
- if (src->home != NULL)
- dest->home = dstrdup(src->home);
- if (src->monitor_dir != NULL)
- dest->monitor_dir = dstrdup(src->monitor_dir);
- if (src->partial_config != NULL)
- dest->partial_config = dstrdup(src->partial_config);
- if (src->reopen_config != NULL)
- dest->reopen_config = dstrdup(src->reopen_config);
- if (src->base_uri != NULL)
- dest->base_uri = dstrdup(src->base_uri);
-
- if (src->uris != NULL) {
- dest->uris = dcalloc(src->table_count, sizeof(char *));
- for (i = 0; i < src->table_count; i++)
- dest->uris[i] = dstrdup(src->uris[i]);
- }
-
- if (src->async_config != NULL)
- dest->async_config = dstrdup(src->async_config);
-
- dest->ckptthreads = NULL;
- dest->popthreads = NULL;
- dest->workers = NULL;
-
- if (src->workload != NULL) {
- dest->workload = dcalloc(WORKLOAD_MAX, sizeof(WORKLOAD));
- memcpy(dest->workload,
- src->workload, WORKLOAD_MAX * sizeof(WORKLOAD));
- }
-
- for (i = 0; i < sizeof(config_opts) / sizeof(config_opts[0]); i++)
- if (config_opts[i].type == STRING_TYPE ||
- config_opts[i].type == CONFIG_STRING_TYPE) {
- pstr = (char **)
- ((u_char *)dest + config_opts[i].offset);
- if (*pstr != NULL) {
- newstr = dstrdup(*pstr);
- *pstr = newstr;
- }
- }
-
- TAILQ_INIT(&dest->stone_head);
- TAILQ_INIT(&dest->config_head);
-
- /* Clone the config string information into the new cfg object */
- TAILQ_FOREACH(conf_line, &src->config_head, c) {
- tmp_line = dcalloc(sizeof(CONFIG_QUEUE_ENTRY), 1);
- tmp_line->string = dstrdup(conf_line->string);
- TAILQ_INSERT_TAIL(&dest->config_head, tmp_line, c);
- }
-}
-
-/*
- * config_free --
- * Free any storage allocated in the config struct.
- */
-void
-config_free(CONFIG *cfg)
-{
- CONFIG_QUEUE_ENTRY *config_line;
- size_t i;
- char **pstr;
-
- free(cfg->home);
- free(cfg->monitor_dir);
- free(cfg->partial_config);
- free(cfg->reopen_config);
-
- /* Free the various URIs */
- free(cfg->base_uri);
- free(cfg->log_table_uri);
-
- if (cfg->uris != NULL) {
- for (i = 0; i < cfg->table_count; i++)
- free(cfg->uris[i]);
- free(cfg->uris);
- }
-
- free(cfg->async_config);
-
- free(cfg->ckptthreads);
- free(cfg->popthreads);
-
- free(cfg->workers);
- free(cfg->workload);
-
- cleanup_truncate_config(cfg);
-
- while (!TAILQ_EMPTY(&cfg->config_head)) {
- config_line = TAILQ_FIRST(&cfg->config_head);
- TAILQ_REMOVE(&cfg->config_head, config_line, c);
- free(config_line->string);
- free(config_line);
- }
-
- for (i = 0; i < sizeof(config_opts) / sizeof(config_opts[0]); i++)
- if (config_opts[i].type == STRING_TYPE ||
- config_opts[i].type == CONFIG_STRING_TYPE) {
- pstr = (char **)
- ((u_char *)cfg + config_opts[i].offset);
- free(*pstr);
- *pstr = NULL;
- }
-}
-
-/*
- * config_compress --
- * Parse the compression configuration.
- */
-int
-config_compress(CONFIG *cfg)
-{
- int ret;
- const char *s;
-
- ret = 0;
- s = cfg->compression;
- if (strcmp(s, "none") == 0) {
- cfg->compress_ext = NULL;
- cfg->compress_table = NULL;
- } else if (strcmp(s, "lz4") == 0) {
-#ifndef HAVE_BUILTIN_EXTENSION_LZ4
- cfg->compress_ext = LZ4_EXT;
-#endif
- cfg->compress_table = LZ4_BLK;
- } else if (strcmp(s, "snappy") == 0) {
-#ifndef HAVE_BUILTIN_EXTENSION_SNAPPY
- cfg->compress_ext = SNAPPY_EXT;
-#endif
- cfg->compress_table = SNAPPY_BLK;
- } else if (strcmp(s, "zlib") == 0) {
-#ifndef HAVE_BUILTIN_EXTENSION_ZLIB
- cfg->compress_ext = ZLIB_EXT;
-#endif
- cfg->compress_table = ZLIB_BLK;
- } else {
- fprintf(stderr,
- "invalid compression configuration: %s\n", s);
- ret = EINVAL;
- }
- return (ret);
-
-}
-
-/*
* config_threads --
* Parse the thread configuration.
*/
static int
-config_threads(CONFIG *cfg, const char *config, size_t len)
+config_threads(WTPERF *wtperf, const char *config, size_t len)
{
WORKLOAD *workp;
WT_CONFIG_ITEM groupk, groupv, k, v;
@@ -263,19 +176,19 @@ config_threads(CONFIG *cfg, const char *config, size_t len)
int ret;
group = scan = NULL;
- if (cfg->workload != NULL) {
+ if (wtperf->workload != NULL) {
/*
* This call overrides an earlier call. Free and
* reset everything.
*/
- free(cfg->workload);
- cfg->workload = NULL;
- cfg->workload_cnt = 0;
- cfg->workers_cnt = 0;
+ free(wtperf->workload);
+ wtperf->workload = NULL;
+ wtperf->workload_cnt = 0;
+ wtperf->workers_cnt = 0;
}
/* Allocate the workload array. */
- cfg->workload = dcalloc(WORKLOAD_MAX, sizeof(WORKLOAD));
- cfg->workload_cnt = 0;
+ wtperf->workload = dcalloc(WORKLOAD_MAX, sizeof(WORKLOAD));
+ wtperf->workload_cnt = 0;
/*
* The thread configuration may be in multiple groups, that is, we have
@@ -294,14 +207,14 @@ config_threads(CONFIG *cfg, const char *config, size_t len)
goto err;
/* Move to the next workload slot. */
- if (cfg->workload_cnt == WORKLOAD_MAX) {
+ if (wtperf->workload_cnt == WORKLOAD_MAX) {
fprintf(stderr,
"too many workloads configured, only %d workloads "
"supported\n",
WORKLOAD_MAX);
return (EINVAL);
}
- workp = &cfg->workload[cfg->workload_cnt++];
+ workp = &wtperf->workload[wtperf->workload_cnt++];
while ((ret = scan->next(scan, &k, &v)) == 0) {
if (STRING_MATCH("count", k.str, k.len)) {
@@ -334,9 +247,9 @@ config_threads(CONFIG *cfg, const char *config, size_t len)
if ((workp->truncate = v.val) != 1)
goto err;
/* There can only be one Truncate thread. */
- if (F_ISSET(cfg, CFG_TRUNCATE))
+ if (F_ISSET(wtperf, CFG_TRUNCATE))
goto err;
- F_SET(cfg, CFG_TRUNCATE);
+ F_SET(wtperf, CFG_TRUNCATE);
continue;
}
if (STRING_MATCH("truncate_pct", k.str, k.len)) {
@@ -364,13 +277,13 @@ config_threads(CONFIG *cfg, const char *config, size_t len)
goto err;
/* Special random value */
workp->update_delta = INT64_MAX;
- F_SET(cfg, CFG_GROW);
+ F_SET(wtperf, CFG_GROW);
} else {
workp->update_delta = v.val;
if (v.val > 0)
- F_SET(cfg, CFG_GROW);
+ F_SET(wtperf, CFG_GROW);
if (v.val < 0)
- F_SET(cfg, CFG_SHRINK);
+ F_SET(wtperf, CFG_SHRINK);
}
continue;
}
@@ -400,7 +313,7 @@ config_threads(CONFIG *cfg, const char *config, size_t len)
if (workp->truncate != 0 &&
(workp->insert > 0 || workp->read > 0 || workp->update > 0))
goto err;
- cfg->workers_cnt += (u_int)workp->threads;
+ wtperf->workers_cnt += (u_int)workp->threads;
}
ret = group->close(group);
@@ -428,32 +341,34 @@ err: if (group != NULL)
* value.
*/
static int
-config_opt(CONFIG *cfg, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
+config_opt(WTPERF *wtperf, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
{
- CONFIG_OPT *popt;
+ CONFIG_OPTS *opts;
+ CONFIG_OPT *desc;
char *begin, *newstr, **strp;
int ret;
- size_t i, newlen, nopt;
+ size_t i, newlen;
void *valueloc;
- popt = NULL;
- nopt = sizeof(config_opts)/sizeof(config_opts[0]);
- for (i = 0; i < nopt; i++)
- if (strlen(config_opts[i].name) == k->len &&
- strncmp(config_opts[i].name, k->str, k->len) == 0) {
- popt = &config_opts[i];
+ opts = wtperf->opts;
+
+ desc = NULL;
+ for (i = 0; i < WT_ELEMENTS(config_opts_desc); i++)
+ if (strlen(config_opts_desc[i].name) == k->len &&
+ strncmp(config_opts_desc[i].name, k->str, k->len) == 0) {
+ desc = &config_opts_desc[i];
break;
}
- if (popt == NULL) {
+ if (desc == NULL) {
fprintf(stderr, "wtperf: Error: "
"unknown option \'%.*s\'\n", (int)k->len, k->str);
fprintf(stderr, "Options:\n");
- for (i = 0; i < nopt; i++)
- fprintf(stderr, "\t%s\n", config_opts[i].name);
+ for (i = 0; i < WT_ELEMENTS(config_opts_desc); i++)
+ fprintf(stderr, "\t%s\n", config_opts_desc[i].name);
return (EINVAL);
}
- valueloc = ((u_char *)cfg + popt->offset);
- switch (popt->type) {
+ valueloc = ((uint8_t *)opts + desc->offset);
+ switch (desc->type) {
case BOOL_TYPE:
if (v->type != WT_CONFIG_ITEM_BOOL) {
fprintf(stderr, "wtperf: Error: "
@@ -531,7 +446,7 @@ config_opt(CONFIG *cfg, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
*/
if (v->type == WT_CONFIG_ITEM_STRUCT &&
STRING_MATCH("threads", k->str, k->len))
- return (config_threads(cfg, v->str, v->len));
+ return (config_threads(wtperf, v->str, v->len));
if (v->type != WT_CONFIG_ITEM_STRING &&
v->type != WT_CONFIG_ITEM_ID) {
@@ -559,7 +474,7 @@ config_opt(CONFIG *cfg, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
* via lines ending in '\'.
*/
int
-config_opt_file(CONFIG *cfg, const char *filename)
+config_opt_file(WTPERF *wtperf, const char *filename)
{
FILE *fp;
size_t linelen, optionpos;
@@ -659,7 +574,7 @@ config_opt_file(CONFIG *cfg, const char *filename)
if (contline)
optionpos += linelen;
else {
- if ((ret = config_opt_line(cfg, option)) != 0) {
+ if ((ret = config_opt_str(wtperf, option)) != 0) {
fprintf(stderr, "wtperf: %s: %d: parse error\n",
filename, linenum);
break;
@@ -684,23 +599,26 @@ config_opt_file(CONFIG *cfg, const char *filename)
}
/*
- * config_opt_line --
+ * config_opt_str --
* Parse a single line of config options. Continued lines have already
* been joined.
*/
int
-config_opt_line(CONFIG *cfg, const char *optstr)
+config_opt_str(WTPERF *wtperf, const char *optstr)
{
+ CONFIG_OPTS *opts;
CONFIG_QUEUE_ENTRY *config_line;
WT_CONFIG_ITEM k, v;
WT_CONFIG_PARSER *scan;
size_t len;
int ret, t_ret;
+ opts = wtperf->opts;
+
len = strlen(optstr);
if ((ret = wiredtiger_config_parser_open(
NULL, optstr, len, &scan)) != 0) {
- lprintf(cfg, ret, 0, "Error in config_scan_begin");
+ lprintf(wtperf, ret, 0, "Error in config_scan_begin");
return (ret);
}
@@ -712,7 +630,7 @@ config_opt_line(CONFIG *cfg, const char *optstr)
*/
config_line = dcalloc(sizeof(CONFIG_QUEUE_ENTRY), 1);
config_line->string = dstrdup(optstr);
- TAILQ_INSERT_TAIL(&cfg->config_head, config_line, c);
+ TAILQ_INSERT_TAIL(&opts->config_head, config_line, q);
while (ret == 0) {
if ((ret = scan->next(scan, &k, &v)) != 0) {
@@ -721,10 +639,10 @@ config_opt_line(CONFIG *cfg, const char *optstr)
ret = 0;
break;
}
- ret = config_opt(cfg, &k, &v);
+ ret = config_opt(wtperf, &k, &v);
}
if ((t_ret = scan->close(scan)) != 0) {
- lprintf(cfg, ret, 0, "Error in config_scan_end");
+ lprintf(wtperf, ret, 0, "Error in config_scan_end");
if (ret == 0)
ret = t_ret;
}
@@ -733,19 +651,20 @@ config_opt_line(CONFIG *cfg, const char *optstr)
}
/*
- * config_opt_str --
- * Set a single string config option.
+ * config_opt_name_value --
+ * Set a name/value configuration pair.
*/
int
-config_opt_str(CONFIG *cfg, const char *name, const char *value)
+config_opt_name_value(WTPERF *wtperf, const char *name, const char *value)
{
+ size_t len;
int ret;
char *optstr;
-
/* name="value" */
- optstr = dmalloc(strlen(name) + strlen(value) + 4);
- sprintf(optstr, "%s=\"%s\"", name, value);
- ret = config_opt_line(cfg, optstr);
+ len = strlen(name) + strlen(value) + 4;
+ optstr = dmalloc(len);
+ snprintf(optstr, len, "%s=\"%s\"", name, value);
+ ret = config_opt_str(wtperf, optstr);
free(optstr);
return (ret);
}
@@ -755,60 +674,63 @@ config_opt_str(CONFIG *cfg, const char *name, const char *value)
* Configuration sanity checks.
*/
int
-config_sanity(CONFIG *cfg)
+config_sanity(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
WORKLOAD *workp;
u_int i;
+ opts = wtperf->opts;
+
/* Various intervals should be less than the run-time. */
- if (cfg->run_time > 0 &&
- ((cfg->checkpoint_threads != 0 &&
- cfg->checkpoint_interval > cfg->run_time) ||
- cfg->report_interval > cfg->run_time ||
- cfg->sample_interval > cfg->run_time)) {
+ if (opts->run_time > 0 &&
+ ((opts->checkpoint_threads != 0 &&
+ opts->checkpoint_interval > opts->run_time) ||
+ opts->report_interval > opts->run_time ||
+ opts->sample_interval > opts->run_time)) {
fprintf(stderr, "interval value longer than the run-time\n");
return (EINVAL);
}
/* The maximum is here to keep file name construction simple. */
- if (cfg->table_count < 1 || cfg->table_count > 99999) {
+ if (opts->table_count < 1 || opts->table_count > 99999) {
fprintf(stderr,
"invalid table count, less than 1 or greater than 99999\n");
return (EINVAL);
}
- if (cfg->database_count < 1 || cfg->database_count > 99) {
+ if (opts->database_count < 1 || opts->database_count > 99) {
fprintf(stderr,
"invalid database count, less than 1 or greater than 99\n");
return (EINVAL);
}
- if (cfg->pareto > 100) {
+ if (opts->pareto > 100) {
fprintf(stderr,
"Invalid pareto distribution - should be a percentage\n");
return (EINVAL);
}
- if (cfg->value_sz_max < cfg->value_sz) {
- if (F_ISSET(cfg, CFG_GROW)) {
+ if (opts->value_sz_max < opts->value_sz) {
+ if (F_ISSET(wtperf, CFG_GROW)) {
fprintf(stderr, "value_sz_max %" PRIu32
" must be greater than or equal to value_sz %"
- PRIu32 "\n", cfg->value_sz_max, cfg->value_sz);
+ PRIu32 "\n", opts->value_sz_max, opts->value_sz);
return (EINVAL);
} else
- cfg->value_sz_max = cfg->value_sz;
+ opts->value_sz_max = opts->value_sz;
}
- if (cfg->value_sz_min > cfg->value_sz) {
- if (F_ISSET(cfg, CFG_SHRINK)) {
+ if (opts->value_sz_min > opts->value_sz) {
+ if (F_ISSET(wtperf, CFG_SHRINK)) {
fprintf(stderr, "value_sz_min %" PRIu32
" must be less than or equal to value_sz %"
- PRIu32 "\n", cfg->value_sz_min, cfg->value_sz);
+ PRIu32 "\n", opts->value_sz_min, opts->value_sz);
return (EINVAL);
} else
- cfg->value_sz_min = cfg->value_sz;
+ opts->value_sz_min = opts->value_sz;
}
- if (cfg->readonly && cfg->workload != NULL)
- for (i = 0, workp = cfg->workload;
- i < cfg->workload_cnt; ++i, ++workp)
+ if (opts->readonly && wtperf->workload != NULL)
+ for (i = 0, workp = wtperf->workload;
+ i < wtperf->workload_cnt; ++i, ++workp)
if (workp->insert != 0 || workp->update != 0 ||
workp->truncate != 0) {
fprintf(stderr,
@@ -824,21 +746,21 @@ config_sanity(CONFIG *cfg)
* Consolidate repeated configuration settings so that it only appears
* once in the configuration output file.
*/
-void
-config_consolidate(CONFIG *cfg)
+static void
+config_consolidate(CONFIG_OPTS *opts)
{
CONFIG_QUEUE_ENTRY *conf_line, *test_line, *tmp;
char *string_key;
/*
- * This loop iterates over the config queue and for entry checks if an
- * entry later in the queue has the same key. If a match is found then
- * the current queue entry is removed and we continue.
+ * This loop iterates over the config queue and for each entry checks if
+ * a later queue entry has the same key. If there's a match, the current
+ * queue entry is removed and we continue.
*/
- conf_line = TAILQ_FIRST(&cfg->config_head);
+ conf_line = TAILQ_FIRST(&opts->config_head);
while (conf_line != NULL) {
string_key = strchr(conf_line->string, '=');
- tmp = test_line = TAILQ_NEXT(conf_line, c);
+ tmp = test_line = TAILQ_NEXT(conf_line, q);
while (test_line != NULL) {
/*
* The + 1 here forces the '=' sign to be matched
@@ -849,89 +771,75 @@ config_consolidate(CONFIG *cfg)
if (strncmp(conf_line->string, test_line->string,
(size_t)((string_key - conf_line->string) + 1))
== 0) {
- TAILQ_REMOVE(&cfg->config_head, conf_line, c);
+ TAILQ_REMOVE(&opts->config_head, conf_line, q);
free(conf_line->string);
free(conf_line);
break;
}
- test_line = TAILQ_NEXT(test_line, c);
+ test_line = TAILQ_NEXT(test_line, q);
}
conf_line = tmp;
}
}
/*
- * config_to_file --
+ * config_opt_log --
* Write the final config used in this execution to a file.
*/
void
-config_to_file(CONFIG *cfg)
+config_opt_log(CONFIG_OPTS *opts, const char *path)
{
CONFIG_QUEUE_ENTRY *config_line;
FILE *fp;
- size_t req_len;
- char *path;
- fp = NULL;
+ testutil_checkfmt(((fp = fopen(path, "w")) == NULL), "%s", path);
- /* Backup the config */
- req_len = strlen(cfg->home) + strlen("/CONFIG.wtperf") + 1;
- path = dcalloc(req_len, 1);
- snprintf(path, req_len, "%s/CONFIG.wtperf", cfg->home);
- if ((fp = fopen(path, "w")) == NULL) {
- lprintf(cfg, errno, 0, "%s", path);
- goto err;
- }
+ config_consolidate(opts);
- /* Print the config dump */
- fprintf(fp,"# Warning. This config includes "
+ fprintf(fp,"# Warning: This config includes "
"unwritten, implicit configuration defaults.\n"
"# Changes to those values may cause differences in behavior.\n");
- config_consolidate(cfg);
- config_line = TAILQ_FIRST(&cfg->config_head);
- while (config_line != NULL) {
+ TAILQ_FOREACH(config_line, &opts->config_head, q)
fprintf(fp, "%s\n", config_line->string);
- config_line = TAILQ_NEXT(config_line, c);
- }
-
-err: free(path);
- if (fp != NULL)
- (void)fclose(fp);
+ testutil_check(fclose(fp));
}
/*
- * config_print --
+ * config_opt_print --
* Print out the configuration in verbose mode.
*/
void
-config_print(CONFIG *cfg)
+config_opt_print(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
WORKLOAD *workp;
u_int i;
+ opts = wtperf->opts;
+
printf("Workload configuration:\n");
- printf("\t" "Home: %s\n", cfg->home);
- printf("\t" "Table name: %s\n", cfg->table_name);
- printf("\t" "Connection configuration: %s\n", cfg->conn_config);
- if (cfg->sess_config != NULL)
- printf("\t" "Session configuration: %s\n", cfg->sess_config);
+ printf("\t" "Home: %s\n", wtperf->home);
+ printf("\t" "Table name: %s\n", opts->table_name);
+ printf("\t" "Connection configuration: %s\n", opts->conn_config);
+ if (opts->sess_config != NULL)
+ printf("\t" "Session configuration: %s\n", opts->sess_config);
printf("\t%s table: %s\n",
- cfg->create ? "Creating new" : "Using existing",
- cfg->table_config);
+ opts->create ? "Creating new" : "Using existing",
+ opts->table_config);
printf("\t" "Key size: %" PRIu32 ", value size: %" PRIu32 "\n",
- cfg->key_sz, cfg->value_sz);
- if (cfg->create)
+ opts->key_sz, opts->value_sz);
+ if (opts->create)
printf("\t" "Populate threads: %" PRIu32 ", inserting %" PRIu32
" rows\n",
- cfg->populate_threads, cfg->icount);
+ opts->populate_threads, opts->icount);
printf("\t" "Workload seconds, operations: %" PRIu32 ", %" PRIu32 "\n",
- cfg->run_time, cfg->run_ops);
- if (cfg->workload != NULL) {
+ opts->run_time, opts->run_ops);
+ if (wtperf->workload != NULL) {
printf("\t" "Workload configuration(s):\n");
- for (i = 0, workp = cfg->workload;
- i < cfg->workload_cnt; ++i, ++workp)
+ for (i = 0, workp = wtperf->workload;
+ i < wtperf->workload_cnt; ++i, ++workp)
printf("\t\t%" PRId64 " threads (inserts=%" PRId64
", reads=%" PRId64 ", updates=%" PRId64
", truncates=% " PRId64 ")\n",
@@ -941,11 +849,11 @@ config_print(CONFIG *cfg)
}
printf("\t" "Checkpoint threads, interval: %" PRIu32 ", %" PRIu32 "\n",
- cfg->checkpoint_threads, cfg->checkpoint_interval);
- printf("\t" "Reporting interval: %" PRIu32 "\n", cfg->report_interval);
- printf("\t" "Sampling interval: %" PRIu32 "\n", cfg->sample_interval);
+ opts->checkpoint_threads, opts->checkpoint_interval);
+ printf("\t" "Reporting interval: %" PRIu32 "\n", opts->report_interval);
+ printf("\t" "Sampling interval: %" PRIu32 "\n", opts->sample_interval);
- printf("\t" "Verbosity: %" PRIu32 "\n", cfg->verbose);
+ printf("\t" "Verbosity: %" PRIu32 "\n", opts->verbose);
}
/*
@@ -975,10 +883,10 @@ pretty_print(const char *p, const char *indent)
* config_opt_usage --
* Configuration usage error message.
*/
-static void
+void
config_opt_usage(void)
{
- size_t i, nopt;
+ size_t i;
const char *defaultval, *typestr;
pretty_print(
@@ -988,11 +896,10 @@ config_opt_usage(void)
"String values must be enclosed in \" quotes, boolean values must "
"be either true or false.\n", NULL);
- nopt = sizeof(config_opts)/sizeof(config_opts[0]);
- for (i = 0; i < nopt; i++) {
- defaultval = config_opts[i].defaultval;
+ for (i = 0; i < WT_ELEMENTS(config_opts_desc); i++) {
+ defaultval = config_opts_desc[i].defaultval;
typestr = "string";
- switch (config_opts[i].type) {
+ switch (config_opts_desc[i].type) {
case BOOL_TYPE:
typestr = "boolean";
if (strcmp(defaultval, "0") == 0)
@@ -1011,28 +918,7 @@ config_opt_usage(void)
break;
}
printf("%s (%s, default=%s)\n",
- config_opts[i].name, typestr, defaultval);
- pretty_print(config_opts[i].description, "\t");
+ config_opts_desc[i].name, typestr, defaultval);
+ pretty_print(config_opts_desc[i].description, "\t");
}
}
-
-/*
- * usage --
- * wtperf usage print, no error.
- */
-void
-usage(void)
-{
- printf("wtperf [-C config] "
- "[-H mount] [-h home] [-O file] [-o option] [-T config]\n");
- printf("\t-C <string> additional connection configuration\n");
- printf("\t (added to option conn_config)\n");
- printf("\t-H <mount> configure Helium volume mount point\n");
- printf("\t-h <string> Wired Tiger home must exist, default WT_TEST\n");
- printf("\t-O <file> file contains options as listed below\n");
- printf("\t-o option=val[,option=val,...] set options listed below\n");
- printf("\t-T <string> additional table configuration\n");
- printf("\t (added to option table_config)\n");
- printf("\n");
- config_opt_usage();
-}
diff --git a/src/third_party/wiredtiger/bench/wtperf/config_opt.h b/src/third_party/wiredtiger/bench/wtperf/config_opt.h
index b7eff8e143f..3f1ab642227 100644
--- a/src/third_party/wiredtiger/bench/wtperf/config_opt.h
+++ b/src/third_party/wiredtiger/bench/wtperf/config_opt.h
@@ -37,3 +37,17 @@ typedef struct {
CONFIG_OPT_TYPE type;
size_t offset;
} CONFIG_OPT;
+
+typedef struct __config_queue_entry {
+ char *string;
+ TAILQ_ENTRY(__config_queue_entry) q;
+} CONFIG_QUEUE_ENTRY;
+
+typedef struct { /* Option structure */
+#define OPT_DECLARE_STRUCT
+#include "wtperf_opt.i"
+#undef OPT_DECLARE_STRUCT
+
+ /* Queue head to save a copy of the config to be output */
+ TAILQ_HEAD(__config_qh, __config_queue_entry) config_head;
+} CONFIG_OPTS;
diff --git a/src/third_party/wiredtiger/bench/wtperf/doxy.c b/src/third_party/wiredtiger/bench/wtperf/doxy.c
deleted file mode 100644
index 26d73168ef2..00000000000
--- a/src/third_party/wiredtiger/bench/wtperf/doxy.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- * Public Domain 2014-2016 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.
- */
-
-#include <string.h>
-#include <stdio.h>
-
-#include "config_opt.h"
-
-static const CONFIG_OPT config_opts[] = {
-#define OPT_DEFINE_DOXYGEN
-#include "wtperf_opt.i"
-#undef OPT_DEFINE_DOXYGEN
-};
-
-/*
- * pretty_print --
- * Print out lines of text for a 80 character window.
- */
-static void
-pretty_print(const char *p, const char *indent)
-{
- const char *t;
-
- for (;; p = t + 1) {
- if (strlen(p) <= 70)
- break;
- for (t = p + 70; t > p && *t != ' '; --t)
- ;
- if (t == p) /* No spaces? */
- break;
- printf("%s%.*s\n",
- indent == NULL ? "" : indent, (int)(t - p), p);
- }
- if (*p != '\0')
- printf("%s%s\n", indent == NULL ? "" : indent, p);
-}
-
-/*
- * config_doxygen --
- * Output the configuration information for doxgen.
- */
-static void
-config_doxygen(void)
-{
- size_t i, nopt;
- const char *defaultval, *typestr;
-
- nopt = sizeof(config_opts)/sizeof(config_opts[0]);
- for (i = 0; i < nopt; i++) {
- defaultval = config_opts[i].defaultval;
- typestr = "string";
- switch (config_opts[i].type) {
- case BOOL_TYPE:
- typestr = "boolean";
- if (strcmp(defaultval, "0") == 0)
- defaultval = "false";
- else
- defaultval = "true";
- break;
- case CONFIG_STRING_TYPE:
- case STRING_TYPE:
- break;
- case INT_TYPE:
- typestr = "int";
- break;
- case UINT32_TYPE:
- typestr = "unsigned int";
- break;
- }
- printf("@par %s (%s, default=%s)\n",
- config_opts[i].name, typestr, defaultval);
- pretty_print(config_opts[i].description, NULL);
- }
-}
-
-/*
- * config_doxygen --
- * A standalone program to output the configuration options in a doxygen
- * format.
- */
-int
-main()
-{
- config_doxygen();
- return (0);
-}
diff --git a/src/third_party/wiredtiger/bench/wtperf/idle_table_cycle.c b/src/third_party/wiredtiger/bench/wtperf/idle_table_cycle.c
index 3c079bb560f..13fa55e86f5 100644
--- a/src/third_party/wiredtiger/bench/wtperf/idle_table_cycle.c
+++ b/src/third_party/wiredtiger/bench/wtperf/idle_table_cycle.c
@@ -29,28 +29,25 @@
#include "wtperf.h"
static int
-check_timing(CONFIG *cfg,
+check_timing(WTPERF *wtperf,
const char *name, struct timespec start, struct timespec *stop)
{
+ CONFIG_OPTS *opts;
uint64_t last_interval;
- int ret;
- if ((ret = __wt_epoch(NULL, stop)) != 0) {
- lprintf(cfg, ret, 0,
- "Get time failed in cycle_idle_tables.");
- cfg->error = ret;
- return (ret);
- }
+ opts = wtperf->opts;
+
+ __wt_epoch(NULL, stop);
last_interval = (uint64_t)(WT_TIMEDIFF_SEC(*stop, start));
- if (last_interval > cfg->idle_table_cycle) {
- lprintf(cfg, ret, 0,
+ if (last_interval > opts->idle_table_cycle) {
+ lprintf(wtperf, ETIMEDOUT, 0,
"Cycling idle table failed because %s took %" PRIu64
" seconds which is longer than configured acceptable"
" maximum of %" PRIu32 ".",
- name, last_interval, cfg->idle_table_cycle);
- cfg->error = ETIMEDOUT;
+ name, last_interval, opts->idle_table_cycle);
+ wtperf->error = true;
return (ETIMEDOUT);
}
return (0);
@@ -64,64 +61,62 @@ static void *
cycle_idle_tables(void *arg)
{
struct timespec start, stop;
- CONFIG *cfg;
- WT_SESSION *session;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf;
WT_CURSOR *cursor;
+ WT_SESSION *session;
int cycle_count, ret;
char uri[512];
- cfg = (CONFIG *)arg;
+ wtperf = (WTPERF *)arg;
+ opts = wtperf->opts;
cycle_count = 0;
- if ((ret = cfg->conn->open_session(
- cfg->conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0,
- "Error opening a session on %s", cfg->home);
+ if ((ret = wtperf->conn->open_session(
+ wtperf->conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "Error opening a session on %s", wtperf->home);
return (NULL);
}
- for (cycle_count = 0; cfg->idle_cycle_run; ++cycle_count) {
- snprintf(uri, 512, "%s_cycle%07d", cfg->uris[0], cycle_count);
+ for (cycle_count = 0; wtperf->idle_cycle_run; ++cycle_count) {
+ snprintf(uri, sizeof(uri),
+ "%s_cycle%07d", wtperf->uris[0], cycle_count);
/* Don't busy cycle in this loop. */
__wt_sleep(1, 0);
/* Setup a start timer. */
- if ((ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0,
- "Get time failed in cycle_idle_tables.");
- cfg->error = ret;
- return (NULL);
- }
+ __wt_epoch(NULL, &start);
/* Create a table. */
if ((ret = session->create(
- session, uri, cfg->table_config)) != 0) {
+ session, uri, opts->table_config)) != 0) {
if (ret == EBUSY)
continue;
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Table create failed in cycle_idle_tables.");
- cfg->error = ret;
+ wtperf->error = true;
return (NULL);
}
- if (check_timing(cfg, "create", start, &stop) != 0)
+ if (check_timing(wtperf, "create", start, &stop) != 0)
return (NULL);
start = stop;
/* Open and close cursor. */
if ((ret = session->open_cursor(
session, uri, NULL, NULL, &cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Cursor open failed in cycle_idle_tables.");
- cfg->error = ret;
+ wtperf->error = true;
return (NULL);
}
if ((ret = cursor->close(cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Cursor close failed in cycle_idle_tables.");
- cfg->error = ret;
+ wtperf->error = true;
return (NULL);
}
- if (check_timing(cfg, "cursor", start, &stop) != 0)
+ if (check_timing(wtperf, "cursor", start, &stop) != 0)
return (NULL);
start = stop;
@@ -134,12 +129,12 @@ cycle_idle_tables(void *arg)
__wt_sleep(1, 0);
if (ret != 0 && ret != EBUSY) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Table drop failed in cycle_idle_tables.");
- cfg->error = ret;
+ wtperf->error = true;
return (NULL);
}
- if (check_timing(cfg, "drop", start, &stop) != 0)
+ if (check_timing(wtperf, "drop", start, &stop) != 0)
return (NULL);
}
@@ -154,20 +149,23 @@ cycle_idle_tables(void *arg)
* initialization isn't necessary.
*/
int
-start_idle_table_cycle(CONFIG *cfg, pthread_t *idle_table_cycle_thread)
+start_idle_table_cycle(WTPERF *wtperf, pthread_t *idle_table_cycle_thread)
{
+ CONFIG_OPTS *opts;
pthread_t thread_id;
int ret;
- if (cfg->idle_table_cycle == 0)
+ opts = wtperf->opts;
+
+ if (opts->idle_table_cycle == 0)
return (0);
- cfg->idle_cycle_run = true;
+ wtperf->idle_cycle_run = true;
if ((ret = pthread_create(
- &thread_id, NULL, cycle_idle_tables, cfg)) != 0) {
- lprintf(
- cfg, ret, 0, "Error creating idle table cycle thread.");
- cfg->idle_cycle_run = false;
+ &thread_id, NULL, cycle_idle_tables, wtperf)) != 0) {
+ lprintf(wtperf,
+ ret, 0, "Error creating idle table cycle thread.");
+ wtperf->idle_cycle_run = false;
return (ret);
}
*idle_table_cycle_thread = thread_id;
@@ -176,17 +174,20 @@ start_idle_table_cycle(CONFIG *cfg, pthread_t *idle_table_cycle_thread)
}
int
-stop_idle_table_cycle(CONFIG *cfg, pthread_t idle_table_cycle_thread)
+stop_idle_table_cycle(WTPERF *wtperf, pthread_t idle_table_cycle_thread)
{
+ CONFIG_OPTS *opts;
int ret;
- if (cfg->idle_table_cycle == 0 || !cfg->idle_cycle_run)
+ opts = wtperf->opts;
+
+ if (opts->idle_table_cycle == 0 || !wtperf->idle_cycle_run)
return (0);
- cfg->idle_cycle_run = false;
+ wtperf->idle_cycle_run = false;
if ((ret = pthread_join(idle_table_cycle_thread, NULL)) != 0) {
lprintf(
- cfg, ret, 0, "Error joining idle table cycle thread.");
+ wtperf, ret, 0, "Error joining idle table cycle thread.");
return (ret);
}
return (0);
diff --git a/src/third_party/wiredtiger/bench/wtperf/misc.c b/src/third_party/wiredtiger/bench/wtperf/misc.c
index 2821216f240..24b3323a49a 100644
--- a/src/third_party/wiredtiger/bench/wtperf/misc.c
+++ b/src/third_party/wiredtiger/bench/wtperf/misc.c
@@ -30,31 +30,34 @@
/* Setup the logging output mechanism. */
int
-setup_log_file(CONFIG *cfg)
+setup_log_file(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
+ size_t len;
int ret;
char *fname;
+ opts = wtperf->opts;
ret = 0;
- if (cfg->verbose < 1)
+ if (opts->verbose < 1)
return (0);
- fname = dcalloc(strlen(cfg->monitor_dir) +
- strlen(cfg->table_name) + strlen(".stat") + 2, 1);
-
- sprintf(fname, "%s/%s.stat", cfg->monitor_dir, cfg->table_name);
- cfg->logf = fopen(fname, "w");
- if (cfg->logf == NULL) {
+ len = strlen(wtperf->monitor_dir) +
+ strlen(opts->table_name) + strlen(".stat") + 2;
+ fname = dmalloc(len);
+ snprintf(fname, len,
+ "%s/%s.stat", wtperf->monitor_dir, opts->table_name);
+ if ((wtperf->logf = fopen(fname, "w")) == NULL) {
ret = errno;
fprintf(stderr, "%s: %s\n", fname, strerror(ret));
}
free(fname);
- if (cfg->logf == NULL)
+ if (wtperf->logf == NULL)
return (ret);
/* Use line buffering for the log file. */
- __wt_stream_set_line_buffer(cfg->logf);
+ __wt_stream_set_line_buffer(wtperf->logf);
return (0);
}
@@ -62,17 +65,20 @@ setup_log_file(CONFIG *cfg)
* Log printf - output a log message.
*/
void
-lprintf(const CONFIG *cfg, int err, uint32_t level, const char *fmt, ...)
+lprintf(const WTPERF *wtperf, int err, uint32_t level, const char *fmt, ...)
{
+ CONFIG_OPTS *opts;
va_list ap;
- if (err == 0 && level <= cfg->verbose) {
+ opts = wtperf->opts;
+
+ if (err == 0 && level <= opts->verbose) {
va_start(ap, fmt);
- vfprintf(cfg->logf, fmt, ap);
+ vfprintf(wtperf->logf, fmt, ap);
va_end(ap);
- fprintf(cfg->logf, "\n");
+ fprintf(wtperf->logf, "\n");
- if (level < cfg->verbose) {
+ if (level < opts->verbose) {
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
@@ -87,11 +93,11 @@ lprintf(const CONFIG *cfg, int err, uint32_t level, const char *fmt, ...)
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, " Error: %s\n", wiredtiger_strerror(err));
- if (cfg->logf != NULL) {
+ if (wtperf->logf != NULL) {
va_start(ap, fmt);
- vfprintf(cfg->logf, fmt, ap);
+ vfprintf(wtperf->logf, fmt, ap);
va_end(ap);
- fprintf(cfg->logf, " Error: %s\n", wiredtiger_strerror(err));
+ fprintf(wtperf->logf, " Error: %s\n", wiredtiger_strerror(err));
}
/* Never attempt to continue if we got a panic from WiredTiger. */
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-50r50u.wtperf b/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-50r50u.wtperf
index 06745bf7cca..536127f0dd8 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-50r50u.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-50r50u.wtperf
@@ -10,6 +10,9 @@ create=false
compression="snappy"
sess_config="isolation=snapshot"
table_count=2
+# close_conn as false allows this test to close/finish faster, but if running
+# as the set, the next test will need to run recovery.
+close_conn=false
key_sz=40
value_sz=120
max_latency=2000
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-80r20u.wtperf b/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-80r20u.wtperf
index 77edbfb4941..d6218c44af0 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-80r20u.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/runners/500m-btree-80r20u.wtperf
@@ -8,6 +8,9 @@
conn_config="cache_size=16G,checkpoint=(wait=60,log_size=2GB),session_max=20000,log=(enabled),statistics=(fast),statistics_log=(wait=30,json),eviction=(threads_max=4)"
create=false
compression="snappy"
+# close_conn as false allows this test to close/finish faster, but if running
+# as the set, the next test will need to run recovery.
+close_conn=false
sess_config="isolation=snapshot
table_count=2
key_sz=40
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/checkpoint-stress.wtperf b/src/third_party/wiredtiger/bench/wtperf/runners/checkpoint-stress.wtperf
index 0c98a0c2db0..bbd3a3ba5ed 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/checkpoint-stress.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/runners/checkpoint-stress.wtperf
@@ -4,6 +4,7 @@ conn_config="cache_size=16GB,eviction=(threads_max=4),log=(enabled=false)"
table_config="leaf_page_max=32k,internal_page_max=16k,allocation_size=4k,split_pct=90,type=file"
# Enough data to fill the cache. 150 million 1k records results in two ~11GB
# tables
+close_conn=false
icount=150000000
create=true
compression="snappy"
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/evict-btree-stress-multi.wtperf b/src/third_party/wiredtiger/bench/wtperf/runners/evict-btree-stress-multi.wtperf
index 9699b9ae3bb..a5a29f66fa0 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/evict-btree-stress-multi.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/runners/evict-btree-stress-multi.wtperf
@@ -1,6 +1,7 @@
conn_config="cache_size=1G,eviction=(threads_max=4),session_max=2000"
table_config="type=file"
table_count=100
+close_conn=false
icount=100000000
report_interval=5
run_time=600
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/btree-split-stress.wtperf b/src/third_party/wiredtiger/bench/wtperf/stress/btree-split-stress.wtperf
index deb8c70d12f..deb8c70d12f 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/btree-split-stress.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/stress/btree-split-stress.wtperf
diff --git a/src/third_party/wiredtiger/bench/wtperf/runners/shared-cache-stress.wtperf b/src/third_party/wiredtiger/bench/wtperf/stress/shared-cache-stress.wtperf
index 87d14f4f5c1..87d14f4f5c1 100644
--- a/src/third_party/wiredtiger/bench/wtperf/runners/shared-cache-stress.wtperf
+++ b/src/third_party/wiredtiger/bench/wtperf/stress/shared-cache-stress.wtperf
diff --git a/src/third_party/wiredtiger/bench/wtperf/track.c b/src/third_party/wiredtiger/bench/wtperf/track.c
index b3f4847d9d0..822bdaa4b4a 100644
--- a/src/third_party/wiredtiger/bench/wtperf/track.c
+++ b/src/third_party/wiredtiger/bench/wtperf/track.c
@@ -32,16 +32,18 @@
* Return total insert operations for the populate phase.
*/
uint64_t
-sum_pop_ops(CONFIG *cfg)
+sum_pop_ops(WTPERF *wtperf)
{
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF_THREAD *thread;
uint64_t total;
u_int i;
+ opts = wtperf->opts;
total = 0;
- for (i = 0, thread = cfg->popthreads;
- thread != NULL && i < cfg->populate_threads; ++i, ++thread)
+ for (i = 0, thread = wtperf->popthreads;
+ thread != NULL && i < opts->populate_threads; ++i, ++thread)
total += thread->insert.ops;
return (total);
}
@@ -50,16 +52,18 @@ sum_pop_ops(CONFIG *cfg)
* Return total checkpoint operations.
*/
uint64_t
-sum_ckpt_ops(CONFIG *cfg)
+sum_ckpt_ops(WTPERF *wtperf)
{
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF_THREAD *thread;
uint64_t total;
u_int i;
+ opts = wtperf->opts;
total = 0;
- for (i = 0, thread = cfg->ckptthreads;
- thread != NULL && i < cfg->checkpoint_threads; ++i, ++thread)
+ for (i = 0, thread = wtperf->ckptthreads;
+ thread != NULL && i < opts->checkpoint_threads; ++i, ++thread)
total += thread->ckpt.ops;
return (total);
}
@@ -68,19 +72,22 @@ sum_ckpt_ops(CONFIG *cfg)
* Return total operations count for the worker threads.
*/
static uint64_t
-sum_ops(CONFIG *cfg, size_t field_offset)
+sum_ops(WTPERF *wtperf, size_t field_offset)
{
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF_THREAD *thread;
uint64_t total;
int64_t i, th_cnt;
+ opts = wtperf->opts;
total = 0;
- if (cfg->popthreads == NULL) {
- thread = cfg->workers;
- th_cnt = cfg->workers_cnt;
+
+ if (wtperf->popthreads == NULL) {
+ thread = wtperf->workers;
+ th_cnt = wtperf->workers_cnt;
} else {
- thread = cfg->popthreads;
- th_cnt = cfg->populate_threads;
+ thread = wtperf->popthreads;
+ th_cnt = opts->populate_threads;
}
for (i = 0; thread != NULL && i < th_cnt; ++i, ++thread)
total += ((TRACK *)((uint8_t *)thread + field_offset))->ops;
@@ -88,24 +95,24 @@ sum_ops(CONFIG *cfg, size_t field_offset)
return (total);
}
uint64_t
-sum_insert_ops(CONFIG *cfg)
+sum_insert_ops(WTPERF *wtperf)
{
- return (sum_ops(cfg, offsetof(CONFIG_THREAD, insert)));
+ return (sum_ops(wtperf, offsetof(WTPERF_THREAD, insert)));
}
uint64_t
-sum_read_ops(CONFIG *cfg)
+sum_read_ops(WTPERF *wtperf)
{
- return (sum_ops(cfg, offsetof(CONFIG_THREAD, read)));
+ return (sum_ops(wtperf, offsetof(WTPERF_THREAD, read)));
}
uint64_t
-sum_truncate_ops(CONFIG *cfg)
+sum_truncate_ops(WTPERF *wtperf)
{
- return (sum_ops(cfg, offsetof(CONFIG_THREAD, truncate)));
+ return (sum_ops(wtperf, offsetof(WTPERF_THREAD, truncate)));
}
uint64_t
-sum_update_ops(CONFIG *cfg)
+sum_update_ops(WTPERF *wtperf)
{
- return (sum_ops(cfg, offsetof(CONFIG_THREAD, update)));
+ return (sum_ops(wtperf, offsetof(WTPERF_THREAD, update)));
}
/*
@@ -114,25 +121,27 @@ sum_update_ops(CONFIG *cfg)
* particular operation.
*/
static void
-latency_op(CONFIG *cfg,
+latency_op(WTPERF *wtperf,
size_t field_offset, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
{
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
TRACK *track;
+ WTPERF_THREAD *thread;
uint64_t ops, latency, tmp;
int64_t i, th_cnt;
uint32_t max, min;
+ opts = wtperf->opts;
ops = latency = 0;
max = 0;
min = UINT32_MAX;
- if (cfg->popthreads == NULL) {
- thread = cfg->workers;
- th_cnt = cfg->workers_cnt;
+ if (wtperf->popthreads == NULL) {
+ thread = wtperf->workers;
+ th_cnt = wtperf->workers_cnt;
} else {
- thread = cfg->popthreads;
- th_cnt = cfg->populate_threads;
+ thread = wtperf->popthreads;
+ th_cnt = opts->populate_threads;
}
for (i = 0; thread != NULL && i < th_cnt; ++i, ++thread) {
track = (TRACK *)((uint8_t *)thread + field_offset);
@@ -160,11 +169,11 @@ latency_op(CONFIG *cfg,
}
}
void
-latency_read(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
+latency_read(WTPERF *wtperf, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
{
static uint32_t last_avg = 0, last_max = 0, last_min = 0;
- latency_op(cfg, offsetof(CONFIG_THREAD, read), avgp, minp, maxp);
+ latency_op(wtperf, offsetof(WTPERF_THREAD, read), avgp, minp, maxp);
/*
* If nothing happened, graph the average, minimum and maximum as they
@@ -181,11 +190,11 @@ latency_read(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
}
}
void
-latency_insert(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
+latency_insert(WTPERF *wtperf, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
{
static uint32_t last_avg = 0, last_max = 0, last_min = 0;
- latency_op(cfg, offsetof(CONFIG_THREAD, insert), avgp, minp, maxp);
+ latency_op(wtperf, offsetof(WTPERF_THREAD, insert), avgp, minp, maxp);
/*
* If nothing happened, graph the average, minimum and maximum as they
@@ -202,11 +211,11 @@ latency_insert(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
}
}
void
-latency_update(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
+latency_update(WTPERF *wtperf, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
{
static uint32_t last_avg = 0, last_max = 0, last_min = 0;
- latency_op(cfg, offsetof(CONFIG_THREAD, update), avgp, minp, maxp);
+ latency_op(wtperf, offsetof(WTPERF_THREAD, update), avgp, minp, maxp);
/*
* If nothing happened, graph the average, minimum and maximum as they
@@ -228,17 +237,17 @@ latency_update(CONFIG *cfg, uint32_t *avgp, uint32_t *minp, uint32_t *maxp)
* Sum latency for a set of threads.
*/
static void
-sum_latency(CONFIG *cfg, size_t field_offset, TRACK *total)
+sum_latency(WTPERF *wtperf, size_t field_offset, TRACK *total)
{
- CONFIG_THREAD *thread;
+ WTPERF_THREAD *thread;
TRACK *trk;
int64_t i;
u_int j;
memset(total, 0, sizeof(*total));
- for (i = 0, thread = cfg->workers;
- thread != NULL && i < cfg->workers_cnt; ++i, ++thread) {
+ for (i = 0, thread = wtperf->workers;
+ thread != NULL && i < wtperf->workers_cnt; ++i, ++thread) {
trk = (TRACK *)((uint8_t *)thread + field_offset);
for (j = 0; j < ELEMENTS(trk->us); ++j) {
@@ -256,32 +265,33 @@ sum_latency(CONFIG *cfg, size_t field_offset, TRACK *total)
}
}
static void
-sum_insert_latency(CONFIG *cfg, TRACK *total)
+sum_insert_latency(WTPERF *wtperf, TRACK *total)
{
- sum_latency(cfg, offsetof(CONFIG_THREAD, insert), total);
+ sum_latency(wtperf, offsetof(WTPERF_THREAD, insert), total);
}
static void
-sum_read_latency(CONFIG *cfg, TRACK *total)
+sum_read_latency(WTPERF *wtperf, TRACK *total)
{
- sum_latency(cfg, offsetof(CONFIG_THREAD, read), total);
+ sum_latency(wtperf, offsetof(WTPERF_THREAD, read), total);
}
static void
-sum_update_latency(CONFIG *cfg, TRACK *total)
+sum_update_latency(WTPERF *wtperf, TRACK *total)
{
- sum_latency(cfg, offsetof(CONFIG_THREAD, update), total);
+ sum_latency(wtperf, offsetof(WTPERF_THREAD, update), total);
}
static void
-latency_print_single(CONFIG *cfg, TRACK *total, const char *name)
+latency_print_single(WTPERF *wtperf, TRACK *total, const char *name)
{
FILE *fp;
u_int i;
uint64_t cumops;
char path[1024];
- snprintf(path, sizeof(path), "%s/latency.%s", cfg->monitor_dir, name);
+ snprintf(path, sizeof(path),
+ "%s/latency.%s", wtperf->monitor_dir, name);
if ((fp = fopen(path, "w")) == NULL) {
- lprintf(cfg, errno, 0, "%s", path);
+ lprintf(wtperf, errno, 0, "%s", path);
return;
}
@@ -317,14 +327,14 @@ latency_print_single(CONFIG *cfg, TRACK *total, const char *name)
}
void
-latency_print(CONFIG *cfg)
+latency_print(WTPERF *wtperf)
{
TRACK total;
- sum_insert_latency(cfg, &total);
- latency_print_single(cfg, &total, "insert");
- sum_read_latency(cfg, &total);
- latency_print_single(cfg, &total, "read");
- sum_update_latency(cfg, &total);
- latency_print_single(cfg, &total, "update");
+ sum_insert_latency(wtperf, &total);
+ latency_print_single(wtperf, &total, "insert");
+ sum_read_latency(wtperf, &total);
+ latency_print_single(wtperf, &total, "read");
+ sum_update_latency(wtperf, &total);
+ latency_print_single(wtperf, &total, "update");
}
diff --git a/src/third_party/wiredtiger/bench/wtperf/wtperf.c b/src/third_party/wiredtiger/bench/wtperf/wtperf.c
index bf6b156bb69..8c7f0053388 100644
--- a/src/third_party/wiredtiger/bench/wtperf/wtperf.c
+++ b/src/third_party/wiredtiger/bench/wtperf/wtperf.c
@@ -31,78 +31,38 @@
/* Default values. */
#define DEFAULT_HOME "WT_TEST"
#define DEFAULT_MONITOR_DIR "WT_TEST"
-static const CONFIG default_cfg = {
- NULL, /* home */
- NULL, /* monitor dir */
- NULL, /* partial logging */
- NULL, /* reopen config */
- NULL, /* base_uri */
- NULL, /* log_table_uri */
- NULL, /* uris */
- NULL, /* conn */
- NULL, /* logf */
- NULL, /* async */
- NULL, NULL, /* compressor ext, blk */
- NULL, NULL, /* populate, checkpoint threads */
-
- NULL, /* worker threads */
- 0, /* worker thread count */
- NULL, /* workloads */
- 0, /* workload count */
- 0, /* use_asyncops */
- 0, /* checkpoint operations */
- 0, /* insert operations */
- 0, /* read operations */
- 0, /* truncate operations */
- 0, /* update operations */
- 0, /* insert key */
- 0, /* log like table key */
- 0, /* checkpoint in progress */
- 0, /* thread error */
- 0, /* notify threads to stop */
- 0, /* in warmup phase */
- false, /* Signal for idle cycle thread */
- 0, /* total seconds running */
- 0, /* flags */
- {NULL, NULL}, /* the truncate queue */
- {NULL, NULL}, /* the config queue */
-
-#define OPT_DEFINE_DEFAULT
-#include "wtperf_opt.i"
-#undef OPT_DEFINE_DEFAULT
-};
static const char * const debug_cconfig = "";
static const char * const debug_tconfig = "";
static void *checkpoint_worker(void *);
-static int drop_all_tables(CONFIG *);
-static int execute_populate(CONFIG *);
-static int execute_workload(CONFIG *);
-static int find_table_count(CONFIG *);
+static int drop_all_tables(WTPERF *);
+static int execute_populate(WTPERF *);
+static int execute_workload(WTPERF *);
+static int find_table_count(WTPERF *);
static void *monitor(void *);
static void *populate_thread(void *);
-static void randomize_value(CONFIG_THREAD *, char *);
+static void randomize_value(WTPERF_THREAD *, char *);
static void recreate_dir(const char *);
-static int start_all_runs(CONFIG *);
-static int start_run(CONFIG *);
-static int start_threads(CONFIG *,
- WORKLOAD *, CONFIG_THREAD *, u_int, void *(*)(void *));
-static int stop_threads(CONFIG *, u_int, CONFIG_THREAD *);
+static int start_all_runs(WTPERF *);
+static int start_run(WTPERF *);
+static int start_threads(WTPERF *,
+ WORKLOAD *, WTPERF_THREAD *, u_int, void *(*)(void *));
+static int stop_threads(WTPERF *, u_int, WTPERF_THREAD *);
static void *thread_run_wtperf(void *);
-static void update_value_delta(CONFIG_THREAD *);
+static void update_value_delta(WTPERF_THREAD *);
static void *worker(void *);
-static uint64_t wtperf_rand(CONFIG_THREAD *);
-static uint64_t wtperf_value_range(CONFIG *);
+static uint64_t wtperf_rand(WTPERF_THREAD *);
+static uint64_t wtperf_value_range(WTPERF *);
-#define INDEX_COL_NAMES ",columns=(key,val)"
+#define INDEX_COL_NAMES "columns=(key,val)"
/* Retrieve an ID for the next insert operation. */
static inline uint64_t
-get_next_incr(CONFIG *cfg)
+get_next_incr(WTPERF *wtperf)
{
- return (__wt_atomic_add64(&cfg->insert_key, 1));
+ return (__wt_atomic_add64(&wtperf->insert_key, 1));
}
/*
@@ -110,11 +70,14 @@ get_next_incr(CONFIG *cfg)
* other element in the value buffer.
*/
static void
-randomize_value(CONFIG_THREAD *thread, char *value_buf)
+randomize_value(WTPERF_THREAD *thread, char *value_buf)
{
+ CONFIG_OPTS *opts;
uint8_t *vb;
uint32_t i, max_range, rand_val;
+ opts = thread->wtperf->opts;
+
/*
* Limit how much of the buffer we validate for length, this means
* that only threads that do growing updates will ever make changes to
@@ -123,11 +86,11 @@ randomize_value(CONFIG_THREAD *thread, char *value_buf)
* in this performance sensitive function.
*/
if (thread->workload == NULL || thread->workload->update_delta == 0)
- max_range = thread->cfg->value_sz;
+ max_range = opts->value_sz;
else if (thread->workload->update_delta > 0)
- max_range = thread->cfg->value_sz_max;
+ max_range = opts->value_sz_max;
else
- max_range = thread->cfg->value_sz_min;
+ max_range = opts->value_sz_min;
/*
* Generate a single random value and re-use it. We generally only
@@ -157,17 +120,17 @@ randomize_value(CONFIG_THREAD *thread, char *value_buf)
* Partition data by key ranges.
*/
static uint32_t
-map_key_to_table(CONFIG *cfg, uint64_t k)
+map_key_to_table(CONFIG_OPTS *opts, uint64_t k)
{
- if (cfg->range_partition) {
+ if (opts->range_partition) {
/* Take care to return a result in [0..table_count-1]. */
- if (k > cfg->icount + cfg->random_range)
+ if (k > opts->icount + opts->random_range)
return (0);
return ((uint32_t)((k - 1) /
- ((cfg->icount + cfg->random_range + cfg->table_count - 1) /
- cfg->table_count)));
+ ((opts->icount + opts->random_range +
+ opts->table_count - 1) / opts->table_count)));
} else
- return ((uint32_t)(k % cfg->table_count));
+ return ((uint32_t)(k % opts->table_count));
}
/*
@@ -176,26 +139,28 @@ map_key_to_table(CONFIG *cfg, uint64_t k)
* scratch buffer.
*/
static inline void
-update_value_delta(CONFIG_THREAD *thread)
+update_value_delta(WTPERF_THREAD *thread)
{
- CONFIG *cfg;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf;
char * value;
int64_t delta, len, new_len;
- cfg = thread->cfg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
value = thread->value_buf;
delta = thread->workload->update_delta;
len = (int64_t)strlen(value);
if (delta == INT64_MAX)
delta = __wt_random(&thread->rnd) %
- (cfg->value_sz_max - cfg->value_sz);
+ (opts->value_sz_max - opts->value_sz);
/* Ensure we aren't changing across boundaries */
- if (delta > 0 && len + delta > cfg->value_sz_max)
- delta = cfg->value_sz_max - len;
- else if (delta < 0 && len + delta < cfg->value_sz_min)
- delta = cfg->value_sz_min - len;
+ if (delta > 0 && len + delta > opts->value_sz_max)
+ delta = opts->value_sz_max - len;
+ else if (delta < 0 && len + delta < opts->value_sz_min)
+ delta = opts->value_sz_min - len;
/* Bail if there isn't anything to do */
if (delta == 0)
@@ -206,7 +171,7 @@ update_value_delta(CONFIG_THREAD *thread)
else {
/* Extend the value by the configured amount. */
for (new_len = len;
- new_len < cfg->value_sz_max && new_len - len < delta;
+ new_len < opts->value_sz_max && new_len - len < delta;
new_len++)
value[new_len] = 'a';
}
@@ -215,24 +180,24 @@ update_value_delta(CONFIG_THREAD *thread)
static int
cb_asyncop(WT_ASYNC_CALLBACK *cb, WT_ASYNC_OP *op, int ret, uint32_t flags)
{
- CONFIG *cfg;
- CONFIG_THREAD *thread;
TRACK *trk;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_ASYNC_OPTYPE type;
- char *value;
uint32_t *tables;
int t_ret;
+ char *value;
(void)cb;
(void)flags;
- cfg = NULL; /* -Wconditional-uninitialized */
+ wtperf = NULL; /* -Wconditional-uninitialized */
thread = NULL; /* -Wconditional-uninitialized */
type = op->get_type(op);
if (type != WT_AOP_COMPACT) {
- thread = (CONFIG_THREAD *)op->app_private;
- cfg = thread->cfg;
+ thread = (WTPERF_THREAD *)op->app_private;
+ wtperf = thread->wtperf;
}
trk = NULL;
@@ -249,7 +214,7 @@ cb_asyncop(WT_ASYNC_CALLBACK *cb, WT_ASYNC_OP *op, int ret, uint32_t flags)
if (ret == 0 &&
(t_ret = op->get_value(op, &value)) != 0) {
ret = t_ret;
- lprintf(cfg, ret, 0, "get_value in read.");
+ lprintf(wtperf, ret, 0, "get_value in read.");
goto err;
}
break;
@@ -259,7 +224,8 @@ cb_asyncop(WT_ASYNC_CALLBACK *cb, WT_ASYNC_OP *op, int ret, uint32_t flags)
case WT_AOP_NONE:
case WT_AOP_REMOVE:
/* We never expect this type. */
- lprintf(cfg, ret, 0, "No type in op %" PRIu64, op->get_id(op));
+ lprintf(wtperf,
+ ret, 0, "No type in op %" PRIu64, op->get_id(op));
goto err;
}
@@ -273,15 +239,14 @@ cb_asyncop(WT_ASYNC_CALLBACK *cb, WT_ASYNC_OP *op, int ret, uint32_t flags)
if (type == WT_AOP_COMPACT)
return (0);
if (ret == 0 || (ret == WT_NOTFOUND && type != WT_AOP_INSERT)) {
- if (!cfg->in_warmup)
+ if (!wtperf->in_warmup)
(void)__wt_atomic_add64(&trk->ops, 1);
return (0);
}
err:
/* Panic if error */
- lprintf(cfg, ret, 0, "Error in op %" PRIu64,
- op->get_id(op));
- cfg->error = cfg->stop = 1;
+ lprintf(wtperf, ret, 0, "Error in op %" PRIu64, op->get_id(op));
+ wtperf->error = wtperf->stop = true;
return (1);
}
@@ -353,8 +318,9 @@ op_name(uint8_t *op)
static void *
worker_async(void *arg)
{
- CONFIG *cfg;
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_ASYNC_OP *asyncop;
WT_CONNECTION *conn;
uint64_t next_val;
@@ -362,9 +328,10 @@ worker_async(void *arg)
int ret;
char *key_buf, *value_buf;
- thread = (CONFIG_THREAD *)arg;
- cfg = thread->cfg;
- conn = cfg->conn;
+ thread = (WTPERF_THREAD *)arg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
key_buf = thread->key_buf;
value_buf = thread->value_buf;
@@ -372,7 +339,7 @@ worker_async(void *arg)
op = thread->workload->ops;
op_end = op + sizeof(thread->workload->ops);
- while (!cfg->stop) {
+ while (!wtperf->stop) {
/*
* Generate the next key and setup operation specific
* statistics tracking objects.
@@ -380,10 +347,10 @@ worker_async(void *arg)
switch (*op) {
case WORKER_INSERT:
case WORKER_INSERT_RMW:
- if (cfg->random_range)
+ if (opts->random_range)
next_val = wtperf_rand(thread);
else
- next_val = cfg->icount + get_next_incr(cfg);
+ next_val = opts->icount + get_next_incr(wtperf);
break;
case WORKER_READ:
case WORKER_UPDATE:
@@ -394,22 +361,22 @@ worker_async(void *arg)
* we rely on at least one insert to get a valid item
* id.
*/
- if (wtperf_value_range(cfg) < next_val)
+ if (wtperf_value_range(wtperf) < next_val)
continue;
break;
default:
goto err; /* can't happen */
}
- generate_key(cfg, key_buf, next_val);
+ generate_key(opts, key_buf, next_val);
/*
* Spread the data out around the multiple databases.
* Sleep to allow workers a chance to run and process async ops.
* Then retry to get an async op.
*/
- while ((ret = conn->async_new_op(
- conn, cfg->uris[map_key_to_table(cfg, next_val)],
+ while ((ret = conn->async_new_op(conn,
+ wtperf->uris[map_key_to_table(wtperf->opts, next_val)],
NULL, &cb, &asyncop)) == EBUSY)
(void)usleep(10000);
if (ret != 0)
@@ -424,23 +391,23 @@ worker_async(void *arg)
break;
goto op_err;
case WORKER_INSERT:
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
asyncop->set_value(asyncop, value_buf);
if ((ret = asyncop->insert(asyncop)) == 0)
break;
goto op_err;
case WORKER_UPDATE:
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
asyncop->set_value(asyncop, value_buf);
if ((ret = asyncop->update(asyncop)) == 0)
break;
goto op_err;
default:
-op_err: lprintf(cfg, ret, 0,
+op_err: lprintf(wtperf, ret, 0,
"%s failed for: %s, range: %"PRIu64,
- op_name(op), key_buf, wtperf_value_range(cfg));
+ op_name(op), key_buf, wtperf_value_range(wtperf));
goto err; /* can't happen */
}
@@ -454,7 +421,7 @@ op_err: lprintf(cfg, ret, 0,
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
return (NULL);
}
@@ -465,17 +432,19 @@ err: cfg->error = cfg->stop = 1;
* search do them. Ensuring the keys we see are always in order.
*/
static int
-do_range_reads(CONFIG *cfg, WT_CURSOR *cursor)
+do_range_reads(WTPERF *wtperf, WT_CURSOR *cursor)
{
+ CONFIG_OPTS *opts;
size_t range;
uint64_t next_val, prev_val;
char *range_key_buf;
char buf[512];
int ret;
+ opts = wtperf->opts;
ret = 0;
- if (cfg->read_range == 0)
+ if (opts->read_range == 0)
return (0);
memset(&buf[0], 0, 512 * sizeof(char));
@@ -485,7 +454,7 @@ do_range_reads(CONFIG *cfg, WT_CURSOR *cursor)
testutil_check(cursor->get_key(cursor, &range_key_buf));
extract_key(range_key_buf, &next_val);
- for (range = 0; range < cfg->read_range; ++range) {
+ for (range = 0; range < opts->read_range; ++range) {
prev_val = next_val;
ret = cursor->next(cursor);
/* We are done if we reach the end. */
@@ -496,7 +465,7 @@ do_range_reads(CONFIG *cfg, WT_CURSOR *cursor)
testutil_check(cursor->get_key(cursor, &range_key_buf));
extract_key(range_key_buf, &next_val);
if (next_val < prev_val) {
- lprintf(cfg, EINVAL, 0,
+ lprintf(wtperf, EINVAL, 0,
"Out of order keys %" PRIu64
" came before %" PRIu64,
prev_val, next_val);
@@ -510,9 +479,10 @@ static void *
worker(void *arg)
{
struct timespec start, stop;
- CONFIG *cfg;
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
TRACK *trk;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_CONNECTION *conn;
WT_CURSOR **cursors, *cursor, *log_table_cursor, *tmp_cursor;
WT_SESSION *session;
@@ -524,9 +494,10 @@ worker(void *arg)
char *value_buf, *key_buf, *value;
char buf[512];
- thread = (CONFIG_THREAD *)arg;
- cfg = thread->cfg;
- conn = cfg->conn;
+ thread = (WTPERF_THREAD *)arg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
cursors = NULL;
log_table_cursor = NULL; /* -Wconditional-initialized */
ops = 0;
@@ -535,42 +506,40 @@ worker(void *arg)
trk = NULL;
if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0, "worker: WT_CONNECTION.open_session");
+ conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0, "worker: WT_CONNECTION.open_session");
goto err;
}
- cursors = dcalloc(cfg->table_count, sizeof(WT_CURSOR *));
- for (i = 0; i < cfg->table_count_idle; i++) {
- snprintf(buf, 512, "%s_idle%05d", cfg->uris[0], (int)i);
+ cursors = dcalloc(opts->table_count, sizeof(WT_CURSOR *));
+ for (i = 0; i < opts->table_count_idle; i++) {
+ snprintf(buf, 512, "%s_idle%05d", wtperf->uris[0], (int)i);
if ((ret = session->open_cursor(
session, buf, NULL, NULL, &tmp_cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Error opening idle table %s", buf);
goto err;
}
if ((ret = tmp_cursor->close(tmp_cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Error closing idle table %s", buf);
goto err;
}
}
- for (i = 0; i < cfg->table_count; i++) {
+ for (i = 0; i < opts->table_count; i++) {
if ((ret = session->open_cursor(session,
- cfg->uris[i], NULL, NULL, &cursors[i])) != 0) {
- lprintf(cfg, ret, 0,
+ wtperf->uris[i], NULL, NULL, &cursors[i])) != 0) {
+ lprintf(wtperf, ret, 0,
"worker: WT_SESSION.open_cursor: %s",
- cfg->uris[i]);
+ wtperf->uris[i]);
goto err;
}
}
- if (cfg->log_like_table) {
- if ((ret = session->open_cursor(session,
- cfg->log_table_uri, NULL, NULL, &log_table_cursor)) != 0) {
- lprintf(cfg, ret, 0,
- "worker: WT_SESSION.open_cursor: %s",
- cfg->log_table_uri);
- goto err;
- }
+ if (opts->log_like_table && (ret = session->open_cursor(session,
+ wtperf->log_table_uri, NULL, NULL, &log_table_cursor)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "worker: WT_SESSION.open_cursor: %s",
+ wtperf->log_table_uri);
+ goto err;
}
/* Setup the timer for throttling. */
@@ -579,7 +548,7 @@ worker(void *arg)
/* Setup for truncate */
if (thread->workload->truncate != 0)
- if ((ret = setup_truncate(cfg, thread, session)) != 0)
+ if ((ret = setup_truncate(wtperf, thread, session)) != 0)
goto err;
key_buf = thread->key_buf;
@@ -588,13 +557,13 @@ worker(void *arg)
op = thread->workload->ops;
op_end = op + sizeof(thread->workload->ops);
- if ((ops_per_txn != 0 || cfg->log_like_table) &&
+ if ((ops_per_txn != 0 || opts->log_like_table) &&
(ret = session->begin_transaction(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "First transaction begin failed");
+ lprintf(wtperf, ret, 0, "First transaction begin failed");
goto err;
}
- while (!cfg->stop) {
+ while (!wtperf->stop) {
/*
* Generate the next key and setup operation specific
* statistics tracking objects.
@@ -603,10 +572,10 @@ worker(void *arg)
case WORKER_INSERT:
case WORKER_INSERT_RMW:
trk = &thread->insert;
- if (cfg->random_range)
+ if (opts->random_range)
next_val = wtperf_rand(thread);
else
- next_val = cfg->icount + get_next_incr(cfg);
+ next_val = opts->icount + get_next_incr(wtperf);
break;
case WORKER_READ:
trk = &thread->read;
@@ -621,7 +590,7 @@ worker(void *arg)
* we rely on at least one insert to get a valid item
* id.
*/
- if (wtperf_value_range(cfg) < next_val)
+ if (wtperf_value_range(wtperf) < next_val)
continue;
break;
case WORKER_TRUNCATE:
@@ -632,24 +601,22 @@ worker(void *arg)
goto err; /* can't happen */
}
- generate_key(cfg, key_buf, next_val);
+ generate_key(opts, key_buf, next_val);
/*
* Spread the data out around the multiple databases.
*/
- cursor = cursors[map_key_to_table(cfg, next_val)];
+ cursor = cursors[map_key_to_table(wtperf->opts, next_val)];
/*
* Skip the first time we do an operation, when trk->ops
* is 0, to avoid first time latency spikes.
*/
measure_latency =
- cfg->sample_interval != 0 && trk != NULL &&
- trk->ops != 0 && (trk->ops % cfg->sample_rate == 0);
- if (measure_latency && (ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ opts->sample_interval != 0 && trk != NULL &&
+ trk->ops != 0 && (trk->ops % opts->sample_rate == 0);
+ if (measure_latency)
+ __wt_epoch(NULL, &start);
cursor->set_key(cursor, key_buf);
@@ -666,7 +633,7 @@ worker(void *arg)
if (ret == 0) {
if ((ret = cursor->get_value(
cursor, &value)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"get_value in read.");
goto err;
}
@@ -675,7 +642,7 @@ worker(void *arg)
* for several operations, confirming that the
* next key is in the correct order.
*/
- ret = do_range_reads(cfg, cursor);
+ ret = do_range_reads(wtperf, cursor);
}
if (ret == 0 || ret == WT_NOTFOUND)
@@ -690,15 +657,15 @@ worker(void *arg)
/* FALLTHROUGH */
case WORKER_INSERT:
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
cursor->set_value(cursor, value_buf);
if ((ret = cursor->insert(cursor)) == 0)
break;
goto op_err;
case WORKER_TRUNCATE:
- if ((ret = run_truncate(
- cfg, thread, cursor, session, &truncated)) == 0) {
+ if ((ret = run_truncate(wtperf,
+ thread, cursor, session, &truncated)) == 0) {
if (truncated)
trk = &thread->truncate;
else
@@ -712,7 +679,7 @@ worker(void *arg)
if ((ret = cursor->search(cursor)) == 0) {
if ((ret = cursor->get_value(
cursor, &value)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"get_value in update.");
goto err;
}
@@ -721,14 +688,14 @@ worker(void *arg)
* safe, and be sure to NUL-terminate.
*/
strncpy(value_buf,
- value, cfg->value_sz_max - 1);
+ value, opts->value_sz_max - 1);
if (thread->workload->update_delta != 0)
update_value_delta(thread);
if (value_buf[0] == 'a')
value_buf[0] = 'b';
else
value_buf[0] = 'a';
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
cursor->set_value(cursor, value_buf);
if ((ret = cursor->update(cursor)) == 0)
@@ -756,62 +723,59 @@ op_err: if (ret == WT_ROLLBACK && ops_per_txn != 0) {
* order in cases of ordered inserts, as we
* aren't retrying here.
*/
- lprintf(cfg, ret, 1,
+ lprintf(wtperf, ret, 1,
"%s for: %s, range: %"PRIu64, op_name(op),
- key_buf, wtperf_value_range(cfg));
+ key_buf, wtperf_value_range(wtperf));
if ((ret = session->rollback_transaction(
session, NULL)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Failed rollback_transaction");
goto err;
}
if ((ret = session->begin_transaction(
session, NULL)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Worker begin transaction failed");
goto err;
}
break;
}
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"%s failed for: %s, range: %"PRIu64,
- op_name(op), key_buf, wtperf_value_range(cfg));
+ op_name(op), key_buf, wtperf_value_range(wtperf));
goto err;
default:
goto err; /* can't happen */
}
/* Update the log-like table. */
- if (cfg->log_like_table &&
+ if (opts->log_like_table &&
(*op != WORKER_READ && *op != WORKER_TRUNCATE)) {
- log_id = __wt_atomic_add64(&cfg->log_like_table_key, 1);
+ log_id =
+ __wt_atomic_add64(&wtperf->log_like_table_key, 1);
log_table_cursor->set_key(log_table_cursor, log_id);
log_table_cursor->set_value(
log_table_cursor, value_buf);
if ((ret =
log_table_cursor->insert(log_table_cursor)) != 0) {
- lprintf(cfg, ret, 0, "Cursor insert failed");
+ lprintf(wtperf, ret, 0, "Cursor insert failed");
goto err;
}
}
/* Release the cursor, if we have multiple tables. */
- if (cfg->table_count > 1 && ret == 0 &&
+ if (opts->table_count > 1 && ret == 0 &&
*op != WORKER_INSERT && *op != WORKER_INSERT_RMW) {
if ((ret = cursor->reset(cursor)) != 0) {
- lprintf(cfg, ret, 0, "Cursor reset failed");
+ lprintf(wtperf, ret, 0, "Cursor reset failed");
goto err;
}
}
/* Gather statistics */
- if (!cfg->in_warmup) {
+ if (!wtperf->in_warmup) {
if (measure_latency) {
- if ((ret = __wt_epoch(NULL, &stop)) != 0) {
- lprintf(cfg, ret, 0,
- "Get time call failed");
- goto err;
- }
+ __wt_epoch(NULL, &stop);
++trk->latency_ops;
usecs = WT_TIMEDIFF_US(stop, start);
track_operation(trk, usecs);
@@ -824,17 +788,17 @@ op_err: if (ret == WT_ROLLBACK && ops_per_txn != 0) {
* Commit the transaction if grouping operations together
* or tracking changes in our log table.
*/
- if ((cfg->log_like_table && ops_per_txn == 0) ||
+ if ((opts->log_like_table && ops_per_txn == 0) ||
(ops_per_txn != 0 && ops++ % ops_per_txn == 0)) {
if ((ret = session->commit_transaction(
session, NULL)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Worker transaction commit failed");
goto err;
}
if ((ret = session->begin_transaction(
session, NULL)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Worker begin transaction failed");
goto err;
}
@@ -854,13 +818,13 @@ op_err: if (ret == WT_ROLLBACK && ops_per_txn != 0) {
}
if ((ret = session->close(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Session close in worker failed");
+ lprintf(wtperf, ret, 0, "Session close in worker failed");
goto err;
}
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
free(cursors);
@@ -913,14 +877,17 @@ run_mix_schedule_op(WORKLOAD *workp, int op, int64_t op_cnt)
* Schedule the mixed-run operations.
*/
static int
-run_mix_schedule(CONFIG *cfg, WORKLOAD *workp)
+run_mix_schedule(WTPERF *wtperf, WORKLOAD *workp)
{
+ CONFIG_OPTS *opts;
int64_t pct;
+ opts = wtperf->opts;
+
/* Confirm reads, inserts, truncates and updates cannot all be zero. */
if (workp->insert == 0 && workp->read == 0 &&
workp->truncate == 0 && workp->update == 0) {
- lprintf(cfg, EINVAL, 0, "no operations scheduled");
+ lprintf(wtperf, EINVAL, 0, "no operations scheduled");
return (EINVAL);
}
@@ -931,7 +898,7 @@ run_mix_schedule(CONFIG *cfg, WORKLOAD *workp)
if (workp->truncate != 0) {
if (workp->insert != 0 ||
workp->read != 0 || workp->update != 0) {
- lprintf(cfg, EINVAL, 0,
+ lprintf(wtperf, EINVAL, 0,
"Can't configure truncate in a mixed workload");
return (EINVAL);
}
@@ -947,7 +914,7 @@ run_mix_schedule(CONFIG *cfg, WORKLOAD *workp)
*/
if (workp->insert != 0 && workp->read == 0 && workp->update == 0) {
memset(workp->ops,
- cfg->insert_rmw ? WORKER_INSERT_RMW : WORKER_INSERT,
+ opts->insert_rmw ? WORKER_INSERT_RMW : WORKER_INSERT,
sizeof(workp->ops));
return (0);
}
@@ -979,7 +946,7 @@ run_mix_schedule(CONFIG *cfg, WORKLOAD *workp)
(workp->insert + workp->read + workp->update);
if (pct != 0)
run_mix_schedule_op(workp,
- cfg->insert_rmw ? WORKER_INSERT_RMW : WORKER_INSERT, pct);
+ opts->insert_rmw ? WORKER_INSERT_RMW : WORKER_INSERT, pct);
pct = (workp->update * 100) /
(workp->insert + workp->read + workp->update);
if (pct != 0)
@@ -991,9 +958,10 @@ static void *
populate_thread(void *arg)
{
struct timespec start, stop;
- CONFIG *cfg;
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
TRACK *trk;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_CONNECTION *conn;
WT_CURSOR **cursors, *cursor;
WT_SESSION *session;
@@ -1004,9 +972,10 @@ populate_thread(void *arg)
char *value_buf, *key_buf;
const char *cursor_config;
- thread = (CONFIG_THREAD *)arg;
- cfg = thread->cfg;
- conn = cfg->conn;
+ thread = (WTPERF_THREAD *)arg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
session = NULL;
cursors = NULL;
ret = stress_checkpoint_due = 0;
@@ -1016,37 +985,37 @@ populate_thread(void *arg)
value_buf = thread->value_buf;
if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0, "populate: WT_CONNECTION.open_session");
+ conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0, "populate: WT_CONNECTION.open_session");
goto err;
}
/* Do bulk loads if populate is single-threaded. */
cursor_config =
- (cfg->populate_threads == 1 && !cfg->index) ? "bulk" : NULL;
+ (opts->populate_threads == 1 && !opts->index) ? "bulk" : NULL;
/* Create the cursors. */
- cursors = dcalloc(cfg->table_count, sizeof(WT_CURSOR *));
- for (i = 0; i < cfg->table_count; i++) {
+ cursors = dcalloc(opts->table_count, sizeof(WT_CURSOR *));
+ for (i = 0; i < opts->table_count; i++) {
if ((ret = session->open_cursor(
- session, cfg->uris[i], NULL,
+ session, wtperf->uris[i], NULL,
cursor_config, &cursors[i])) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"populate: WT_SESSION.open_cursor: %s",
- cfg->uris[i]);
+ wtperf->uris[i]);
goto err;
}
}
/* Populate the databases. */
for (intxn = 0, opcount = 0;;) {
- op = get_next_incr(cfg);
- if (op > cfg->icount)
+ op = get_next_incr(wtperf);
+ if (op > opts->icount)
break;
- if (cfg->populate_ops_per_txn != 0 && !intxn) {
+ if (opts->populate_ops_per_txn != 0 && !intxn) {
if ((ret = session->begin_transaction(
- session, cfg->transaction_config)) != 0) {
- lprintf(cfg, ret, 0,
+ session, opts->transaction_config)) != 0) {
+ lprintf(wtperf, ret, 0,
"Failed starting transaction.");
goto err;
}
@@ -1055,31 +1024,29 @@ populate_thread(void *arg)
/*
* Figure out which table this op belongs to.
*/
- cursor = cursors[map_key_to_table(cfg, op)];
- generate_key(cfg, key_buf, op);
+ cursor = cursors[map_key_to_table(wtperf->opts, op)];
+ generate_key(opts, key_buf, op);
measure_latency =
- cfg->sample_interval != 0 &&
- trk->ops != 0 && (trk->ops % cfg->sample_rate == 0);
- if (measure_latency && (ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ opts->sample_interval != 0 &&
+ trk->ops != 0 && (trk->ops % opts->sample_rate == 0);
+ if (measure_latency)
+ __wt_epoch(NULL, &start);
cursor->set_key(cursor, key_buf);
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
cursor->set_value(cursor, value_buf);
if ((ret = cursor->insert(cursor)) == WT_ROLLBACK) {
- lprintf(cfg, ret, 0, "insert retrying");
+ lprintf(wtperf, ret, 0, "insert retrying");
if ((ret = session->rollback_transaction(
session, NULL)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Failed rollback_transaction");
goto err;
}
intxn = 0;
continue;
} else if (ret != 0) {
- lprintf(cfg, ret, 0, "Failed inserting");
+ lprintf(wtperf, ret, 0, "Failed inserting");
goto err;
}
/*
@@ -1089,28 +1056,25 @@ populate_thread(void *arg)
* of them.
*/
if (measure_latency) {
- if ((ret = __wt_epoch(NULL, &stop)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ __wt_epoch(NULL, &stop);
++trk->latency_ops;
usecs = WT_TIMEDIFF_US(stop, start);
track_operation(trk, usecs);
}
++thread->insert.ops; /* Same as trk->ops */
- if (cfg->checkpoint_stress_rate != 0 &&
- (op % cfg->checkpoint_stress_rate) == 0)
+ if (opts->checkpoint_stress_rate != 0 &&
+ (op % opts->checkpoint_stress_rate) == 0)
stress_checkpoint_due = 1;
- if (cfg->populate_ops_per_txn != 0) {
- if (++opcount < cfg->populate_ops_per_txn)
+ if (opts->populate_ops_per_txn != 0) {
+ if (++opcount < opts->populate_ops_per_txn)
continue;
opcount = 0;
if ((ret = session->commit_transaction(
session, NULL)) != 0)
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Fail committing, transaction was aborted");
intxn = 0;
}
@@ -1118,24 +1082,24 @@ populate_thread(void *arg)
if (stress_checkpoint_due && intxn == 0) {
stress_checkpoint_due = 0;
if ((ret = session->checkpoint(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Checkpoint failed");
+ lprintf(wtperf, ret, 0, "Checkpoint failed");
goto err;
}
}
}
if (intxn &&
(ret = session->commit_transaction(session, NULL)) != 0)
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Fail committing, transaction was aborted");
if ((ret = session->close(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error closing session in populate");
+ lprintf(wtperf, ret, 0, "Error closing session in populate");
goto err;
}
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
free(cursors);
@@ -1146,9 +1110,10 @@ static void *
populate_async(void *arg)
{
struct timespec start, stop;
- CONFIG *cfg;
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
TRACK *trk;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_ASYNC_OP *asyncop;
WT_CONNECTION *conn;
WT_SESSION *session;
@@ -1156,9 +1121,10 @@ populate_async(void *arg)
int measure_latency, ret;
char *value_buf, *key_buf;
- thread = (CONFIG_THREAD *)arg;
- cfg = thread->cfg;
- conn = cfg->conn;
+ thread = (WTPERF_THREAD *)arg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
session = NULL;
ret = 0;
trk = &thread->insert;
@@ -1167,8 +1133,8 @@ populate_async(void *arg)
value_buf = thread->value_buf;
if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0, "populate: WT_CONNECTION.open_session");
+ conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0, "populate: WT_CONNECTION.open_session");
goto err;
}
@@ -1178,38 +1144,38 @@ populate_async(void *arg)
* the time to process by workers.
*/
measure_latency =
- cfg->sample_interval != 0 &&
- trk->ops != 0 && (trk->ops % cfg->sample_rate == 0);
- if (measure_latency && (ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ opts->sample_interval != 0 &&
+ trk->ops != 0 && (trk->ops % opts->sample_rate == 0);
+ if (measure_latency)
+ __wt_epoch(NULL, &start);
+
/* Populate the databases. */
for (;;) {
- op = get_next_incr(cfg);
- if (op > cfg->icount)
+ op = get_next_incr(wtperf);
+ if (op > opts->icount)
break;
/*
* Allocate an async op for whichever table.
*/
while ((ret = conn->async_new_op(
- conn, cfg->uris[map_key_to_table(cfg, op)],
+ conn, wtperf->uris[map_key_to_table(wtperf->opts, op)],
NULL, &cb, &asyncop)) == EBUSY)
(void)usleep(10000);
if (ret != 0)
goto err;
asyncop->app_private = thread;
- generate_key(cfg, key_buf, op);
+ generate_key(opts, key_buf, op);
asyncop->set_key(asyncop, key_buf);
- if (cfg->random_value)
+ if (opts->random_value)
randomize_value(thread, value_buf);
asyncop->set_value(asyncop, value_buf);
if ((ret = asyncop->insert(asyncop)) != 0) {
- lprintf(cfg, ret, 0, "Failed inserting");
+ lprintf(wtperf, ret, 0, "Failed inserting");
goto err;
}
}
+
/*
* Gather statistics.
* We measure the latency of inserting a single key. If there
@@ -1221,22 +1187,19 @@ populate_async(void *arg)
if (conn->async_flush(conn) != 0)
goto err;
if (measure_latency) {
- if ((ret = __wt_epoch(NULL, &stop)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ __wt_epoch(NULL, &stop);
++trk->latency_ops;
usecs = WT_TIMEDIFF_US(stop, start);
track_operation(trk, usecs);
}
if ((ret = session->close(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error closing session in populate");
+ lprintf(wtperf, ret, 0, "Error closing session in populate");
goto err;
}
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
return (NULL);
}
@@ -1246,8 +1209,9 @@ monitor(void *arg)
{
struct timespec t;
struct tm *tm, _tm;
- CONFIG *cfg;
+ CONFIG_OPTS *opts;
FILE *fp;
+ WTPERF *wtperf;
size_t len;
uint64_t min_thr, reads, inserts, updates;
uint64_t cur_reads, cur_inserts, cur_updates;
@@ -1257,24 +1221,26 @@ monitor(void *arg)
uint32_t update_avg, update_min, update_max;
uint32_t latency_max, level;
u_int i;
- int msg_err, ret;
+ int msg_err;
const char *str;
char buf[64], *path;
- cfg = (CONFIG *)arg;
- assert(cfg->sample_interval != 0);
+ wtperf = (WTPERF *)arg;
+ opts = wtperf->opts;
+ assert(opts->sample_interval != 0);
+
fp = NULL;
path = NULL;
- min_thr = (uint64_t)cfg->min_throughput;
- latency_max = (uint32_t)ms_to_us(cfg->max_latency);
+ min_thr = (uint64_t)opts->min_throughput;
+ latency_max = (uint32_t)ms_to_us(opts->max_latency);
/* Open the logging file. */
- len = strlen(cfg->monitor_dir) + 100;
+ len = strlen(wtperf->monitor_dir) + 100;
path = dmalloc(len);
- snprintf(path, len, "%s/monitor", cfg->monitor_dir);
+ snprintf(path, len, "%s/monitor", wtperf->monitor_dir);
if ((fp = fopen(path, "w")) == NULL) {
- lprintf(cfg, errno, 0, "%s", path);
+ lprintf(wtperf, errno, 0, "%s", path);
goto err;
}
/* Set line buffering for monitor file. */
@@ -1297,34 +1263,31 @@ monitor(void *arg)
"update maximum latency(uS)"
"\n");
last_reads = last_inserts = last_updates = 0;
- while (!cfg->stop) {
- for (i = 0; i < cfg->sample_interval; i++) {
+ while (!wtperf->stop) {
+ for (i = 0; i < opts->sample_interval; i++) {
sleep(1);
- if (cfg->stop)
+ if (wtperf->stop)
break;
}
/* If the workers are done, don't bother with a final call. */
- if (cfg->stop)
+ if (wtperf->stop)
break;
- if (cfg->in_warmup)
+ if (wtperf->in_warmup)
continue;
- if ((ret = __wt_epoch(NULL, &t)) != 0) {
- lprintf(cfg, ret, 0, "Get time call failed");
- goto err;
- }
+ __wt_epoch(NULL, &t);
tm = localtime_r(&t.tv_sec, &_tm);
(void)strftime(buf, sizeof(buf), "%b %d %H:%M:%S", tm);
- reads = sum_read_ops(cfg);
- inserts = sum_insert_ops(cfg);
- updates = sum_update_ops(cfg);
- latency_read(cfg, &read_avg, &read_min, &read_max);
- latency_insert(cfg, &insert_avg, &insert_min, &insert_max);
- latency_update(cfg, &update_avg, &update_min, &update_max);
+ reads = sum_read_ops(wtperf);
+ inserts = sum_insert_ops(wtperf);
+ updates = sum_update_ops(wtperf);
+ latency_read(wtperf, &read_avg, &read_min, &read_max);
+ latency_insert(wtperf, &insert_avg, &insert_min, &insert_max);
+ latency_update(wtperf, &update_avg, &update_min, &update_max);
- cur_reads = (reads - last_reads) / cfg->sample_interval;
- cur_updates = (updates - last_updates) / cfg->sample_interval;
+ cur_reads = (reads - last_reads) / opts->sample_interval;
+ cur_updates = (updates - last_updates) / opts->sample_interval;
/*
* For now the only item we need to worry about changing is
* inserts when we transition from the populate phase to
@@ -1334,7 +1297,7 @@ monitor(void *arg)
cur_inserts = 0;
else
cur_inserts =
- (inserts - last_inserts) / cfg->sample_interval;
+ (inserts - last_inserts) / opts->sample_interval;
(void)fprintf(fp,
"%s,%" PRIu32
@@ -1344,9 +1307,9 @@ monitor(void *arg)
",%" PRIu32 ",%" PRIu32 ",%" PRIu32
",%" PRIu32 ",%" PRIu32 ",%" PRIu32
"\n",
- buf, cfg->totalsec,
+ buf, wtperf->totalsec,
cur_reads, cur_inserts, cur_updates,
- cfg->ckpt ? 'Y' : 'N',
+ wtperf->ckpt ? 'Y' : 'N',
read_avg, read_min, read_max,
insert_avg, insert_min, insert_max,
update_avg, update_min, update_max);
@@ -1354,7 +1317,7 @@ monitor(void *arg)
if (latency_max != 0 &&
(read_max > latency_max || insert_max > latency_max ||
update_max > latency_max)) {
- if (cfg->max_latency_fatal) {
+ if (opts->max_latency_fatal) {
level = 1;
msg_err = WT_PANIC;
str = "ERROR";
@@ -1363,7 +1326,7 @@ monitor(void *arg)
msg_err = 0;
str = "WARNING";
}
- lprintf(cfg, msg_err, level,
+ lprintf(wtperf, msg_err, level,
"%s: max latency exceeded: threshold %" PRIu32
" read max %" PRIu32 " insert max %" PRIu32
" update max %" PRIu32, str, latency_max,
@@ -1373,7 +1336,7 @@ monitor(void *arg)
((cur_reads != 0 && cur_reads < min_thr) ||
(cur_inserts != 0 && cur_inserts < min_thr) ||
(cur_updates != 0 && cur_updates < min_thr))) {
- if (cfg->min_throughput_fatal) {
+ if (opts->min_throughput_fatal) {
level = 1;
msg_err = WT_PANIC;
str = "ERROR";
@@ -1382,7 +1345,7 @@ monitor(void *arg)
msg_err = 0;
str = "WARNING";
}
- lprintf(cfg, msg_err, level,
+ lprintf(wtperf, msg_err, level,
"%s: minimum throughput not met: threshold %" PRIu64
" reads %" PRIu64 " inserts %" PRIu64
" updates %" PRIu64, str, min_thr, cur_reads,
@@ -1395,7 +1358,7 @@ monitor(void *arg)
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
if (fp != NULL)
@@ -1408,75 +1371,73 @@ err: cfg->error = cfg->stop = 1;
static void *
checkpoint_worker(void *arg)
{
- CONFIG *cfg;
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf;
+ WTPERF_THREAD *thread;
WT_CONNECTION *conn;
WT_SESSION *session;
struct timespec e, s;
uint32_t i;
int ret;
- thread = (CONFIG_THREAD *)arg;
- cfg = thread->cfg;
- conn = cfg->conn;
+ thread = (WTPERF_THREAD *)arg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
session = NULL;
if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0,
+ conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0,
"open_session failed in checkpoint thread.");
goto err;
}
- while (!cfg->stop) {
+ while (!wtperf->stop) {
/* Break the sleep up, so we notice interrupts faster. */
- for (i = 0; i < cfg->checkpoint_interval; i++) {
+ for (i = 0; i < opts->checkpoint_interval; i++) {
sleep(1);
- if (cfg->stop)
+ if (wtperf->stop)
break;
}
/* If the workers are done, don't bother with a final call. */
- if (cfg->stop)
+ if (wtperf->stop)
break;
- if ((ret = __wt_epoch(NULL, &s)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in checkpoint.");
- goto err;
- }
- cfg->ckpt = 1;
+ __wt_epoch(NULL, &s);
+
+ wtperf->ckpt = true;
if ((ret = session->checkpoint(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Checkpoint failed.");
+ lprintf(wtperf, ret, 0, "Checkpoint failed.");
goto err;
}
- cfg->ckpt = 0;
+ wtperf->ckpt = false;
++thread->ckpt.ops;
- if ((ret = __wt_epoch(NULL, &e)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in checkpoint.");
- goto err;
- }
+ __wt_epoch(NULL, &e);
}
if (session != NULL &&
((ret = session->close(session, NULL)) != 0)) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Error closing session in checkpoint worker.");
goto err;
}
/* Notify our caller we failed and shut the system down. */
if (0) {
-err: cfg->error = cfg->stop = 1;
+err: wtperf->error = wtperf->stop = true;
}
return (NULL);
}
static int
-execute_populate(CONFIG *cfg)
+execute_populate(WTPERF *wtperf)
{
struct timespec start, stop;
- CONFIG_THREAD *popth;
+ CONFIG_OPTS *opts;
+ WTPERF_THREAD *popth;
WT_ASYNC_OP *asyncop;
pthread_t idle_table_cycle_thread;
size_t i;
@@ -1486,59 +1447,57 @@ execute_populate(CONFIG *cfg)
int elapsed, ret;
void *(*pfunc)(void *);
- lprintf(cfg, 0, 1,
+ opts = wtperf->opts;
+
+ lprintf(wtperf, 0, 1,
"Starting %" PRIu32
" populate thread(s) for %" PRIu32 " items",
- cfg->populate_threads, cfg->icount);
+ opts->populate_threads, opts->icount);
/* Start cycling idle tables if configured. */
- if ((ret = start_idle_table_cycle(cfg, &idle_table_cycle_thread)) != 0)
+ if ((ret =
+ start_idle_table_cycle(wtperf, &idle_table_cycle_thread)) != 0)
return (ret);
- cfg->insert_key = 0;
+ wtperf->insert_key = 0;
- cfg->popthreads = dcalloc(cfg->populate_threads, sizeof(CONFIG_THREAD));
- if (cfg->use_asyncops > 0) {
- lprintf(cfg, 0, 1, "Starting %" PRIu32 " async thread(s)",
- cfg->async_threads);
+ wtperf->popthreads =
+ dcalloc(opts->populate_threads, sizeof(WTPERF_THREAD));
+ if (wtperf->use_asyncops) {
+ lprintf(wtperf, 0, 1, "Starting %" PRIu32 " async thread(s)",
+ opts->async_threads);
pfunc = populate_async;
} else
pfunc = populate_thread;
- if ((ret = start_threads(cfg, NULL,
- cfg->popthreads, cfg->populate_threads, pfunc)) != 0)
+ if ((ret = start_threads(wtperf, NULL,
+ wtperf->popthreads, opts->populate_threads, pfunc)) != 0)
return (ret);
- if ((ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in populate.");
- return (ret);
- }
+ __wt_epoch(NULL, &start);
for (elapsed = 0, interval = 0, last_ops = 0;
- cfg->insert_key < cfg->icount && cfg->error == 0;) {
+ wtperf->insert_key < opts->icount && !wtperf->error;) {
/*
* Sleep for 100th of a second, report_interval is in second
* granularity, each 100th increment of elapsed is a single
* increment of interval.
*/
(void)usleep(10000);
- if (cfg->report_interval == 0 || ++elapsed < 100)
+ if (opts->report_interval == 0 || ++elapsed < 100)
continue;
elapsed = 0;
- if (++interval < cfg->report_interval)
+ if (++interval < opts->report_interval)
continue;
interval = 0;
- cfg->totalsec += cfg->report_interval;
- cfg->insert_ops = sum_pop_ops(cfg);
- lprintf(cfg, 0, 1,
+ wtperf->totalsec += opts->report_interval;
+ wtperf->insert_ops = sum_pop_ops(wtperf);
+ lprintf(wtperf, 0, 1,
"%" PRIu64 " populate inserts (%" PRIu64 " of %"
PRIu32 ") in %" PRIu32 " secs (%" PRIu32 " total secs)",
- cfg->insert_ops - last_ops, cfg->insert_ops,
- cfg->icount, cfg->report_interval, cfg->totalsec);
- last_ops = cfg->insert_ops;
- }
- if ((ret = __wt_epoch(NULL, &stop)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in populate.");
- return (ret);
+ wtperf->insert_ops - last_ops, wtperf->insert_ops,
+ opts->icount, opts->report_interval, wtperf->totalsec);
+ last_ops = wtperf->insert_ops;
}
+ __wt_epoch(NULL, &stop);
/*
* Move popthreads aside to narrow possible race with the monitor
@@ -1546,21 +1505,22 @@ execute_populate(CONFIG *cfg)
* NULL when the populate phase is finished, to know that the workload
* phase has started.
*/
- popth = cfg->popthreads;
- cfg->popthreads = NULL;
- ret = stop_threads(cfg, cfg->populate_threads, popth);
+ popth = wtperf->popthreads;
+ wtperf->popthreads = NULL;
+ ret = stop_threads(wtperf, opts->populate_threads, popth);
free(popth);
if (ret != 0)
return (ret);
/* Report if any worker threads didn't finish. */
- if (cfg->error != 0) {
- lprintf(cfg, WT_ERROR, 0,
+ if (wtperf->error) {
+ lprintf(wtperf, WT_ERROR, 0,
"Populate thread(s) exited without finishing.");
return (WT_ERROR);
}
- lprintf(cfg, 0, 1, "Finished load of %" PRIu32 " items", cfg->icount);
+ lprintf(wtperf,
+ 0, 1, "Finished load of %" PRIu32 " items", opts->icount);
msecs = WT_TIMEDIFF_MS(stop, start);
/*
@@ -1572,9 +1532,9 @@ execute_populate(CONFIG *cfg)
print_ops_sec = 0;
} else {
print_secs = (double)msecs / (double)MSEC_PER_SEC;
- print_ops_sec = (uint64_t)(cfg->icount / print_secs);
+ print_ops_sec = (uint64_t)(opts->icount / print_secs);
}
- lprintf(cfg, 0, 1,
+ lprintf(wtperf, 0, 1,
"Load time: %.2f\n" "load ops/sec: %" PRIu64,
print_secs, print_ops_sec);
@@ -1583,58 +1543,57 @@ execute_populate(CONFIG *cfg)
* set an unlimited timeout because if we close the connection
* then any in-progress compact/merge is aborted.
*/
- if (cfg->compact) {
- assert(cfg->async_threads > 0);
- lprintf(cfg, 0, 1, "Compact after populate");
- if ((ret = __wt_epoch(NULL, &start)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in populate.");
- return (ret);
- }
- tables = cfg->table_count;
- for (i = 0; i < cfg->table_count; i++) {
+ if (opts->compact) {
+ assert(opts->async_threads > 0);
+ lprintf(wtperf, 0, 1, "Compact after populate");
+ __wt_epoch(NULL, &start);
+ tables = opts->table_count;
+ for (i = 0; i < opts->table_count; i++) {
/*
* If no ops are available, retry. Any other error,
* return.
*/
- while ((ret = cfg->conn->async_new_op(cfg->conn,
- cfg->uris[i], "timeout=0", &cb, &asyncop)) == EBUSY)
+ while ((ret = wtperf->conn->async_new_op(
+ wtperf->conn, wtperf->uris[i],
+ "timeout=0", &cb, &asyncop)) == EBUSY)
(void)usleep(10000);
if (ret != 0)
return (ret);
asyncop->app_private = &tables;
if ((ret = asyncop->compact(asyncop)) != 0) {
- lprintf(cfg, ret, 0, "Async compact failed.");
+ lprintf(wtperf,
+ ret, 0, "Async compact failed.");
return (ret);
}
}
- if ((ret = cfg->conn->async_flush(cfg->conn)) != 0) {
- lprintf(cfg, ret, 0, "Populate async flush failed.");
- return (ret);
- }
- if ((ret = __wt_epoch(NULL, &stop)) != 0) {
- lprintf(cfg, ret, 0, "Get time failed in populate.");
+ if ((ret = wtperf->conn->async_flush(wtperf->conn)) != 0) {
+ lprintf(wtperf, ret, 0, "Populate async flush failed.");
return (ret);
}
- lprintf(cfg, 0, 1,
+ __wt_epoch(NULL, &stop);
+ lprintf(wtperf, 0, 1,
"Compact completed in %" PRIu64 " seconds",
(uint64_t)(WT_TIMEDIFF_SEC(stop, start)));
assert(tables == 0);
}
/* Stop cycling idle tables. */
- if ((ret = stop_idle_table_cycle(cfg, idle_table_cycle_thread)) != 0)
+ if ((ret = stop_idle_table_cycle(wtperf, idle_table_cycle_thread)) != 0)
return (ret);
return (0);
}
static int
-close_reopen(CONFIG *cfg)
+close_reopen(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
int ret;
- if (!cfg->readonly && !cfg->reopen_connection)
+ opts = wtperf->opts;
+
+ if (!opts->readonly && !opts->reopen_connection)
return (0);
/*
* Reopen the connection. We do this so that the workload phase always
@@ -1642,16 +1601,16 @@ close_reopen(CONFIG *cfg)
* be identified. This is particularly important for LSM, where the
* merge algorithm is more aggressive for read-only trees.
*/
- /* cfg->conn is released no matter the return value from close(). */
- ret = cfg->conn->close(cfg->conn, NULL);
- cfg->conn = NULL;
+ /* wtperf->conn is released no matter the return value from close(). */
+ ret = wtperf->conn->close(wtperf->conn, NULL);
+ wtperf->conn = NULL;
if (ret != 0) {
- lprintf(cfg, ret, 0, "Closing the connection failed");
+ lprintf(wtperf, ret, 0, "Closing the connection failed");
return (ret);
}
if ((ret = wiredtiger_open(
- cfg->home, NULL, cfg->reopen_config, &cfg->conn)) != 0) {
- lprintf(cfg, ret, 0, "Re-opening the connection failed");
+ wtperf->home, NULL, wtperf->reopen_config, &wtperf->conn)) != 0) {
+ lprintf(wtperf, ret, 0, "Re-opening the connection failed");
return (ret);
}
/*
@@ -1660,10 +1619,10 @@ close_reopen(CONFIG *cfg)
* threads looking for work that will never arrive don't affect
* performance.
*/
- if (cfg->compact && cfg->use_asyncops == 0) {
- if ((ret = cfg->conn->reconfigure(
- cfg->conn, "async=(enabled=false)")) != 0) {
- lprintf(cfg, ret, 0, "Reconfigure async off failed");
+ if (opts->compact && !wtperf->use_asyncops) {
+ if ((ret = wtperf->conn->reconfigure(
+ wtperf->conn, "async=(enabled=false)")) != 0) {
+ lprintf(wtperf, ret, 0, "Reconfigure async off failed");
return (ret);
}
}
@@ -1671,10 +1630,11 @@ close_reopen(CONFIG *cfg)
}
static int
-execute_workload(CONFIG *cfg)
+execute_workload(WTPERF *wtperf)
{
- CONFIG_THREAD *threads;
+ CONFIG_OPTS *opts;
WORKLOAD *workp;
+ WTPERF_THREAD *threads;
WT_CONNECTION *conn;
WT_SESSION **sessions;
pthread_t idle_table_cycle_thread;
@@ -1685,9 +1645,11 @@ execute_workload(CONFIG *cfg)
int ret, t_ret;
void *(*pfunc)(void *);
- cfg->insert_key = 0;
- cfg->insert_ops = cfg->read_ops = cfg->truncate_ops = 0;
- cfg->update_ops = 0;
+ opts = wtperf->opts;
+
+ wtperf->insert_key = 0;
+ wtperf->insert_ops = wtperf->read_ops = wtperf->truncate_ops = 0;
+ wtperf->update_ops = 0;
last_ckpts = last_inserts = last_reads = last_truncates = 0;
last_updates = 0;
@@ -1696,38 +1658,40 @@ execute_workload(CONFIG *cfg)
sessions = NULL;
/* Start cycling idle tables. */
- if ((ret = start_idle_table_cycle(cfg, &idle_table_cycle_thread)) != 0)
+ if ((ret =
+ start_idle_table_cycle(wtperf, &idle_table_cycle_thread)) != 0)
return (ret);
- if (cfg->warmup != 0)
- cfg->in_warmup = 1;
+ if (opts->warmup != 0)
+ wtperf->in_warmup = true;
/* Allocate memory for the worker threads. */
- cfg->workers = dcalloc((size_t)cfg->workers_cnt, sizeof(CONFIG_THREAD));
+ wtperf->workers =
+ dcalloc((size_t)wtperf->workers_cnt, sizeof(WTPERF_THREAD));
- if (cfg->use_asyncops > 0) {
- lprintf(cfg, 0, 1, "Starting %" PRIu32 " async thread(s)",
- cfg->async_threads);
+ if (wtperf->use_asyncops) {
+ lprintf(wtperf, 0, 1, "Starting %" PRIu32 " async thread(s)",
+ opts->async_threads);
pfunc = worker_async;
} else
pfunc = worker;
- if (cfg->session_count_idle != 0) {
- sessions = dcalloc((size_t)cfg->session_count_idle,
+ if (opts->session_count_idle != 0) {
+ sessions = dcalloc((size_t)opts->session_count_idle,
sizeof(WT_SESSION *));
- conn = cfg->conn;
- for (i = 0; i < cfg->session_count_idle; ++i)
- if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &sessions[i])) != 0) {
- lprintf(cfg, ret, 0,
+ conn = wtperf->conn;
+ for (i = 0; i < opts->session_count_idle; ++i)
+ if ((ret = conn->open_session(conn,
+ NULL, opts->sess_config, &sessions[i])) != 0) {
+ lprintf(wtperf, ret, 0,
"execute_workload: idle open_session");
goto err;
}
}
/* Start each workload. */
- for (threads = cfg->workers, i = 0,
- workp = cfg->workload; i < cfg->workload_cnt; ++i, ++workp) {
- lprintf(cfg, 0, 1,
+ for (threads = wtperf->workers, i = 0,
+ workp = wtperf->workload; i < wtperf->workload_cnt; ++i, ++workp) {
+ lprintf(wtperf, 0, 1,
"Starting workload #%u: %" PRId64 " threads, inserts=%"
PRId64 ", reads=%" PRId64 ", updates=%" PRId64
", truncate=%" PRId64 ", throttle=%" PRId64,
@@ -1736,25 +1700,26 @@ execute_workload(CONFIG *cfg)
workp->throttle);
/* Figure out the workload's schedule. */
- if ((ret = run_mix_schedule(cfg, workp)) != 0)
+ if ((ret = run_mix_schedule(wtperf, workp)) != 0)
goto err;
/* Start the workload's threads. */
if ((ret = start_threads(
- cfg, workp, threads, (u_int)workp->threads, pfunc)) != 0)
+ wtperf, workp, threads, (u_int)workp->threads, pfunc)) != 0)
goto err;
threads += workp->threads;
}
- if (cfg->warmup != 0) {
- lprintf(cfg, 0, 1,
- "Waiting for warmup duration of %" PRIu32, cfg->warmup);
- sleep(cfg->warmup);
- cfg->in_warmup = 0;
+ if (opts->warmup != 0) {
+ lprintf(wtperf, 0, 1,
+ "Waiting for warmup duration of %" PRIu32, opts->warmup);
+ sleep(opts->warmup);
+ wtperf->in_warmup = false;
}
- for (interval = cfg->report_interval, run_time = cfg->run_time,
- run_ops = cfg->run_ops; cfg->error == 0;) {
+ for (interval = opts->report_interval,
+ run_time = opts->run_time, run_ops = opts->run_ops;
+ !wtperf->error;) {
/*
* Sleep for one second at a time.
* If we are tracking run time, check to see if we're done, and
@@ -1769,59 +1734,60 @@ execute_workload(CONFIG *cfg)
}
/* Sum the operations we've done. */
- cfg->ckpt_ops = sum_ckpt_ops(cfg);
- cfg->insert_ops = sum_insert_ops(cfg);
- cfg->read_ops = sum_read_ops(cfg);
- cfg->update_ops = sum_update_ops(cfg);
- cfg->truncate_ops = sum_truncate_ops(cfg);
+ wtperf->ckpt_ops = sum_ckpt_ops(wtperf);
+ wtperf->insert_ops = sum_insert_ops(wtperf);
+ wtperf->read_ops = sum_read_ops(wtperf);
+ wtperf->update_ops = sum_update_ops(wtperf);
+ wtperf->truncate_ops = sum_truncate_ops(wtperf);
/* If we're checking total operations, see if we're done. */
if (run_ops != 0 && run_ops <=
- cfg->insert_ops + cfg->read_ops + cfg->update_ops)
+ wtperf->insert_ops + wtperf->read_ops + wtperf->update_ops)
break;
/* If writing out throughput information, see if it's time. */
if (interval == 0 || --interval > 0)
continue;
- interval = cfg->report_interval;
- cfg->totalsec += cfg->report_interval;
+ interval = opts->report_interval;
+ wtperf->totalsec += opts->report_interval;
- lprintf(cfg, 0, 1,
+ lprintf(wtperf, 0, 1,
"%" PRIu64 " reads, %" PRIu64 " inserts, %" PRIu64
" updates, %" PRIu64 " truncates, %" PRIu64
" checkpoints in %" PRIu32 " secs (%" PRIu32 " total secs)",
- cfg->read_ops - last_reads,
- cfg->insert_ops - last_inserts,
- cfg->update_ops - last_updates,
- cfg->truncate_ops - last_truncates,
- cfg->ckpt_ops - last_ckpts,
- cfg->report_interval, cfg->totalsec);
- last_reads = cfg->read_ops;
- last_inserts = cfg->insert_ops;
- last_updates = cfg->update_ops;
- last_truncates = cfg->truncate_ops;
- last_ckpts = cfg->ckpt_ops;
+ wtperf->read_ops - last_reads,
+ wtperf->insert_ops - last_inserts,
+ wtperf->update_ops - last_updates,
+ wtperf->truncate_ops - last_truncates,
+ wtperf->ckpt_ops - last_ckpts,
+ opts->report_interval, wtperf->totalsec);
+ last_reads = wtperf->read_ops;
+ last_inserts = wtperf->insert_ops;
+ last_updates = wtperf->update_ops;
+ last_truncates = wtperf->truncate_ops;
+ last_ckpts = wtperf->ckpt_ops;
}
/* Notify the worker threads they are done. */
-err: cfg->stop = 1;
+err: wtperf->stop = true;
/* Stop cycling idle tables. */
- if ((ret = stop_idle_table_cycle(cfg, idle_table_cycle_thread)) != 0)
+ if ((ret = stop_idle_table_cycle(wtperf, idle_table_cycle_thread)) != 0)
return (ret);
- if ((t_ret = stop_threads(
- cfg, (u_int)cfg->workers_cnt, cfg->workers)) != 0 && ret == 0)
+ if ((t_ret = stop_threads(wtperf,
+ (u_int)wtperf->workers_cnt, wtperf->workers)) != 0 && ret == 0)
ret = t_ret;
/* Drop tables if configured to and this isn't an error path */
- if (ret == 0 && cfg->drop_tables && (ret = drop_all_tables(cfg)) != 0)
- lprintf(cfg, ret, 0, "Drop tables failed.");
+ if (ret == 0 &&
+ opts->drop_tables && (ret = drop_all_tables(wtperf)) != 0)
+ lprintf(wtperf, ret, 0, "Drop tables failed.");
free(sessions);
/* Report if any worker threads didn't finish. */
- if (cfg->error != 0) {
- lprintf(cfg, WT_ERROR, 0,
+ if (wtperf->error) {
+ lprintf(wtperf, WT_ERROR, 0,
"Worker thread(s) exited without finishing.");
if (ret == 0)
ret = WT_ERROR;
@@ -1834,8 +1800,9 @@ err: cfg->stop = 1;
* existing table.
*/
static int
-find_table_count(CONFIG *cfg)
+find_table_count(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
WT_CONNECTION *conn;
WT_CURSOR *cursor;
WT_SESSION *session;
@@ -1843,29 +1810,30 @@ find_table_count(CONFIG *cfg)
int ret, t_ret;
char *key;
- conn = cfg->conn;
+ opts = wtperf->opts;
+ conn = wtperf->conn;
max_icount = 0;
if ((ret = conn->open_session(
- conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0,
+ conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0,
"find_table_count: open_session failed");
goto out;
}
- for (i = 0; i < cfg->table_count; i++) {
- if ((ret = session->open_cursor(session, cfg->uris[i],
+ for (i = 0; i < opts->table_count; i++) {
+ if ((ret = session->open_cursor(session, wtperf->uris[i],
NULL, NULL, &cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"find_table_count: open_cursor failed");
goto err;
}
if ((ret = cursor->prev(cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"find_table_count: cursor prev failed");
goto err;
}
if ((ret = cursor->get_key(cursor, &key)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"find_table_count: cursor get_key failed");
goto err;
}
@@ -1874,7 +1842,7 @@ find_table_count(CONFIG *cfg)
max_icount = table_icount;
if ((ret = cursor->close(cursor)) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"find_table_count: cursor close failed");
goto err;
}
@@ -1882,91 +1850,99 @@ find_table_count(CONFIG *cfg)
err: if ((t_ret = session->close(session, NULL)) != 0) {
if (ret == 0)
ret = t_ret;
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"find_table_count: session close failed");
}
- cfg->icount = max_icount;
+ opts->icount = max_icount;
out: return (ret);
}
/*
- * Populate the uri array if more than one table is being used.
+ * Populate the uri array.
*/
static void
-create_uris(CONFIG *cfg)
+create_uris(WTPERF *wtperf)
{
- size_t base_uri_len;
+ CONFIG_OPTS *opts;
+ size_t len;
uint32_t i;
- char *uri;
- base_uri_len = strlen(cfg->base_uri);
- cfg->uris = dcalloc(cfg->table_count, sizeof(char *));
- for (i = 0; i < cfg->table_count; i++) {
- uri = cfg->uris[i] = dcalloc(base_uri_len + 6, 1);
- /*
- * If there is only one table, just use base name.
- */
- if (cfg->table_count == 1)
- memcpy(uri, cfg->base_uri, base_uri_len);
+ opts = wtperf->opts;
+
+ wtperf->uris = dcalloc(opts->table_count, sizeof(char *));
+ len = strlen("table:") + strlen(opts->table_name) + 20;
+ for (i = 0; i < opts->table_count; i++) {
+ /* If there is only one table, just use the base name. */
+ wtperf->uris[i] = dmalloc(len);
+ if (opts->table_count == 1)
+ snprintf(wtperf->uris[i],
+ len, "table:%s", opts->table_name);
else
- sprintf(uri, "%s%05d", cfg->base_uri, i);
+ snprintf(wtperf->uris[i],
+ len, "table:%s%05d", opts->table_name, i);
}
/* Create the log-like-table URI. */
- cfg->log_table_uri = dcalloc(base_uri_len + 11, 1);
- sprintf(cfg->log_table_uri, "%s_log_table", cfg->base_uri);
+ len = strlen("table:") +
+ strlen(opts->table_name) + strlen("_log_table") + 1;
+ wtperf->log_table_uri = dmalloc(len);
+ snprintf(
+ wtperf->log_table_uri, len, "table:%s_log_table", opts->table_name);
}
static int
-create_tables(CONFIG *cfg)
+create_tables(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
WT_SESSION *session;
size_t i;
int ret;
char buf[512];
- if ((ret = cfg->conn->open_session(
- cfg->conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0,
- "Error opening a session on %s", cfg->home);
+ opts = wtperf->opts;
+
+ if ((ret = wtperf->conn->open_session(
+ wtperf->conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "Error opening a session on %s", wtperf->home);
return (ret);
}
- for (i = 0; i < cfg->table_count_idle; i++) {
- snprintf(buf, 512, "%s_idle%05d", cfg->uris[0], (int)i);
+ for (i = 0; i < opts->table_count_idle; i++) {
+ snprintf(buf, 512, "%s_idle%05d", wtperf->uris[0], (int)i);
if ((ret = session->create(
- session, buf, cfg->table_config)) != 0) {
- lprintf(cfg, ret, 0,
+ session, buf, opts->table_config)) != 0) {
+ lprintf(wtperf, ret, 0,
"Error creating idle table %s", buf);
return (ret);
}
}
- if (cfg->log_like_table && (ret = session->create(session,
- cfg->log_table_uri, "key_format=Q,value_format=S")) != 0) {
- lprintf(cfg, ret, 0, "Error creating log table %s", buf);
+ if (opts->log_like_table && (ret = session->create(session,
+ wtperf->log_table_uri, "key_format=Q,value_format=S")) != 0) {
+ lprintf(wtperf, ret, 0, "Error creating log table %s", buf);
return (ret);
}
- for (i = 0; i < cfg->table_count; i++) {
- if (cfg->log_partial && i > 0) {
+ for (i = 0; i < opts->table_count; i++) {
+ if (opts->log_partial && i > 0) {
if (((ret = session->create(session,
- cfg->uris[i], cfg->partial_config)) != 0)) {
- lprintf(cfg, ret, 0,
- "Error creating table %s", cfg->uris[i]);
+ wtperf->uris[i], wtperf->partial_config)) != 0)) {
+ lprintf(wtperf, ret, 0,
+ "Error creating table %s", wtperf->uris[i]);
return (ret);
}
} else if ((ret = session->create(
- session, cfg->uris[i], cfg->table_config)) != 0) {
- lprintf(cfg, ret, 0,
- "Error creating table %s", cfg->uris[i]);
+ session, wtperf->uris[i], opts->table_config)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "Error creating table %s", wtperf->uris[i]);
return (ret);
}
- if (cfg->index) {
+ if (opts->index) {
snprintf(buf, 512, "index:%s:val_idx",
- cfg->uris[i] + strlen("table:"));
+ wtperf->uris[i] + strlen("table:"));
if ((ret = session->create(
session, buf, "columns=(val)")) != 0) {
- lprintf(cfg, ret, 0,
+ lprintf(wtperf, ret, 0,
"Error creating index %s", buf);
return (ret);
}
@@ -1974,76 +1950,213 @@ create_tables(CONFIG *cfg)
}
if ((ret = session->close(session, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error closing session");
+ lprintf(wtperf, ret, 0, "Error closing session");
return (ret);
}
return (0);
}
+/*
+ * wtperf_copy --
+ * Create a new WTPERF structure as a duplicate of a previous one.
+ */
+static void
+wtperf_copy(const WTPERF *src, WTPERF **retp)
+{
+ CONFIG_OPTS *opts;
+ WTPERF *dest;
+ size_t i;
+
+ opts = src->opts;
+
+ dest = dcalloc(1, sizeof(WTPERF));
+
+ /*
+ * Don't copy the home and monitor directories, they are filled in by
+ * our caller, explicitly.
+ */
+
+ if (src->partial_config != NULL)
+ dest->partial_config = dstrdup(src->partial_config);
+ if (src->reopen_config != NULL)
+ dest->reopen_config = dstrdup(src->reopen_config);
+
+ if (src->uris != NULL) {
+ dest->uris = dcalloc(opts->table_count, sizeof(char *));
+ for (i = 0; i < opts->table_count; i++)
+ dest->uris[i] = dstrdup(src->uris[i]);
+ }
+
+ if (src->async_config != NULL)
+ dest->async_config = dstrdup(src->async_config);
+
+ dest->ckptthreads = NULL;
+ dest->popthreads = NULL;
+
+ dest->workers = NULL;
+ dest->workers_cnt = src->workers_cnt;
+ if (src->workload_cnt != 0) {
+ dest->workload_cnt = src->workload_cnt;
+ dest->workload = dcalloc(src->workload_cnt, sizeof(WORKLOAD));
+ memcpy(dest->workload,
+ src->workload, src->workload_cnt * sizeof(WORKLOAD));
+ }
+
+ TAILQ_INIT(&dest->stone_head);
+
+ dest->opts = src->opts;
+
+ *retp = dest;
+}
+
+/*
+ * wtperf_free --
+ * Free any storage allocated in the WTPERF structure.
+ */
+static void
+wtperf_free(WTPERF *wtperf)
+{
+ CONFIG_OPTS *opts;
+ size_t i;
+
+ opts = wtperf->opts;
+
+ free(wtperf->home);
+ free(wtperf->monitor_dir);
+ free(wtperf->partial_config);
+ free(wtperf->reopen_config);
+ free(wtperf->log_table_uri);
+
+ if (wtperf->uris != NULL) {
+ for (i = 0; i < opts->table_count; i++)
+ free(wtperf->uris[i]);
+ free(wtperf->uris);
+ }
+
+ free(wtperf->async_config);
+
+ free(wtperf->ckptthreads);
+ free(wtperf->popthreads);
+
+ free(wtperf->workers);
+ free(wtperf->workload);
+
+ cleanup_truncate_config(wtperf);
+}
+
+/*
+ * config_compress --
+ * Parse the compression configuration.
+ */
+static int
+config_compress(WTPERF *wtperf)
+{
+ CONFIG_OPTS *opts;
+ int ret;
+ const char *s;
+
+ opts = wtperf->opts;
+ ret = 0;
+
+ s = opts->compression;
+ if (strcmp(s, "none") == 0) {
+ wtperf->compress_ext = NULL;
+ wtperf->compress_table = NULL;
+ } else if (strcmp(s, "lz4") == 0) {
+#ifndef HAVE_BUILTIN_EXTENSION_LZ4
+ wtperf->compress_ext = LZ4_EXT;
+#endif
+ wtperf->compress_table = LZ4_BLK;
+ } else if (strcmp(s, "snappy") == 0) {
+#ifndef HAVE_BUILTIN_EXTENSION_SNAPPY
+ wtperf->compress_ext = SNAPPY_EXT;
+#endif
+ wtperf->compress_table = SNAPPY_BLK;
+ } else if (strcmp(s, "zlib") == 0) {
+#ifndef HAVE_BUILTIN_EXTENSION_ZLIB
+ wtperf->compress_ext = ZLIB_EXT;
+#endif
+ wtperf->compress_table = ZLIB_BLK;
+ } else if (strcmp(s, "zstd") == 0) {
+#ifndef HAVE_BUILTIN_EXTENSION_ZSTD
+ wtperf->compress_ext = ZSTD_EXT;
+#endif
+ wtperf->compress_table = ZSTD_BLK;
+ } else {
+ fprintf(stderr,
+ "invalid compression configuration: %s\n", s);
+ ret = EINVAL;
+ }
+ return (ret);
+
+}
+
static int
-start_all_runs(CONFIG *cfg)
+start_all_runs(WTPERF *wtperf)
{
- CONFIG *next_cfg, **configs;
+ CONFIG_OPTS *opts;
+ WTPERF *next_wtperf, **wtperfs;
pthread_t *threads;
- size_t home_len, i;
+ size_t i, len;
int ret, t_ret;
- char *new_home;
+ opts = wtperf->opts;
+ wtperfs = NULL;
ret = 0;
- configs = NULL;
- if (cfg->database_count == 1)
- return (start_run(cfg));
+ if (opts->database_count == 1)
+ return (start_run(wtperf));
- /* Allocate an array to hold our config struct copies. */
- configs = dcalloc(cfg->database_count, sizeof(CONFIG *));
+ /* Allocate an array to hold our WTPERF copies. */
+ wtperfs = dcalloc(opts->database_count, sizeof(WTPERF *));
/* Allocate an array to hold our thread IDs. */
- threads = dcalloc(cfg->database_count, sizeof(pthread_t));
-
- home_len = strlen(cfg->home);
- for (i = 0; i < cfg->database_count; i++) {
- next_cfg = dcalloc(1, sizeof(CONFIG));
- configs[i] = next_cfg;
- config_copy(next_cfg, cfg);
-
- /* Setup a unique home directory for each database. */
- new_home = dmalloc(home_len + 5);
- snprintf(new_home, home_len + 5, "%s/D%02d", cfg->home, (int)i);
- free(next_cfg->home);
- next_cfg->home = new_home;
-
- /* If the monitor dir is default, update it too. */
- if (strcmp(cfg->monitor_dir, cfg->home) == 0) {
- free(next_cfg->monitor_dir);
- next_cfg->monitor_dir = dstrdup(new_home);
- }
+ threads = dcalloc(opts->database_count, sizeof(pthread_t));
- /* If creating the sub-database, recreate its home */
- if (cfg->create != 0)
- recreate_dir(next_cfg->home);
+ for (i = 0; i < opts->database_count; i++) {
+ wtperf_copy(wtperf, &next_wtperf);
+ wtperfs[i] = next_wtperf;
+
+ /*
+ * Set up unique home/monitor directories for each database.
+ * Re-create the directories if creating the databases.
+ */
+ len = strlen(wtperf->home) + 5;
+ next_wtperf->home = dmalloc(len);
+ snprintf(
+ next_wtperf->home, len, "%s/D%02d", wtperf->home, (int)i);
+ if (opts->create != 0)
+ recreate_dir(next_wtperf->home);
+
+ len = strlen(wtperf->monitor_dir) + 5;
+ next_wtperf->monitor_dir = dmalloc(len);
+ snprintf(next_wtperf->monitor_dir,
+ len, "%s/D%02d", wtperf->monitor_dir, (int)i);
+ if (opts->create != 0 &&
+ strcmp(next_wtperf->home, next_wtperf->monitor_dir) != 0)
+ recreate_dir(next_wtperf->monitor_dir);
if ((ret = pthread_create(
- &threads[i], NULL, thread_run_wtperf, next_cfg)) != 0) {
- lprintf(cfg, ret, 0, "Error creating thread");
+ &threads[i], NULL, thread_run_wtperf, next_wtperf)) != 0) {
+ lprintf(wtperf, ret, 0, "Error creating thread");
goto err;
}
}
/* Wait for threads to finish. */
- for (i = 0; i < cfg->database_count; i++)
+ for (i = 0; i < opts->database_count; i++)
if ((t_ret = pthread_join(threads[i], NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error joining thread");
+ lprintf(wtperf, ret, 0, "Error joining thread");
if (ret == 0)
ret = t_ret;
}
-err: for (i = 0; i < cfg->database_count && configs[i] != NULL; i++) {
- config_free(configs[i]);
- free(configs[i]);
+err: for (i = 0; i < opts->database_count && wtperfs[i] != NULL; i++) {
+ wtperf_free(wtperfs[i]);
+ free(wtperfs[i]);
}
- free(configs);
+ free(wtperfs);
free(threads);
return (ret);
@@ -2053,120 +2166,124 @@ err: for (i = 0; i < cfg->database_count && configs[i] != NULL; i++) {
static void *
thread_run_wtperf(void *arg)
{
- CONFIG *cfg;
+ WTPERF *wtperf;
int ret;
- cfg = (CONFIG *)arg;
- if ((ret = start_run(cfg)) != 0)
- lprintf(cfg, ret, 0, "Run failed for: %s.", cfg->home);
+ wtperf = (WTPERF *)arg;
+ if ((ret = start_run(wtperf)) != 0)
+ lprintf(wtperf, ret, 0, "Run failed for: %s.", wtperf->home);
return (NULL);
}
static int
-start_run(CONFIG *cfg)
+start_run(WTPERF *wtperf)
{
+ CONFIG_OPTS *opts;
pthread_t monitor_thread;
uint64_t total_ops;
uint32_t run_time;
int monitor_created, ret, t_ret;
+ opts = wtperf->opts;
monitor_created = ret = 0;
/* [-Wconditional-uninitialized] */
memset(&monitor_thread, 0, sizeof(monitor_thread));
- if ((ret = setup_log_file(cfg)) != 0)
+ if ((ret = setup_log_file(wtperf)) != 0)
goto err;
if ((ret = wiredtiger_open( /* Open the real connection. */
- cfg->home, NULL, cfg->conn_config, &cfg->conn)) != 0) {
- lprintf(cfg, ret, 0, "Error connecting to %s", cfg->home);
+ wtperf->home, NULL, opts->conn_config, &wtperf->conn)) != 0) {
+ lprintf(wtperf, ret, 0, "Error connecting to %s", wtperf->home);
goto err;
}
- create_uris(cfg);
+ create_uris(wtperf);
/* If creating, create the tables. */
- if (cfg->create != 0 && (ret = create_tables(cfg)) != 0)
+ if (opts->create != 0 && (ret = create_tables(wtperf)) != 0)
goto err;
/* Start the monitor thread. */
- if (cfg->sample_interval != 0) {
+ if (opts->sample_interval != 0) {
if ((ret = pthread_create(
- &monitor_thread, NULL, monitor, cfg)) != 0) {
- lprintf(
- cfg, ret, 0, "Error creating monitor thread.");
+ &monitor_thread, NULL, monitor, wtperf)) != 0) {
+ lprintf(wtperf,
+ ret, 0, "Error creating monitor thread.");
goto err;
}
monitor_created = 1;
}
/* If creating, populate the table. */
- if (cfg->create != 0 && execute_populate(cfg) != 0)
+ if (opts->create != 0 && execute_populate(wtperf) != 0)
goto err;
/* Optional workload. */
- if (cfg->workers_cnt != 0 &&
- (cfg->run_time != 0 || cfg->run_ops != 0)) {
+ if (wtperf->workers_cnt != 0 &&
+ (opts->run_time != 0 || opts->run_ops != 0)) {
/*
* If we have a workload, close and reopen the connection so
* that LSM can detect read-only workloads.
*/
- if (close_reopen(cfg) != 0)
+ if (close_reopen(wtperf) != 0)
goto err;
/* Didn't create, set insert count. */
- if (cfg->create == 0 && cfg->random_range == 0 &&
- find_table_count(cfg) != 0)
+ if (opts->create == 0 &&
+ opts->random_range == 0 && find_table_count(wtperf) != 0)
goto err;
/* Start the checkpoint thread. */
- if (cfg->checkpoint_threads != 0) {
- lprintf(cfg, 0, 1,
+ if (opts->checkpoint_threads != 0) {
+ lprintf(wtperf, 0, 1,
"Starting %" PRIu32 " checkpoint thread(s)",
- cfg->checkpoint_threads);
- cfg->ckptthreads = dcalloc(
- cfg->checkpoint_threads, sizeof(CONFIG_THREAD));
- if (start_threads(cfg, NULL, cfg->ckptthreads,
- cfg->checkpoint_threads, checkpoint_worker) != 0)
+ opts->checkpoint_threads);
+ wtperf->ckptthreads = dcalloc(
+ opts->checkpoint_threads, sizeof(WTPERF_THREAD));
+ if (start_threads(wtperf, NULL, wtperf->ckptthreads,
+ opts->checkpoint_threads, checkpoint_worker) != 0)
goto err;
}
/* Execute the workload. */
- if ((ret = execute_workload(cfg)) != 0)
+ if ((ret = execute_workload(wtperf)) != 0)
goto err;
/* One final summation of the operations we've completed. */
- cfg->read_ops = sum_read_ops(cfg);
- cfg->insert_ops = sum_insert_ops(cfg);
- cfg->truncate_ops = sum_truncate_ops(cfg);
- cfg->update_ops = sum_update_ops(cfg);
- cfg->ckpt_ops = sum_ckpt_ops(cfg);
- total_ops = cfg->read_ops + cfg->insert_ops + cfg->update_ops;
-
- run_time = cfg->run_time == 0 ? 1 : cfg->run_time;
- lprintf(cfg, 0, 1,
+ wtperf->read_ops = sum_read_ops(wtperf);
+ wtperf->insert_ops = sum_insert_ops(wtperf);
+ wtperf->truncate_ops = sum_truncate_ops(wtperf);
+ wtperf->update_ops = sum_update_ops(wtperf);
+ wtperf->ckpt_ops = sum_ckpt_ops(wtperf);
+ total_ops =
+ wtperf->read_ops + wtperf->insert_ops + wtperf->update_ops;
+
+ run_time = opts->run_time == 0 ? 1 : opts->run_time;
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu64 " read operations (%" PRIu64
"%%) %" PRIu64 " ops/sec",
- cfg->read_ops, (cfg->read_ops * 100) / total_ops,
- cfg->read_ops / run_time);
- lprintf(cfg, 0, 1,
+ wtperf->read_ops, (wtperf->read_ops * 100) / total_ops,
+ wtperf->read_ops / run_time);
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu64 " insert operations (%" PRIu64
"%%) %" PRIu64 " ops/sec",
- cfg->insert_ops, (cfg->insert_ops * 100) / total_ops,
- cfg->insert_ops / run_time);
- lprintf(cfg, 0, 1,
+ wtperf->insert_ops, (wtperf->insert_ops * 100) / total_ops,
+ wtperf->insert_ops / run_time);
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu64 " truncate operations (%" PRIu64
"%%) %" PRIu64 " ops/sec",
- cfg->truncate_ops, (cfg->truncate_ops * 100) / total_ops,
- cfg->truncate_ops / run_time);
- lprintf(cfg, 0, 1,
+ wtperf->truncate_ops,
+ (wtperf->truncate_ops * 100) / total_ops,
+ wtperf->truncate_ops / run_time);
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu64 " update operations (%" PRIu64
"%%) %" PRIu64 " ops/sec",
- cfg->update_ops, (cfg->update_ops * 100) / total_ops,
- cfg->update_ops / run_time);
- lprintf(cfg, 0, 1,
+ wtperf->update_ops, (wtperf->update_ops * 100) / total_ops,
+ wtperf->update_ops / run_time);
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu64 " checkpoint operations",
- cfg->ckpt_ops);
+ wtperf->ckpt_ops);
- latency_print(cfg);
+ latency_print(wtperf);
}
if (0) {
@@ -2175,40 +2292,41 @@ err: if (ret == 0)
}
/* Notify the worker threads they are done. */
- cfg->stop = 1;
+ wtperf->stop = true;
- if ((t_ret = stop_threads(cfg, 1, cfg->ckptthreads)) != 0)
+ if ((t_ret = stop_threads(wtperf, 1, wtperf->ckptthreads)) != 0)
if (ret == 0)
ret = t_ret;
if (monitor_created != 0 &&
(t_ret = pthread_join(monitor_thread, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error joining monitor thread.");
+ lprintf(wtperf, ret, 0, "Error joining monitor thread.");
if (ret == 0)
ret = t_ret;
}
- if (cfg->conn != NULL &&
- (t_ret = cfg->conn->close(cfg->conn, NULL)) != 0) {
- lprintf(cfg, t_ret, 0,
- "Error closing connection to %s", cfg->home);
+ if (wtperf->conn != NULL && opts->close_conn &&
+ (t_ret = wtperf->conn->close(wtperf->conn, NULL)) != 0) {
+ lprintf(wtperf, t_ret, 0,
+ "Error closing connection to %s", wtperf->home);
if (ret == 0)
ret = t_ret;
}
if (ret == 0) {
- if (cfg->run_time == 0 && cfg->run_ops == 0)
- lprintf(cfg, 0, 1, "Run completed");
+ if (opts->run_time == 0 && opts->run_ops == 0)
+ lprintf(wtperf, 0, 1, "Run completed");
else
- lprintf(cfg, 0, 1, "Run completed: %" PRIu32 " %s",
- cfg->run_time == 0 ? cfg->run_ops : cfg->run_time,
- cfg->run_time == 0 ? "operations" : "seconds");
+ lprintf(wtperf, 0, 1, "Run completed: %" PRIu32 " %s",
+ opts->run_time == 0 ?
+ opts->run_ops : opts->run_time,
+ opts->run_time == 0 ? "operations" : "seconds");
}
- if (cfg->logf != NULL) {
- if ((t_ret = fflush(cfg->logf)) != 0 && ret == 0)
+ if (wtperf->logf != NULL) {
+ if ((t_ret = fflush(wtperf->logf)) != 0 && ret == 0)
ret = t_ret;
- if ((t_ret = fclose(cfg->logf)) != 0 && ret == 0)
+ if ((t_ret = fclose(wtperf->logf)) != 0 && ret == 0)
ret = t_ret;
}
return (ret);
@@ -2216,33 +2334,56 @@ err: if (ret == 0)
extern int __wt_optind, __wt_optreset;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
+
+/*
+ * usage --
+ * wtperf usage print, no error.
+ */
+static void
+usage(void)
+{
+ printf("wtperf [-C config] "
+ "[-H mount] [-h home] [-O file] [-o option] [-T config]\n");
+ printf("\t-C <string> additional connection configuration\n");
+ printf("\t (added to option conn_config)\n");
+ printf("\t-H <mount> configure Helium volume mount point\n");
+ printf("\t-h <string> Wired Tiger home must exist, default WT_TEST\n");
+ printf("\t-O <file> file contains options as listed below\n");
+ printf("\t-o option=val[,option=val,...] set options listed below\n");
+ printf("\t-T <string> additional table configuration\n");
+ printf("\t (added to option table_config)\n");
+ printf("\n");
+ config_opt_usage();
+}
int
main(int argc, char *argv[])
{
- CONFIG *cfg, _cfg;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf, _wtperf;
size_t req_len, sreq_len;
bool monitor_set;
int ch, ret;
- const char *opts = "C:h:m:O:o:T:";
+ const char *cmdflags = "C:h:m:O:o:T:";
const char *config_opts;
- char *cc_buf, *sess_cfg, *tc_buf, *user_cconfig, *user_tconfig;
+ char *cc_buf, *path, *sess_cfg, *tc_buf, *user_cconfig, *user_tconfig;
+
+ /* The first WTPERF structure (from which all others are derived). */
+ wtperf = &_wtperf;
+ memset(wtperf, 0, sizeof(*wtperf));
+ wtperf->home = dstrdup(DEFAULT_HOME);
+ wtperf->monitor_dir = dstrdup(DEFAULT_MONITOR_DIR);
+ TAILQ_INIT(&wtperf->stone_head);
+ config_opt_init(&wtperf->opts);
+ opts = wtperf->opts;
monitor_set = false;
ret = 0;
config_opts = NULL;
cc_buf = sess_cfg = tc_buf = user_cconfig = user_tconfig = NULL;
- /* Setup the default configuration values. */
- cfg = &_cfg;
- memset(cfg, 0, sizeof(*cfg));
- config_copy(cfg, &default_cfg);
- cfg->home = dstrdup(DEFAULT_HOME);
- cfg->monitor_dir = dstrdup(DEFAULT_MONITOR_DIR);
-
/* Do a basic validation of options, and home is needed before open. */
- while ((ch = __wt_getopt("wtperf", argc, argv, opts)) != EOF)
+ while ((ch = __wt_getopt("wtperf", argc, argv, cmdflags)) != EOF)
switch (ch) {
case 'C':
if (user_cconfig == NULL)
@@ -2256,12 +2397,12 @@ main(int argc, char *argv[])
}
break;
case 'h':
- free(cfg->home);
- cfg->home = dstrdup(__wt_optarg);
+ free(wtperf->home);
+ wtperf->home = dstrdup(__wt_optarg);
break;
case 'm':
- free(cfg->monitor_dir);
- cfg->monitor_dir = dstrdup(__wt_optarg);
+ free(wtperf->monitor_dir);
+ wtperf->monitor_dir = dstrdup(__wt_optarg);
monitor_set = true;
break;
case 'O':
@@ -2288,47 +2429,48 @@ main(int argc, char *argv[])
* monitor directory to the home dir.
*/
if (!monitor_set) {
- free(cfg->monitor_dir);
- cfg->monitor_dir = dstrdup(cfg->home);
+ free(wtperf->monitor_dir);
+ wtperf->monitor_dir = dstrdup(wtperf->home);
}
/* Parse configuration settings from configuration file. */
- if (config_opts != NULL && config_opt_file(cfg, config_opts) != 0)
+ if (config_opts != NULL && config_opt_file(wtperf, config_opts) != 0)
goto einval;
/* Parse options that override values set via a configuration file. */
__wt_optreset = __wt_optind = 1;
- while ((ch = __wt_getopt("wtperf", argc, argv, opts)) != EOF)
+ while ((ch = __wt_getopt("wtperf", argc, argv, cmdflags)) != EOF)
switch (ch) {
case 'o':
/* Allow -o key=value */
- if (config_opt_line(cfg, __wt_optarg) != 0)
+ if (config_opt_str(wtperf, __wt_optarg) != 0)
goto einval;
break;
}
- if (cfg->populate_threads == 0 && cfg->icount != 0) {
- lprintf(cfg, 1, 0,
+ if (opts->populate_threads == 0 && opts->icount != 0) {
+ lprintf(wtperf, 1, 0,
"Cannot have 0 populate threads when icount is set\n");
goto err;
}
- cfg->async_config = NULL;
+ wtperf->async_config = NULL;
/*
* If the user specified async_threads we use async for all ops.
* If the user wants compaction, then we also enable async for
* the compact operation, but not for the workloads.
*/
- if (cfg->async_threads > 0) {
- if (F_ISSET(cfg, CFG_TRUNCATE)) {
- lprintf(cfg, 1, 0, "Cannot run truncate and async\n");
+ if (opts->async_threads > 0) {
+ if (F_ISSET(wtperf, CFG_TRUNCATE)) {
+ lprintf(wtperf,
+ 1, 0, "Cannot run truncate and async\n");
goto err;
}
- cfg->use_asyncops = 1;
+ wtperf->use_asyncops = true;
}
- if (cfg->compact && cfg->async_threads == 0)
- cfg->async_threads = 2;
- if (cfg->async_threads > 0) {
+ if (opts->compact && opts->async_threads == 0)
+ opts->async_threads = 2;
+ if (opts->async_threads > 0) {
/*
* The maximum number of async threads is two digits, so just
* use that to compute the space we need. Assume the default
@@ -2336,145 +2478,133 @@ main(int argc, char *argv[])
* to 4096 if needed.
*/
req_len = strlen(",async=(enabled=true,threads=)") + 4;
- cfg->async_config = dmalloc(req_len);
- snprintf(cfg->async_config, req_len,
+ wtperf->async_config = dmalloc(req_len);
+ snprintf(wtperf->async_config, req_len,
",async=(enabled=true,threads=%" PRIu32 ")",
- cfg->async_threads);
+ opts->async_threads);
}
- if ((ret = config_compress(cfg)) != 0)
+ if ((ret = config_compress(wtperf)) != 0)
goto err;
/* You can't have truncate on a random collection. */
- if (F_ISSET(cfg, CFG_TRUNCATE) && cfg->random_range) {
- lprintf(cfg, 1, 0, "Cannot run truncate and random_range\n");
+ if (F_ISSET(wtperf, CFG_TRUNCATE) && opts->random_range) {
+ lprintf(wtperf, 1, 0, "Cannot run truncate and random_range\n");
goto err;
}
/* We can't run truncate with more than one table. */
- if (F_ISSET(cfg, CFG_TRUNCATE) && cfg->table_count > 1) {
- lprintf(cfg, 1, 0, "Cannot truncate more than 1 table\n");
+ if (F_ISSET(wtperf, CFG_TRUNCATE) && opts->table_count > 1) {
+ lprintf(wtperf, 1, 0, "Cannot truncate more than 1 table\n");
goto err;
}
- /* Build the URI from the table name. */
- req_len = strlen("table:") + strlen(cfg->table_name) + 2;
- cfg->base_uri = dmalloc(req_len);
- snprintf(cfg->base_uri, req_len, "table:%s", cfg->table_name);
-
/* Make stdout line buffered, so verbose output appears quickly. */
__wt_stream_set_line_buffer(stdout);
/* Concatenate non-default configuration strings. */
- if (cfg->verbose > 1 || user_cconfig != NULL ||
- cfg->session_count_idle > 0 || cfg->compress_ext != NULL ||
- cfg->async_config != NULL) {
- req_len = strlen(debug_cconfig) + 3;
+ if (opts->verbose > 1 || user_cconfig != NULL ||
+ opts->session_count_idle > 0 || wtperf->compress_ext != NULL ||
+ wtperf->async_config != NULL) {
+ req_len = strlen(debug_cconfig) + 20;
if (user_cconfig != NULL)
req_len += strlen(user_cconfig);
- if (cfg->async_config != NULL)
- req_len += strlen(cfg->async_config);
- if (cfg->compress_ext != NULL)
- req_len += strlen(cfg->compress_ext);
- if (cfg->session_count_idle > 0) {
+ if (wtperf->async_config != NULL)
+ req_len += strlen(wtperf->async_config);
+ if (wtperf->compress_ext != NULL)
+ req_len += strlen(wtperf->compress_ext);
+ if (opts->session_count_idle > 0) {
sreq_len = strlen(",session_max=") + 6;
req_len += sreq_len;
sess_cfg = dmalloc(sreq_len);
snprintf(sess_cfg, sreq_len,
",session_max=%" PRIu32,
- cfg->session_count_idle + cfg->workers_cnt +
- cfg->populate_threads + 10);
+ opts->session_count_idle +
+ wtperf->workers_cnt + opts->populate_threads + 10);
}
cc_buf = dmalloc(req_len);
- /*
- * This is getting hard to parse.
- */
- snprintf(cc_buf, req_len, "%s%s%s%s%s%s%s",
- cfg->async_config ? cfg->async_config : "",
- cfg->compress_ext ? cfg->compress_ext : "",
- cfg->verbose > 1 && strlen(debug_cconfig) ? ",": "",
- cfg->verbose > 1 &&
- strlen(debug_cconfig) ? debug_cconfig : "",
- sess_cfg ? sess_cfg : "",
- user_cconfig ? ",": "",
- user_cconfig ? user_cconfig : "");
- if (strlen(cc_buf))
- if ((ret = config_opt_str(
- cfg, "conn_config", cc_buf)) != 0)
- goto err;
+ snprintf(cc_buf, req_len, "%s,%s,%s,%s,%s",
+ wtperf->async_config ? wtperf->async_config : "",
+ wtperf->compress_ext ? wtperf->compress_ext : "",
+ opts->verbose > 1 ? debug_cconfig : "",
+ sess_cfg != NULL ? sess_cfg : "",
+ user_cconfig != NULL ? user_cconfig : "");
+ if (strlen(cc_buf) && (ret =
+ config_opt_name_value(wtperf, "conn_config", cc_buf)) != 0)
+ goto err;
}
- if (cfg->verbose > 1 || cfg->index ||
- user_tconfig != NULL || cfg->compress_table != NULL) {
- req_len = strlen(debug_tconfig) + 3;
+ if (opts->verbose > 1 || opts->index ||
+ user_tconfig != NULL || wtperf->compress_table != NULL) {
+ req_len = strlen(debug_tconfig) + 20;
if (user_tconfig != NULL)
req_len += strlen(user_tconfig);
- if (cfg->compress_table != NULL)
- req_len += strlen(cfg->compress_table);
- if (cfg->index)
+ if (wtperf->compress_table != NULL)
+ req_len += strlen(wtperf->compress_table);
+ if (opts->index)
req_len += strlen(INDEX_COL_NAMES);
tc_buf = dmalloc(req_len);
- /*
- * This is getting hard to parse.
- */
- snprintf(tc_buf, req_len, "%s%s%s%s%s%s",
- cfg->index ? INDEX_COL_NAMES : "",
- cfg->compress_table ? cfg->compress_table : "",
- cfg->verbose > 1 && strlen(debug_tconfig) ? ",": "",
- cfg->verbose > 1 &&
- strlen(debug_tconfig) ? debug_tconfig : "",
- user_tconfig ? ",": "",
+ snprintf(tc_buf, req_len, "%s,%s,%s,%s",
+ opts->index ? INDEX_COL_NAMES : "",
+ wtperf->compress_table != NULL ?
+ wtperf->compress_table : "",
+ opts->verbose > 1 ? debug_tconfig : "",
user_tconfig ? user_tconfig : "");
- if (strlen(tc_buf))
- if ((ret = config_opt_str(
- cfg, "table_config", tc_buf)) != 0)
- goto err;
+ if (strlen(tc_buf) && (ret =
+ config_opt_name_value(wtperf, "table_config", tc_buf)) != 0)
+ goto err;
}
- if (cfg->log_partial && cfg->table_count > 1) {
- req_len = strlen(cfg->table_config) +
+ if (opts->log_partial && opts->table_count > 1) {
+ req_len = strlen(opts->table_config) +
strlen(LOG_PARTIAL_CONFIG) + 1;
- cfg->partial_config = dmalloc(req_len);
- snprintf(cfg->partial_config, req_len, "%s%s",
- cfg->table_config, LOG_PARTIAL_CONFIG);
+ wtperf->partial_config = dmalloc(req_len);
+ snprintf(wtperf->partial_config, req_len, "%s%s",
+ opts->table_config, LOG_PARTIAL_CONFIG);
}
/*
* Set the config for reopen. If readonly add in that string.
* If not readonly then just copy the original conn_config.
*/
- if (cfg->readonly)
- req_len = strlen(cfg->conn_config) +
+ if (opts->readonly)
+ req_len = strlen(opts->conn_config) +
strlen(READONLY_CONFIG) + 1;
else
- req_len = strlen(cfg->conn_config) + 1;
- cfg->reopen_config = dmalloc(req_len);
- if (cfg->readonly)
- snprintf(cfg->reopen_config, req_len, "%s%s",
- cfg->conn_config, READONLY_CONFIG);
+ req_len = strlen(opts->conn_config) + 1;
+ wtperf->reopen_config = dmalloc(req_len);
+ if (opts->readonly)
+ snprintf(wtperf->reopen_config, req_len, "%s%s",
+ opts->conn_config, READONLY_CONFIG);
else
- snprintf(cfg->reopen_config, req_len, "%s",
- cfg->conn_config);
+ snprintf(wtperf->reopen_config,
+ req_len, "%s", opts->conn_config);
/* Sanity-check the configuration. */
- if ((ret = config_sanity(cfg)) != 0)
+ if ((ret = config_sanity(wtperf)) != 0)
goto err;
/* If creating, remove and re-create the home directory. */
- if (cfg->create != 0)
- recreate_dir(cfg->home);
+ if (opts->create != 0)
+ recreate_dir(wtperf->home);
/* Write a copy of the config. */
- config_to_file(cfg);
+ req_len = strlen(wtperf->home) + strlen("/CONFIG.wtperf") + 1;
+ path = dmalloc(req_len);
+ snprintf(path, req_len, "%s/CONFIG.wtperf", wtperf->home);
+ config_opt_log(opts, path);
+ free(path);
/* Display the configuration. */
- if (cfg->verbose > 1)
- config_print(cfg);
+ if (opts->verbose > 1)
+ config_opt_print(wtperf);
- if ((ret = start_all_runs(cfg)) != 0)
+ if ((ret = start_all_runs(wtperf)) != 0)
goto err;
if (0) {
einval: ret = EINVAL;
}
-err: config_free(cfg);
+err: wtperf_free(wtperf);
+ config_opt_cleanup(opts);
+
free(cc_buf);
free(sess_cfg);
free(tc_buf);
@@ -2485,26 +2615,26 @@ err: config_free(cfg);
}
static int
-start_threads(CONFIG *cfg,
- WORKLOAD *workp, CONFIG_THREAD *base, u_int num, void *(*func)(void *))
+start_threads(WTPERF *wtperf,
+ WORKLOAD *workp, WTPERF_THREAD *base, u_int num, void *(*func)(void *))
{
- CONFIG_THREAD *thread;
+ CONFIG_OPTS *opts;
+ WTPERF_THREAD *thread;
u_int i;
int ret;
+ opts = wtperf->opts;
+
/* Initialize the threads. */
for (i = 0, thread = base; i < num; ++i, ++thread) {
- thread->cfg = cfg;
+ thread->wtperf = wtperf;
thread->workload = workp;
/*
* We don't want the threads executing in lock-step, seed each
* one differently.
*/
- if ((ret = __wt_random_init_seed(NULL, &thread->rnd)) != 0) {
- lprintf(cfg, ret, 0, "Error initializing RNG");
- return (ret);
- }
+ __wt_random_init_seed(NULL, &thread->rnd);
/*
* Every thread gets a key/data buffer because we don't bother
@@ -2512,14 +2642,14 @@ start_threads(CONFIG *cfg,
* don't, it's not enough memory to bother. These buffers hold
* strings: trailing NUL is included in the size.
*/
- thread->key_buf = dcalloc(cfg->key_sz, 1);
- thread->value_buf = dcalloc(cfg->value_sz_max, 1);
+ thread->key_buf = dcalloc(opts->key_sz, 1);
+ thread->value_buf = dcalloc(opts->value_sz_max, 1);
/*
* Initialize and then toss in a bit of random values if needed.
*/
- memset(thread->value_buf, 'a', cfg->value_sz - 1);
- if (cfg->random_value)
+ memset(thread->value_buf, 'a', opts->value_sz - 1);
+ if (opts->random_value)
randomize_value(thread, thread->value_buf);
/*
@@ -2537,7 +2667,7 @@ start_threads(CONFIG *cfg,
for (i = 0, thread = base; i < num; ++i, ++thread)
if ((ret = pthread_create(
&thread->handle, NULL, func, thread)) != 0) {
- lprintf(cfg, ret, 0, "Error creating thread");
+ lprintf(wtperf, ret, 0, "Error creating thread");
return (ret);
}
@@ -2545,7 +2675,7 @@ start_threads(CONFIG *cfg,
}
static int
-stop_threads(CONFIG *cfg, u_int num, CONFIG_THREAD *threads)
+stop_threads(WTPERF *wtperf, u_int num, WTPERF_THREAD *threads)
{
u_int i;
int ret;
@@ -2555,7 +2685,7 @@ stop_threads(CONFIG *cfg, u_int num, CONFIG_THREAD *threads)
for (i = 0; i < num; ++i, ++threads) {
if ((ret = pthread_join(threads->handle, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Error joining thread");
+ lprintf(wtperf, ret, 0, "Error joining thread");
return (ret);
}
@@ -2588,35 +2718,38 @@ recreate_dir(const char *name)
}
static int
-drop_all_tables(CONFIG *cfg)
+drop_all_tables(WTPERF *wtperf)
{
struct timespec start, stop;
+ CONFIG_OPTS *opts;
WT_SESSION *session;
size_t i;
uint64_t msecs;
int ret, t_ret;
+ opts = wtperf->opts;
+
/* Drop any tables. */
- if ((ret = cfg->conn->open_session(
- cfg->conn, NULL, cfg->sess_config, &session)) != 0) {
- lprintf(cfg, ret, 0,
- "Error opening a session on %s", cfg->home);
+ if ((ret = wtperf->conn->open_session(
+ wtperf->conn, NULL, opts->sess_config, &session)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "Error opening a session on %s", wtperf->home);
return (ret);
}
- testutil_check(__wt_epoch(NULL, &start));
- for (i = 0; i < cfg->table_count; i++) {
- if ((ret = session->drop(
- session, cfg->uris[i], NULL)) != 0) {
- lprintf(cfg, ret, 0,
- "Error dropping table %s", cfg->uris[i]);
+ __wt_epoch(NULL, &start);
+ for (i = 0; i < opts->table_count; i++) {
+ if ((ret =
+ session->drop(session, wtperf->uris[i], NULL)) != 0) {
+ lprintf(wtperf, ret, 0,
+ "Error dropping table %s", wtperf->uris[i]);
goto err;
}
}
- testutil_check(__wt_epoch(NULL, &stop));
+ __wt_epoch(NULL, &stop);
msecs = WT_TIMEDIFF_MS(stop, start);
- lprintf(cfg, 0, 1,
+ lprintf(wtperf, 0, 1,
"Executed %" PRIu32 " drop operations average time %" PRIu64 "ms",
- cfg->table_count, msecs / cfg->table_count);
+ opts->table_count, msecs / opts->table_count);
err: if ((t_ret = session->close(session, NULL)) != 0 && ret == 0)
ret = t_ret;
@@ -2624,27 +2757,34 @@ err: if ((t_ret = session->close(session, NULL)) != 0 && ret == 0)
}
static uint64_t
-wtperf_value_range(CONFIG *cfg)
+wtperf_value_range(WTPERF *wtperf)
{
- if (cfg->random_range)
- return (cfg->icount + cfg->random_range);
+ CONFIG_OPTS *opts;
+
+ opts = wtperf->opts;
+
+ if (opts->random_range)
+ return (opts->icount + opts->random_range);
/*
* It is legal to configure a zero size populate phase, hide that
* from other code by pretending the range is 1 in that case.
*/
- if (cfg->icount + cfg->insert_key == 0)
+ if (opts->icount + wtperf->insert_key == 0)
return (1);
- return (cfg->icount + cfg->insert_key - (u_int)(cfg->workers_cnt + 1));
+ return (opts->icount +
+ wtperf->insert_key - (u_int)(wtperf->workers_cnt + 1));
}
static uint64_t
-wtperf_rand(CONFIG_THREAD *thread)
+wtperf_rand(WTPERF_THREAD *thread)
{
- CONFIG *cfg;
+ CONFIG_OPTS *opts;
+ WTPERF *wtperf;
double S1, S2, U;
uint64_t rval;
- cfg = thread->cfg;
+ wtperf = thread->wtperf;
+ opts = wtperf->opts;
/*
* Use WiredTiger's random number routine: it's lock-free and fairly
@@ -2653,11 +2793,11 @@ wtperf_rand(CONFIG_THREAD *thread)
rval = __wt_random(&thread->rnd);
/* Use Pareto distribution to give 80/20 hot/cold values. */
- if (cfg->pareto != 0) {
+ if (opts->pareto != 0) {
#define PARETO_SHAPE 1.5
S1 = (-1 / PARETO_SHAPE);
- S2 = wtperf_value_range(cfg) *
- (cfg->pareto / 100.0) * (PARETO_SHAPE - 1);
+ S2 = wtperf_value_range(wtperf) *
+ (opts->pareto / 100.0) * (PARETO_SHAPE - 1);
U = 1 - (double)rval / (double)UINT32_MAX;
rval = (uint64_t)((pow(U, S1) - 1) * S2);
/*
@@ -2665,13 +2805,13 @@ wtperf_rand(CONFIG_THREAD *thread)
* 2% of the time, from my testing. That will lead to the
* first item in the table being "hot".
*/
- if (rval > wtperf_value_range(cfg))
+ if (rval > wtperf_value_range(wtperf))
rval = 0;
}
/*
* Wrap the key to within the expected range and avoid zero: we never
* insert that key.
*/
- rval = (rval % wtperf_value_range(cfg)) + 1;
+ rval = (rval % wtperf_value_range(wtperf)) + 1;
return (rval);
}
diff --git a/src/third_party/wiredtiger/bench/wtperf/wtperf.h b/src/third_party/wiredtiger/bench/wtperf/wtperf.h
index 1bb94db2634..81d74e134f6 100644
--- a/src/third_party/wiredtiger/bench/wtperf/wtperf.h
+++ b/src/third_party/wiredtiger/bench/wtperf/wtperf.h
@@ -36,8 +36,8 @@
#include "config_opt.h"
-typedef struct __config CONFIG;
-typedef struct __config_thread CONFIG_THREAD;
+typedef struct __wtperf WTPERF;
+typedef struct __wtperf_thread WTPERF_THREAD;
typedef struct __truncate_queue_entry TRUNCATE_QUEUE_ENTRY;
#define EXT_PFX ",extensions=("
@@ -54,6 +54,9 @@ typedef struct __truncate_queue_entry TRUNCATE_QUEUE_ENTRY;
#define ZLIB_BLK BLKCMP_PFX "zlib"
#define ZLIB_EXT \
EXT_PFX EXTPATH "zlib/.libs/libwiredtiger_zlib.so" EXT_SFX
+#define ZSTD_BLK BLKCMP_PFX "zstd"
+#define ZSTD_EXT \
+ EXT_PFX EXTPATH "zstd/.libs/libwiredtiger_zstd.so" EXT_SFX
typedef struct {
int64_t threads; /* Thread count */
@@ -95,12 +98,6 @@ struct __truncate_queue_entry {
TAILQ_ENTRY(__truncate_queue_entry) q;
};
-struct __config_queue_entry {
- char *string;
- TAILQ_ENTRY(__config_queue_entry) c;
-};
-typedef struct __config_queue_entry CONFIG_QUEUE_ENTRY;
-
/* Steering for the throttle configuration */
typedef struct {
struct timespec last_increment; /* Time that we last added more ops */
@@ -111,40 +108,35 @@ typedef struct {
#define LOG_PARTIAL_CONFIG ",log=(enabled=false)"
#define READONLY_CONFIG ",readonly=true"
-/*
- * NOTE: If you add any fields to this structure here, you must also add
- * an initialization in wtperf.c in the default_cfg.
- */
-struct __config { /* Configuration structure */
+struct __wtperf { /* Per-database structure */
char *home; /* WiredTiger home */
char *monitor_dir; /* Monitor output dir */
char *partial_config; /* Config string for partial logging */
char *reopen_config; /* Config string for conn reopen */
- char *base_uri; /* Object URI */
- char *log_table_uri; /* URI for log table */
- char **uris; /* URIs if multiple tables */
+ char *log_table_uri; /* URI for log table */
+ char **uris; /* URIs */
WT_CONNECTION *conn; /* Database connection */
FILE *logf; /* Logging handle */
- char *async_config; /* Config string for async */
+ char *async_config; /* Config string for async */
+ bool use_asyncops; /* Use async operations */
const char *compress_ext; /* Compression extension for conn */
const char *compress_table; /* Compression arg to table create */
- CONFIG_THREAD *ckptthreads, *popthreads;
+ WTPERF_THREAD *ckptthreads; /* Checkpoint threads */
+ WTPERF_THREAD *popthreads; /* Populate threads */
#define WORKLOAD_MAX 50
- CONFIG_THREAD *workers; /* Worker threads */
+ WTPERF_THREAD *workers; /* Worker threads */
u_int workers_cnt;
WORKLOAD *workload; /* Workloads */
u_int workload_cnt;
- uint32_t use_asyncops; /* Use async operations */
/* State tracking variables. */
-
uint64_t ckpt_ops; /* checkpoint operations */
uint64_t insert_ops; /* insert operations */
uint64_t read_ops; /* read operations */
@@ -154,10 +146,10 @@ struct __config { /* Configuration structure */
uint64_t insert_key; /* insert key */
uint64_t log_like_table_key; /* used to allocate IDs for log table */
- volatile int ckpt; /* checkpoint in progress */
- volatile int error; /* thread error */
- volatile int stop; /* notify threads to stop */
- volatile int in_warmup; /* Running warmup phase */
+ volatile bool ckpt; /* checkpoint in progress */
+ volatile bool error; /* thread error */
+ volatile bool stop; /* notify threads to stop */
+ volatile bool in_warmup; /* running warmup phase */
volatile bool idle_cycle_run; /* Signal for idle cycle thread */
@@ -171,13 +163,7 @@ struct __config { /* Configuration structure */
/* Queue head for use with the Truncate Logic */
TAILQ_HEAD(__truncate_qh, __truncate_queue_entry) stone_head;
- /* Queue head to save a copy of the config to be output */
- TAILQ_HEAD(__config_qh, __config_queue_entry) config_head;
-
- /* Fields changeable on command line are listed in wtperf_opt.i */
-#define OPT_DECLARE_STRUCT
-#include "wtperf_opt.i"
-#undef OPT_DECLARE_STRUCT
+ CONFIG_OPTS *opts; /* Global configuration */
};
#define ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
@@ -237,8 +223,8 @@ typedef struct {
uint32_t sec[100]; /* < 1s 2s ... 100s */
} TRACK;
-struct __config_thread { /* Per-thread structure */
- CONFIG *cfg; /* Enclosing configuration */
+struct __wtperf_thread { /* Per-thread structure */
+ WTPERF *wtperf; /* Enclosing configuration */
WT_RAND_STATE rnd; /* Random number generation state */
@@ -260,50 +246,45 @@ struct __config_thread { /* Per-thread structure */
TRACK truncate_sleep; /* Truncate sleep operations */
};
-void cleanup_truncate_config(CONFIG *);
-int config_compress(CONFIG *);
-void config_free(CONFIG *);
-void config_copy(CONFIG *, const CONFIG *);
-int config_opt_file(CONFIG *, const char *);
-int config_opt_line(CONFIG *, const char *);
-int config_opt_str(CONFIG *, const char *, const char *);
-void config_to_file(CONFIG *);
-void config_consolidate(CONFIG *);
-void config_print(CONFIG *);
-int config_sanity(CONFIG *);
-void latency_insert(CONFIG *, uint32_t *, uint32_t *, uint32_t *);
-void latency_read(CONFIG *, uint32_t *, uint32_t *, uint32_t *);
-void latency_update(CONFIG *, uint32_t *, uint32_t *, uint32_t *);
-void latency_print(CONFIG *);
+void cleanup_truncate_config(WTPERF *);
+int config_opt_file(WTPERF *, const char *);
+void config_opt_cleanup(CONFIG_OPTS *);
+void config_opt_init(CONFIG_OPTS **);
+void config_opt_log(CONFIG_OPTS *, const char *);
+int config_opt_name_value(WTPERF *, const char *, const char *);
+void config_opt_print(WTPERF *);
+int config_opt_str(WTPERF *, const char *);
+void config_opt_usage(void);
+int config_sanity(WTPERF *);
+void latency_insert(WTPERF *, uint32_t *, uint32_t *, uint32_t *);
+void latency_print(WTPERF *);
+void latency_read(WTPERF *, uint32_t *, uint32_t *, uint32_t *);
+void latency_update(WTPERF *, uint32_t *, uint32_t *, uint32_t *);
int run_truncate(
- CONFIG *, CONFIG_THREAD *, WT_CURSOR *, WT_SESSION *, int *);
-int setup_log_file(CONFIG *);
-void setup_throttle(CONFIG_THREAD*);
-int setup_truncate(CONFIG *, CONFIG_THREAD *, WT_SESSION *);
-int start_idle_table_cycle(CONFIG *, pthread_t *);
-int stop_idle_table_cycle(CONFIG *, pthread_t);
-uint64_t sum_ckpt_ops(CONFIG *);
-uint64_t sum_insert_ops(CONFIG *);
-uint64_t sum_pop_ops(CONFIG *);
-uint64_t sum_read_ops(CONFIG *);
-uint64_t sum_truncate_ops(CONFIG *);
-uint64_t sum_update_ops(CONFIG *);
-void usage(void);
-void worker_throttle(CONFIG_THREAD*);
-
-void lprintf(const CONFIG *, int err, uint32_t, const char *, ...)
+ WTPERF *, WTPERF_THREAD *, WT_CURSOR *, WT_SESSION *, int *);
+int setup_log_file(WTPERF *);
+void setup_throttle(WTPERF_THREAD *);
+int setup_truncate(WTPERF *, WTPERF_THREAD *, WT_SESSION *);
+int start_idle_table_cycle(WTPERF *, pthread_t *);
+int stop_idle_table_cycle(WTPERF *, pthread_t);
+void worker_throttle(WTPERF_THREAD *);
+uint64_t sum_ckpt_ops(WTPERF *);
+uint64_t sum_insert_ops(WTPERF *);
+uint64_t sum_pop_ops(WTPERF *);
+uint64_t sum_read_ops(WTPERF *);
+uint64_t sum_truncate_ops(WTPERF *);
+uint64_t sum_update_ops(WTPERF *);
+
+void lprintf(const WTPERF *, int err, uint32_t, const char *, ...)
#if defined(__GNUC__)
__attribute__((format (printf, 4, 5)))
#endif
;
static inline void
-generate_key(CONFIG *cfg, char *key_buf, uint64_t keyno)
+generate_key(CONFIG_OPTS *opts, char *key_buf, uint64_t keyno)
{
- /*
- * Don't change to snprintf, sprintf is faster in some tests.
- */
- sprintf(key_buf, "%0*" PRIu64, cfg->key_sz - 1, keyno);
+ u64_to_string_zf(keyno, key_buf, opts->key_sz);
}
static inline void
diff --git a/src/third_party/wiredtiger/bench/wtperf/wtperf_opt.i b/src/third_party/wiredtiger/bench/wtperf/wtperf_opt.i
index 17517ffe477..680eb53a90e 100644
--- a/src/third_party/wiredtiger/bench/wtperf/wtperf_opt.i
+++ b/src/third_party/wiredtiger/bench/wtperf/wtperf_opt.i
@@ -38,14 +38,14 @@
#ifdef OPT_DEFINE_DESC
#define DEF_OPT_AS_BOOL(name, initval, desc) \
- { #name, desc, #initval, BOOL_TYPE, offsetof(CONFIG, name) },
+ { #name, desc, #initval, BOOL_TYPE, offsetof(CONFIG_OPTS, name) },
#define DEF_OPT_AS_CONFIG_STRING(name, initval, desc) \
{ #name, desc, initval, CONFIG_STRING_TYPE, \
- offsetof(CONFIG, name) },
+ offsetof(CONFIG_OPTS, name) },
#define DEF_OPT_AS_STRING(name, initval, desc) \
- { #name, desc, initval, STRING_TYPE, offsetof(CONFIG, name) },
+ { #name, desc, initval, STRING_TYPE, offsetof(CONFIG_OPTS, name) },
#define DEF_OPT_AS_UINT32(name, initval, desc) \
- { #name, desc, #initval, UINT32_TYPE, offsetof(CONFIG, name) },
+ { #name, desc, #initval, UINT32_TYPE, offsetof(CONFIG_OPTS, name) },
#endif
#ifdef OPT_DEFINE_DEFAULT
@@ -57,13 +57,13 @@
#ifdef OPT_DEFINE_DOXYGEN
#define DEF_OPT_AS_BOOL(name, initval, desc) \
- { #name, desc, #initval, BOOL_TYPE, 0 },
+ OPTION #name, desc, #initval, boolean
#define DEF_OPT_AS_CONFIG_STRING(name, initval, desc) \
- { #name, desc, initval, CONFIG_STRING_TYPE, 0 },
+ OPTION #name, desc, initval, string
#define DEF_OPT_AS_STRING(name, initval, desc) \
- { #name, desc, initval, STRING_TYPE, 0 },
+ OPTION #name, desc, initval, string
#define DEF_OPT_AS_UINT32(name, initval, desc) \
- { #name, desc, #initval, UINT32_TYPE, 0 },
+ OPTION #name, desc, #initval, unsigned int
#endif
/*
@@ -94,17 +94,20 @@ DEF_OPT_AS_UINT32(checkpoint_stress_rate, 0,
DEF_OPT_AS_UINT32(checkpoint_threads, 0, "number of checkpoint threads")
DEF_OPT_AS_CONFIG_STRING(conn_config, "create",
"connection configuration string")
+DEF_OPT_AS_BOOL(close_conn, 1, "properly close connection at end of test. "
+ "Setting to false does not sync data to disk and can result in lost "
+ "data after test exits.")
DEF_OPT_AS_BOOL(compact, 0, "post-populate compact for LSM merging activity")
DEF_OPT_AS_STRING(compression, "none",
"compression extension. Allowed configuration values are: "
- "'none', 'lz4', 'snappy', 'zlib'")
+ "'none', 'lz4', 'snappy', 'zlib', 'zstd'")
DEF_OPT_AS_BOOL(create, 1,
"do population phase; false to use existing database")
DEF_OPT_AS_UINT32(database_count, 1,
"number of WiredTiger databases to use. Each database will execute the"
" workload using a separate home directory and complete set of worker"
" threads")
-DEF_OPT_AS_UINT32(drop_tables, 0,
+DEF_OPT_AS_BOOL(drop_tables, 0,
"Whether to drop all tables at the end of the run, and report time taken"
" to do the drop.")
DEF_OPT_AS_UINT32(icount, 5000,
@@ -193,8 +196,8 @@ DEF_OPT_AS_STRING(threads, "", "workload configuration: each 'count' "
"'updates', 'truncate', 'truncate_pct' and 'truncate_count'. There are "
"also behavior modifiers, supported modifiers are 'ops_per_txn'")
DEF_OPT_AS_CONFIG_STRING(transaction_config, "",
- "transaction configuration string, relevant when populate_opts_per_txn "
- "is nonzero")
+ "WT_SESSION.begin_transaction configuration string, applied during the "
+ "populate phase when populate_ops_per_txn is nonzero")
DEF_OPT_AS_STRING(table_name, "test", "table name")
DEF_OPT_AS_BOOL(truncate_single_ops, 0,
"Implement truncate via cursor remove instead of session API")
diff --git a/src/third_party/wiredtiger/bench/wtperf/wtperf_throttle.c b/src/third_party/wiredtiger/bench/wtperf/wtperf_throttle.c
index e49bca00d07..d104a68175d 100644
--- a/src/third_party/wiredtiger/bench/wtperf/wtperf_throttle.c
+++ b/src/third_party/wiredtiger/bench/wtperf/wtperf_throttle.c
@@ -32,7 +32,7 @@
* Put the initial config together for running a throttled workload.
*/
void
-setup_throttle(CONFIG_THREAD *thread)
+setup_throttle(WTPERF_THREAD *thread)
{
THROTTLE_CONFIG *throttle_cfg;
@@ -70,7 +70,7 @@ setup_throttle(CONFIG_THREAD *thread)
throttle_cfg->ops_count = throttle_cfg->ops_per_increment;
/* Set the first timestamp of when we incremented */
- testutil_check(__wt_epoch(NULL, &throttle_cfg->last_increment));
+ __wt_epoch(NULL, &throttle_cfg->last_increment);
}
/*
@@ -78,7 +78,7 @@ setup_throttle(CONFIG_THREAD *thread)
* counter to perform more operations.
*/
void
-worker_throttle(CONFIG_THREAD *thread)
+worker_throttle(WTPERF_THREAD *thread)
{
THROTTLE_CONFIG *throttle_cfg;
struct timespec now;
@@ -86,7 +86,7 @@ worker_throttle(CONFIG_THREAD *thread)
throttle_cfg = &thread->throttle_cfg;
- testutil_check(__wt_epoch(NULL, &now));
+ __wt_epoch(NULL, &now);
/*
* If we did enough operations in the current interval, sleep for
@@ -101,7 +101,7 @@ worker_throttle(CONFIG_THREAD *thread)
/*
* After sleeping, set the interval to the current time.
*/
- testutil_check(__wt_epoch(NULL, &throttle_cfg->last_increment));
+ __wt_epoch(NULL, &throttle_cfg->last_increment);
} else {
throttle_cfg->ops_count = (usecs_delta *
throttle_cfg->ops_per_increment) /
diff --git a/src/third_party/wiredtiger/bench/wtperf/wtperf_truncate.c b/src/third_party/wiredtiger/bench/wtperf/wtperf_truncate.c
index e6ebc83c681..3fbb740d2c8 100644
--- a/src/third_party/wiredtiger/bench/wtperf/wtperf_truncate.c
+++ b/src/third_party/wiredtiger/bench/wtperf/wtperf_truncate.c
@@ -35,8 +35,9 @@ decode_key(char *key_buf)
}
int
-setup_truncate(CONFIG *cfg, CONFIG_THREAD *thread, WT_SESSION *session) {
-
+setup_truncate(WTPERF *wtperf, WTPERF_THREAD *thread, WT_SESSION *session)
+{
+ CONFIG_OPTS *opts;
TRUNCATE_CONFIG *trunc_cfg;
TRUNCATE_QUEUE_ENTRY *truncate_item;
WORKLOAD *workload;
@@ -45,13 +46,14 @@ setup_truncate(CONFIG *cfg, CONFIG_THREAD *thread, WT_SESSION *session) {
int ret;
uint64_t end_point, final_stone_gap, i, start_point;
+ opts = wtperf->opts;
end_point = final_stone_gap = start_point = 0;
trunc_cfg = &thread->trunc_cfg;
workload = thread->workload;
/* We are limited to only one table when running truncate. */
if ((ret = session->open_cursor(
- session, cfg->uris[0], NULL, NULL, &cursor)) != 0)
+ session, wtperf->uris[0], NULL, NULL, &cursor)) != 0)
goto err;
/*
@@ -79,14 +81,14 @@ setup_truncate(CONFIG *cfg, CONFIG_THREAD *thread, WT_SESSION *session) {
*/
if ((ret = cursor->next(cursor)) != 0 ||
(ret = cursor->get_key(cursor, &key)) != 0) {
- lprintf(cfg, ret, 0, "truncate setup start: failed");
+ lprintf(wtperf, ret, 0, "truncate setup start: failed");
goto err;
}
start_point = decode_key(key);
if ((cursor->reset(cursor)) != 0 || (ret = cursor->prev(cursor)) != 0 ||
(ret = cursor->get_key(cursor, &key)) != 0) {
- lprintf(cfg, ret, 0, "truncate setup end: failed");
+ lprintf(wtperf, ret, 0, "truncate setup end: failed");
goto err;
}
end_point = decode_key(key);
@@ -104,12 +106,13 @@ setup_truncate(CONFIG *cfg, CONFIG_THREAD *thread, WT_SESSION *session) {
for (i = 1; i <= trunc_cfg->needed_stones; i++) {
truncate_item =
dcalloc(sizeof(TRUNCATE_QUEUE_ENTRY), 1);
- truncate_item->key = dcalloc(cfg->key_sz, 1);
+ truncate_item->key = dcalloc(opts->key_sz, 1);
generate_key(
- cfg, truncate_item->key, trunc_cfg->stone_gap * i);
+ opts, truncate_item->key, trunc_cfg->stone_gap * i);
truncate_item->diff =
(trunc_cfg->stone_gap * i) - trunc_cfg->last_key;
- TAILQ_INSERT_TAIL(&cfg->stone_head, truncate_item, q);
+ TAILQ_INSERT_TAIL(
+ &wtperf->stone_head, truncate_item, q);
trunc_cfg->last_key = trunc_cfg->stone_gap * i;
trunc_cfg->num_stones++;
}
@@ -117,27 +120,29 @@ setup_truncate(CONFIG *cfg, CONFIG_THREAD *thread, WT_SESSION *session) {
trunc_cfg->stone_gap = final_stone_gap;
err: if ((ret = cursor->close(cursor)) != 0) {
- lprintf(cfg, ret, 0, "truncate setup: cursor close failed");
+ lprintf(wtperf, ret, 0, "truncate setup: cursor close failed");
}
return (ret);
}
int
-run_truncate(CONFIG *cfg, CONFIG_THREAD *thread,
- WT_CURSOR *cursor, WT_SESSION *session, int *truncatedp) {
-
+run_truncate(WTPERF *wtperf, WTPERF_THREAD *thread,
+ WT_CURSOR *cursor, WT_SESSION *session, int *truncatedp)
+{
+ CONFIG_OPTS *opts;
TRUNCATE_CONFIG *trunc_cfg;
TRUNCATE_QUEUE_ENTRY *truncate_item;
char *next_key;
int ret, t_ret;
uint64_t used_stone_gap;
- ret = 0;
+ opts = wtperf->opts;
trunc_cfg = &thread->trunc_cfg;
+ ret = 0;
*truncatedp = 0;
/* Update the total inserts */
- trunc_cfg->total_inserts = sum_insert_ops(cfg);
+ trunc_cfg->total_inserts = sum_insert_ops(wtperf);
trunc_cfg->expected_total +=
(trunc_cfg->total_inserts - trunc_cfg->last_total_inserts);
trunc_cfg->last_total_inserts = trunc_cfg->total_inserts;
@@ -170,10 +175,10 @@ run_truncate(CONFIG *cfg, CONFIG_THREAD *thread,
while (trunc_cfg->num_stones < trunc_cfg->needed_stones) {
trunc_cfg->last_key += used_stone_gap;
truncate_item = dcalloc(sizeof(TRUNCATE_QUEUE_ENTRY), 1);
- truncate_item->key = dcalloc(cfg->key_sz, 1);
- generate_key(cfg, truncate_item->key, trunc_cfg->last_key);
+ truncate_item->key = dcalloc(opts->key_sz, 1);
+ generate_key(opts, truncate_item->key, trunc_cfg->last_key);
truncate_item->diff = used_stone_gap;
- TAILQ_INSERT_TAIL(&cfg->stone_head, truncate_item, q);
+ TAILQ_INSERT_TAIL(&wtperf->stone_head, truncate_item, q);
trunc_cfg->num_stones++;
}
@@ -182,34 +187,35 @@ run_truncate(CONFIG *cfg, CONFIG_THREAD *thread,
trunc_cfg->expected_total <= thread->workload->truncate_count)
return (0);
- truncate_item = TAILQ_FIRST(&cfg->stone_head);
+ truncate_item = TAILQ_FIRST(&wtperf->stone_head);
trunc_cfg->num_stones--;
- TAILQ_REMOVE(&cfg->stone_head, truncate_item, q);
+ TAILQ_REMOVE(&wtperf->stone_head, truncate_item, q);
/*
* Truncate the content via a single truncate call or a cursor walk
* depending on the configuration.
*/
- if (cfg->truncate_single_ops) {
+ if (opts->truncate_single_ops) {
while ((ret = cursor->next(cursor)) == 0) {
testutil_check(cursor->get_key(cursor, &next_key));
if (strcmp(next_key, truncate_item->key) == 0)
break;
if ((ret = cursor->remove(cursor)) != 0) {
- lprintf(cfg, ret, 0, "Truncate remove: failed");
+ lprintf(wtperf,
+ ret, 0, "Truncate remove: failed");
goto err;
}
}
} else {
cursor->set_key(cursor,truncate_item->key);
if ((ret = cursor->search(cursor)) != 0) {
- lprintf(cfg, ret, 0, "Truncate search: failed");
+ lprintf(wtperf, ret, 0, "Truncate search: failed");
goto err;
}
if ((ret = session->truncate(
session, NULL, NULL, cursor, NULL)) != 0) {
- lprintf(cfg, ret, 0, "Truncate: failed");
+ lprintf(wtperf, ret, 0, "Truncate: failed");
goto err;
}
}
@@ -221,19 +227,20 @@ err: free(truncate_item->key);
free(truncate_item);
t_ret = cursor->reset(cursor);
if (t_ret != 0)
- lprintf(cfg, t_ret, 0, "Cursor reset failed");
+ lprintf(wtperf, t_ret, 0, "Cursor reset failed");
if (ret == 0 && t_ret != 0)
ret = t_ret;
return (ret);
}
void
-cleanup_truncate_config(CONFIG *cfg) {
+cleanup_truncate_config(WTPERF *wtperf)
+{
TRUNCATE_QUEUE_ENTRY *truncate_item;
- while (!TAILQ_EMPTY(&cfg->stone_head)) {
- truncate_item = TAILQ_FIRST(&cfg->stone_head);
- TAILQ_REMOVE(&cfg->stone_head, truncate_item, q);
+ while (!TAILQ_EMPTY(&wtperf->stone_head)) {
+ truncate_item = TAILQ_FIRST(&wtperf->stone_head);
+ TAILQ_REMOVE(&wtperf->stone_head, truncate_item, q);
free(truncate_item->key);
free(truncate_item);
}
diff --git a/src/third_party/wiredtiger/build_posix/Make.base b/src/third_party/wiredtiger/build_posix/Make.base
index 4efbe3f76c3..5b945aca5e0 100644
--- a/src/third_party/wiredtiger/build_posix/Make.base
+++ b/src/third_party/wiredtiger/build_posix/Make.base
@@ -77,6 +77,9 @@ endif
if HAVE_BUILTIN_EXTENSION_ZLIB
libwiredtiger_la_LIBADD += ext/compressors/zlib/libwiredtiger_zlib.la
endif
+if HAVE_BUILTIN_EXTENSION_ZSTD
+libwiredtiger_la_LIBADD += ext/compressors/zstd/libwiredtiger_zstd.la
+endif
libwiredtiger_static_la_LIBADD=$(libwiredtiger_la_LIBADD)
libwiredtiger_static_la_SOURCES=$(libwiredtiger_la_SOURCES)
diff --git a/src/third_party/wiredtiger/build_posix/Make.subdirs b/src/third_party/wiredtiger/build_posix/Make.subdirs
index 0b5175e4196..55941837249 100644
--- a/src/third_party/wiredtiger/build_posix/Make.subdirs
+++ b/src/third_party/wiredtiger/build_posix/Make.subdirs
@@ -11,6 +11,7 @@ ext/compressors/lz4 LZ4
ext/compressors/nop
ext/compressors/snappy SNAPPY
ext/compressors/zlib ZLIB
+ext/compressors/zstd ZSTD
ext/datasources/helium HAVE_HELIUM
ext/encryptors/nop
ext/encryptors/rotn
diff --git a/src/third_party/wiredtiger/build_posix/aclocal/options.m4 b/src/third_party/wiredtiger/build_posix/aclocal/options.m4
index 1f6a1690279..7043430a6d6 100644
--- a/src/third_party/wiredtiger/build_posix/aclocal/options.m4
+++ b/src/third_party/wiredtiger/build_posix/aclocal/options.m4
@@ -19,10 +19,12 @@ AH_TEMPLATE(HAVE_BUILTIN_EXTENSION_SNAPPY,
[Snappy support automatically loaded.])
AH_TEMPLATE(HAVE_BUILTIN_EXTENSION_ZLIB,
[Zlib support automatically loaded.])
+AH_TEMPLATE(HAVE_BUILTIN_EXTENSION_ZSTD,
+ [ZSTD support automatically loaded.])
AC_MSG_CHECKING(if --with-builtins option specified)
AC_ARG_WITH(builtins,
[AS_HELP_STRING([--with-builtins],
- [builtin extension names (lz4, snappy, zlib).])],
+ [builtin extension names (lz4, snappy, zlib, zstd).])],
[with_builtins=$withval],
[with_builtins=])
@@ -36,6 +38,8 @@ for builtin_i in $builtin_list; do
wt_cv_with_builtin_extension_snappy=yes;;
zlib) AC_DEFINE(HAVE_BUILTIN_EXTENSION_ZLIB)
wt_cv_with_builtin_extension_zlib=yes;;
+ zstd) AC_DEFINE(HAVE_BUILTIN_EXTENSION_ZSTD)
+ wt_cv_with_builtin_extension_zstd=yes;;
*) AC_MSG_ERROR([Unknown builtin extension "$builtin_i"]);;
esac
done
@@ -45,6 +49,8 @@ AM_CONDITIONAL([HAVE_BUILTIN_EXTENSION_SNAPPY],
[test "$wt_cv_with_builtin_extension_snappy" = "yes"])
AM_CONDITIONAL([HAVE_BUILTIN_EXTENSION_ZLIB],
[test "$wt_cv_with_builtin_extension_zlib" = "yes"])
+AM_CONDITIONAL([HAVE_BUILTIN_EXTENSION_ZSTD],
+ [test "$wt_cv_with_builtin_extension_zstd" = "yes"])
AC_MSG_RESULT($with_builtins)
AH_TEMPLATE(
@@ -276,4 +282,30 @@ if test "$wt_cv_enable_zlib" = "yes"; then
fi
AM_CONDITIONAL([ZLIB], [test "$wt_cv_enable_zlib" = "yes"])
+AC_MSG_CHECKING(if --enable-zstd option specified)
+AC_ARG_ENABLE(zstd,
+ [AS_HELP_STRING([--enable-zstd],
+ [Build the zstd compressor extension.])], r=$enableval, r=no)
+case "$r" in
+no) if test "$wt_cv_with_builtin_extension_zstd" = "yes"; then
+ wt_cv_enable_zstd=yes
+ else
+ wt_cv_enable_zstd=no
+ fi
+ ;;
+*) if test "$wt_cv_with_builtin_extension_zstd" = "yes"; then
+ AC_MSG_ERROR(
+ [Only one of --enable-zstd --with-builtins=zstd allowed])
+ fi
+ wt_cv_enable_zstd=yes;;
+esac
+AC_MSG_RESULT($wt_cv_enable_zstd)
+if test "$wt_cv_enable_zstd" = "yes"; then
+ AC_CHECK_HEADER(zstd.h,,
+ [AC_MSG_ERROR([--enable-zstd requires zstd.h])])
+ AC_CHECK_LIB(zstd, ZSTD_compress,,
+ [AC_MSG_ERROR([--enable-zstd requires Zstd library])])
+fi
+AM_CONDITIONAL([ZSTD], [test "$wt_cv_enable_zstd" = "yes"])
+
])
diff --git a/src/third_party/wiredtiger/build_posix/configure.ac.in b/src/third_party/wiredtiger/build_posix/configure.ac.in
index 608d602937d..ad00b19a3bb 100644
--- a/src/third_party/wiredtiger/build_posix/configure.ac.in
+++ b/src/third_party/wiredtiger/build_posix/configure.ac.in
@@ -74,7 +74,7 @@ AM_CONDITIONAL([ARM64_HOST], [test "$wt_cv_arm64" = "yes"])
# support compiling the ASM code we have to perform the CRC checks on PowerPC.
# To compile with clang we need to override the ASM compiler with CCAS to use
# gcc. Unfortunately, doing the compilation in this manner means libtool can't
-# determine what tag to use for that one .S file. If we catch that we are using
+# determine what tag to use for that one .sx file. If we catch that we are using
# two different compilers for CC and CCAS and we are on a PowerPC system we
# overload the libtool flags to provide CC by default.
if test "$wt_cv_powerpc" = "yes" -a "$CC" != "$CCAS"; then
diff --git a/src/third_party/wiredtiger/build_win/wiredtiger_config.h b/src/third_party/wiredtiger/build_win/wiredtiger_config.h
index 83ddc6eb194..78d2784cb70 100644
--- a/src/third_party/wiredtiger/build_win/wiredtiger_config.h
+++ b/src/third_party/wiredtiger/build_win/wiredtiger_config.h
@@ -19,6 +19,9 @@
/* Zlib support automatically loaded. */
/* #undef HAVE_BUILTIN_EXTENSION_ZLIB */
+/* ZSTD support automatically loaded. */
+/* #undef HAVE_BUILTIN_EXTENSION_ZSTD */
+
/* Define to 1 if you have the `clock_gettime' function. */
/* #undef HAVE_CLOCK_GETTIME */
@@ -70,6 +73,9 @@
/* Define to 1 if you have the `z' library (-lz). */
/* #undef HAVE_LIBZ */
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* #undef HAVE_LIBZSTD */
+
/* Define to 1 if you have the <memory.h> header file. */
/* #undef HAVE_MEMORY_H */
diff --git a/src/third_party/wiredtiger/dist/api_data.py b/src/third_party/wiredtiger/dist/api_data.py
index 9781e58a807..22d06c380ae 100644
--- a/src/third_party/wiredtiger/dist/api_data.py
+++ b/src/third_party/wiredtiger/dist/api_data.py
@@ -136,8 +136,8 @@ file_config = format_meta + [
configure a compressor for file blocks. Permitted values are \c "none"
or custom compression engine name created with
WT_CONNECTION::add_compressor. If WiredTiger has builtin support for
- \c "snappy", \c "lz4" or \c "zlib" compression, these names are also
- available. See @ref compression for more information'''),
+ \c "lz4", \c "snappy", \c "zlib" or \c "zstd" compression, these names
+ are also available. See @ref compression for more information'''),
Config('cache_resident', 'false', r'''
do not ever evict the object's pages from cache. Not compatible with
LSM tables; see @ref tuning_cache_resident for more information''',
@@ -183,6 +183,12 @@ file_config = format_meta + [
configure Huffman encoding for values. Permitted values are
\c "none", \c "english", \c "utf8<file>" or \c "utf16<file>".
See @ref huffman for more information'''),
+ Config('ignore_in_memory_cache_size', 'false', r'''
+ allow update and insert operations to proceed even if the cache is
+ already at capacity. Only valid in conjunction with in-memory
+ databases. Should be used with caution - this configuration allows
+ WiredTiger to consume memory over the configured cache limit''',
+ type='boolean'),
Config('internal_key_truncate', 'true', r'''
configure internal key truncation, discarding unnecessary
trailing bytes on internal keys (ignored for custom
@@ -410,13 +416,13 @@ connection_runtime_config = [
Config('eviction_dirty_target', '5', r'''
perform eviction in worker threads when the cache contains at least
this much dirty content, expressed as a percentage of the total cache
- size. Ignored if \c in_memory is \c true''',
+ size.''',
min=1, max=99),
Config('eviction_dirty_trigger', '20', r'''
trigger application threads to perform eviction when the cache contains
at least this much dirty content, expressed as a percentage of the
total cache size. This setting only alters behavior if it is lower than
- eviction_trigger. Ignored if \c in_memory is \c true''',
+ eviction_trigger''',
min=1, max=99),
Config('eviction_target', '80', r'''
perform eviction in worker threads when the cache contains at least
@@ -496,7 +502,8 @@ connection_runtime_config = [
is used to gather statistics, as well as each time statistics
are logged using the \c statistics_log configuration. See
@ref statistics for more information''',
- type='list', choices=['all', 'fast', 'none', 'clear']),
+ type='list',
+ choices=['all', 'cache_walk', 'fast', 'none', 'clear', 'tree_walk']),
Config('verbose', '', r'''
enable messages for various events. Only available if WiredTiger
is configured with --enable-verbose. Options are given as a
@@ -563,8 +570,9 @@ wiredtiger_open_log_configuration = [
configure a compressor for log records. Permitted values are
\c "none" or custom compression engine name created with
WT_CONNECTION::add_compressor. If WiredTiger has builtin support
- for \c "snappy", \c "lz4" or \c "zlib" compression, these names
- are also available. See @ref compression for more information'''),
+ for \c "lz4", \c "snappy", \c "zlib" or \c "zstd" compression,
+ these names are also available. See @ref compression for more
+ information'''),
Config('file_max', '100MB', r'''
the maximum size of log files''',
min='100KB', max='2GB'),
@@ -861,6 +869,10 @@ methods = {
Config('bloom_bit_count', '16', r'''
the number of bits used per item for the bloom filter''',
min='2', max='1000'),
+ Config('bloom_false_positives', 'false', r'''
+ return all values that pass the bloom filter, without eliminating
+ any false positives''',
+ type='boolean'),
Config('bloom_hash_count', '8', r'''
the number of hash values per item for the bloom filter''',
min='2', max='100'),
@@ -970,7 +982,8 @@ methods = {
gathering them, where appropriate (for example, a cache size statistic
is not cleared, while the count of cursor insert operations will be
cleared). See @ref statistics for more information''',
- type='list', choices=['all', 'fast', 'clear', 'size']),
+ type='list',
+ choices=['all', 'cache_walk', 'fast', 'clear', 'size', 'tree_walk']),
Config('target', '', r'''
if non-empty, backup the list of objects; valid only for a
backup data source''',
diff --git a/src/third_party/wiredtiger/dist/filelist b/src/third_party/wiredtiger/dist/filelist
index 19fa1122a27..fe9a17b7799 100644
--- a/src/third_party/wiredtiger/dist/filelist
+++ b/src/third_party/wiredtiger/dist/filelist
@@ -48,12 +48,12 @@ src/btree/row_modify.c
src/btree/row_srch.c
src/cache/cache_las.c
src/checksum/arm64/crc32-arm64.c ARM64_HOST
-src/checksum/power8/crc32.S POWERPC_HOST
+src/checksum/power8/crc32.sx POWERPC_HOST
src/checksum/power8/crc32_wrapper.c POWERPC_HOST
src/checksum/software/checksum.c
src/checksum/x86/crc32-x86.c X86_HOST
src/checksum/zseries/crc32-s390x.c ZSERIES_HOST
-src/checksum/zseries/crc32le-vx.S ZSERIES_HOST
+src/checksum/zseries/crc32le-vx.sx ZSERIES_HOST
src/config/config.c
src/config/config_api.c
src/config/config_check.c
@@ -90,6 +90,7 @@ src/cursor/cur_table.c
src/evict/evict_file.c
src/evict/evict_lru.c
src/evict/evict_page.c
+src/evict/evict_stat.c
src/log/log.c
src/log/log_auto.c
src/log/log_slot.c
diff --git a/src/third_party/wiredtiger/dist/flags.py b/src/third_party/wiredtiger/dist/flags.py
index 93b6e0cbbf4..e200f95fba6 100644
--- a/src/third_party/wiredtiger/dist/flags.py
+++ b/src/third_party/wiredtiger/dist/flags.py
@@ -133,6 +133,16 @@ flags = {
'SESSION_QUIET_CORRUPT_FILE',
'SESSION_SERVER_ASYNC',
],
+ 'stat' : [
+ 'STAT_CLEAR',
+ 'STAT_JSON',
+ 'STAT_ON_CLOSE',
+ 'STAT_TYPE_ALL',
+ 'STAT_TYPE_CACHE_WALK',
+ 'STAT_TYPE_FAST',
+ 'STAT_TYPE_SIZE',
+ 'STAT_TYPE_TREE_WALK',
+ ],
}
flag_cnt = {} # Dictionary [flag] : [reference count]
diff --git a/src/third_party/wiredtiger/dist/log.py b/src/third_party/wiredtiger/dist/log.py
index 8743dd3a71c..ac35d884eac 100644
--- a/src/third_party/wiredtiger/dist/log.py
+++ b/src/third_party/wiredtiger/dist/log.py
@@ -98,34 +98,6 @@ def printf_line(f, optype, i, ishex):
return ifbegin + body + ifend
#####################################################################
-# Update log.h with #defines for types
-#####################################################################
-log_defines = (
- ''.join('/*! %s */\n#define\t%s\t%d\n' % (r.desc, r.macro_name(), i)
- for i, r in enumerate(log_data.rectypes)) +
- ''.join('/*! %s */\n#define\t%s\t%d\n' % (r.desc, r.macro_name(), i)
- for i, r in enumerate(log_data.optypes,start=1))
-)
-
-tfile = open(tmp_file, 'w')
-skip = 0
-for line in open('../src/include/wiredtiger.in', 'r'):
- if skip:
- if 'Log record declarations: END' in line:
- tfile.write('/*\n' + line)
- skip = 0
- else:
- tfile.write(line)
- if 'Log record declarations: BEGIN' in line:
- skip = 1
- tfile.write(' */\n')
- tfile.write('/*! invalid operation */\n')
- tfile.write('#define\tWT_LOGOP_INVALID\t0\n')
- tfile.write(log_defines)
-tfile.close()
-compare_srcfile(tmp_file, '../src/include/wiredtiger.in')
-
-#####################################################################
# Create log_auto.c with handlers for each record / operation type.
#####################################################################
f='../src/log/log_auto.c'
@@ -270,11 +242,13 @@ __wt_logop_%(name)s_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
%(arg_decls)s)
{
+\tWT_DECL_RET;
\tconst char *fmt = WT_UNCHECKED_STRING(%(fmt)s);
\tuint32_t optype, size;
-\tWT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
-\t &optype, &size%(arg_names)s));
+\tif ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+\t &optype, &size%(arg_names)s)) != 0)
+\t\tWT_RET_MSG(session, ret, "logop_%(name)s: unpack failure");
\tWT_ASSERT(session, optype == %(macro)s);
\t*pp += size;
diff --git a/src/third_party/wiredtiger/dist/log_data.py b/src/third_party/wiredtiger/dist/log_data.py
index 8fd1a5787fe..bbfb51708bf 100644
--- a/src/third_party/wiredtiger/dist/log_data.py
+++ b/src/third_party/wiredtiger/dist/log_data.py
@@ -18,6 +18,11 @@ class LogRecordType:
def prname(self):
return '__logrec_print_' + self.name
+#
+# If you add a new record type you must also add its record type value in
+# src/include/wiredtiger.in. The values cannot be generated because they must
+# never change after they're written in a log file.
+#
rectypes = [
# A database-wide checkpoint.
LogRecordType('checkpoint', 'checkpoint', [
@@ -46,6 +51,11 @@ class LogOperationType:
def macro_name(self):
return 'WT_LOGOP_%s' % self.name.upper()
+#
+# If you add a new operation type you must also add its type value in
+# src/include/wiredtiger.in. The values cannot be generated because they must
+# never change after they're written in a log file.
+#
optypes = [
LogOperationType('col_put', 'column put',
[('uint32', 'fileid'), ('recno', 'recno'), ('item', 'value')]),
diff --git a/src/third_party/wiredtiger/dist/s_define.list b/src/third_party/wiredtiger/dist/s_define.list
index 6a1a32004ea..7b11d665de5 100644
--- a/src/third_party/wiredtiger/dist/s_define.list
+++ b/src/third_party/wiredtiger/dist/s_define.list
@@ -39,14 +39,18 @@ WT_READ_BARRIER
WT_REF_SIZE
WT_SESSION_LOCKED_CHECKPOINT
WT_SESSION_LOCKED_TURTLE
-WT_STATS_FIELD_TO_SLOT
+WT_STATS_FIELD_TO_OFFSET
WT_STATS_SLOT_ID
WT_STAT_CONN_DECRV
WT_STAT_DATA_DECRV
WT_STAT_DECR
WT_STAT_DECRV
WT_STAT_DECRV_ATOMIC
+WT_STAT_DECRV_ATOMIC_BASE
+WT_STAT_DECRV_BASE
WT_STAT_INCRV_ATOMIC
+WT_STAT_INCRV_ATOMIC_BASE
+WT_STAT_INCRV_BASE
WT_STAT_WRITE
WT_TIMEDIFF_US
WT_TRET_ERROR_OK
diff --git a/src/third_party/wiredtiger/dist/s_docs b/src/third_party/wiredtiger/dist/s_docs
index e2b1d2aed11..f4332257193 100755
--- a/src/third_party/wiredtiger/dist/s_docs
+++ b/src/third_party/wiredtiger/dist/s_docs
@@ -1,7 +1,7 @@
#! /bin/sh
t=__wt.$$
-trap 'rm -f $t /tmp/__doxy' 0 1 2 3 13 15
+trap 'rm -f $t' 0 1 2 3 13 15
# Skip this when building release packages: docs are built separately
test -n "$WT_RELEASE_BUILD" && exit 0
@@ -30,18 +30,22 @@ wtperf_config()
{
# The Linux ed command writes line numbers to stderr, redirect both
# stdout and stderr to keep things quiet.
- cc -o /tmp/__doxy ../bench/wtperf/doxy.c &&
+ #
+ # The OS X cpp program injects line number output in the middle of lines
+ # and doesn't stringify #XXX entries; use the -E option to the compiler
+ # instead.
+ cat ../bench/wtperf/wtperf_opt.i |
+ ${CC:-cc} -E -DOPT_DEFINE_DOXYGEN - | python wtperf_config.py > $t
(echo '/START_AUTO_GENERATED_WTPERF_CONFIGURATION/+3,/STOP_AUTO_GENERATED_WTPERF_CONFIGURATION/-1d'
echo 'i'
echo ''
echo '.'
- echo ".r !/tmp/__doxy"
+ echo ".r $t"
echo 'a'
echo ''
echo '.'
echo 'w'
- echo 'q') | ed ../src/docs/wtperf.dox 1>/dev/null 2>/dev/null &&
- rm -f /tmp/__doxy
+ echo 'q') | ed ../src/docs/wtperf.dox 1>/dev/null 2>/dev/null
}
structurechk()
@@ -179,7 +183,8 @@ EOF
clean=0
additional_languages=1
-filter="|sed '/PROJECT_NUMBER/s,=.*,=\"Version $WIREDTIGER_VERSION\",'"
+filter="|sed '/PROJECT_BRIEF/s,=.*,=\"$WIREDTIGER_VERSION\",'"
+filter="$filter| sed '/PROJECT_NUMBER/s,=.*,=\"Version $WIREDTIGER_VERSION\",'"
while :
do case "$1" in
-a) # Build from scratch
@@ -196,6 +201,13 @@ while :
-t) # Include the TODO list
filter="$filter| sed '/GENERATE_TODOLIST/s,=.*,=YES,'"
shift;;
+ -v) # Override the version with <slug> <version string>
+ filter="$filter| sed '/HTML_HEADER/s,=.*,= style/header-web.html,'"
+ shift
+ filter="$filter| sed '/PROJECT_BRIEF/s,=.*,=\"$1\",'"
+ shift
+ filter="$filter| sed '/PROJECT_NUMBER/s,=.*,=\"$1\",'"
+ shift;;
*)
break;;
esac
diff --git a/src/third_party/wiredtiger/dist/s_errno b/src/third_party/wiredtiger/dist/s_errno
new file mode 100644
index 00000000000..9981842e458
--- /dev/null
+++ b/src/third_party/wiredtiger/dist/s_errno
@@ -0,0 +1,73 @@
+#! /bin/sh
+
+# Complain about code that returns a system error value without an associated
+# verbose message.
+#
+# This script is a kluge and isn't run by default.
+
+t=__wt.$$
+trap 'rm -f $t' 0 1 2 3 13 15
+
+cd ..
+
+# Strip out a list of errors that will be flagged, but are OK.
+error_ok()
+{
+ sed -e '/ERET(/d' \
+ -e '/WT_ERR_MSG(/d' \
+ -e '/WT_ERR_TEST(/d' \
+ -e '/WT_PANIC_ERR(/d' \
+ -e '/WT_PANIC_RET(/d' \
+ -e '/WT_RET_MSG(/d' \
+ -e '/\/intpack.i:.*EINVAL/d'\
+ -e '/\/intpack.i:.*ENOMEM/d'\
+ -e '/\/pack_impl.c:.*EINVAL/d'\
+ -e '/\/pack_impl.c:.*ENOMEM/d'\
+ -e '/\/pack_stream.c:.*ENOMEM/d'\
+ -e '/\/packing.i:.*EINVAL/d'\
+ -e '/__config_err(/d' \
+ -e '/__wt_block_panic(/d' \
+ -e '/__wt_err(/d' \
+ -e '/__wt_errx(/d' \
+ -e '/csv_error(/d' \
+ -e '/nop_error(/d' \
+ -e '/rotn_error(/d' \
+ -e '/zlib_error(/d'
+}
+
+# Loop through source files.
+for f in `find ext src -name '*.[ci]'`; do
+ if expr "$f" : 'ext/datasources/helium/helium.c' > /dev/null; then
+ continue
+ fi
+ if expr "$f" : 'src/os_win/os_winerr.c' > /dev/null; then
+ continue
+ fi
+ if expr "$f" : 'src/utilities/.*' > /dev/null; then
+ continue
+ fi
+
+ # Strip include files,
+ # then use the C preprocessor to strip comments,
+ # then turn each file into a single line,
+ # then chunk the file by semicolons,
+ # then search for explicit error returns (ignoring EBUSY),
+ # then prepend the file name to each line,
+ # then skip known calls that include verbose messages.
+ sed '/^#include/d' $f |
+ ${CC:-cc} -E - |
+ tr -s '\012' ' ' | tr ';' '\012' |
+ egrep -w 'EPERM|ENOENT|ESRCH|EINTR|EIO|ENXIO|E2BIG|ENOEXEC|EBADF|ECHILD|EDEADLK|ENOMEM|EACCES|EFAULT|ENOTBLK|EEXIST|EXDEV|ENODEV|ENOTDIR|EISDIR|EINVAL|ENFILE|EMFILE|ENOTTY|ETXTBSY|EFBIG|ENOSPC|ESPIPE|EROFS|EMLINK|EPIPE|EDOM|ERANGE|EAGAIN|EWOULDBLOCK|EINPROGRESS|EALREADY|ENOTSOCK|EDESTADDRREQ|EMSGSIZE|EPROTOTYPE|ENOPROTOOPT|EPROTONOSUPPORT|ESOCKTNOSUPPORT|EOPNOTSUPP|ENOTSUP|EPFNOSUPPORT|EAFNOSUPPORT|EADDRINUSE|EADDRNOTAVAIL|ENETDOWN|ENETUNREACH|ENETRESET|ECONNABORTED|ECONNRESET|ENOBUFS|EISCONN|ENOTCONN|ESHUTDOWN|ETOOMANYREFS|ETIMEDOUT|ECONNREFUSED|ELOOP|ENAMETOOLONG|EHOSTDOWN|EHOSTUNREACH|ENOTEMPTY|EPROCLIM|EUSERS|EDQUOT|ESTALE|EREMOTE|EBADRPC|ERPCMISMATCH|EPROGUNAVAIL|EPROGMISMATCH|EPROCUNAVAIL|ENOLCK|ENOSYS|EFTYPE|EAUTH|ENEEDAUTH|EIDRM|ENOMSG|EOVERFLOW|ECANCELED|EILSEQ|ENOATTR|EDOOFUS|EBADMSG|EMULTIHOP|ENOLINK|EPROTO|ENOTCAPABLE|ECAPMODE|ENOTRECOVERABLE|EOWNERDEAD|ELAST|ERESTART|EJUSTRETURN|ENOIOCTL|EDIRIOCTL' |
+ sed -e 's/^[ ]*//' \
+ -e "s;^;$f: ;" |
+ error_ok
+done > $t
+
+test -s $t && {
+ echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
+ echo 'Unexpected error usage.'
+ echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
+ cat $t
+ exit 1
+}
+exit 0
diff --git a/src/third_party/wiredtiger/dist/s_export b/src/third_party/wiredtiger/dist/s_export
index dc69238b270..b8e42c970f9 100755
--- a/src/third_party/wiredtiger/dist/s_export
+++ b/src/third_party/wiredtiger/dist/s_export
@@ -26,7 +26,7 @@ check()
sort |
uniq -u |
egrep -v \
- 'zlib_extension_init|lz4_extension_init|snappy_extension_init' > $t
+ 'lz4_extension_init|snappy_extension_init|zlib_extension_init|zstd_extension_init' > $t
test -s $t && {
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
diff --git a/src/third_party/wiredtiger/dist/s_stat b/src/third_party/wiredtiger/dist/s_stat
index 935c7e1fb43..5d5937e1833 100755
--- a/src/third_party/wiredtiger/dist/s_stat
+++ b/src/third_party/wiredtiger/dist/s_stat
@@ -20,6 +20,25 @@ search=`sed \
-e d ../src/include/stat.h |
sort`
+# There are some fields that are used, but we can't detect it.
+cat << UNUSED_STAT_FIELDS
+lock_checkpoint_count
+lock_checkpoint_wait_application
+lock_checkpoint_wait_internal
+lock_handle_list_count
+lock_handle_list_wait_application
+lock_handle_list_wait_internal
+lock_metadata_count
+lock_metadata_wait_application
+lock_metadata_wait_internal
+lock_schema_count
+lock_schema_wait_application
+lock_schema_wait_internal
+lock_table_count
+lock_table_wait_application
+lock_table_wait_internal
+UNUSED_STAT_FIELDS
+
echo "$search"
fgrep -who "$search" $l) | sort | uniq -u > $t
diff --git a/src/third_party/wiredtiger/dist/s_string.ok b/src/third_party/wiredtiger/dist/s_string.ok
index 1887cbd936f..d19c05e802b 100644
--- a/src/third_party/wiredtiger/dist/s_string.ok
+++ b/src/third_party/wiredtiger/dist/s_string.ok
@@ -18,6 +18,7 @@ ASYNC
Addr
Ailamaki
Alakuijala
+Alexandrescu's
Alloc
Async
Athanassoulis
@@ -59,6 +60,7 @@ COVERITY
CPUs
CRC
CSV
+CStream
CURSORs
CURSTD
CallsCustDate
@@ -68,6 +70,7 @@ Checksum
Checksums
CityHash
CloseHandle
+Collet
Comparator
Config
Coverity
@@ -124,6 +127,7 @@ FORALL
FOREACH
FS
FULLFSYNC
+Facebook
FindClose
FindFirstFile
Fixup
@@ -165,6 +169,7 @@ INSN
INTL
ISA
ITEMs
+ITER
InitializeCriticalSectionAndSpinCount
Inline
Intra
@@ -372,6 +377,7 @@ WILLNEED
WIREDTIGER
WRLSN
WRNOLOCK
+WTPERF
WaitForSingleObject
WakeAllConditionVariable
Wconditional
@@ -395,6 +401,12 @@ WriteFile
Wuninitialized
Wunused
XP
+Yann
+ZSTD
+Zlib
+Zlib's
+Zstd
+Zstd's
abcdef
abcdefghijklmnopqrstuvwxyz
addl
@@ -416,6 +428,7 @@ argc
args
argv
asm
+assertfmt
async
asyncopp
asyncops
@@ -511,6 +524,7 @@ collatorp
comparator
comparep
compat
+compressStream
concat
cond
conf
@@ -530,6 +544,7 @@ cp
cpuid
crc
create's
+createCStream
crypto
cryptobad
csv
@@ -622,6 +637,7 @@ emp
encodings
encryptor
encryptors
+endStream
endian
english
enqueue
@@ -749,6 +765,7 @@ infeasible
inflateInit
infmt
init
+initCStream
initializers
initn
initsize
@@ -784,6 +801,7 @@ isupper
isxdigit
iter
iteratively
+iters
jnr
jrx
json
@@ -824,6 +842,7 @@ localtime
logf
logmgr
lognum
+logop
logread
logrec
logsize
@@ -844,11 +863,13 @@ lu
lwsync
lz
lzo
+mT
madvise
majorp
malloc
marshall
marshalled
+maxCLevel
maxcpu
maxdbs
mbll
@@ -989,12 +1010,14 @@ qdown
qrrSS
qsort
quartile
+queueable
qup
rN
rS
rb
rbrace
rbracket
+rcursor
rdonly
rduppo
readlock
@@ -1201,6 +1224,7 @@ waitpid
walk's
warmup
wb
+wcursor
wiredTiger
wiredtiger
workFactor
@@ -1222,6 +1246,10 @@ xxxx
xxxxx
xxxxxx
zalloc
+zf
zfree
zlib
+zlib's
+zstd
+zstd's
zu
diff --git a/src/third_party/wiredtiger/dist/s_style b/src/third_party/wiredtiger/dist/s_style
index 3860a23b991..8e755224ee2 100755
--- a/src/third_party/wiredtiger/dist/s_style
+++ b/src/third_party/wiredtiger/dist/s_style
@@ -108,7 +108,7 @@ else
! expr "$f" : 'test/.*' > /dev/null &&
! expr "$f" : '.*/utilities/.*' > /dev/null; then
if ! expr "$f" : '.*/os_alloc.c' > /dev/null &&
- egrep '[[:space:]]free[(]|[[:space:]]strdup[(]|[[:space:]]strndup[(]|[[:space:]]malloc[(]|[[:space:]]calloc[(]|[[:space:]]realloc[(]' $f > $t; then
+ egrep '[[:space:]]free[(]|[[:space:]]strdup[(]|[[:space:]]strndup[(]|[[:space:]]malloc[(]|[[:space:]]calloc[(]|[[:space:]]realloc[(]|[[:space:]]sprintf[(]' $f > $t; then
test -s $t && {
echo "$f: call to illegal function"
cat $t
diff --git a/src/third_party/wiredtiger/dist/s_void b/src/third_party/wiredtiger/dist/s_void
index f7bfbcc7e8e..b9c3f5e0ef7 100644
--- a/src/third_party/wiredtiger/dist/s_void
+++ b/src/third_party/wiredtiger/dist/s_void
@@ -32,6 +32,7 @@ func_ok()
-e '/int __bm_stat$/d' \
-e '/int __checkpoint_presync$/d' \
-e '/int __compact_uri_analyze$/d' \
+ -e '/int __config_parser_close$/d' \
-e '/int __curlog_reset$/d' \
-e '/int __handle_close_default$/d' \
-e '/int __handle_progress_default$/d' \
@@ -63,6 +64,7 @@ func_ok()
-e '/int __wt_stat_join_desc$/d' \
-e '/int __wt_win_directory_list_free$/d' \
-e '/int bdb_compare_reverse$/d' \
+ -e '/int csv_error$/d' \
-e '/int csv_terminate$/d' \
-e '/int demo_file_close$/d' \
-e '/int demo_file_lock$/d' \
@@ -81,6 +83,7 @@ func_ok()
-e '/int main$/d' \
-e '/int nop_decompress$/d' \
-e '/int nop_decrypt$/d' \
+ -e '/int nop_error$/d' \
-e '/int nop_pre_size$/d' \
-e '/int nop_sizing$/d' \
-e '/int nop_terminate$/d' \
@@ -90,16 +93,19 @@ func_ok()
-e '/int rotn_error$/d' \
-e '/int rotn_sizing$/d' \
-e '/int rotn_terminate$/d' \
+ -e '/int snappy_pre_size$/d' \
+ -e '/int snappy_terminate$/d' \
-e '/int uri2name$/d' \
-e '/int usage$/d' \
-e '/int util_err$/d' \
-e '/int wiredtiger_extension_init$/d' \
-e '/int wiredtiger_extension_terminate$/d' \
-e '/int wiredtiger_pack_close$/d' \
- -e '/int wt_snappy_pre_size$/d' \
- -e '/int wt_snappy_terminate$/d' \
-e '/int zlib_error$/d' \
- -e '/int zlib_terminate$/d'
+ -e '/int zlib_terminate$/d' \
+ -e '/int zstd_error$/d' \
+ -e '/int zstd_pre_size$/d' \
+ -e '/int zstd_terminate$/d'
}
# Complain about functions which return an "int" but which don't return except
diff --git a/src/third_party/wiredtiger/dist/stat.py b/src/third_party/wiredtiger/dist/stat.py
index c3c85bbe9b4..d04a0514c49 100644
--- a/src/third_party/wiredtiger/dist/stat.py
+++ b/src/third_party/wiredtiger/dist/stat.py
@@ -42,8 +42,11 @@ compare_srcfile(tmp_file, '../src/include/stat.h')
def print_defines_one(capname, base, stats):
for v, l in enumerate(stats, base):
desc = l.desc
- if 'all_only' in l.flags:
- desc += ', only reported if statistics=all is set'
+ if 'cache_walk' in l.flags:
+ desc += \
+ ', only reported if cache_walk or all statistics are enabled'
+ if 'tree_walk' in l.flags:
+ desc += ', only reported if tree_walk or all statistics are enabled'
if len(textwrap.wrap(desc, 70)) > 1:
f.write('/*!\n')
f.write(' * %s\n' % '\n * '.join(textwrap.wrap(desc, 70)))
@@ -135,15 +138,27 @@ __wt_stat_''' + name + '_init_single(WT_' + name.upper() + '''_STATS *stats)
if handle != None:
f.write('''
-void
-__wt_stat_''' + name + '_init(' + handle + ''' *handle)
+int
+__wt_stat_''' + name + '''_init(
+ WT_SESSION_IMPL *session, ''' + handle + ''' *handle)
{
\tint i;
+\tWT_RET(__wt_calloc(session, (size_t)WT_COUNTER_SLOTS,
+\t sizeof(*handle->stat_array), &handle->stat_array));
+
\tfor (i = 0; i < WT_COUNTER_SLOTS; ++i) {
\t\thandle->stats[i] = &handle->stat_array[i];
\t\t__wt_stat_''' + name + '''_init_single(handle->stats[i]);
\t}
+\treturn (0);
+}
+
+void
+__wt_stat_''' + name + '''_discard(
+ WT_SESSION_IMPL *session, ''' + handle + ''' *handle)
+{
+\t__wt_free(session, handle->stat_array);
}
''')
diff --git a/src/third_party/wiredtiger/dist/stat_data.py b/src/third_party/wiredtiger/dist/stat_data.py
index 5087afa44dc..bcf5201bd90 100644
--- a/src/third_party/wiredtiger/dist/stat_data.py
+++ b/src/third_party/wiredtiger/dist/stat_data.py
@@ -9,7 +9,8 @@
#
# Data-source statistics are normally aggregated across the set of underlying
# objects. Additional optional configuration flags are available:
-# all_only Only gets reported when statistics=all set
+# cache_walk Only reported when statistics=cache_walk is set
+# tree_walk Only reported when statistics=tree_walk is set
# max_aggregate Take the maximum value when aggregating statistics
# no_clear Value not cleared when statistics cleared
# no_scale Don't scale value per second in the logging tool script
@@ -46,6 +47,11 @@ class CacheStat(Stat):
prefix = 'cache'
def __init__(self, name, desc, flags=''):
Stat.__init__(self, name, CacheStat.prefix, desc, flags)
+class CacheWalkStat(Stat):
+ prefix = 'cache_walk'
+ def __init__(self, name, desc, flags=''):
+ flags += ',cache_walk'
+ Stat.__init__(self, name, CacheWalkStat.prefix, desc, flags)
class CompressStat(Stat):
prefix = 'compression'
def __init__(self, name, desc, flags=''):
@@ -66,6 +72,10 @@ class JoinStat(Stat):
prefix = '' # prefix is inserted dynamically
def __init__(self, name, desc, flags=''):
Stat.__init__(self, name, JoinStat.prefix, desc, flags)
+class LockStat(Stat):
+ prefix = 'lock'
+ def __init__(self, name, desc, flags=''):
+ Stat.__init__(self, name, LockStat.prefix, desc, flags)
class LogStat(Stat):
prefix = 'log'
def __init__(self, name, desc, flags=''):
@@ -105,11 +115,16 @@ groups['cursor'] = [CursorStat.prefix, SessionStat.prefix]
groups['evict'] = [
BlockStat.prefix,
CacheStat.prefix,
+ CacheWalkStat.prefix,
ConnStat.prefix,
ThreadStat.prefix
]
groups['lsm'] = [LSMStat.prefix, TxnStat.prefix]
-groups['memory'] = [CacheStat.prefix, ConnStat.prefix, RecStat.prefix]
+groups['memory'] = [
+ CacheStat.prefix,
+ CacheWalkStat.prefix,
+ ConnStat.prefix,
+ RecStat.prefix]
groups['system'] = [
ConnStat.prefix,
DhandleStat.prefix,
@@ -226,13 +241,32 @@ connection_stats = [
CacheStat('cache_pages_inuse', 'pages currently held in the cache', 'no_clear,no_scale'),
CacheStat('cache_pages_requested', 'pages requested from the cache'),
CacheStat('cache_read', 'pages read into cache'),
+ CacheStat('cache_read_app_count', 'application threads page read from disk to cache count'),
+ CacheStat('cache_read_app_time', 'application threads page read from disk to cache time (usecs)'),
CacheStat('cache_read_lookaside', 'pages read into cache requiring lookaside entries'),
CacheStat('cache_read_overflow', 'overflow pages read into cache'),
CacheStat('cache_write', 'pages written from cache'),
+ CacheStat('cache_write_app_count', 'application threads page write from cache to disk count'),
+ CacheStat('cache_write_app_time', 'application threads page write from cache to disk time (usecs)'),
CacheStat('cache_write_lookaside', 'page written requiring lookaside records'),
CacheStat('cache_write_restore', 'pages written requiring in-memory restoration'),
##########################################
+ # Cursor operations
+ ##########################################
+ CursorStat('cursor_create', 'cursor create calls'),
+ CursorStat('cursor_insert', 'cursor insert calls'),
+ CursorStat('cursor_next', 'cursor next calls'),
+ CursorStat('cursor_prev', 'cursor prev calls'),
+ CursorStat('cursor_remove', 'cursor remove calls'),
+ CursorStat('cursor_reset', 'cursor reset calls'),
+ CursorStat('cursor_restart', 'cursor restarted searches'),
+ CursorStat('cursor_search', 'cursor search calls'),
+ CursorStat('cursor_search_near', 'cursor search near calls'),
+ CursorStat('cursor_truncate', 'truncate calls'),
+ CursorStat('cursor_update', 'cursor update calls'),
+
+ ##########################################
# Dhandle statistics
##########################################
DhandleStat('dh_conn_handle_count', 'connection data handles currently active', 'no_clear,no_scale'),
@@ -245,6 +279,25 @@ connection_stats = [
DhandleStat('dh_sweeps', 'connection sweeps'),
##########################################
+ # Locking statistics
+ ##########################################
+ LockStat('lock_checkpoint_count', 'checkpoint lock acquisitions'),
+ LockStat('lock_checkpoint_wait_application', 'checkpoint lock application thread wait time (usecs)'),
+ LockStat('lock_checkpoint_wait_internal', 'checkpoint lock internal thread wait time (usecs)'),
+ LockStat('lock_handle_list_count', 'handle-list lock acquisitions'),
+ LockStat('lock_handle_list_wait_application', 'handle-list lock application thread wait time (usecs)'),
+ LockStat('lock_handle_list_wait_internal', 'handle-list lock internal thread wait time (usecs)'),
+ LockStat('lock_metadata_count', 'metadata lock acquisitions'),
+ LockStat('lock_metadata_wait_application', 'metadata lock application thread wait time (usecs)'),
+ LockStat('lock_metadata_wait_internal', 'metadata lock internal thread wait time (usecs)'),
+ LockStat('lock_schema_count', 'schema lock acquisitions'),
+ LockStat('lock_schema_wait_application', 'schema lock application thread wait time (usecs)'),
+ LockStat('lock_schema_wait_internal', 'schema lock internal thread wait time (usecs)'),
+ LockStat('lock_table_count', 'table lock acquisitions'),
+ LockStat('lock_table_wait_application', 'table lock application thread time waiting for the table lock (usecs)'),
+ LockStat('lock_table_wait_internal', 'table lock internal thread time waiting for the table lock (usecs)'),
+
+ ##########################################
# Logging statistics
##########################################
LogStat('log_buffer_size', 'total log buffer size', 'no_clear,no_scale,size'),
@@ -286,41 +339,6 @@ connection_stats = [
LogStat('log_zero_fills', 'log files manually zero-filled'),
##########################################
- # Reconciliation statistics
- ##########################################
- RecStat('rec_page_delete', 'pages deleted'),
- RecStat('rec_page_delete_fast', 'fast-path pages deleted'),
- RecStat('rec_pages', 'page reconciliation calls'),
- RecStat('rec_pages_eviction', 'page reconciliation calls for eviction'),
- RecStat('rec_split_stashed_bytes', 'split bytes currently awaiting free', 'no_clear,no_scale,size'),
- RecStat('rec_split_stashed_objects', 'split objects currently awaiting free', 'no_clear,no_scale'),
-
- ##########################################
- # Transaction statistics
- ##########################################
- TxnStat('txn_begin', 'transaction begins'),
- TxnStat('txn_checkpoint', 'transaction checkpoints'),
- TxnStat('txn_checkpoint_fsync_post', 'transaction fsync calls for checkpoint after allocating the transaction ID'),
- TxnStat('txn_checkpoint_fsync_post_duration', 'transaction fsync duration for checkpoint after allocating the transaction ID (usecs)', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_generation', 'transaction checkpoint generation', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_running', 'transaction checkpoint currently running', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_scrub_target', 'transaction checkpoint scrub dirty target', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_scrub_time', 'transaction checkpoint scrub time (msecs)', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_time_max', 'transaction checkpoint max time (msecs)', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_time_min', 'transaction checkpoint min time (msecs)', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_time_recent', 'transaction checkpoint most recent time (msecs)', 'no_clear,no_scale'),
- TxnStat('txn_checkpoint_time_total', 'transaction checkpoint total time (msecs)', 'no_clear,no_scale'),
- TxnStat('txn_commit', 'transactions committed'),
- TxnStat('txn_fail_cache', 'transaction failures due to cache overflow'),
- TxnStat('txn_pinned_checkpoint_range', 'transaction range of IDs currently pinned by a checkpoint', 'no_clear,no_scale'),
- TxnStat('txn_pinned_range', 'transaction range of IDs currently pinned', 'no_clear,no_scale'),
- TxnStat('txn_pinned_snapshot_range', 'transaction range of IDs currently pinned by named snapshots', 'no_clear,no_scale'),
- TxnStat('txn_rollback', 'transactions rolled back'),
- TxnStat('txn_snapshots_created', 'number of named snapshots created'),
- TxnStat('txn_snapshots_dropped', 'number of named snapshots dropped'),
- TxnStat('txn_sync', 'transaction sync calls'),
-
- ##########################################
# LSM statistics
##########################################
LSMStat('lsm_checkpoint_throttle', 'sleep for LSM checkpoint throttle'),
@@ -335,6 +353,16 @@ connection_stats = [
LSMStat('lsm_work_units_done', 'tree maintenance operations executed'),
##########################################
+ # Reconciliation statistics
+ ##########################################
+ RecStat('rec_page_delete', 'pages deleted'),
+ RecStat('rec_page_delete_fast', 'fast-path pages deleted'),
+ RecStat('rec_pages', 'page reconciliation calls'),
+ RecStat('rec_pages_eviction', 'page reconciliation calls for eviction'),
+ RecStat('rec_split_stashed_bytes', 'split bytes currently awaiting free', 'no_clear,no_scale,size'),
+ RecStat('rec_split_stashed_objects', 'split objects currently awaiting free', 'no_clear,no_scale'),
+
+ ##########################################
# Session operations
##########################################
SessionStat('session_cursor_open', 'open cursor count', 'no_clear,no_scale'),
@@ -357,21 +385,6 @@ connection_stats = [
SessionStat('session_table_verify_success', 'table verify successful calls', 'no_clear,no_scale'),
##########################################
- # Total cursor operations
- ##########################################
- CursorStat('cursor_create', 'cursor create calls'),
- CursorStat('cursor_insert', 'cursor insert calls'),
- CursorStat('cursor_next', 'cursor next calls'),
- CursorStat('cursor_prev', 'cursor prev calls'),
- CursorStat('cursor_remove', 'cursor remove calls'),
- CursorStat('cursor_reset', 'cursor reset calls'),
- CursorStat('cursor_restart', 'cursor restarted searches'),
- CursorStat('cursor_search', 'cursor search calls'),
- CursorStat('cursor_search_near', 'cursor search near calls'),
- CursorStat('cursor_truncate', 'truncate calls'),
- CursorStat('cursor_update', 'cursor update calls'),
-
- ##########################################
# Thread Count statistics
##########################################
ThreadStat('thread_fsync_active', 'active filesystem fsync calls','no_clear,no_scale'),
@@ -379,8 +392,36 @@ connection_stats = [
ThreadStat('thread_write_active', 'active filesystem write calls','no_clear,no_scale'),
##########################################
+ # Transaction statistics
+ ##########################################
+ TxnStat('txn_begin', 'transaction begins'),
+ TxnStat('txn_checkpoint', 'transaction checkpoints'),
+ TxnStat('txn_checkpoint_fsync_post', 'transaction fsync calls for checkpoint after allocating the transaction ID'),
+ TxnStat('txn_checkpoint_fsync_post_duration', 'transaction fsync duration for checkpoint after allocating the transaction ID (usecs)', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_generation', 'transaction checkpoint generation', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_running', 'transaction checkpoint currently running', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_scrub_target', 'transaction checkpoint scrub dirty target', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_scrub_time', 'transaction checkpoint scrub time (msecs)', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_skipped', 'transaction checkpoints skipped because database was clean'),
+ TxnStat('txn_checkpoint_time_max', 'transaction checkpoint max time (msecs)', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_time_min', 'transaction checkpoint min time (msecs)', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_time_recent', 'transaction checkpoint most recent time (msecs)', 'no_clear,no_scale'),
+ TxnStat('txn_checkpoint_time_total', 'transaction checkpoint total time (msecs)', 'no_clear,no_scale'),
+ TxnStat('txn_commit', 'transactions committed'),
+ TxnStat('txn_fail_cache', 'transaction failures due to cache overflow'),
+ TxnStat('txn_pinned_checkpoint_range', 'transaction range of IDs currently pinned by a checkpoint', 'no_clear,no_scale'),
+ TxnStat('txn_pinned_range', 'transaction range of IDs currently pinned', 'no_clear,no_scale'),
+ TxnStat('txn_pinned_snapshot_range', 'transaction range of IDs currently pinned by named snapshots', 'no_clear,no_scale'),
+ TxnStat('txn_rollback', 'transactions rolled back'),
+ TxnStat('txn_snapshots_created', 'number of named snapshots created'),
+ TxnStat('txn_snapshots_dropped', 'number of named snapshots dropped'),
+ TxnStat('txn_sync', 'transaction sync calls'),
+
+ ##########################################
# Yield statistics
##########################################
+ YieldStat('application_cache_time', 'application thread time waiting for cache (usecs)'),
+ YieldStat('application_evict_time', 'application thread time evicting (usecs)'),
YieldStat('page_busy_blocked', 'page acquire busy blocked'),
YieldStat('page_forcible_evict_blocked', 'page acquire eviction blocked'),
YieldStat('page_locked_blocked', 'page acquire locked blocked'),
@@ -395,41 +436,30 @@ connection_stats = sorted(connection_stats, key=attrgetter('desc'))
##########################################
dsrc_stats = [
##########################################
- # Session operations
- ##########################################
- SessionStat('session_compact', 'object compaction'),
- SessionStat('session_cursor_open', 'open cursor count', 'no_clear,no_scale'),
-
- ##########################################
- # Cursor operations
+ # Block manager statistics
##########################################
- CursorStat('cursor_create', 'create calls'),
- CursorStat('cursor_insert', 'insert calls'),
- CursorStat('cursor_insert_bulk', 'bulk-loaded cursor-insert calls'),
- CursorStat('cursor_insert_bytes', 'cursor-insert key and value bytes inserted', 'size'),
- CursorStat('cursor_next', 'next calls'),
- CursorStat('cursor_prev', 'prev calls'),
- CursorStat('cursor_remove', 'remove calls'),
- CursorStat('cursor_remove_bytes', 'cursor-remove key bytes removed', 'size'),
- CursorStat('cursor_reset', 'reset calls'),
- CursorStat('cursor_restart', 'restarted searches'),
- CursorStat('cursor_search', 'search calls'),
- CursorStat('cursor_search_near', 'search near calls'),
- CursorStat('cursor_truncate', 'truncate calls'),
- CursorStat('cursor_update', 'update calls'),
- CursorStat('cursor_update_bytes', 'cursor-update value bytes updated', 'size'),
+ BlockStat('allocation_size', 'file allocation unit size', 'max_aggregate,no_scale,size'),
+ BlockStat('block_alloc', 'blocks allocated'),
+ BlockStat('block_checkpoint_size', 'checkpoint size', 'no_scale,size'),
+ BlockStat('block_extension', 'allocations requiring file extension'),
+ BlockStat('block_free', 'blocks freed'),
+ BlockStat('block_magic', 'file magic number', 'max_aggregate,no_scale'),
+ BlockStat('block_major', 'file major version number', 'max_aggregate,no_scale'),
+ BlockStat('block_minor', 'minor version number', 'max_aggregate,no_scale'),
+ BlockStat('block_reuse_bytes', 'file bytes available for reuse', 'no_scale,size'),
+ BlockStat('block_size', 'file size in bytes', 'no_scale,size'),
##########################################
# Btree statistics
##########################################
BtreeStat('btree_checkpoint_generation', 'btree checkpoint generation', 'no_clear,no_scale'),
- BtreeStat('btree_column_deleted', 'column-store variable-size deleted values', 'no_scale,all_only'),
- BtreeStat('btree_column_fix', 'column-store fixed-size leaf pages', 'no_scale,all_only'),
- BtreeStat('btree_column_internal', 'column-store internal pages', 'no_scale,all_only'),
- BtreeStat('btree_column_rle', 'column-store variable-size RLE encoded values', 'no_scale,all_only'),
- BtreeStat('btree_column_variable', 'column-store variable-size leaf pages', 'no_scale,all_only'),
+ BtreeStat('btree_column_deleted', 'column-store variable-size deleted values', 'no_scale,tree_walk'),
+ BtreeStat('btree_column_fix', 'column-store fixed-size leaf pages', 'no_scale,tree_walk'),
+ BtreeStat('btree_column_internal', 'column-store internal pages', 'no_scale,tree_walk'),
+ BtreeStat('btree_column_rle', 'column-store variable-size RLE encoded values', 'no_scale,tree_walk'),
+ BtreeStat('btree_column_variable', 'column-store variable-size leaf pages', 'no_scale,tree_walk'),
BtreeStat('btree_compact_rewrite', 'pages rewritten by compaction'),
- BtreeStat('btree_entries', 'number of key/value pairs', 'no_scale,all_only'),
+ BtreeStat('btree_entries', 'number of key/value pairs', 'no_scale,tree_walk'),
BtreeStat('btree_fixed_len', 'fixed-record size', 'max_aggregate,no_scale,size'),
BtreeStat('btree_maximum_depth', 'maximum tree depth', 'max_aggregate,no_scale'),
BtreeStat('btree_maxintlkey', 'maximum internal page key size', 'max_aggregate,no_scale,size'),
@@ -437,39 +467,9 @@ dsrc_stats = [
BtreeStat('btree_maxleafkey', 'maximum leaf page key size', 'max_aggregate,no_scale,size'),
BtreeStat('btree_maxleafpage', 'maximum leaf page size', 'max_aggregate,no_scale,size'),
BtreeStat('btree_maxleafvalue', 'maximum leaf page value size', 'max_aggregate,no_scale,size'),
- BtreeStat('btree_overflow', 'overflow pages', 'no_scale,all_only'),
- BtreeStat('btree_row_internal', 'row-store internal pages', 'no_scale,all_only'),
- BtreeStat('btree_row_leaf', 'row-store leaf pages', 'no_scale,all_only'),
-
- ##########################################
- # LSM statistics
- ##########################################
- LSMStat('bloom_count', 'bloom filters in the LSM tree', 'no_scale'),
- LSMStat('bloom_false_positive', 'bloom filter false positives'),
- LSMStat('bloom_hit', 'bloom filter hits'),
- LSMStat('bloom_miss', 'bloom filter misses'),
- LSMStat('bloom_page_evict', 'bloom filter pages evicted from cache'),
- LSMStat('bloom_page_read', 'bloom filter pages read into cache'),
- LSMStat('bloom_size', 'total size of bloom filters', 'no_scale,size'),
- LSMStat('lsm_checkpoint_throttle', 'sleep for LSM checkpoint throttle'),
- LSMStat('lsm_chunk_count', 'chunks in the LSM tree', 'no_scale'),
- LSMStat('lsm_generation_max', 'highest merge generation in the LSM tree', 'max_aggregate,no_scale'),
- LSMStat('lsm_lookup_no_bloom', 'queries that could have benefited from a Bloom filter that did not exist'),
- LSMStat('lsm_merge_throttle', 'sleep for LSM merge throttle'),
-
- ##########################################
- # Block manager statistics
- ##########################################
- BlockStat('allocation_size', 'file allocation unit size', 'max_aggregate,no_scale,size'),
- BlockStat('block_alloc', 'blocks allocated'),
- BlockStat('block_checkpoint_size', 'checkpoint size', 'no_scale,size'),
- BlockStat('block_extension', 'allocations requiring file extension'),
- BlockStat('block_free', 'blocks freed'),
- BlockStat('block_magic', 'file magic number', 'max_aggregate,no_scale'),
- BlockStat('block_major', 'file major version number', 'max_aggregate,no_scale'),
- BlockStat('block_minor', 'minor version number', 'max_aggregate,no_scale'),
- BlockStat('block_reuse_bytes', 'file bytes available for reuse', 'no_scale,size'),
- BlockStat('block_size', 'file size in bytes', 'no_scale,size'),
+ BtreeStat('btree_overflow', 'overflow pages', 'no_scale,tree_walk'),
+ BtreeStat('btree_row_internal', 'row-store internal pages', 'no_scale,tree_walk'),
+ BtreeStat('btree_row_leaf', 'row-store leaf pages', 'no_scale,tree_walk'),
##########################################
# Cache and eviction statistics
@@ -498,6 +498,28 @@ dsrc_stats = [
CacheStat('cache_write_restore', 'pages written requiring in-memory restoration'),
##########################################
+ # Cache content statistics
+ ##########################################
+ CacheWalkStat('cache_state_avg_written_size', 'Average on-disk page image size seen', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_gen_avg_gap', 'Average difference between current eviction generation when the page was last considered', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_gen_current', 'Current eviction generation', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_gen_max_gap', 'Maximum difference between current eviction generation when the page was last considered', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_max_pagesize', 'Maximum page size seen', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_memory', 'Pages created in memory and never written', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_min_written_size', 'Minimum on-disk page image size seen', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_not_queueable', 'Pages that could not be queued for eviction', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_pages', 'Total number of pages currently in cache', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_pages_clean', 'Clean pages currently in cache', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_pages_dirty', 'Dirty pages currently in cache', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_pages_internal', 'Internal pages currently in cache', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_pages_leaf', 'Leaf pages currently in cache', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_queued', 'Pages currently queued for eviction', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_refs_skipped', 'Refs skipped during cache traversal', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_root_entries', 'Entries in the root page', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_root_size', 'Size of the root page', 'no_clear,no_scale'),
+ CacheWalkStat('cache_state_smaller_alloc_size', 'On-disk page image sizes smaller than a single allocation unit', 'no_clear,no_scale'),
+
+ ##########################################
# Compression statistics
##########################################
CompressStat('compress_raw_fail', 'raw compression call failed, no additional data available'),
@@ -509,6 +531,41 @@ dsrc_stats = [
CompressStat('compress_write_too_small', 'page written was too small to compress'),
##########################################
+ # Cursor operations
+ ##########################################
+ CursorStat('cursor_create', 'create calls'),
+ CursorStat('cursor_insert', 'insert calls'),
+ CursorStat('cursor_insert_bulk', 'bulk-loaded cursor-insert calls'),
+ CursorStat('cursor_insert_bytes', 'cursor-insert key and value bytes inserted', 'size'),
+ CursorStat('cursor_next', 'next calls'),
+ CursorStat('cursor_prev', 'prev calls'),
+ CursorStat('cursor_remove', 'remove calls'),
+ CursorStat('cursor_remove_bytes', 'cursor-remove key bytes removed', 'size'),
+ CursorStat('cursor_reset', 'reset calls'),
+ CursorStat('cursor_restart', 'restarted searches'),
+ CursorStat('cursor_search', 'search calls'),
+ CursorStat('cursor_search_near', 'search near calls'),
+ CursorStat('cursor_truncate', 'truncate calls'),
+ CursorStat('cursor_update', 'update calls'),
+ CursorStat('cursor_update_bytes', 'cursor-update value bytes updated', 'size'),
+
+ ##########################################
+ # LSM statistics
+ ##########################################
+ LSMStat('bloom_count', 'bloom filters in the LSM tree', 'no_scale'),
+ LSMStat('bloom_false_positive', 'bloom filter false positives'),
+ LSMStat('bloom_hit', 'bloom filter hits'),
+ LSMStat('bloom_miss', 'bloom filter misses'),
+ LSMStat('bloom_page_evict', 'bloom filter pages evicted from cache'),
+ LSMStat('bloom_page_read', 'bloom filter pages read into cache'),
+ LSMStat('bloom_size', 'total size of bloom filters', 'no_scale,size'),
+ LSMStat('lsm_checkpoint_throttle', 'sleep for LSM checkpoint throttle'),
+ LSMStat('lsm_chunk_count', 'chunks in the LSM tree', 'no_scale'),
+ LSMStat('lsm_generation_max', 'highest merge generation in the LSM tree', 'max_aggregate,no_scale'),
+ LSMStat('lsm_lookup_no_bloom', 'queries that could have benefited from a Bloom filter that did not exist'),
+ LSMStat('lsm_merge_throttle', 'sleep for LSM merge throttle'),
+
+ ##########################################
# Reconciliation statistics
##########################################
RecStat('rec_dictionary', 'dictionary matches'),
@@ -527,6 +584,12 @@ dsrc_stats = [
RecStat('rec_suffix_compression', 'internal page key bytes discarded using suffix compression', 'size'),
##########################################
+ # Session operations
+ ##########################################
+ SessionStat('session_compact', 'object compaction'),
+ SessionStat('session_cursor_open', 'open cursor count', 'no_clear,no_scale'),
+
+ ##########################################
# Transaction statistics
##########################################
TxnStat('txn_update_conflict', 'update conflicts'),
diff --git a/src/third_party/wiredtiger/dist/wtperf_config.py b/src/third_party/wiredtiger/dist/wtperf_config.py
new file mode 100644
index 00000000000..72256ed5527
--- /dev/null
+++ b/src/third_party/wiredtiger/dist/wtperf_config.py
@@ -0,0 +1,25 @@
+# Output a doxgen version of the wtperf configuration options.
+import string, sys
+
+for line in sys.stdin:
+ if not line.startswith('OPTION '):
+ continue
+
+ line = line.replace('OPTION ', '')
+ v = line.split('",')
+ v[0] = v[0].replace('"', '').strip()
+ v[1] = v[1].replace('"', '').strip()
+ v[2] = v[2].replace('"', '').strip()
+ v[3] = v[3].replace('"', '').strip()
+
+ if v[3] == 'boolean':
+ if v[2] == '0':
+ d = 'false'
+ else:
+ d = 'true'
+ elif v[3] == 'string':
+ d = '"' + v[2] + '"'
+ else:
+ d = v[2]
+ print '@par ' + v[0] + ' (' + v[3] + ', default=' + d + ')'
+ print v[1]
diff --git a/src/third_party/wiredtiger/examples/c/ex_all.c b/src/third_party/wiredtiger/examples/c/ex_all.c
index a2042c22bbb..ea646604a76 100644
--- a/src/third_party/wiredtiger/examples/c/ex_all.c
+++ b/src/third_party/wiredtiger/examples/c/ex_all.c
@@ -611,6 +611,13 @@ session_ops(WT_SESSION *session)
"block_compressor=zlib,key_format=S,value_format=S");
/*! [Create a zlib compressed table] */
ret = session->drop(session, "table:mytable", NULL);
+
+ /*! [Create a zstd compressed table] */
+ ret = session->create(session,
+ "table:mytable",
+ "block_compressor=zstd,key_format=S,value_format=S");
+ /*! [Create a zstd compressed table] */
+ ret = session->drop(session, "table:mytable", NULL);
#endif
/*! [Configure checksums to uncompressed] */
@@ -1108,6 +1115,32 @@ main(void)
if (ret == 0)
(void)conn->close(conn, NULL);
+ /*! [Configure zlib extension with compression level] */
+ ret = wiredtiger_open(home, NULL,
+ "create,"
+ "extensions=[/usr/local/lib/"
+ "libwiredtiger_zlib.so=[config=[compression_level=3]]]", &conn);
+ /*! [Configure zlib extension with compression level] */
+ if (ret == 0)
+ (void)conn->close(conn, NULL);
+
+ /*! [Configure zstd extension] */
+ ret = wiredtiger_open(home, NULL,
+ "create,"
+ "extensions=[/usr/local/lib/libwiredtiger_zstd.so]", &conn);
+ /*! [Configure zstd extension] */
+ if (ret == 0)
+ (void)conn->close(conn, NULL);
+
+ /*! [Configure zstd extension with compression level] */
+ ret = wiredtiger_open(home, NULL,
+ "create,"
+ "extensions=[/usr/local/lib/"
+ "libwiredtiger_zstd.so=[config=[compression_level=9]]]", &conn);
+ /*! [Configure zstd extension with compression level] */
+ if (ret == 0)
+ (void)conn->close(conn, NULL);
+
/*
* This example code gets run, and direct I/O might not be available,
* causing the open to fail. The documentation requires code snippets,
diff --git a/src/third_party/wiredtiger/examples/java/com/wiredtiger/examples/ex_all.java b/src/third_party/wiredtiger/examples/java/com/wiredtiger/examples/ex_all.java
index 83a37e9a6a5..cf8491aa4f8 100644
--- a/src/third_party/wiredtiger/examples/java/com/wiredtiger/examples/ex_all.java
+++ b/src/third_party/wiredtiger/examples/java/com/wiredtiger/examples/ex_all.java
@@ -549,6 +549,12 @@ session_ops(Session session)
"block_compressor=zlib,key_format=S,value_format=S");
/*! [Create a zlib compressed table] */
ret = session.drop("table:mytable", null);
+
+ /*! [Create a zstd compressed table] */
+ ret = session.create("table:mytable",
+ "block_compressor=zstd,key_format=S,value_format=S");
+ /*! [Create a zstd compressed table] */
+ ret = session.drop("table:mytable", null);
} // if (false)
/*! [Configure checksums to uncompressed] */
@@ -942,6 +948,29 @@ allExample()
/*! [Configure zlib extension] */
conn.close(null);
+ /*! [Configure zlib extension with compression level] */
+ conn = wiredtiger.open(home,
+ "create," +
+ "extensions=[/usr/local/lib/" +
+ "libwiredtiger_zlib.so=[config=[compression_level=3]]]");
+ /*! [Configure zlib extension with compression level] */
+ conn.close(null);
+
+ /*! [Configure zstd extension] */
+ conn = wiredtiger.open(home,
+ "create," +
+ "extensions=[/usr/local/lib/libwiredtiger_zstd.so]");
+ /*! [Configure zstd extension] */
+ conn.close(null);
+
+ /*! [Configure zstd extension with compression level] */
+ conn = wiredtiger.open(home,
+ "create," +
+ "extensions=[/usr/local/lib/" +
+ "libwiredtiger_zstd.so=[config=[compression_level=9]]]");
+ /*! [Configure zstd extension with compression level] */
+ conn.close(null);
+
/*
* This example code gets run, and direct I/O might not be available,
* causing the open to fail. The documentation requires code snippets,
diff --git a/src/third_party/wiredtiger/ext/collators/revint/revint_collator.c b/src/third_party/wiredtiger/ext/collators/revint/revint_collator.c
index b8ebbdc8585..cfad3989adb 100644
--- a/src/third_party/wiredtiger/ext/collators/revint/revint_collator.c
+++ b/src/third_party/wiredtiger/ext/collators/revint/revint_collator.c
@@ -52,14 +52,14 @@ revint_compare(WT_COLLATOR *collator,
WT_SESSION *session, const WT_ITEM *k1, const WT_ITEM *k2, int *cmp)
{
const REVINT_COLLATOR *revint_collator;
- WT_EXTENSION_API *wtapi;
+ WT_EXTENSION_API *wt_api;
WT_PACK_STREAM *pstream;
int64_t i1, i2, p1, p2;
int ret;
i1 = i2 = p1 = p2 = 0;
revint_collator = (const REVINT_COLLATOR *)collator;
- wtapi = revint_collator->wt_api;
+ wt_api = revint_collator->wt_api;
/*
* All indices using this collator have an integer key, and the
@@ -79,25 +79,25 @@ revint_compare(WT_COLLATOR *collator,
* To keep this code simple, we do not reverse the ordering
* when comparing primary keys.
*/
- if ((ret = wtapi->unpack_start(
- wtapi, session, "ii", k1->data, k1->size, &pstream)) != 0 ||
- (ret = wtapi->unpack_int(wtapi, pstream, &i1)) != 0)
+ if ((ret = wt_api->unpack_start(
+ wt_api, session, "ii", k1->data, k1->size, &pstream)) != 0 ||
+ (ret = wt_api->unpack_int(wt_api, pstream, &i1)) != 0)
return (ret);
- if ((ret = wtapi->unpack_int(wtapi, pstream, &p1)) != 0)
+ if ((ret = wt_api->unpack_int(wt_api, pstream, &p1)) != 0)
/* A missing primary key is OK and sorts first. */
p1 = INT64_MIN;
- if ((ret = wtapi->pack_close(wtapi, pstream, NULL)) != 0)
+ if ((ret = wt_api->pack_close(wt_api, pstream, NULL)) != 0)
return (ret);
/* Unpack the second pair of numbers. */
- if ((ret = wtapi->unpack_start(
- wtapi, session, "ii", k2->data, k2->size, &pstream)) != 0 ||
- (ret = wtapi->unpack_int(wtapi, pstream, &i2)) != 0)
+ if ((ret = wt_api->unpack_start(
+ wt_api, session, "ii", k2->data, k2->size, &pstream)) != 0 ||
+ (ret = wt_api->unpack_int(wt_api, pstream, &i2)) != 0)
return (ret);
- if ((ret = wtapi->unpack_int(wtapi, pstream, &p2)) != 0)
+ if ((ret = wt_api->unpack_int(wt_api, pstream, &p2)) != 0)
/* A missing primary key is OK and sorts first. */
p2 = INT64_MIN;
- if ((ret = wtapi->pack_close(wtapi, pstream, NULL)) != 0)
+ if ((ret = wt_api->pack_close(wt_api, pstream, NULL)) != 0)
return (ret);
/* sorting is reversed */
diff --git a/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c b/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c
index 35159d0fa76..885701e564b 100644
--- a/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c
+++ b/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c
@@ -31,10 +31,20 @@
#include <stdlib.h>
#include <string.h>
+/*
+ * We need to include the configuration file to detect whether this extension
+ * is being built into the WiredTiger library; application-loaded compression
+ * functions won't need it.
+ */
#include <wiredtiger_config.h>
+
#include <wiredtiger.h>
#include <wiredtiger_ext.h>
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
/* Local compressor structure. */
typedef struct {
WT_COMPRESSOR compressor; /* Must come first */
@@ -171,8 +181,6 @@ lz4_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session,
int decoded;
uint8_t *dst_tmp;
- (void)src_len; /* Unused parameters */
-
wt_api = ((LZ4_COMPRESSOR *)compressor)->wt_api;
/*
@@ -183,6 +191,13 @@ lz4_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session,
#ifdef WORDS_BIGENDIAN
lz4_prefix_swap(&prefix);
#endif
+ if (prefix.compressed_len + sizeof(LZ4_PREFIX) > src_len) {
+ (void)wt_api->err_printf(wt_api,
+ session,
+ "WT_COMPRESSOR.decompress: stored size exceeds source "
+ "size");
+ return (WT_ERROR);
+ }
/*
* Decompress, starting after the prefix bytes. Use safe decompression:
@@ -267,18 +282,24 @@ lz4_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session,
size_t *result_lenp, uint32_t *result_slotsp)
{
LZ4_PREFIX prefix;
- int lz4_len;
uint32_t slot;
- int sourceSize, targetDestSize;
+ int lz4_len, sourceSize, targetDestSize;
(void)compressor; /* Unused parameters */
(void)session;
(void)split_pct;
(void)final;
- sourceSize = (int)offsets[slots]; /* Type conversion */
- targetDestSize =
- (int)((dst_len < page_max ? dst_len : page_max) - extra);
+ /*
+ * Set the source and target sizes. The target size is complicated: we
+ * don't want to exceed the smaller of the maximum page size or the
+ * destination buffer length, and in both cases we have to take into
+ * account the space for our overhead and the extra bytes required by
+ * our caller.
+ */
+ sourceSize = (int)offsets[slots];
+ targetDestSize = (int)(page_max < dst_len ? page_max : dst_len);
+ targetDestSize -= (int)(sizeof(LZ4_PREFIX) + extra);
/* Compress, starting after the prefix bytes. */
lz4_len = LZ4_compress_destSize((const char *)src,
@@ -352,7 +373,7 @@ lz4_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session)
* Add a LZ4 compressor.
*/
static int
-lz_add_compressor(WT_CONNECTION *connection, int raw, const char *name)
+lz_add_compressor(WT_CONNECTION *connection, bool raw, const char *name)
{
LZ4_COMPRESSOR *lz4_compressor;
@@ -391,9 +412,9 @@ lz4_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
(void)config; /* Unused parameters */
- if ((ret = lz_add_compressor(connection, 1, "lz4")) != 0)
+ if ((ret = lz_add_compressor(connection, true, "lz4")) != 0)
return (ret);
- if ((ret = lz_add_compressor(connection, 0, "lz4-noraw")) != 0)
+ if ((ret = lz_add_compressor(connection, false, "lz4-noraw")) != 0)
return (ret);
return (0);
}
diff --git a/src/third_party/wiredtiger/ext/compressors/snappy/snappy_compress.c b/src/third_party/wiredtiger/ext/compressors/snappy/snappy_compress.c
index 981e334a2de..32f1ddcb9a0 100644
--- a/src/third_party/wiredtiger/ext/compressors/snappy/snappy_compress.c
+++ b/src/third_party/wiredtiger/ext/compressors/snappy/snappy_compress.c
@@ -31,10 +31,20 @@
#include <stdlib.h>
#include <string.h>
+/*
+ * We need to include the configuration file to detect whether this extension
+ * is being built into the WiredTiger library; application-loaded compression
+ * functions won't need it.
+ */
#include <wiredtiger_config.h>
+
#include <wiredtiger.h>
#include <wiredtiger_ext.h>
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
/* Local compressor structure. */
typedef struct {
WT_COMPRESSOR compressor; /* Must come first */
@@ -42,6 +52,12 @@ typedef struct {
WT_EXTENSION_API *wt_api; /* Extension API */
} SNAPPY_COMPRESSOR;
+/*
+ * Snappy decompression requires an exact compressed byte count. WiredTiger
+ * doesn't track that value, store it in the destination buffer.
+ */
+#define SNAPPY_PREFIX sizeof(uint64_t)
+
#ifdef WORDS_BIGENDIAN
/*
* snappy_bswap64 --
@@ -64,11 +80,11 @@ snappy_bswap64(uint64_t v)
#endif
/*
- * wt_snappy_error --
+ * snappy_error --
* Output an error message, and return a standard error code.
*/
static int
-wt_snappy_error(WT_COMPRESSOR *compressor,
+snappy_error(WT_COMPRESSOR *compressor,
WT_SESSION *session, const char *call, snappy_status snret)
{
WT_EXTENSION_API *wt_api;
@@ -94,68 +110,69 @@ wt_snappy_error(WT_COMPRESSOR *compressor,
}
/*
- * wt_snappy_compress --
+ * snappy_compression --
* WiredTiger snappy compression.
*/
static int
-wt_snappy_compress(WT_COMPRESSOR *compressor, WT_SESSION *session,
+snappy_compression(WT_COMPRESSOR *compressor, WT_SESSION *session,
uint8_t *src, size_t src_len,
uint8_t *dst, size_t dst_len,
size_t *result_lenp, int *compression_failed)
{
snappy_status snret;
size_t snaplen;
+ uint64_t snaplen_u64;
char *snapbuf;
/*
- * dst_len was computed in wt_snappy_pre_size, so we know it's big
- * enough. Skip past the space we'll use to store the final count
- * of compressed bytes.
+ * dst_len was computed in snappy_pre_size, so we know it's big enough.
+ * Skip past the space we'll use to store the final count of compressed
+ * bytes.
*/
- snaplen = dst_len - sizeof(size_t);
- snapbuf = (char *)dst + sizeof(size_t);
+ snaplen = dst_len - SNAPPY_PREFIX;
+ snapbuf = (char *)dst + SNAPPY_PREFIX;
/* snaplen is an input and an output arg. */
snret = snappy_compress((char *)src, src_len, snapbuf, &snaplen);
- if (snret == SNAPPY_OK) {
- if (snaplen + sizeof(size_t) < src_len) {
- *result_lenp = snaplen + sizeof(size_t);
- *compression_failed = 0;
-
- /*
- * On decompression, snappy requires an exact compressed
- * byte count (the current value of snaplen). WiredTiger
- * does not preserve that value, so save snaplen at the
- * beginning of the destination buffer.
- *
- * Store the value in little-endian format.
- */
+ if (snret == SNAPPY_OK && snaplen + SNAPPY_PREFIX < src_len) {
+ *result_lenp = snaplen + SNAPPY_PREFIX;
+ *compression_failed = 0;
+
+ /*
+ * On decompression, snappy requires an exact compressed byte
+ * count (the current value of snaplen). WiredTiger does not
+ * preserve that value, so save snaplen at the beginning of
+ * the destination buffer.
+ *
+ * Store the value in little-endian format.
+ */
+ snaplen_u64 = snaplen;
#ifdef WORDS_BIGENDIAN
- snaplen = snappy_bswap64(snaplen);
+ snaplen_u64 = snappy_bswap64(snaplen_u64);
#endif
- *(size_t *)dst = snaplen;
- } else
- /* The compressor failed to produce a smaller result. */
- *compression_failed = 1;
+ *(uint64_t *)dst = snaplen_u64;
return (0);
}
- return (wt_snappy_error(compressor, session, "snappy_compress", snret));
+
+ *compression_failed = 1;
+ return (snret == SNAPPY_OK ?
+ 0 : snappy_error(compressor, session, "snappy_compress", snret));
}
/*
- * wt_snappy_decompress --
+ * snappy_decompression --
* WiredTiger snappy decompression.
*/
static int
-wt_snappy_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session,
+snappy_decompression(WT_COMPRESSOR *compressor, WT_SESSION *session,
uint8_t *src, size_t src_len,
uint8_t *dst, size_t dst_len,
size_t *result_lenp)
{
WT_EXTENSION_API *wt_api;
snappy_status snret;
- size_t snaplen;
+ uint64_t snaplen;
wt_api = ((SNAPPY_COMPRESSOR *)compressor)->wt_api;
@@ -163,36 +180,36 @@ wt_snappy_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session,
* Retrieve the saved length, handling little- to big-endian conversion
* as necessary.
*/
- snaplen = *(size_t *)src;
+ snaplen = *(uint64_t *)src;
#ifdef WORDS_BIGENDIAN
snaplen = snappy_bswap64(snaplen);
#endif
- if (snaplen + sizeof(size_t) > src_len) {
+ if (snaplen + SNAPPY_PREFIX > src_len) {
(void)wt_api->err_printf(wt_api,
session,
- "wt_snappy_decompress: stored size exceeds buffer size");
+ "WT_COMPRESSOR.decompress: stored size exceeds source "
+ "size");
return (WT_ERROR);
}
/* dst_len is an input and an output arg. */
snret = snappy_uncompress(
- (char *)src + sizeof(size_t), snaplen, (char *)dst, &dst_len);
+ (char *)src + SNAPPY_PREFIX,
+ (size_t)snaplen, (char *)dst, &dst_len);
if (snret == SNAPPY_OK) {
*result_lenp = dst_len;
return (0);
}
-
- return (
- wt_snappy_error(compressor, session, "snappy_decompress", snret));
+ return (snappy_error(compressor, session, "snappy_decompress", snret));
}
/*
- * wt_snappy_pre_size --
+ * snappy_pre_size --
* WiredTiger snappy destination buffer sizing.
*/
static int
-wt_snappy_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session,
+snappy_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session,
uint8_t *src, size_t src_len,
size_t *result_lenp)
{
@@ -203,19 +220,19 @@ wt_snappy_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session,
/*
* Snappy requires the dest buffer be somewhat larger than the source.
* Fortunately, this is fast to compute, and will give us a dest buffer
- * in wt_snappy_compress that we can compress to directly. We add space
+ * in snappy_compress that we can compress to directly. We add space
* in the dest buffer to store the accurate compressed size.
*/
- *result_lenp = snappy_max_compressed_length(src_len) + sizeof(size_t);
+ *result_lenp = snappy_max_compressed_length(src_len) + SNAPPY_PREFIX;
return (0);
}
/*
- * wt_snappy_terminate --
+ * snappy_terminate --
* WiredTiger snappy compression termination.
*/
static int
-wt_snappy_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session)
+snappy_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session)
{
(void)session; /* Unused parameters */
@@ -227,9 +244,9 @@ int snappy_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
/*
* snappy_extension_init --
- * WiredTiger snappy compression extension - called directly when
- * Snappy support is built in, or via wiredtiger_extension_init when
- * snappy support is included via extension loading.
+ * WiredTiger snappy compression extension - called directly when snappy
+ * support is built in, or via wiredtiger_extension_init when snappy support
+ * is included via extension loading.
*/
int
snappy_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
@@ -241,11 +258,11 @@ snappy_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
if ((snappy_compressor = calloc(1, sizeof(SNAPPY_COMPRESSOR))) == NULL)
return (errno);
- snappy_compressor->compressor.compress = wt_snappy_compress;
+ snappy_compressor->compressor.compress = snappy_compression;
snappy_compressor->compressor.compress_raw = NULL;
- snappy_compressor->compressor.decompress = wt_snappy_decompress;
- snappy_compressor->compressor.pre_size = wt_snappy_pre_size;
- snappy_compressor->compressor.terminate = wt_snappy_terminate;
+ snappy_compressor->compressor.decompress = snappy_decompression;
+ snappy_compressor->compressor.pre_size = snappy_pre_size;
+ snappy_compressor->compressor.terminate = snappy_terminate;
snappy_compressor->wt_api = connection->get_extension_api(connection);
diff --git a/src/third_party/wiredtiger/ext/compressors/zlib/zlib_compress.c b/src/third_party/wiredtiger/ext/compressors/zlib/zlib_compress.c
index 484df0a6785..3665ec48b9a 100644
--- a/src/third_party/wiredtiger/ext/compressors/zlib/zlib_compress.c
+++ b/src/third_party/wiredtiger/ext/compressors/zlib/zlib_compress.c
@@ -32,16 +32,18 @@
#include <stdlib.h>
#include <string.h>
-#include <wiredtiger.h>
-#include <wiredtiger_ext.h>
-
/*
* We need to include the configuration file to detect whether this extension
- * is being built into the WiredTiger library.
+ * is being built into the WiredTiger library; application-loaded compression
+ * functions won't need it.
*/
-#include "wiredtiger_config.h"
+#include <wiredtiger_config.h>
+
+#include <wiredtiger.h>
+#include <wiredtiger_ext.h>
+
#ifdef _MSC_VER
-#define inline __inline
+#define inline __inline
#endif
/* Local compressor structure. */
@@ -234,148 +236,198 @@ zlib_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session,
{
ZLIB_COMPRESSOR *zlib_compressor;
ZLIB_OPAQUE opaque;
- z_stream *best_zs, last_zs, zs;
- uint32_t curr_slot, last_slot;
- int ret;
+ z_stream *best_zs, *last_zs, _last_zs, *zs, _zs;
+ uint32_t curr_slot, last_slot, zlib_reserved;
+ bool increase_reserve;
+ int ret, tret;
- curr_slot = last_slot = 0;
- (void)split_pct;
- (void)dst_len;
+ (void)split_pct; /* Unused parameters */
(void)final;
zlib_compressor = (ZLIB_COMPRESSOR *)compressor;
- memset(&zs, 0, sizeof(zs));
- zs.zalloc = zalloc;
- zs.zfree = zfree;
- opaque.compressor = compressor;
- opaque.session = session;
- zs.opaque = &opaque;
-
- if ((ret = deflateInit(&zs, zlib_compressor->zlib_level)) != Z_OK)
- return (zlib_error(compressor, session, "deflateInit", ret));
-
- zs.next_in = src;
- zs.next_out = dst;
/*
* Experimentally derived, reserve this many bytes for zlib to finish
* up a buffer. If this isn't sufficient, we don't fail but we will be
* inefficient.
*/
#define WT_ZLIB_RESERVED 24
- zs.avail_out = (uint32_t)(page_max - (extra + WT_ZLIB_RESERVED));
+#define WT_ZLIB_RESERVED_MAX 48
+ zlib_reserved = WT_ZLIB_RESERVED;
+
+ if (0) {
+retry: /* If we reached our maximum reserve, quit. */
+ if (zlib_reserved == WT_ZLIB_RESERVED_MAX)
+ return (0);
+ zlib_reserved = WT_ZLIB_RESERVED_MAX;
+ }
+
+ best_zs = last_zs = NULL;
+ last_slot = 0;
+ increase_reserve = false;
+ ret = 0;
+
+ zs = &_zs;
+ memset(zs, 0, sizeof(*zs));
+ zs->zalloc = zalloc;
+ zs->zfree = zfree;
+ opaque.compressor = compressor;
+ opaque.session = session;
+ zs->opaque = &opaque;
+
+ if ((ret = deflateInit(zs, zlib_compressor->zlib_level)) != Z_OK)
+ return (zlib_error(compressor, session, "deflateInit", ret));
- /* Save the stream state in case the chosen data doesn't fit. */
- if ((ret = deflateCopy(&last_zs, &zs)) != Z_OK)
- return (zlib_error(compressor, session, "deflateCopy", ret));
+ zs->next_in = src;
+ zs->next_out = dst;
+
+ /*
+ * Set the target size. The target size is complicated: we don't want
+ * to exceed the smaller of the maximum page size or the destination
+ * buffer length, and in both cases we have to take into account the
+ * space required by zlib to finish up the buffer and the extra bytes
+ * required by our caller.
+ */
+ zs->avail_out = (uint32_t)(page_max < dst_len ? page_max : dst_len);
+ zs->avail_out -= (uint32_t)(zlib_reserved + extra);
/*
* Strategy: take the available output size and compress that much
* input. Continue until there is no input small enough or the
* compression fails to fit.
*/
- for (best_zs = NULL;;) {
+ for (;;) {
/* Find the next slot we will try to compress up to. */
- if ((curr_slot = zlib_find_slot(
- zs.total_in + zs.avail_out, offsets, slots)) > last_slot) {
- zs.avail_in = offsets[curr_slot] - offsets[last_slot];
- while (zs.avail_in > 0 && zs.avail_out > 0)
- if ((ret = deflate(&zs, Z_SYNC_FLUSH)) != Z_OK)
- return (zlib_error(compressor,
- session, "deflate", ret));
+ curr_slot = zlib_find_slot(
+ zs->total_in + zs->avail_out, offsets, slots);
+ if (curr_slot > last_slot) {
+ zs->avail_in = offsets[curr_slot] - offsets[last_slot];
+ while (zs->avail_in > 0 && zs->avail_out > 0)
+ if ((ret = deflate(zs, Z_SYNC_FLUSH)) != Z_OK) {
+ ret = zlib_error(compressor,
+ session, "deflate", ret);
+ goto err;
+ }
}
/*
* We didn't do a deflate, or it didn't work: use the last saved
- * position.
+ * position (if any).
*/
- if (curr_slot <= last_slot || zs.avail_in > 0) {
- if ((ret = deflateEnd(&zs)) != Z_OK &&
- ret != Z_DATA_ERROR)
- return (zlib_error(
- compressor, session, "deflateEnd", ret));
-
- best_zs = &last_zs;
+ if (curr_slot <= last_slot || zs->avail_in > 0) {
+ best_zs = last_zs;
break;
}
- /* The last deflation succeeded, discard the saved one. */
- if ((ret = deflateEnd(&last_zs)) != Z_OK && ret != Z_DATA_ERROR)
- return (zlib_error(
- compressor, session, "deflateEnd", ret));
-
/*
* If there's more compression to do, save a snapshot and keep
* going, otherwise, use the current compression.
*/
last_slot = curr_slot;
- if (zs.avail_out > 0) {
- if ((ret = deflateCopy(&last_zs, &zs)) != Z_OK)
- return (zlib_error(
- compressor, session, "deflateCopy", ret));
+ if (zs->avail_out > 0) {
+ /* Discard any previously saved snapshot. */
+ if (last_zs != NULL) {
+ ret = deflateEnd(last_zs);
+ last_zs = NULL;
+ if (ret != Z_OK && ret != Z_DATA_ERROR) {
+ ret = zlib_error(compressor,
+ session, "deflateEnd", ret);
+ goto err;
+ }
+ }
+ last_zs = &_last_zs;
+ if ((ret = deflateCopy(last_zs, zs)) != Z_OK) {
+ last_zs = NULL;
+ ret = zlib_error(
+ compressor, session, "deflateCopy", ret);
+ goto err;
+ }
continue;
}
- best_zs = &zs;
+ best_zs = zs;
break;
}
- best_zs->avail_out += WT_ZLIB_RESERVED;
- ret = deflate(best_zs, Z_FINISH);
+ if (last_slot > 0 && best_zs != NULL) {
+ /* Add the reserved bytes and try to finish the compression. */
+ best_zs->avail_out += zlib_reserved;
+ ret = deflate(best_zs, Z_FINISH);
- /*
- * If the end marker didn't fit, report that we got no work done,
- * WiredTiger will compress the (possibly large) page image using
- * ordinary compression instead.
- */
- if (ret == Z_OK || ret == Z_BUF_ERROR)
- last_slot = 0;
- else if (ret != Z_STREAM_END)
- return (
- zlib_error(compressor, session, "deflate end block", ret));
+ /*
+ * If the end marker didn't fit with the default value, try
+ * again with a maximum value; if that doesn't work, report we
+ * got no work done, WiredTiger will compress the (possibly
+ * large) page image using ordinary compression instead.
+ */
+ if (ret == Z_OK || ret == Z_BUF_ERROR) {
+ last_slot = 0;
+ increase_reserve = true;
+ } else if (ret != Z_STREAM_END) {
+ ret = zlib_error(
+ compressor, session, "deflate end block", ret);
+ goto err;
+ }
+ ret = 0;
+ }
- if ((ret = deflateEnd(best_zs)) != Z_OK && ret != Z_DATA_ERROR)
- return (zlib_error(compressor, session, "deflateEnd", ret));
+err: if ((tret = deflateEnd(zs)) != Z_OK && tret != Z_DATA_ERROR)
+ ret = zlib_error(compressor, session, "deflateEnd", tret);
+ if (last_zs != NULL &&
+ (tret = deflateEnd(last_zs)) != Z_OK && tret != Z_DATA_ERROR)
+ ret = zlib_error(compressor, session, "deflateEnd", tret);
- if (last_slot > 0) {
+ if (ret == 0 && last_slot > 0) {
*result_slotsp = last_slot;
*result_lenp = (size_t)best_zs->total_out;
} else {
- /* We didn't manage to compress anything: don't retry. */
+ /* We didn't manage to compress anything. */
*result_slotsp = 0;
*result_lenp = 1;
+
+ if (increase_reserve)
+ goto retry;
}
#if 0
/* Decompress the result and confirm it matches the original source. */
- if (last_slot > 0) {
+ if (ret == 0 && last_slot > 0) {
+ WT_EXTENSION_API *wt_api;
void *decomp;
size_t result_len;
+ wt_api = ((ZLIB_COMPRESSOR *)compressor)->wt_api;
+
if ((decomp = zalloc(
- &opaque, 1, (uint32_t)best_zs->total_in + 100)) == NULL)
+ &opaque, 1, (uint32_t)best_zs->total_in + 100)) == NULL) {
+ (void)wt_api->err_printf(wt_api, session,
+ "zlib_compress_raw: zalloc failure");
return (ENOMEM);
+ }
if ((ret = zlib_decompress(
compressor, session, dst, (size_t)best_zs->total_out,
decomp, (size_t)best_zs->total_in + 100, &result_len)) == 0)
- if (memcmp(src, decomp, result_len) != 0)
- ret = zlib_error(compressor, session,
- "deflate compare with original source",
- Z_DATA_ERROR);
+ if (memcmp(src, decomp, result_len) != 0) {
+ (void)wt_api->err_printf(wt_api, session,
+ "zlib_compress_raw: "
+ "deflate compare with original source");
+ return (WT_ERROR);
+ }
zfree(&opaque, decomp);
- if (ret != 0)
- return (ret);
}
#endif
#if 0
- fprintf(stderr,
- "zlib_compress_raw (%s): page_max %" PRIuMAX ", slots %" PRIu32
- ", take %" PRIu32 ": %" PRIu32 " -> %" PRIuMAX "\n",
- final ? "final" : "not final", (uintmax_t)page_max,
- slots, last_slot, offsets[last_slot], (uintmax_t)*result_lenp);
+ if (ret == 0 && last_slot > 0)
+ fprintf(stderr,
+ "zlib_compress_raw (%s): page_max %" PRIuMAX ", slots %"
+ PRIu32 ", take %" PRIu32 ": %" PRIu32 " -> %" PRIuMAX "\n",
+ final ? "final" : "not final", (uintmax_t)page_max,
+ slots, last_slot, offsets[last_slot],
+ (uintmax_t)*result_lenp);
#endif
- return (0);
+
+ return (ret);
}
/*
@@ -396,7 +448,8 @@ zlib_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session)
* Add a zlib compressor.
*/
static int
-zlib_add_compressor(WT_CONNECTION *connection, int raw, const char *name)
+zlib_add_compressor(
+ WT_CONNECTION *connection, bool raw, const char *name, int zlib_level)
{
ZLIB_COMPRESSOR *zlib_compressor;
@@ -415,17 +468,81 @@ zlib_add_compressor(WT_CONNECTION *connection, int raw, const char *name)
zlib_compressor->compressor.terminate = zlib_terminate;
zlib_compressor->wt_api = connection->get_extension_api(connection);
-
- /*
- * Between 0-10: level: see zlib manual.
- */
- zlib_compressor->zlib_level = Z_DEFAULT_COMPRESSION;
+ zlib_compressor->zlib_level = zlib_level;
/* Load the compressor. */
return (connection->add_compressor(
connection, name, (WT_COMPRESSOR *)zlib_compressor, NULL));
}
+/*
+ * zlib_init_config --
+ * Handle zlib configuration.
+ */
+static int
+zlib_init_config(
+ WT_CONNECTION *connection, WT_CONFIG_ARG *config, int *zlib_levelp)
+{
+ WT_CONFIG_ITEM k, v;
+ WT_CONFIG_PARSER *config_parser;
+ WT_EXTENSION_API *wt_api;
+ int ret, zlib_level;
+
+ /* If configured as a built-in, there's no configuration argument. */
+ if (config == NULL)
+ return (0);
+
+ /*
+ * Zlib compression engine allows applications to specify a compression
+ * level; review the configuration.
+ */
+ wt_api = connection->get_extension_api(connection);
+ if ((ret =
+ wt_api->config_get(wt_api, NULL, config, "config", &v)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_EXTENSION_API.config_get: zlib configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ if ((ret = wt_api->config_parser_open(
+ wt_api, NULL, v.str, v.len, &config_parser)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_EXTENSION_API.config_parser_open: zlib configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ while ((ret = config_parser->next(config_parser, &k, &v)) == 0)
+ if (strlen("compression_level") == k.len &&
+ strncmp("compression_level", k.str, k.len) == 0) {
+ /*
+ * Between 0-9: level: see zlib manual.
+ */
+ zlib_level = (int)v.val;
+ if (zlib_level < 0 || zlib_level > 9) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_CONFIG_PARSER.next: zlib configure: "
+ "unsupported compression level %d",
+ zlib_level);
+ return (EINVAL);
+ }
+ *zlib_levelp = zlib_level;
+ continue;
+ }
+ if (ret != WT_NOTFOUND) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_CONFIG_PARSER.next: zlib configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ if ((ret = config_parser->close(config_parser)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_CONFIG_PARSER.close: zlib configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ return (0);
+}
+
int zlib_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
/*
@@ -437,13 +554,17 @@ int zlib_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
int
zlib_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
{
- int ret;
+ int ret, zlib_level;
- (void)config; /* Unused parameters */
+ zlib_level = Z_DEFAULT_COMPRESSION; /* Default */
+ if ((ret = zlib_init_config(connection, config, &zlib_level)) != 0)
+ return (ret);
- if ((ret = zlib_add_compressor(connection, 1, "zlib")) != 0)
+ if ((ret = zlib_add_compressor(
+ connection, true, "zlib", zlib_level)) != 0)
return (ret);
- if ((ret = zlib_add_compressor(connection, 0, "zlib-noraw")) != 0)
+ if ((ret = zlib_add_compressor(
+ connection, false, "zlib-noraw", zlib_level)) != 0)
return (ret);
return (0);
}
diff --git a/src/third_party/wiredtiger/ext/compressors/zstd/Makefile.am b/src/third_party/wiredtiger/ext/compressors/zstd/Makefile.am
new file mode 100644
index 00000000000..9f0997011e9
--- /dev/null
+++ b/src/third_party/wiredtiger/ext/compressors/zstd/Makefile.am
@@ -0,0 +1,11 @@
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)/src/include
+
+if HAVE_BUILTIN_EXTENSION_ZSTD
+noinst_LTLIBRARIES = libwiredtiger_zstd.la
+else
+lib_LTLIBRARIES = libwiredtiger_zstd.la
+libwiredtiger_zstd_la_LDFLAGS = -avoid-version -module
+endif
+
+libwiredtiger_zstd_la_SOURCES = zstd_compress.c
+libwiredtiger_zstd_la_LIBADD = -lzstd
diff --git a/src/third_party/wiredtiger/ext/compressors/zstd/zstd_compress.c b/src/third_party/wiredtiger/ext/compressors/zstd/zstd_compress.c
new file mode 100644
index 00000000000..a459b01d60a
--- /dev/null
+++ b/src/third_party/wiredtiger/ext/compressors/zstd/zstd_compress.c
@@ -0,0 +1,359 @@
+/*-
+ * Public Domain 2014-2016 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.
+ */
+
+#include <zstd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * We need to include the configuration file to detect whether this extension
+ * is being built into the WiredTiger library; application-loaded compression
+ * functions won't need it.
+ */
+#include <wiredtiger_config.h>
+
+#include <wiredtiger.h>
+#include <wiredtiger_ext.h>
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+/* Local compressor structure. */
+typedef struct {
+ WT_COMPRESSOR compressor; /* Must come first */
+
+ WT_EXTENSION_API *wt_api; /* Extension API */
+
+ int compression_level; /* compression level */
+} ZSTD_COMPRESSOR;
+
+/*
+ * Zstd decompression requires an exact compressed byte count. WiredTiger
+ * doesn't track that value, store it in the destination buffer.
+ */
+#define ZSTD_PREFIX sizeof(uint64_t)
+
+#ifdef WORDS_BIGENDIAN
+/*
+ * zstd_bswap64 --
+ * 64-bit unsigned little-endian to/from big-endian value.
+ */
+static inline uint64_t
+zstd_bswap64(uint64_t v)
+{
+ return (
+ ((v << 56) & 0xff00000000000000UL) |
+ ((v << 40) & 0x00ff000000000000UL) |
+ ((v << 24) & 0x0000ff0000000000UL) |
+ ((v << 8) & 0x000000ff00000000UL) |
+ ((v >> 8) & 0x00000000ff000000UL) |
+ ((v >> 24) & 0x0000000000ff0000UL) |
+ ((v >> 40) & 0x000000000000ff00UL) |
+ ((v >> 56) & 0x00000000000000ffUL)
+ );
+}
+#endif
+
+/*
+ * zstd_error --
+ * Output an error message, and return a standard error code.
+ */
+static int
+zstd_error(WT_COMPRESSOR *compressor,
+ WT_SESSION *session, const char *call, size_t error)
+{
+ WT_EXTENSION_API *wt_api;
+
+ wt_api = ((ZSTD_COMPRESSOR *)compressor)->wt_api;
+
+ (void)wt_api->err_printf(wt_api, session,
+ "zstd error: %s: %s", call, ZSTD_getErrorName(error));
+ return (WT_ERROR);
+}
+
+/*
+ * zstd_compress --
+ * WiredTiger Zstd compression.
+ */
+static int
+zstd_compress(WT_COMPRESSOR *compressor, WT_SESSION *session,
+ uint8_t *src, size_t src_len,
+ uint8_t *dst, size_t dst_len,
+ size_t *result_lenp, int *compression_failed)
+{
+ ZSTD_COMPRESSOR *zcompressor;
+ size_t zstd_ret;
+ uint64_t zstd_len;
+
+ zcompressor = (ZSTD_COMPRESSOR *)compressor;
+
+ /* Compress, starting past the prefix bytes. */
+ zstd_ret = ZSTD_compress(
+ dst + ZSTD_PREFIX, dst_len - ZSTD_PREFIX,
+ src, src_len, zcompressor->compression_level);
+
+ /*
+ * If compression succeeded and the compressed length is smaller than
+ * the original size, return success.
+ */
+ if (!ZSTD_isError(zstd_ret) && zstd_ret + ZSTD_PREFIX < src_len) {
+ *result_lenp = zstd_ret + ZSTD_PREFIX;
+ *compression_failed = 0;
+
+ /*
+ * On decompression, Zstd requires an exact compressed byte
+ * count (the current value of zstd_ret). WiredTiger does not
+ * preserve that value, so save zstd_ret at the beginning of
+ * the destination buffer.
+ *
+ * Store the value in little-endian format.
+ */
+ zstd_len = zstd_ret;
+#ifdef WORDS_BIGENDIAN
+ zstd_len = zstd_bswap64(zstd_len);
+#endif
+ *(uint64_t *)dst = zstd_len;
+ return (0);
+ }
+
+ *compression_failed = 1;
+ return (ZSTD_isError(zstd_ret) ?
+ zstd_error(compressor, session, "ZSTD_compress", zstd_ret) : 0);
+}
+
+/*
+ * zstd_decompress --
+ * WiredTiger Zstd decompression.
+ */
+static int
+zstd_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session,
+ uint8_t *src, size_t src_len,
+ uint8_t *dst, size_t dst_len,
+ size_t *result_lenp)
+{
+ WT_EXTENSION_API *wt_api;
+ size_t zstd_ret;
+ uint64_t zstd_len;
+
+ wt_api = ((ZSTD_COMPRESSOR *)compressor)->wt_api;
+
+ /*
+ * Retrieve the saved length, handling little- to big-endian conversion
+ * as necessary.
+ */
+ zstd_len = *(uint64_t *)src;
+#ifdef WORDS_BIGENDIAN
+ zstd_len = zstd_bswap64(zstd_len);
+#endif
+ if (zstd_len + ZSTD_PREFIX > src_len) {
+ (void)wt_api->err_printf(wt_api,
+ session,
+ "WT_COMPRESSOR.decompress: stored size exceeds source "
+ "size");
+ return (WT_ERROR);
+ }
+
+ zstd_ret =
+ ZSTD_decompress(dst, dst_len, src + ZSTD_PREFIX, (size_t)zstd_len);
+
+ if (!ZSTD_isError(zstd_ret)) {
+ *result_lenp = zstd_ret;
+ return (0);
+ }
+ return (zstd_error(compressor, session, "ZSTD_decompress", zstd_ret));
+}
+
+/*
+ * zstd_pre_size --
+ * WiredTiger Zstd destination buffer sizing for compression.
+ */
+static int
+zstd_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session,
+ uint8_t *src, size_t src_len, size_t *result_lenp)
+{
+ (void)compressor; /* Unused parameters */
+ (void)session;
+ (void)src;
+
+ /*
+ * Zstd compression runs faster if the destination buffer is sized at
+ * the upper-bound of the buffer size needed by the compression. Use
+ * the library calculation of that overhead (plus our overhead).
+ */
+ *result_lenp = ZSTD_compressBound(src_len) + ZSTD_PREFIX;
+ return (0);
+}
+
+/*
+ * zstd_terminate --
+ * WiredTiger Zstd compression termination.
+ */
+static int
+zstd_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session)
+{
+ (void)session; /* Unused parameters */
+
+ free(compressor);
+ return (0);
+}
+
+/*
+ * zstd_init_config --
+ * Handle zstd configuration.
+ */
+static int
+zstd_init_config(
+ WT_CONNECTION *connection, WT_CONFIG_ARG *config, int *compression_levelp)
+{
+ WT_CONFIG_ITEM k, v;
+ WT_CONFIG_PARSER *config_parser;
+ WT_EXTENSION_API *wt_api;
+ int ret;
+
+ /* If configured as a built-in, there's no configuration argument. */
+ if (config == NULL)
+ return (0);
+
+ /*
+ * Zstd compression engine allows applications to specify a compression
+ * level; review the configuration.
+ */
+ wt_api = connection->get_extension_api(connection);
+ if ((ret =
+ wt_api->config_get(wt_api, NULL, config, "config", &v)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_EXTENSION_API.config_get: zstd configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ if ((ret = wt_api->config_parser_open(
+ wt_api, NULL, v.str, v.len, &config_parser)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_EXTENSION_API.config_parser_open: zstd configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ while ((ret = config_parser->next(config_parser, &k, &v)) == 0)
+ if (strlen("compression_level") == k.len &&
+ strncmp("compression_level", k.str, k.len) == 0) {
+ *compression_levelp = (int)v.val;
+ continue;
+ }
+ if (ret != WT_NOTFOUND) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_CONFIG_PARSER.next: zstd configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ if ((ret = config_parser->close(config_parser)) != 0) {
+ (void)wt_api->err_printf(wt_api, NULL,
+ "WT_CONFIG_PARSER.close: zstd configure: %s",
+ wt_api->strerror(wt_api, NULL, ret));
+ return (ret);
+ }
+ return (0);
+}
+
+int zstd_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
+
+/*
+ * zstd_extension_init --
+ * WiredTiger Zstd compression extension - called directly when Zstd
+ * support is built in, or via wiredtiger_extension_init when Zstd support
+ * is included via extension loading.
+ */
+int
+zstd_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
+{
+ ZSTD_COMPRESSOR *zstd_compressor;
+ int compression_level, ret;
+
+ /*
+ * Zstd's sweet-spot is better compression than zlib at significantly
+ * faster compression/decompression speeds. LZ4 and snappy are faster
+ * than zstd, but have worse compression ratios. Applications wanting
+ * faster compression/decompression with worse compression will select
+ * LZ4 or snappy, so we configure zstd for better compression.
+ *
+ * From the zstd github site, default measurements of the compression
+ * engines we support, listing compression ratios with compression and
+ * decompression speeds:
+ *
+ * Name Ratio C.speed D.speed
+ * MB/s MB/s
+ * zstd 2.877 330 940
+ * zlib 2.730 95 360
+ * LZ4 2.101 620 3100
+ * snappy 2.091 480 1600
+ *
+ * Set the zstd compression level to 3: according to the zstd web site,
+ * that reduces zstd's compression speed to around 200 MB/s, increasing
+ * the compression ratio to 3.100 (close to zlib's best compression
+ * ratio). In other words, position zstd as a zlib replacement, having
+ * similar compression at much higher compression/decompression speeds.
+ */
+ compression_level = 3;
+ if ((ret =
+ zstd_init_config(connection, config, &compression_level)) != 0)
+ return (ret);
+
+ if ((zstd_compressor = calloc(1, sizeof(ZSTD_COMPRESSOR))) == NULL)
+ return (errno);
+
+ zstd_compressor->compressor.compress = zstd_compress;
+ zstd_compressor->compressor.compress_raw = NULL;
+ zstd_compressor->compressor.decompress = zstd_decompress;
+ zstd_compressor->compressor.pre_size = zstd_pre_size;
+ zstd_compressor->compressor.terminate = zstd_terminate;
+
+ zstd_compressor->wt_api = connection->get_extension_api(connection);
+
+ zstd_compressor->compression_level = compression_level;
+
+ /* Load the compressor */
+ return (connection->add_compressor(
+ connection, "zstd", (WT_COMPRESSOR *)zstd_compressor, NULL));
+}
+
+/*
+ * We have to remove this symbol when building as a builtin extension otherwise
+ * it will conflict with other builtin libraries.
+ */
+#ifndef HAVE_BUILTIN_EXTENSION_ZSTD
+/*
+ * wiredtiger_extension_init --
+ * WiredTiger Zstd compression extension.
+ */
+int
+wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
+{
+ return (zstd_extension_init(connection, config));
+}
+#endif
diff --git a/src/third_party/wiredtiger/ext/datasources/helium/helium.c b/src/third_party/wiredtiger/ext/datasources/helium/helium.c
index e934d61ddf6..473c569f0cc 100644
--- a/src/third_party/wiredtiger/ext/datasources/helium/helium.c
+++ b/src/third_party/wiredtiger/ext/datasources/helium/helium.c
@@ -80,22 +80,22 @@ static int verbose = 0; /* Verbose messages */
} \
} while (0)
#undef ERET
-#define ERET(wtext, session, v, ...) do { \
+#define ERET(wt_api, session, v, ...) do { \
(void) \
- wtext->err_printf(wtext, session, "helium: " __VA_ARGS__); \
+ wt_api->err_printf(wt_api, session, "helium: " __VA_ARGS__);\
ESET(v); \
return (ret); \
} while (0)
#undef EMSG
-#define EMSG(wtext, session, v, ...) do { \
+#define EMSG(wt_api, session, v, ...) do { \
(void) \
- wtext->err_printf(wtext, session, "helium: " __VA_ARGS__); \
+ wt_api->err_printf(wt_api, session, "helium: " __VA_ARGS__);\
ESET(v); \
} while (0)
#undef EMSG_ERR
-#define EMSG_ERR(wtext, session, v, ...) do { \
+#define EMSG_ERR(wt_api, session, v, ...) do { \
(void) \
- wtext->err_printf(wtext, session, "helium: " __VA_ARGS__); \
+ wt_api->err_printf(wt_api, session, "helium: " __VA_ARGS__);\
ESET(v); \
goto err; \
} while (0)
@@ -104,10 +104,10 @@ static int verbose = 0; /* Verbose messages */
#undef VERBOSE_L2
#define VERBOSE_L2 2
#undef VMSG
-#define VMSG(wtext, session, v, ...) do { \
+#define VMSG(wt_api, session, v, ...) do { \
if (verbose >= v) \
- (void)wtext-> \
- msg_printf(wtext, session, "helium: " __VA_ARGS__); \
+ (void)wt_api-> \
+ msg_printf(wt_api, session, "helium: " __VA_ARGS__);\
} while (0)
/*
@@ -183,7 +183,7 @@ typedef struct __he_source {
*/
WT_TXN_NOTIFY txn_notify; /* Transaction commit handler */
- WT_EXTENSION_API *wtext; /* Extension functions */
+ WT_EXTENSION_API *wt_api; /* Extension functions */
char *name; /* Unique WiredTiger name */
char *device; /* Unique Helium volume name */
@@ -231,7 +231,7 @@ typedef struct __he_source {
typedef struct __data_source {
WT_DATA_SOURCE wtds; /* Must come first */
- WT_EXTENSION_API *wtext; /* Extension functions */
+ WT_EXTENSION_API *wt_api; /* Extension functions */
pthread_rwlock_t global_lock; /* Global lock */
int lockinit; /* Lock created */
@@ -269,7 +269,7 @@ typedef struct __cache_record {
typedef struct __cursor {
WT_CURSOR wtcursor; /* Must come first */
- WT_EXTENSION_API *wtext; /* Extension functions */
+ WT_EXTENSION_API *wt_api; /* Extension functions */
WT_SOURCE *ws; /* Underlying source */
@@ -345,12 +345,13 @@ os_errno(void)
* Initialize a lock.
*/
static int
-lock_init(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+lock_init(
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_init(lockp, NULL)) != 0)
- ERET(wtext, session, WT_PANIC,
+ ERET(wt_api, session, WT_PANIC,
"pthread_rwlock_init: %s", strerror(ret));
return (0);
}
@@ -361,12 +362,12 @@ lock_init(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
*/
static int
lock_destroy(
- WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_destroy(lockp)) != 0)
- ERET(wtext, session, WT_PANIC,
+ ERET(wt_api, session, WT_PANIC,
"pthread_rwlock_destroy: %s", strerror(ret));
return (0);
}
@@ -376,12 +377,13 @@ lock_destroy(
* Acquire a write lock.
*/
static inline int
-writelock(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+writelock(
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_wrlock(lockp)) != 0)
- ERET(wtext, session, WT_PANIC,
+ ERET(wt_api, session, WT_PANIC,
"pthread_rwlock_wrlock: %s", strerror(ret));
return (0);
}
@@ -391,12 +393,12 @@ writelock(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
* Release a lock.
*/
static inline int
-unlock(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+unlock(WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_unlock(lockp)) != 0)
- ERET(wtext, session, WT_PANIC,
+ ERET(wt_api, session, WT_PANIC,
"pthread_rwlock_unlock: %s", strerror(ret));
return (0);
}
@@ -425,7 +427,7 @@ helium_dump_kv(const char *pfx, uint8_t *p, size_t len, FILE *fp)
* Dump the records in a Helium store.
*/
static int
-helium_dump(WT_EXTENSION_API *wtext, he_t he, const char *tag)
+helium_dump(WT_EXTENSION_API *wt_api, he_t he, const char *tag)
{
HE_ITEM *r, _r;
uint8_t k[4 * 1024], v[4 * 1024];
@@ -440,7 +442,7 @@ helium_dump(WT_EXTENSION_API *wtext, he_t he, const char *tag)
while ((ret = he_next(he, r, (size_t)0, sizeof(v))) == 0) {
#if 0
uint64_t recno;
- if ((ret = wtext->struct_unpack(wtext,
+ if ((ret = wt_api->struct_unpack(wt_api,
NULL, r->key, r->key_len, "r", &recno)) != 0)
return (ret);
fprintf(stderr, "K: %" PRIu64, recno);
@@ -462,13 +464,13 @@ helium_dump(WT_EXTENSION_API *wtext, he_t he, const char *tag)
*/
static int
helium_stats(
- WT_EXTENSION_API *wtext, WT_SESSION *session, he_t he, const char *tag)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, he_t he, const char *tag)
{
HE_STATS stats;
int ret = 0;
if ((ret = he_stats(he, &stats)) != 0)
- ERET(wtext, session, ret, "he_stats: %s", he_strerror(ret));
+ ERET(wt_api, session, ret, "he_stats: %s", he_strerror(ret));
fprintf(stderr, "== %s\n", tag);
fprintf(stderr, "name=%s\n", stats.name);
fprintf(stderr, "deleted_items=%" PRIu64 "\n", stats.deleted_items);
@@ -490,14 +492,14 @@ helium_call(WT_CURSOR *wtcursor, const char *fname,
{
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
char *p;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
r = &cursor->record;
r->val = cursor->v;
@@ -506,7 +508,7 @@ restart:
if ((ret = f(he, r, (size_t)0, cursor->mem_len)) != 0) {
if (ret == HE_ERR_ITEM_NOT_FOUND)
return (WT_NOTFOUND);
- ERET(wtext, session, ret, "%s: %s", fname, he_strerror(ret));
+ ERET(wt_api, session, ret, "%s: %s", fname, he_strerror(ret));
}
/*
@@ -537,7 +539,7 @@ restart:
if ((ret = he_lookup(he, r, (size_t)0, cursor->mem_len)) != 0) {
if (ret == HE_ERR_ITEM_NOT_FOUND)
goto restart;
- ERET(wtext,
+ ERET(wt_api,
session, ret, "he_lookup: %s", he_strerror(ret));
}
}
@@ -549,7 +551,7 @@ restart:
* Resolve a transaction.
*/
static int
-txn_state_set(WT_EXTENSION_API *wtext,
+txn_state_set(WT_EXTENSION_API *wt_api,
WT_SESSION *session, HELIUM_SOURCE *hs, uint64_t txnid, int commit)
{
HE_ITEM txn;
@@ -571,10 +573,10 @@ txn_state_set(WT_EXTENSION_API *wtext,
txn.val_len = sizeof(val);
if ((ret = he_update(hs->he_txn, &txn)) != 0)
- ERET(wtext, session, ret, "he_update: %s", he_strerror(ret));
+ ERET(wt_api, session, ret, "he_update: %s", he_strerror(ret));
if (commit && (ret = he_commit(hs->he_txn)) != 0)
- ERET(wtext, session, ret, "he_commit: %s", he_strerror(ret));
+ ERET(wt_api, session, ret, "he_commit: %s", he_strerror(ret));
return (0);
}
@@ -589,7 +591,7 @@ txn_notify(WT_TXN_NOTIFY *handler,
HELIUM_SOURCE *hs;
hs = (HELIUM_SOURCE *)handler;
- return (txn_state_set(hs->wtext, session, hs, txnid, committed));
+ return (txn_state_set(hs->wt_api, session, hs, txnid, committed));
}
/*
@@ -627,7 +629,7 @@ cache_value_append(WT_CURSOR *wtcursor, int remove_op)
{
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
uint64_t txnid;
size_t len;
@@ -636,7 +638,7 @@ cache_value_append(WT_CURSOR *wtcursor, int remove_op)
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
r = &cursor->record;
@@ -662,7 +664,7 @@ cache_value_append(WT_CURSOR *wtcursor, int remove_op)
}
/* Get the transaction ID. */
- txnid = wtext->transaction_id(wtext, session);
+ txnid = wt_api->transaction_id(wt_api, session);
/* Update the number of records in this value. */
if (cursor->len == 0) {
@@ -787,17 +789,17 @@ cache_value_update_check(WT_CURSOR *wtcursor)
{
CACHE_RECORD *cp;
CURSOR *cursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
u_int i;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
/* Only interesting for snapshot isolation. */
- if (wtext->
- transaction_isolation_level(wtext, session) != WT_TXN_ISO_SNAPSHOT)
+ if (wt_api->
+ transaction_isolation_level(wt_api, session) != WT_TXN_ISO_SNAPSHOT)
return (0);
/*
@@ -806,7 +808,7 @@ cache_value_update_check(WT_CURSOR *wtcursor)
*/
for (i = 0, cp = cursor->cache; i < cursor->cache_entries; ++i, ++cp)
if (!cache_value_aborted(wtcursor, cp) &&
- !wtext->transaction_visible(wtext, session, cp->txnid))
+ !wt_api->transaction_visible(wt_api, session, cp->txnid))
return (WT_ROLLBACK);
return (0);
}
@@ -821,7 +823,7 @@ cache_value_visible(WT_CURSOR *wtcursor, CACHE_RECORD **cpp)
{
CACHE_RECORD *cp;
CURSOR *cursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
u_int i;
@@ -829,7 +831,7 @@ cache_value_visible(WT_CURSOR *wtcursor, CACHE_RECORD **cpp)
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
/*
* We want the most recent cache entry update; the cache entries are
@@ -839,7 +841,7 @@ cache_value_visible(WT_CURSOR *wtcursor, CACHE_RECORD **cpp)
for (i = 0; i < cursor->cache_entries; ++i) {
--cp;
if (!cache_value_aborted(wtcursor, cp) &&
- wtext->transaction_visible(wtext, session, cp->txnid)) {
+ wt_api->transaction_visible(wt_api, session, cp->txnid)) {
*cpp = cp;
return (1);
}
@@ -970,11 +972,11 @@ cache_value_txnmin(WT_CURSOR *wtcursor, uint64_t *txnminp)
* Common error when a WiredTiger key is too large.
*/
static int
-key_max_err(WT_EXTENSION_API *wtext, WT_SESSION *session, size_t len)
+key_max_err(WT_EXTENSION_API *wt_api, WT_SESSION *session, size_t len)
{
int ret = 0;
- ERET(wtext, session, EINVAL,
+ ERET(wt_api, session, EINVAL,
"key length (%zu bytes) larger than the maximum Helium "
"key length of %d bytes",
len, HE_MAX_KEY_LEN);
@@ -989,7 +991,7 @@ copyin_key(WT_CURSOR *wtcursor, int allocate_key)
{
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
WT_SOURCE *ws;
size_t size;
@@ -998,7 +1000,7 @@ copyin_key(WT_CURSOR *wtcursor, int allocate_key)
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
ws = cursor->ws;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
r = &cursor->record;
if (ws->config_recno) {
@@ -1019,23 +1021,23 @@ copyin_key(WT_CURSOR *wtcursor, int allocate_key)
* not quite right.
*/
if (allocate_key && cursor->config_append) {
- if ((ret = writelock(wtext, session, &ws->lock)) != 0)
+ if ((ret = writelock(wt_api, session, &ws->lock)) != 0)
return (ret);
wtcursor->recno = ++ws->append_recno;
- if ((ret = unlock(wtext, session, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, session, &ws->lock)) != 0)
return (ret);
} else if (wtcursor->recno > ws->append_recno) {
- if ((ret = writelock(wtext, session, &ws->lock)) != 0)
+ if ((ret = writelock(wt_api, session, &ws->lock)) != 0)
return (ret);
if (wtcursor->recno > ws->append_recno)
ws->append_recno = wtcursor->recno;
- if ((ret = unlock(wtext, session, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, session, &ws->lock)) != 0)
return (ret);
}
- if ((ret = wtext->struct_size(wtext, session,
+ if ((ret = wt_api->struct_size(wt_api, session,
&size, "r", wtcursor->recno)) != 0 ||
- (ret = wtext->struct_pack(wtext, session,
+ (ret = wt_api->struct_pack(wt_api, session,
r->key, HE_MAX_KEY_LEN, "r", wtcursor->recno)) != 0)
return (ret);
r->key_len = size;
@@ -1043,7 +1045,7 @@ copyin_key(WT_CURSOR *wtcursor, int allocate_key)
/* I'm not sure this test is necessary, but it's cheap. */
if (wtcursor->key.size > HE_MAX_KEY_LEN)
return (
- key_max_err(wtext, session, wtcursor->key.size));
+ key_max_err(wt_api, session, wtcursor->key.size));
/*
* A set cursor key might reference application memory, which
@@ -1068,19 +1070,19 @@ copyout_key(WT_CURSOR *wtcursor)
{
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
WT_SOURCE *ws;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
ws = cursor->ws;
r = &cursor->record;
if (ws->config_recno) {
- if ((ret = wtext->struct_unpack(wtext,
+ if ((ret = wt_api->struct_unpack(wt_api,
session, r->key, r->key_len, "r", &wtcursor->recno)) != 0)
return (ret);
} else {
@@ -1122,7 +1124,7 @@ nextprev(WT_CURSOR *wtcursor, const char *fname,
CACHE_RECORD *cp;
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_ITEM a, b;
WT_SESSION *session;
WT_SOURCE *ws;
@@ -1132,7 +1134,7 @@ nextprev(WT_CURSOR *wtcursor, const char *fname,
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
ws = cursor->ws;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
r = &cursor->record;
cache_rm = 0;
@@ -1243,8 +1245,8 @@ cache_clean:
a.size = (uint32_t)r->key_len;
b.data = cursor->t2.v; /* b is the cache */
b.size = (uint32_t)cursor->t2.len;
- if ((ret = wtext->collate(
- wtext, session, NULL, &a, &b, &cmp)) != 0)
+ if ((ret = wt_api->collate(
+ wt_api, session, NULL, &a, &b, &cmp)) != 0)
return (ret);
if (f == he_next) {
@@ -1423,14 +1425,14 @@ helium_cursor_insert(WT_CURSOR *wtcursor)
CURSOR *cursor;
HE_ITEM *r;
HELIUM_SOURCE *hs;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
WT_SOURCE *ws;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
ws = cursor->ws;
hs = ws->hs;
r = &cursor->record;
@@ -1439,14 +1441,14 @@ helium_cursor_insert(WT_CURSOR *wtcursor)
if ((ret = copyin_key(wtcursor, 1)) != 0)
return (ret);
- VMSG(wtext, session, VERBOSE_L2,
+ VMSG(wt_api, session, VERBOSE_L2,
"I %.*s.%.*s", (int)r->key_len, r->key, (int)r->val_len, r->val);
/* Clear the value, assume we're adding the first cache entry. */
cursor->len = 0;
/* Updates are read-modify-writes, lock the underlying cache. */
- if ((ret = writelock(wtext, session, &ws->lock)) != 0)
+ if ((ret = writelock(wt_api, session, &ws->lock)) != 0)
return (ret);
/* Read the record from the cache store. */
@@ -1502,19 +1504,19 @@ helium_cursor_insert(WT_CURSOR *wtcursor)
if ((ret = cache_value_append(wtcursor, 0)) != 0)
goto err;
if ((ret = he_update(ws->he_cache, r)) != 0)
- EMSG(wtext, session, ret, "he_update: %s", he_strerror(ret));
+ EMSG(wt_api, session, ret, "he_update: %s", he_strerror(ret));
/* Update the state while still holding the lock. */
if (ws->he_cache_inuse == 0)
ws->he_cache_inuse = 1;
/* Discard the lock. */
-err: ESET(unlock(wtext, session, &ws->lock));
+err: ESET(unlock(wt_api, session, &ws->lock));
/* If successful, request notification at transaction resolution. */
if (ret == 0)
- ESET(
- wtext->transaction_notify(wtext, session, &hs->txn_notify));
+ ESET(wt_api->transaction_notify(
+ wt_api, session, &hs->txn_notify));
return (ret);
}
@@ -1530,14 +1532,14 @@ update(WT_CURSOR *wtcursor, int remove_op)
CURSOR *cursor;
HE_ITEM *r;
HELIUM_SOURCE *hs;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
WT_SOURCE *ws;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
ws = cursor->ws;
hs = ws->hs;
r = &cursor->record;
@@ -1546,7 +1548,7 @@ update(WT_CURSOR *wtcursor, int remove_op)
if ((ret = copyin_key(wtcursor, 0)) != 0)
return (ret);
- VMSG(wtext, session, VERBOSE_L2,
+ VMSG(wt_api, session, VERBOSE_L2,
"%c %.*s.%.*s",
remove_op ? 'R' : 'U',
(int)r->key_len, r->key, (int)r->val_len, r->val);
@@ -1555,7 +1557,7 @@ update(WT_CURSOR *wtcursor, int remove_op)
cursor->len = 0;
/* Updates are read-modify-writes, lock the underlying cache. */
- if ((ret = writelock(wtext, session, &ws->lock)) != 0)
+ if ((ret = writelock(wt_api, session, &ws->lock)) != 0)
return (ret);
/* Read the record from the cache store. */
@@ -1614,19 +1616,19 @@ update(WT_CURSOR *wtcursor, int remove_op)
/* Push the record into the cache. */
if ((ret = he_update(ws->he_cache, r)) != 0)
- EMSG(wtext, session, ret, "he_update: %s", he_strerror(ret));
+ EMSG(wt_api, session, ret, "he_update: %s", he_strerror(ret));
/* Update the state while still holding the lock. */
if (ws->he_cache_inuse == 0)
ws->he_cache_inuse = 1;
/* Discard the lock. */
-err: ESET(unlock(wtext, session, &ws->lock));
+err: ESET(unlock(wt_api, session, &ws->lock));
/* If successful, request notification at transaction resolution. */
if (ret == 0)
- ESET(
- wtext->transaction_notify(wtext, session, &hs->txn_notify));
+ ESET(wt_api->transaction_notify(
+ wt_api, session, &hs->txn_notify));
return (ret);
}
@@ -1674,19 +1676,19 @@ static int
helium_cursor_close(WT_CURSOR *wtcursor)
{
CURSOR *cursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
WT_SOURCE *ws;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
ws = cursor->ws;
- if ((ret = writelock(wtext, session, &ws->lock)) == 0) {
+ if ((ret = writelock(wt_api, session, &ws->lock)) == 0) {
--ws->ref;
- ret = unlock(wtext, session, &ws->lock);
+ ret = unlock(wt_api, session, &ws->lock);
}
cursor_destroy(cursor);
@@ -1702,13 +1704,13 @@ ws_source_name(WT_DATA_SOURCE *wtds,
WT_SESSION *session, const char *uri, const char *suffix, char **pp)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
size_t len;
int ret = 0;
const char *p;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/*
* Create the store's name. Application URIs are "helium:device/name";
@@ -1717,7 +1719,7 @@ ws_source_name(WT_DATA_SOURCE *wtds,
* and add an optional suffix.
*/
if (!prefix_match(uri, "helium:") || (p = strchr(uri, '/')) == NULL)
- ERET(wtext, session, EINVAL, "%s: illegal Helium URI", uri);
+ ERET(wt_api, session, EINVAL, "%s: illegal Helium URI", uri);
++p;
len = strlen(WT_NAME_PREFIX) +
@@ -1734,7 +1736,7 @@ ws_source_name(WT_DATA_SOURCE *wtds,
* Close a WT_SOURCE reference.
*/
static int
-ws_source_close(WT_EXTENSION_API *wtext, WT_SESSION *session, WT_SOURCE *ws)
+ws_source_close(WT_EXTENSION_API *wt_api, WT_SESSION *session, WT_SOURCE *ws)
{
int ret = 0, tret;
@@ -1743,29 +1745,29 @@ ws_source_close(WT_EXTENSION_API *wtext, WT_SESSION *session, WT_SOURCE *ws)
* WiredTiger prevent it, so we don't do anything more than warn.
*/
if (ws->ref != 0)
- EMSG(wtext, session, WT_ERROR,
+ EMSG(wt_api, session, WT_ERROR,
"%s: open object with %u open cursors being closed",
ws->uri, ws->ref);
if (ws->he != NULL) {
if ((tret = he_commit(ws->he)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_commit: %s: %s", ws->uri, he_strerror(tret));
if ((tret = he_close(ws->he)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_close: %s: %s", ws->uri, he_strerror(tret));
ws->he = NULL;
}
if (ws->he_cache != NULL) {
if ((tret = he_close(ws->he_cache)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_close: %s(cache): %s",
ws->uri, he_strerror(tret));
ws->he_cache = NULL;
}
if (ws->lockinit)
- ESET(lock_destroy(wtext, session, &ws->lock));
+ ESET(lock_destroy(wt_api, session, &ws->lock));
free(ws->uri);
OVERWRITE_AND_FREE(ws);
@@ -1783,7 +1785,7 @@ ws_source_open_object(WT_DATA_SOURCE *wtds, WT_SESSION *session,
const char *uri, const char *suffix, int flags, he_t *hep)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
he_t he;
char *p;
int ret = 0;
@@ -1791,16 +1793,16 @@ ws_source_open_object(WT_DATA_SOURCE *wtds, WT_SESSION *session,
*hep = NULL;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
p = NULL;
/* Open the underlying Helium object. */
if ((ret = ws_source_name(wtds, session, uri, suffix, &p)) != 0)
return (ret);
- VMSG(wtext, session, VERBOSE_L1, "open %s/%s", hs->name, p);
+ VMSG(wt_api, session, VERBOSE_L1, "open %s/%s", hs->name, p);
if ((he = he_open(hs->device, p, flags, NULL)) == NULL) {
ret = os_errno();
- EMSG(wtext, session, ret,
+ EMSG(wt_api, session, ret,
"he_open: %s/%s: %s", hs->name, p, he_strerror(ret));
}
*hep = he;
@@ -1824,7 +1826,7 @@ ws_source_open(WT_DATA_SOURCE *wtds, WT_SESSION *session,
DATA_SOURCE *ds;
HELIUM_SOURCE *hs;
WT_CONFIG_ITEM a;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
size_t len;
int oflags, ret = 0;
@@ -1833,7 +1835,7 @@ ws_source_open(WT_DATA_SOURCE *wtds, WT_SESSION *session,
*refp = NULL;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
ws = NULL;
/*
@@ -1844,7 +1846,7 @@ ws_source_open(WT_DATA_SOURCE *wtds, WT_SESSION *session,
goto bad_name;
p = uri + strlen("helium:");
if (p[0] == '/' || (t = strchr(p, '/')) == NULL || t[1] == '\0')
-bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
+bad_name: ERET(wt_api, session, EINVAL, "%s: illegal name format", uri);
len = (size_t)(t - p);
/* Find a matching Helium device. */
@@ -1852,14 +1854,14 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
if (string_match(hs->name, p, len))
break;
if (hs == NULL)
- ERET(wtext, NULL,
+ ERET(wt_api, NULL,
EINVAL, "%s: no matching Helium store found", uri);
/*
* We're about to walk the Helium device's list of files, acquire the
* global lock.
*/
- if ((ret = writelock(wtext, session, &ds->global_lock)) != 0)
+ if ((ret = writelock(wt_api, session, &ds->global_lock)) != 0)
return (ret);
/*
@@ -1872,13 +1874,13 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
/* Check to see if the object is busy. */
if (ws->ref != 0 && (flags & WS_SOURCE_OPEN_BUSY)) {
ret = EBUSY;
- ESET(unlock(wtext, session, &ds->global_lock));
+ ESET(unlock(wt_api, session, &ds->global_lock));
return (ret);
}
/* Swap the global lock for an object lock. */
if (!(flags & WS_SOURCE_OPEN_GLOBAL)) {
- ret = writelock(wtext, session, &ws->lock);
- ESET(unlock(wtext, session, &ds->global_lock));
+ ret = writelock(wt_api, session, &ws->lock);
+ ESET(unlock(wt_api, session, &ds->global_lock));
if (ret != 0)
return (ret);
}
@@ -1892,7 +1894,7 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
ret = os_errno();
goto err;
}
- if ((ret = lock_init(wtext, session, &ws->lock)) != 0)
+ if ((ret = lock_init(wt_api, session, &ws->lock)) != 0)
goto err;
ws->lockinit = 1;
ws->hs = hs;
@@ -1907,13 +1909,13 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
* handles attempts to create existing objects.
*/
oflags = HE_O_CREATE;
- if ((ret = wtext->config_get(wtext,
+ if ((ret = wt_api->config_get(wt_api,
session, config, "helium_o_truncate", &a)) == 0 && a.val != 0)
oflags |= HE_O_TRUNCATE;
if (ret != 0 && ret != WT_NOTFOUND)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"helium_o_truncate configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
if ((ret = ws_source_open_object(
wtds, session, hs, uri, NULL, oflags, &ws->he)) != 0)
@@ -1922,12 +1924,12 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
wtds, session, hs, uri, WT_NAME_CACHE, oflags, &ws->he_cache)) != 0)
goto err;
if ((ret = he_commit(ws->he)) != 0)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"he_commit: %s", he_strerror(ret));
/* Optionally trade the global lock for the object lock. */
if (!(flags & WS_SOURCE_OPEN_GLOBAL) &&
- (ret = writelock(wtext, session, &ws->lock)) != 0)
+ (ret = writelock(wt_api, session, &ws->lock)) != 0)
goto err;
/* Insert the new entry at the head of the list. */
@@ -1939,7 +1941,7 @@ bad_name: ERET(wtext, session, EINVAL, "%s: illegal name format", uri);
if (0) {
err: if (ws != NULL)
- ESET(ws_source_close(wtext, session, ws));
+ ESET(ws_source_close(wt_api, session, ws));
}
/*
@@ -1947,7 +1949,7 @@ err: if (ws != NULL)
* release the global lock.
*/
if (!(flags & WS_SOURCE_OPEN_GLOBAL) || ret != 0)
- ESET(unlock(wtext, session, &ds->global_lock));
+ ESET(unlock(wt_api, session, &ds->global_lock));
return (ret);
}
@@ -1961,12 +1963,12 @@ master_uri_get(WT_DATA_SOURCE *wtds,
WT_SESSION *session, const char *uri, char **valuep)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
- return (wtext->metadata_search(wtext, session, uri, valuep));
+ return (wt_api->metadata_search(wt_api, session, uri, valuep));
}
/*
@@ -1977,12 +1979,12 @@ static int
master_uri_drop(WT_DATA_SOURCE *wtds, WT_SESSION *session, const char *uri)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
- return (wtext->metadata_remove(wtext, session, uri));
+ return (wt_api->metadata_remove(wt_api, session, uri));
}
/*
@@ -1994,25 +1996,26 @@ master_uri_rename(WT_DATA_SOURCE *wtds,
WT_SESSION *session, const char *uri, const char *newuri)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
char *value;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
value = NULL;
/* Insert the record under a new name. */
if ((ret = master_uri_get(wtds, session, uri, &value)) != 0 ||
- (ret = wtext->metadata_insert(wtext, session, newuri, value)) != 0)
+ (ret =
+ wt_api->metadata_insert(wt_api, session, newuri, value)) != 0)
goto err;
/*
* Remove the original record, and if that fails, attempt to remove
* the new record.
*/
- if ((ret = wtext->metadata_remove(wtext, session, uri)) != 0)
- (void)wtext->metadata_remove(wtext, session, newuri);
+ if ((ret = wt_api->metadata_remove(wt_api, session, uri)) != 0)
+ (void)wt_api->metadata_remove(wt_api, session, newuri);
err: free((void *)value);
return (ret);
@@ -2028,53 +2031,53 @@ master_uri_set(WT_DATA_SOURCE *wtds,
{
DATA_SOURCE *ds;
WT_CONFIG_ITEM a, b, c;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int exclusive, ret = 0;
char value[1024];
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
exclusive = 0;
if ((ret =
- wtext->config_get(wtext, session, config, "exclusive", &a)) == 0)
+ wt_api->config_get(wt_api, session, config, "exclusive", &a)) == 0)
exclusive = a.val != 0;
else if (ret != WT_NOTFOUND)
- ERET(wtext, session, ret,
+ ERET(wt_api, session, ret,
"exclusive configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
/* Get the key/value format strings. */
- if ((ret = wtext->config_get(
- wtext, session, config, "key_format", &a)) != 0) {
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "key_format", &a)) != 0) {
if (ret == WT_NOTFOUND) {
a.str = "u";
a.len = 1;
} else
- ERET(wtext, session, ret,
+ ERET(wt_api, session, ret,
"key_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
}
- if ((ret = wtext->config_get(
- wtext, session, config, "value_format", &b)) != 0) {
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "value_format", &b)) != 0) {
if (ret == WT_NOTFOUND) {
b.str = "u";
b.len = 1;
} else
- ERET(wtext, session, ret,
+ ERET(wt_api, session, ret,
"value_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
}
/* Get the compression configuration. */
- if ((ret = wtext->config_get(
- wtext, session, config, "helium_o_compress", &c)) != 0) {
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "helium_o_compress", &c)) != 0) {
if (ret == WT_NOTFOUND)
c.val = 0;
else
- ERET(wtext, session, ret,
+ ERET(wt_api, session, ret,
"helium_o_compress configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
}
/*
@@ -2087,12 +2090,12 @@ master_uri_set(WT_DATA_SOURCE *wtds,
"helium_o_compress=%d",
WIREDTIGER_HELIUM_MAJOR, WIREDTIGER_HELIUM_MINOR,
(int)a.len, a.str, (int)b.len, b.str, c.val ? 1 : 0);
- if ((ret = wtext->metadata_insert(wtext, session, uri, value)) == 0)
+ if ((ret = wt_api->metadata_insert(wt_api, session, uri, value)) == 0)
return (0);
if (ret == WT_DUPLICATE_KEY)
return (exclusive ? EEXIST : 0);
- ERET(wtext,
- session, ret, "%s: %s", uri, wtext->strerror(wtext, session, ret));
+ ERET(wt_api, session,
+ ret, "%s: %s", uri, wt_api->strerror(wt_api, session, ret));
}
/*
@@ -2108,7 +2111,7 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
WT_CONFIG_ITEM v;
WT_CONFIG_PARSER *config_parser;
WT_CURSOR *wtcursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
int locked, own, ret, tret;
char *value;
@@ -2118,7 +2121,7 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
config_parser = NULL;
cursor = NULL;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
ws = NULL;
locked = 0;
ret = tret = 0;
@@ -2128,25 +2131,25 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
if ((cursor = calloc(1, sizeof(CURSOR))) == NULL)
return (os_errno());
- if ((ret = wtext->config_get( /* Parse configuration */
- wtext, session, config, "append", &v)) != 0)
- EMSG_ERR(wtext, session, ret,
+ if ((ret = wt_api->config_get( /* Parse configuration */
+ wt_api, session, config, "append", &v)) != 0)
+ EMSG_ERR(wt_api, session, ret,
"append configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
cursor->config_append = v.val != 0;
- if ((ret = wtext->config_get(
- wtext, session, config, "overwrite", &v)) != 0)
- EMSG_ERR(wtext, session, ret,
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "overwrite", &v)) != 0)
+ EMSG_ERR(wt_api, session, ret,
"overwrite configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
cursor->config_overwrite = v.val != 0;
- if ((ret = wtext->collator_config(
- wtext, session, uri, config, NULL, &own)) != 0)
- EMSG_ERR(wtext, session, ret,
+ if ((ret = wt_api->collator_config(
+ wt_api, session, uri, config, NULL, &own)) != 0)
+ EMSG_ERR(wt_api, session, ret,
"collator configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
/* Finish initializing the cursor. */
cursor->wtcursor.close = helium_cursor_close;
@@ -2159,7 +2162,7 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
cursor->wtcursor.search_near = helium_cursor_search_near;
cursor->wtcursor.update = helium_cursor_update;
- cursor->wtext = wtext;
+ cursor->wt_api = wt_api;
cursor->record.key = cursor->__key;
if ((cursor->v = malloc(128)) == NULL)
goto err;
@@ -2179,31 +2182,31 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
if ((ret = master_uri_get(wtds, session, uri, &value)) != 0)
goto err;
- if ((ret = wtext->config_parser_open(wtext,
+ if ((ret = wt_api->config_parser_open(wt_api,
session, value, strlen(value), &config_parser)) != 0)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"Configuration string parser: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
if ((ret = config_parser->get(
config_parser, "key_format", &v)) != 0)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"key_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
ws->config_recno = v.len == 1 && v.str[0] == 'r';
if ((ret = config_parser->get(
config_parser, "value_format", &v)) != 0)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"value_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
ws->config_bitfield = v.len == 2 &&
isdigit((u_char)v.str[0]) && v.str[1] == 't';
if ((ret = config_parser->get(
config_parser, "helium_o_compress", &v)) != 0)
- EMSG_ERR(wtext, session, ret,
+ EMSG_ERR(wt_api, session, ret,
"helium_o_compress configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
ws->config_compress = v.val ? 1 : 0;
/*
@@ -2229,21 +2232,21 @@ helium_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
/* Increment the open reference count to pin the URI and unlock it. */
++ws->ref;
- if ((ret = unlock(wtext, session, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, session, &ws->lock)) != 0)
goto err;
*new_cursor = (WT_CURSOR *)cursor;
if (0) {
err: if (ws != NULL && locked)
- ESET(unlock(wtext, session, &ws->lock));
+ ESET(unlock(wt_api, session, &ws->lock));
cursor_destroy(cursor);
}
if (config_parser != NULL &&
(tret = config_parser->close(config_parser)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"WT_CONFIG_PARSER.close: %s",
- wtext->strerror(wtext, session, tret));
+ wt_api->strerror(wt_api, session, tret));
free((void *)value);
return (ret);
@@ -2258,12 +2261,12 @@ helium_session_create(WT_DATA_SOURCE *wtds,
WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/*
* Get a locked reference to the WiredTiger source, then immediately
@@ -2271,7 +2274,7 @@ helium_session_create(WT_DATA_SOURCE *wtds,
*/
if ((ret = ws_source_open(wtds, session, uri, config, 0, &ws)) != 0)
return (ret);
- if ((ret = unlock(wtext, session, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, session, &ws->lock)) != 0)
return (ret);
/*
@@ -2297,12 +2300,12 @@ helium_session_drop(WT_DATA_SOURCE *wtds,
{
DATA_SOURCE *ds;
HELIUM_SOURCE *hs;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE **p, *ws;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/*
* Get a locked reference to the data source: hold the global lock,
@@ -2328,7 +2331,7 @@ helium_session_drop(WT_DATA_SOURCE *wtds,
ws->he_cache = NULL; /* The handle is dead. */
/* Close the source, discarding the structure. */
- ESET(ws_source_close(wtext, session, ws));
+ ESET(ws_source_close(wt_api, session, ws));
ws = NULL;
/* Discard the metadata entry. */
@@ -2341,7 +2344,7 @@ helium_session_drop(WT_DATA_SOURCE *wtds,
if (ret != 0)
ret = WT_PANIC;
- ESET(unlock(wtext, session, &ds->global_lock));
+ ESET(unlock(wt_api, session, &ds->global_lock));
return (ret);
}
@@ -2354,13 +2357,13 @@ helium_session_rename(WT_DATA_SOURCE *wtds, WT_SESSION *session,
const char *uri, const char *newuri, WT_CONFIG_ARG *config)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
int ret = 0;
char *p;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/*
* Get a locked reference to the data source; hold the global lock,
@@ -2401,7 +2404,7 @@ helium_session_rename(WT_DATA_SOURCE *wtds, WT_SESSION *session,
if (ret != 0)
ret = WT_PANIC;
-err: ESET(unlock(wtext, session, &ds->global_lock));
+err: ESET(unlock(wt_api, session, &ds->global_lock));
return (ret);
}
@@ -2415,12 +2418,12 @@ helium_session_truncate(WT_DATA_SOURCE *wtds,
WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
int ret = 0, tret;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get a locked reference to the WiredTiger source. */
if ((ret = ws_source_open(wtds, session,
@@ -2429,13 +2432,13 @@ helium_session_truncate(WT_DATA_SOURCE *wtds,
/* Truncate the underlying namespaces. */
if ((tret = he_truncate(ws->he)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_truncate: %s: %s", ws->uri, he_strerror(tret));
if ((tret = he_truncate(ws->he_cache)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_truncate: %s: %s", ws->uri, he_strerror(tret));
- ESET(unlock(wtext, session, &ws->lock));
+ ESET(unlock(wt_api, session, &ws->lock));
return (ret);
}
@@ -2464,18 +2467,18 @@ helium_session_checkpoint(
{
DATA_SOURCE *ds;
HELIUM_SOURCE *hs;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
(void)config;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Flush all volumes. */
if ((hs = ds->hs_head) != NULL &&
(ret = he_commit(hs->he_volume)) != 0)
- ERET(wtext, session, ret,
+ ERET(wt_api, session, ret,
"he_commit: %s: %s", hs->device, he_strerror(ret));
return (0);
@@ -2487,7 +2490,7 @@ helium_session_checkpoint(
*/
static int
helium_source_close(
- WT_EXTENSION_API *wtext, WT_SESSION *session, HELIUM_SOURCE *hs)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, HELIUM_SOURCE *hs)
{
WT_SOURCE *ws;
int ret = 0, tret;
@@ -2497,7 +2500,7 @@ helium_source_close(
hs->cleaner_stop = 1;
if ((tret = pthread_join(hs->cleaner_id, NULL)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"pthread_join: %s", strerror(tret));
hs->cleaner_id = 0;
}
@@ -2505,13 +2508,13 @@ helium_source_close(
/* Close the underlying WiredTiger sources. */
while ((ws = hs->ws_head) != NULL) {
hs->ws_head = ws->next;
- ESET(ws_source_close(wtext, session, ws));
+ ESET(ws_source_close(wt_api, session, ws));
}
/* If the owner, close the database transaction store. */
if (hs->he_txn != NULL && hs->he_owner) {
if ((tret = he_close(hs->he_txn)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_close: %s: %s: %s",
hs->name, WT_NAME_TXN, he_strerror(tret));
hs->he_txn = NULL;
@@ -2520,12 +2523,12 @@ helium_source_close(
/* Flush and close the Helium source. */
if (hs->he_volume != NULL) {
if ((tret = he_commit(hs->he_volume)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_commit: %s: %s",
hs->device, he_strerror(tret));
if ((tret = he_close(hs->he_volume)) != 0)
- EMSG(wtext, session, tret,
+ EMSG(wt_api, session, tret,
"he_close: %s: %s: %s",
hs->name, WT_NAME_INIT, he_strerror(tret));
hs->he_volume = NULL;
@@ -2543,7 +2546,7 @@ helium_source_close(
* Migrate information from the cache to the primary store.
*/
static int
-cache_cleaner(WT_EXTENSION_API *wtext,
+cache_cleaner(WT_EXTENSION_API *wt_api,
WT_CURSOR *wtcursor, uint64_t oldest, uint64_t *txnminp)
{
CACHE_RECORD *cp;
@@ -2612,7 +2615,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
ret = 0;
continue;
}
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_delete: %s", he_strerror(ret));
} else {
r->val = cp->v;
@@ -2628,7 +2631,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
if (ret == 0)
continue;
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_update: %s", he_strerror(ret));
}
}
@@ -2636,7 +2639,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
if (ret == WT_NOTFOUND)
ret = 0;
if (ret != 0)
- ERET(wtext, NULL, ret, "he_next: %s", he_strerror(ret));
+ ERET(wt_api, NULL, ret, "he_next: %s", he_strerror(ret));
/*
* If we didn't move any keys from the cache to the primary, quit. It's
@@ -2651,7 +2654,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
* what Helium handle we commit, so we just commit one of them.)
*/
if ((ret = he_commit(ws->he)) != 0)
- ERET(wtext, NULL, ret, "he_commit: %s", he_strerror(ret));
+ ERET(wt_api, NULL, ret, "he_commit: %s", he_strerror(ret));
/*
* If we're performing recovery, that's all we need to do, we're going
@@ -2668,7 +2671,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
* We're updating the cache, which requires a lock during normal
* cleaning.
*/
- if ((ret = writelock(wtext, NULL, &ws->lock)) != 0)
+ if ((ret = writelock(wt_api, NULL, &ws->lock)) != 0)
goto err;
locked = 1;
@@ -2682,7 +2685,7 @@ cache_cleaner(WT_EXTENSION_API *wtext,
goto err;
if (cache_value_visible_all(wtcursor, oldest)) {
if ((ret = he_delete(ws->he_cache, r)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"he_delete: %s", he_strerror(ret));
continue;
}
@@ -2701,15 +2704,15 @@ cache_cleaner(WT_EXTENSION_API *wtext,
}
locked = 0;
- if ((ret = unlock(wtext, NULL, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, NULL, &ws->lock)) != 0)
goto err;
if (ret == WT_NOTFOUND)
ret = 0;
if (ret != 0)
- EMSG_ERR(wtext, NULL, ret, "he_next: %s", he_strerror(ret));
+ EMSG_ERR(wt_api, NULL, ret, "he_next: %s", he_strerror(ret));
err: if (locked)
- ESET(unlock(wtext, NULL, &ws->lock));
+ ESET(unlock(wt_api, NULL, &ws->lock));
return (ret);
}
@@ -2723,12 +2726,12 @@ txn_cleaner(WT_CURSOR *wtcursor, he_t he_txn, uint64_t txnmin)
{
CURSOR *cursor;
HE_ITEM *r;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
uint64_t txnid;
int ret = 0;
cursor = (CURSOR *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
r = &cursor->record;
/*
@@ -2739,13 +2742,13 @@ txn_cleaner(WT_CURSOR *wtcursor, he_t he_txn, uint64_t txnmin)
(ret = helium_call(wtcursor, "he_next", he_txn, he_next)) == 0;) {
memcpy(&txnid, r->key, sizeof(txnid));
if (txnid < txnmin && (ret = he_delete(he_txn, r)) != 0)
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_delete: %s", he_strerror(ret));
}
if (ret == WT_NOTFOUND)
ret = 0;
if (ret != 0)
- ERET(wtext, NULL, ret, "he_next: %s", he_strerror(ret));
+ ERET(wt_api, NULL, ret, "he_next: %s", he_strerror(ret));
return (0);
}
@@ -2755,7 +2758,7 @@ txn_cleaner(WT_CURSOR *wtcursor, he_t he_txn, uint64_t txnmin)
* Fake up enough of a cursor to do Helium operations.
*/
static int
-fake_cursor(WT_EXTENSION_API *wtext, WT_CURSOR **wtcursorp)
+fake_cursor(WT_EXTENSION_API *wt_api, WT_CURSOR **wtcursorp)
{
CURSOR *cursor;
WT_CURSOR *wtcursor;
@@ -2765,7 +2768,7 @@ fake_cursor(WT_EXTENSION_API *wtext, WT_CURSOR **wtcursorp)
*/
if ((cursor = calloc(1, sizeof(CURSOR))) == NULL)
return (os_errno());
- cursor->wtext = wtext;
+ cursor->wt_api = wt_api;
cursor->record.key = cursor->__key;
if ((cursor->v = malloc(128)) == NULL) {
free(cursor);
@@ -2796,7 +2799,7 @@ cache_cleaner_worker(void *arg)
HELIUM_SOURCE *hs;
HE_STATS stats;
WT_CURSOR *wtcursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
uint64_t oldest, txnmin, txntmp;
int cleaner_stop, delay, ret = 0;
@@ -2804,10 +2807,10 @@ cache_cleaner_worker(void *arg)
hs = (HELIUM_SOURCE *)arg;
cursor = NULL;
- wtext = hs->wtext;
+ wt_api = hs->wt_api;
- if ((ret = fake_cursor(wtext, &wtcursor)) != 0)
- EMSG_ERR(wtext, NULL, ret, "cleaner: %s", strerror(ret));
+ if ((ret = fake_cursor(wt_api, &wtcursor)) != 0)
+ EMSG_ERR(wt_api, NULL, ret, "cleaner: %s", strerror(ret));
cursor = (CURSOR *)wtcursor;
for (cleaner_stop = delay = 0; !cleaner_stop;) {
@@ -2846,7 +2849,7 @@ cache_cleaner_worker(void *arg)
#define CACHE_SIZE_TRIGGER (50 * 1048576)
for (ws = hs->ws_head; ws != NULL; ws = ws->next) {
if ((ret = he_stats(ws->he_cache, &stats)) != 0)
- EMSG_ERR(wtext, NULL,
+ EMSG_ERR(wt_api, NULL,
ret, "he_stats: %s", he_strerror(ret));
if (stats.size > CACHE_SIZE_TRIGGER)
break;
@@ -2862,7 +2865,7 @@ cache_cleaner_worker(void *arg)
* transaction. Do this before doing anything else, avoiding
* any race with creating new WT_SOURCE handles.
*/
- oldest = wtext->transaction_oldest(wtext);
+ oldest = wt_api->transaction_oldest(wt_api);
/*
* If any cache needs cleaning, clean them all, because we have
@@ -2875,7 +2878,7 @@ cache_cleaner_worker(void *arg)
for (ws = hs->ws_head; ws != NULL; ws = ws->next) {
cursor->ws = ws;
if ((ret = cache_cleaner(
- wtext, wtcursor, oldest, &txntmp)) != 0)
+ wt_api, wtcursor, oldest, &txntmp)) != 0)
goto err;
if (txntmp < txnmin)
txnmin = txntmp;
@@ -2904,7 +2907,7 @@ err: cursor_destroy(cursor);
* Parse the Helium configuration.
*/
static int
-helium_config_read(WT_EXTENSION_API *wtext, WT_CONFIG_ITEM *config,
+helium_config_read(WT_EXTENSION_API *wt_api, WT_CONFIG_ITEM *config,
char **devicep, HE_ENV *envp, int *env_setp, int *flagsp)
{
WT_CONFIG_ITEM k, v;
@@ -2915,11 +2918,11 @@ helium_config_read(WT_EXTENSION_API *wtext, WT_CONFIG_ITEM *config,
*flagsp = 0;
/* Traverse the configuration arguments list. */
- if ((ret = wtext->config_parser_open(
- wtext, NULL, config->str, config->len, &config_parser)) != 0)
- ERET(wtext, NULL, ret,
+ if ((ret = wt_api->config_parser_open(
+ wt_api, NULL, config->str, config->len, &config_parser)) != 0)
+ ERET(wt_api, NULL, ret,
"WT_EXTENSION_API.config_parser_open: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
while ((ret = config_parser->next(config_parser, &k, &v)) == 0) {
if (string_match("helium_devices", k.str, k.len)) {
if ((*devicep = calloc(1, v.len + 1)) == NULL)
@@ -2942,21 +2945,21 @@ helium_config_read(WT_EXTENSION_API *wtext, WT_CONFIG_ITEM *config,
*flagsp |= HE_O_VOLUME_TRUNCATE;
continue;
}
- EMSG_ERR(wtext, NULL, EINVAL,
+ EMSG_ERR(wt_api, NULL, EINVAL,
"unknown configuration key value pair %.*s=%.*s",
(int)k.len, k.str, (int)v.len, v.str);
}
if (ret == WT_NOTFOUND)
ret = 0;
if (ret != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"WT_CONFIG_PARSER.next: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
err: if ((tret = config_parser->close(config_parser)) != 0)
- EMSG(wtext, NULL, tret,
+ EMSG(wt_api, NULL, tret,
"WT_CONFIG_PARSER.close: %s",
- wtext->strerror(wtext, NULL, tret));
+ wt_api->strerror(wt_api, NULL, tret));
return (ret);
}
@@ -2970,13 +2973,13 @@ helium_source_open(DATA_SOURCE *ds, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
{
struct he_env env;
HELIUM_SOURCE *hs;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int env_set, flags, ret = 0;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
hs = NULL;
- VMSG(wtext, NULL, VERBOSE_L1, "volume %.*s=%.*s",
+ VMSG(wt_api, NULL, VERBOSE_L1, "volume %.*s=%.*s",
(int)k->len, k->str, (int)v->len, v->str);
/*
@@ -2987,7 +2990,7 @@ helium_source_open(DATA_SOURCE *ds, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
*/
for (hs = ds->hs_head; hs != NULL; hs = hs->next)
if (string_match(hs->name, k->str, k->len))
- ERET(wtext, NULL,
+ ERET(wt_api, NULL,
EINVAL, "%s: device already open", hs->name);
/* Allocate and initialize a new underlying Helium source object. */
@@ -2998,15 +3001,15 @@ helium_source_open(DATA_SOURCE *ds, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
}
memcpy(hs->name, k->str, k->len);
hs->txn_notify.notify = txn_notify;
- hs->wtext = wtext;
+ hs->wt_api = wt_api;
/* Read the configuration, require a device naming the Helium store. */
memset(&env, 0, sizeof(env));
if ((ret = helium_config_read(
- wtext, v, &hs->device, &env, &env_set, &flags)) != 0)
+ wt_api, v, &hs->device, &env, &env_set, &flags)) != 0)
goto err;
if (hs->device == NULL)
- EMSG_ERR(wtext, NULL,
+ EMSG_ERR(wt_api, NULL,
EINVAL, "%s: no Helium volumes specified", hs->name);
/*
@@ -3019,7 +3022,7 @@ helium_source_open(DATA_SOURCE *ds, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
if ((hs->he_volume = he_open(
hs->device, WT_NAME_INIT, flags, env_set ? &env : NULL)) == NULL) {
ret = os_errno();
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"he_open: %s: %s: %s",
hs->name, WT_NAME_INIT, he_strerror(ret));
}
@@ -3030,7 +3033,7 @@ helium_source_open(DATA_SOURCE *ds, WT_CONFIG_ITEM *k, WT_CONFIG_ITEM *v)
if (0) {
err: if (hs != NULL)
- ESET(helium_source_close(wtext, NULL, hs));
+ ESET(helium_source_close(wt_api, NULL, hs));
}
return (ret);
}
@@ -3043,11 +3046,11 @@ static int
helium_source_open_txn(DATA_SOURCE *ds)
{
HELIUM_SOURCE *hs, *hs_txn;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
he_t he_txn, t;
int ret = 0;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/*
* The global txn namespace is per connection, it spans multiple Helium
@@ -3063,7 +3066,7 @@ helium_source_open_txn(DATA_SOURCE *ds)
if (hs_txn != NULL) {
(void)he_close(t);
(void)he_close(hs_txn);
- ERET(wtext, NULL, WT_PANIC,
+ ERET(wt_api, NULL, WT_PANIC,
"found multiple transaction stores, "
"unable to proceed");
}
@@ -3083,17 +3086,17 @@ helium_source_open_txn(DATA_SOURCE *ds)
if ((he_txn = he_open(
hs->device, WT_NAME_TXN, HE_O_CREATE, NULL)) == NULL) {
ret = os_errno();
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_open: %s: %s: %s",
hs->name, WT_NAME_TXN, he_strerror(ret));
}
/* Push the change. */
if ((ret = he_commit(he_txn)) != 0)
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_commit: %s", he_strerror(ret));
}
- VMSG(wtext, NULL, VERBOSE_L1, "%s" "transactional store on %s",
+ VMSG(wt_api, NULL, VERBOSE_L1, "%s" "transactional store on %s",
hs_txn == NULL ? "creating " : "", hs->name);
/* Set the owner field, this Helium source has to be closed last. */
@@ -3117,7 +3120,7 @@ helium_source_recover_namespace(WT_DATA_SOURCE *wtds,
CURSOR *cursor;
DATA_SOURCE *ds;
WT_CURSOR *wtcursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SOURCE *ws;
size_t len;
int ret = 0;
@@ -3125,7 +3128,7 @@ helium_source_recover_namespace(WT_DATA_SOURCE *wtds,
char *uri;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
cursor = NULL;
ws = NULL;
uri = NULL;
@@ -3149,26 +3152,26 @@ helium_source_recover_namespace(WT_DATA_SOURCE *wtds,
*/
if ((ret = ws_source_open(wtds, NULL, uri, config, 0, &ws)) != 0)
goto err;
- if ((ret = unlock(wtext, NULL, &ws->lock)) != 0)
+ if ((ret = unlock(wt_api, NULL, &ws->lock)) != 0)
goto err;
/* Fake up a cursor. */
- if ((ret = fake_cursor(wtext, &wtcursor)) != 0)
- EMSG_ERR(wtext, NULL, ret, "recovery: %s", strerror(ret));
+ if ((ret = fake_cursor(wt_api, &wtcursor)) != 0)
+ EMSG_ERR(wt_api, NULL, ret, "recovery: %s", strerror(ret));
cursor = (CURSOR *)wtcursor;
cursor->ws = ws;
/* Process, then clear, the cache. */
- if ((ret = cache_cleaner(wtext, wtcursor, 0, NULL)) != 0)
+ if ((ret = cache_cleaner(wt_api, wtcursor, 0, NULL)) != 0)
goto err;
if ((ret = he_truncate(ws->he_cache)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"he_truncate: %s(cache): %s", ws->uri, he_strerror(ret));
/* Close the underlying WiredTiger sources. */
err: while ((ws = hs->ws_head) != NULL) {
hs->ws_head = ws->next;
- ESET(ws_source_close(wtext, NULL, ws));
+ ESET(ws_source_close(wt_api, NULL, ws));
}
cursor_destroy(cursor);
@@ -3233,20 +3236,20 @@ helium_source_recover(
{
struct helium_namespace_cookie names;
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
u_int i;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
memset(&names, 0, sizeof(names));
- VMSG(wtext, NULL, VERBOSE_L1, "recover %s", hs->name);
+ VMSG(wt_api, NULL, VERBOSE_L1, "recover %s", hs->name);
/* Get a list of the cache/primary object pairs in the Helium source. */
if ((ret = he_enumerate(
hs->device, helium_namespace_list, &names)) != 0)
- ERET(wtext, NULL, ret,
+ ERET(wt_api, NULL, ret,
"he_enumerate: %s: %s", hs->name, he_strerror(ret));
/* Recover the objects. */
@@ -3257,7 +3260,7 @@ helium_source_recover(
/* Clear the transaction store. */
if ((ret = he_truncate(hs->he_txn)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"he_truncate: %s: %s: %s",
hs->name, WT_NAME_TXN, he_strerror(ret));
@@ -3277,15 +3280,15 @@ helium_terminate(WT_DATA_SOURCE *wtds, WT_SESSION *session)
{
DATA_SOURCE *ds;
HELIUM_SOURCE *hs, *last;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Lock the system down. */
if (ds->lockinit)
- ret = writelock(wtext, session, &ds->global_lock);
+ ret = writelock(wt_api, session, &ds->global_lock);
/*
* Close the Helium sources, close the Helium source that "owns" the
@@ -3298,15 +3301,15 @@ helium_terminate(WT_DATA_SOURCE *wtds, WT_SESSION *session)
last = hs;
continue;
}
- ESET(helium_source_close(wtext, session, hs));
+ ESET(helium_source_close(wt_api, session, hs));
}
if (last != NULL)
- ESET(helium_source_close(wtext, session, last));
+ ESET(helium_source_close(wt_api, session, last));
/* Unlock and destroy the system. */
if (ds->lockinit) {
- ESET(unlock(wtext, session, &ds->global_lock));
- ESET(lock_destroy(wtext, NULL, &ds->global_lock));
+ ESET(unlock(wt_api, session, &ds->global_lock));
+ ESET(lock_destroy(wt_api, NULL, &ds->global_lock));
}
OVERWRITE_AND_FREE(ds);
@@ -3347,24 +3350,24 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
HELIUM_SOURCE *hs;
WT_CONFIG_ITEM k, v;
WT_CONFIG_PARSER *config_parser;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int vmajor, vminor, ret = 0;
const char **p;
config_parser = NULL;
ds = NULL;
- wtext = connection->get_extension_api(connection);
+ wt_api = connection->get_extension_api(connection);
/* Check the library version */
#if HE_VERSION_MAJOR != 2 || HE_VERSION_MINOR != 2
- ERET(wtext, NULL, EINVAL,
+ ERET(wt_api, NULL, EINVAL,
"unsupported Levyx/Helium header file %d.%d, expected version 2.2",
HE_VERSION_MAJOR, HE_VERSION_MINOR);
#endif
he_version(&vmajor, &vminor);
if (vmajor != 2 || vminor != 2)
- ERET(wtext, NULL, EINVAL,
+ ERET(wt_api, NULL, EINVAL,
"unsupported Levyx/Helium library version %d.%d, expected "
"version 2.2", vmajor, vminor);
@@ -3372,23 +3375,23 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
if ((ds = calloc(1, sizeof(DATA_SOURCE))) == NULL)
return (os_errno());
ds->wtds = wtds;
- ds->wtext = wtext;
- if ((ret = lock_init(wtext, NULL, &ds->global_lock)) != 0)
+ ds->wt_api = wt_api;
+ if ((ret = lock_init(wt_api, NULL, &ds->global_lock)) != 0)
goto err;
ds->lockinit = 1;
/* Get the configuration string. */
- if ((ret = wtext->config_get(wtext, NULL, config, "config", &v)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ if ((ret = wt_api->config_get(wt_api, NULL, config, "config", &v)) != 0)
+ EMSG_ERR(wt_api, NULL, ret,
"WT_EXTENSION_API.config_get: config: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
/* Step through the list of Helium sources, opening each one. */
- if ((ret = wtext->config_parser_open(
- wtext, NULL, v.str, v.len, &config_parser)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ if ((ret = wt_api->config_parser_open(
+ wt_api, NULL, v.str, v.len, &config_parser)) != 0)
+ EMSG_ERR(wt_api, NULL, ret,
"WT_EXTENSION_API.config_parser_open: config: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
while ((ret = config_parser->next(config_parser, &k, &v)) == 0) {
if (string_match("helium_verbose", k.str, k.len)) {
verbose = v.val == 0 ? 0 : 1;
@@ -3398,13 +3401,13 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
goto err;
}
if (ret != WT_NOTFOUND)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"WT_CONFIG_PARSER.next: config: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
if ((ret = config_parser->close(config_parser)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"WT_CONFIG_PARSER.close: config: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
config_parser = NULL;
/* Find and open the database transaction store. */
@@ -3420,7 +3423,7 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
for (hs = ds->hs_head; hs != NULL; hs = hs->next)
if ((ret = pthread_create(
&hs->cleaner_id, NULL, cache_cleaner_worker, hs)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"%s: pthread_create: cleaner thread: %s",
hs->name, strerror(ret));
@@ -3428,17 +3431,17 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
for (p = session_create_opts; *p != NULL; ++p)
if ((ret = connection->configure_method(connection,
"WT_SESSION.create", "helium:", *p, "boolean", NULL)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"WT_CONNECTION.configure_method: session.create: "
"%s: %s",
- *p, wtext->strerror(wtext, NULL, ret));
+ *p, wt_api->strerror(wt_api, NULL, ret));
/* Add the data source */
if ((ret = connection->add_data_source(
connection, "helium:", (WT_DATA_SOURCE *)ds, NULL)) != 0)
- EMSG_ERR(wtext, NULL, ret,
+ EMSG_ERR(wt_api, NULL, ret,
"WT_CONNECTION.add_data_source: %s",
- wtext->strerror(wtext, NULL, ret));
+ wt_api->strerror(wt_api, NULL, ret));
return (0);
err: if (ds != NULL)
diff --git a/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c b/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c
index eac7d8f4632..af65f397549 100644
--- a/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c
+++ b/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c
@@ -38,13 +38,29 @@
typedef struct {
WT_ENCRYPTOR encryptor; /* Must come first */
- WT_EXTENSION_API *wt_api; /* Extension API */
+ WT_EXTENSION_API *wt_api; /* Extension API */
- unsigned long nop_calls; /* Count of calls */
+ unsigned long nop_calls; /* Count of calls */
} NOP_ENCRYPTOR;
/*! [WT_ENCRYPTOR initialization structure] */
+/*
+ * nop_error --
+ * Display an error from this module in a standard way.
+ */
+static int
+nop_error(
+ NOP_ENCRYPTOR *encryptor, WT_SESSION *session, int err, const char *msg)
+{
+ WT_EXTENSION_API *wt_api;
+
+ wt_api = encryptor->wt_api;
+ (void)wt_api->err_printf(wt_api, session,
+ "nop encryption: %s: %s", msg, wt_api->strerror(wt_api, NULL, err));
+ return (err);
+}
+
/*! [WT_ENCRYPTOR encrypt] */
/*
* nop_encrypt --
@@ -63,7 +79,8 @@ nop_encrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
++nop_encryptor->nop_calls; /* Call count */
if (dst_len < src_len)
- return (ENOMEM);
+ return (nop_error(nop_encryptor, session,
+ ENOMEM, "encrypt buffer not big enough"));
memcpy(dst, src, src_len);
*result_lenp = src_len;
diff --git a/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c b/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c
index 4d614e2e409..559c8e6e33a 100644
--- a/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c
+++ b/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c
@@ -68,7 +68,7 @@
typedef struct {
WT_ENCRYPTOR encryptor; /* Must come first */
- WT_EXTENSION_API *wtext; /* Extension API */
+ WT_EXTENSION_API *wt_api; /* Extension API */
int rot_N; /* rotN value */
char *keyid; /* Saved keyid */
@@ -89,14 +89,15 @@ typedef struct {
* Display an error from this module in a standard way.
*/
static int
-rotn_error(ROTN_ENCRYPTOR *encryptor, WT_SESSION *session, int err,
- const char *msg)
+rotn_error(
+ ROTN_ENCRYPTOR *encryptor, WT_SESSION *session, int err, const char *msg)
{
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
- wtext = encryptor->wtext;
- (void)wtext->err_printf(wtext, session,
- "rotn encryption: %s: %s", msg, wtext->strerror(wtext, NULL, err));
+ wt_api = encryptor->wt_api;
+ (void)wt_api->err_printf(wt_api, session,
+ "rotn encryption: %s: %s",
+ msg, wt_api->strerror(wt_api, NULL, err));
return (err);
}
@@ -189,7 +190,8 @@ rotn_encrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
(void)session; /* Unused */
if (dst_len < src_len + CHKSUM_LEN + IV_LEN)
- return (ENOMEM);
+ return (rotn_error(rotn_encryptor, session,
+ ENOMEM, "encrypt buffer not big enough"));
/*
* !!! Most implementations would verify any needed
@@ -308,7 +310,7 @@ rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
const ROTN_ENCRYPTOR *orig;
ROTN_ENCRYPTOR *rotn_encryptor;
WT_CONFIG_ITEM keyid, secret;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
size_t i, len;
int ret, keyid_val;
u_char base;
@@ -317,7 +319,7 @@ rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
keyid_val = 0;
orig = (const ROTN_ENCRYPTOR *)encryptor;
- wtext = orig->wtext;
+ wt_api = orig->wt_api;
if ((rotn_encryptor = calloc(1, sizeof(ROTN_ENCRYPTOR))) == NULL)
return (errno);
@@ -327,13 +329,14 @@ rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
/*
* Stash the keyid from the configuration string.
*/
- if ((ret = wtext->config_get(wtext, session, encrypt_config,
+ if ((ret = wt_api->config_get(wt_api, session, encrypt_config,
"keyid", &keyid)) == 0 && keyid.len != 0) {
/*
* In this demonstration, we expect keyid to be a number.
*/
if ((keyid_val = atoi(keyid.str)) < 0) {
- ret = EINVAL;
+ ret = rotn_error(rotn_encryptor,
+ NULL, EINVAL, "rotn_customize: invalid keyid");
goto err;
}
if ((rotn_encryptor->keyid = malloc(keyid.len + 1)) == NULL) {
@@ -349,7 +352,7 @@ rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
* We stash the secret key from the configuration string
* and build some shift bytes to make encryption/decryption easy.
*/
- if ((ret = wtext->config_get(wtext, session, encrypt_config,
+ if ((ret = wt_api->config_get(wt_api, session, encrypt_config,
"secretkey", &secret)) == 0 && secret.len != 0) {
len = secret.len;
if ((rotn_encryptor->secretkey = malloc(len + 1)) == NULL ||
@@ -364,7 +367,8 @@ rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session,
else if ('A' <= secret.str[i] && secret.str[i] <= 'Z')
base = 'A';
else {
- ret = EINVAL;
+ ret = rotn_error(rotn_encryptor, NULL,
+ EINVAL, "rotn_customize: invalid key");
goto err;
}
base -= (u_char)keyid_val;
@@ -427,19 +431,19 @@ rotn_configure(ROTN_ENCRYPTOR *rotn_encryptor, WT_CONFIG_ARG *config)
{
WT_CONFIG_ITEM k, v;
WT_CONFIG_PARSER *config_parser;
- WT_EXTENSION_API *wtext; /* Extension API */
+ WT_EXTENSION_API *wt_api; /* Extension API */
int ret, t_ret;
- wtext = rotn_encryptor->wtext;
+ wt_api = rotn_encryptor->wt_api;
/* Get the configuration string. */
- if ((ret = wtext->config_get(wtext, NULL, config, "config", &v)) != 0)
+ if ((ret = wt_api->config_get(wt_api, NULL, config, "config", &v)) != 0)
return (rotn_error(rotn_encryptor, NULL, ret,
"WT_EXTENSION_API.config_get"));
/* Step through the list of configuration options. */
- if ((ret = wtext->config_parser_open(
- wtext, NULL, v.str, v.len, &config_parser)) != 0)
+ if ((ret = wt_api->config_parser_open(
+ wt_api, NULL, v.str, v.len, &config_parser)) != 0)
return (rotn_error(rotn_encryptor, NULL, ret,
"WT_EXTENSION_API.config_parser_open"));
@@ -492,7 +496,7 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
rotn_encryptor->encryptor.sizing = rotn_sizing;
rotn_encryptor->encryptor.customize = rotn_customize;
rotn_encryptor->encryptor.terminate = rotn_terminate;
- rotn_encryptor->wtext = connection->get_extension_api(connection);
+ rotn_encryptor->wt_api = connection->get_extension_api(connection);
if ((ret = rotn_configure(rotn_encryptor, config)) != 0) {
free(rotn_encryptor);
diff --git a/src/third_party/wiredtiger/ext/extractors/csv/csv_extractor.c b/src/third_party/wiredtiger/ext/extractors/csv/csv_extractor.c
index 698a9d6fa96..e47ce6e2255 100644
--- a/src/third_party/wiredtiger/ext/extractors/csv/csv_extractor.c
+++ b/src/third_party/wiredtiger/ext/extractors/csv/csv_extractor.c
@@ -54,6 +54,22 @@ typedef struct {
} CSV_EXTRACTOR;
/*
+ * csv_error --
+ * Display an error from this module in a standard way.
+ */
+static int
+csv_error(const CSV_EXTRACTOR *csv_extractor,
+ WT_SESSION *session, int err, const char *msg)
+{
+ WT_EXTENSION_API *wt_api;
+
+ wt_api = csv_extractor->wt_api;
+ (void)wt_api->err_printf(wt_api, session,
+ "csv extraction: %s: %s", msg, wt_api->strerror(wt_api, NULL, err));
+ return (err);
+}
+
+/*
* csv_extract --
* WiredTiger CSV extraction.
*/
@@ -61,19 +77,19 @@ static int
csv_extract(WT_EXTRACTOR *extractor, WT_SESSION *session,
const WT_ITEM *key, const WT_ITEM *value, WT_CURSOR *result_cursor)
{
- char *copy, *p, *pend, *valstr;
const CSV_EXTRACTOR *csv_extractor;
- int i, ret, val;
+ WT_EXTENSION_API *wt_api;
size_t len;
- WT_EXTENSION_API *wtapi;
+ int i, ret, val;
+ char *copy, *p, *pend, *valstr;
(void)key; /* Unused parameters */
csv_extractor = (const CSV_EXTRACTOR *)extractor;
- wtapi = csv_extractor->wt_api;
+ wt_api = csv_extractor->wt_api;
/* Unpack the value. */
- if ((ret = wtapi->struct_unpack(wtapi,
+ if ((ret = wt_api->struct_unpack(wt_api,
session, value->data, value->size, "S", &valstr)) != 0)
return (ret);
@@ -98,8 +114,10 @@ csv_extract(WT_EXTRACTOR *extractor, WT_SESSION *session,
copy[len] = '\0';
if (csv_extractor->format_isnum) {
if ((val = atoi(copy)) < 0) {
+ ret = csv_error(csv_extractor,
+ session, EINVAL, "invalid key value");
free(copy);
- return (EINVAL);
+ return (ret);
}
result_cursor->set_key(result_cursor, val);
} else
@@ -125,29 +143,38 @@ csv_customize(WT_EXTRACTOR *extractor, WT_SESSION *session,
CSV_EXTRACTOR *csv_extractor;
WT_CONFIG_ITEM field, format;
WT_CONFIG_PARSER *parser;
- WT_EXTENSION_API *wtapi;
- int ret;
+ WT_EXTENSION_API *wt_api;
long field_num;
+ int ret;
(void)session; /* Unused parameters */
(void)uri; /* Unused parameters */
orig = (const CSV_EXTRACTOR *)extractor;
- wtapi = orig->wt_api;
- if ((ret = wtapi->config_parser_open(wtapi, session, appcfg->str,
+ wt_api = orig->wt_api;
+ if ((ret = wt_api->config_parser_open(wt_api, session, appcfg->str,
appcfg->len, &parser)) != 0)
return (ret);
if ((ret = parser->get(parser, "field", &field)) != 0 ||
(ret = parser->get(parser, "format", &format)) != 0) {
- if (ret == WT_NOTFOUND)
- return (EINVAL);
+ if (ret == WT_NOTFOUND) {
+ (void)wt_api->err_printf(
+ wt_api, session, "field or format not found");
+ return (WT_NOTFOUND);
+ }
return (ret);
}
field_num = strtol(field.str, NULL, 10);
- if (field_num < 0 || field_num > INT_MAX)
+ if (field_num < 0 || field_num > INT_MAX) {
+ (void)wt_api->err_printf(
+ wt_api, session, "field: invalid format");
return (EINVAL);
- if (format.len != 1 || (format.str[0] != 'S' && format.str[0] != 'i'))
+ }
+ if (format.len != 1 || (format.str[0] != 'S' && format.str[0] != 'i')) {
+ (void)wt_api->err_printf(
+ wt_api, session, "format: invalid format");
return (EINVAL);
+ }
if ((csv_extractor = calloc(1, sizeof(CSV_EXTRACTOR))) == NULL)
return (errno);
diff --git a/src/third_party/wiredtiger/ext/test/kvs_bdb/kvs_bdb.c b/src/third_party/wiredtiger/ext/test/kvs_bdb/kvs_bdb.c
index be63b1d7e7d..866cd0663ce 100644
--- a/src/third_party/wiredtiger/ext/test/kvs_bdb/kvs_bdb.c
+++ b/src/third_party/wiredtiger/ext/test/kvs_bdb/kvs_bdb.c
@@ -57,13 +57,13 @@
* int ret;
*/
#undef ERET
-#define ERET(wtext, session, v, ...) do { \
- (void)wtext->err_printf(wtext, session, __VA_ARGS__); \
+#define ERET(wt_api, session, v, ...) do { \
+ (void)wt_api->err_printf(wt_api, session, __VA_ARGS__); \
return (v); \
} while (0)
#undef ESET
-#define ESET(wtext, session, v, ...) do { \
- (void)wtext->err_printf(wtext, session, __VA_ARGS__); \
+#define ESET(wt_api, session, v, ...) do { \
+ (void)wt_api->err_printf(wt_api, session, __VA_ARGS__); \
ret = v; \
} while (0)
#undef ETRET
@@ -80,7 +80,7 @@ typedef struct __data_source DATA_SOURCE;
typedef struct __cursor_source {
WT_CURSOR wtcursor; /* Must come first */
- WT_EXTENSION_API *wtext; /* Extension functions */
+ WT_EXTENSION_API *wt_api; /* Extension functions */
DATA_SOURCE *ds; /* Underlying Berkeley DB */
@@ -98,7 +98,7 @@ typedef struct __cursor_source {
struct __data_source {
WT_DATA_SOURCE wtds; /* Must come first */
- WT_EXTENSION_API *wtext; /* Extension functions */
+ WT_EXTENSION_API *wt_api; /* Extension functions */
/*
* We single thread all WT_SESSION methods and return EBUSY if a
@@ -134,12 +134,12 @@ os_errno(void)
*/
static int
lock_init(
- WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_init(lockp, NULL)) != 0)
- ERET(wtext, session, WT_PANIC, "lock init: %s", strerror(ret));
+ ERET(wt_api, session, WT_PANIC, "lock init: %s", strerror(ret));
return (0);
}
@@ -149,12 +149,12 @@ lock_init(
*/
static int
lock_destroy(
- WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_destroy(lockp)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_PANIC, "lock destroy: %s", strerror(ret));
return (0);
}
@@ -165,12 +165,12 @@ lock_destroy(
*/
static INLINE int
writelock(
- WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+ WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_wrlock(lockp)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_PANIC, "write-lock: %s", strerror(ret));
return (0);
}
@@ -180,12 +180,12 @@ writelock(
* Release an object's lock.
*/
static INLINE int
-unlock(WT_EXTENSION_API *wtext, WT_SESSION *session, pthread_rwlock_t *lockp)
+unlock(WT_EXTENSION_API *wt_api, WT_SESSION *session, pthread_rwlock_t *lockp)
{
int ret = 0;
if ((ret = pthread_rwlock_unlock(lockp)) != 0)
- ERET(wtext, session, WT_PANIC, "unlock: %s", strerror(ret));
+ ERET(wt_api, session, WT_PANIC, "unlock: %s", strerror(ret));
return (0);
}
@@ -194,16 +194,16 @@ single_thread(
WT_DATA_SOURCE *wtds, WT_SESSION *session, pthread_rwlock_t *lockp)
{
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
- if ((ret = writelock(wtext, session, lockp)) != 0)
+ if ((ret = writelock(wt_api, session, lockp)) != 0)
return (ret);
if (ds->open_cursors != 0) {
- if ((ret = unlock(wtext, session, lockp)) != 0)
+ if ((ret = unlock(wt_api, session, lockp)) != 0)
return (ret);
return (EBUSY);
}
@@ -211,13 +211,13 @@ single_thread(
}
static int
-uri2name(WT_EXTENSION_API *wtext,
+uri2name(WT_EXTENSION_API *wt_api,
WT_SESSION *session, const char *uri, const char **namep)
{
const char *name;
if ((name = strchr(uri, ':')) == NULL || *++name == '\0')
- ERET(wtext, session, EINVAL, "unsupported object: %s", uri);
+ ERET(wt_api, session, EINVAL, "unsupported object: %s", uri);
*namep = name;
return (0);
}
@@ -226,15 +226,15 @@ static INLINE int
recno_convert(WT_CURSOR *wtcursor, db_recno_t *recnop)
{
CURSOR_SOURCE *cursor;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
if (wtcursor->recno > UINT32_MAX)
- ERET(wtext,
+ ERET(wt_api,
session, ERANGE, "record number %" PRIuMAX ": %s",
(uintmax_t)wtcursor->recno, strerror(ERANGE));
@@ -315,18 +315,18 @@ bdb_dump(WT_CURSOR *wtcursor, WT_SESSION *session, const char *tag)
DB *db;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
db = cursor->db;
key = &cursor->key;
value = &cursor->value;
if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "Db.cursor: %s", db_strerror(ret));
printf("==> %s\n", tag);
while ((ret = dbc->get(dbc, key, value, DB_NEXT)) == 0)
@@ -340,7 +340,7 @@ bdb_dump(WT_CURSOR *wtcursor, WT_SESSION *session, const char *tag)
(int)value->size, (char *)value->data);
if (ret != DB_NOTFOUND)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
return (0);
@@ -353,13 +353,13 @@ kvs_cursor_next(WT_CURSOR *wtcursor)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -373,7 +373,7 @@ kvs_cursor_next(WT_CURSOR *wtcursor)
if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
return (WT_NOTFOUND);
- ERET(wtext, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
+ ERET(wt_api, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
}
static int
@@ -382,13 +382,13 @@ kvs_cursor_prev(WT_CURSOR *wtcursor)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -402,7 +402,7 @@ kvs_cursor_prev(WT_CURSOR *wtcursor)
if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
return (WT_NOTFOUND);
- ERET(wtext, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
+ ERET(wt_api, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
}
static int
@@ -410,23 +410,23 @@ kvs_cursor_reset(WT_CURSOR *wtcursor)
{
CURSOR_SOURCE *cursor;
DBC *dbc;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
/* Close and re-open the Berkeley DB cursor */
if ((dbc = cursor->dbc) != NULL) {
cursor->dbc = NULL;
if ((ret = dbc->close(dbc)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"DbCursor.close: %s", db_strerror(ret));
if ((ret = cursor->db->cursor(cursor->db, NULL, &dbc, 0)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"Db.cursor: %s", db_strerror(ret));
cursor->dbc = dbc;
}
@@ -439,13 +439,13 @@ kvs_cursor_search(WT_CURSOR *wtcursor)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -462,7 +462,7 @@ kvs_cursor_search(WT_CURSOR *wtcursor)
if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
return (WT_NOTFOUND);
- ERET(wtext, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
+ ERET(wt_api, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
}
static int
@@ -471,14 +471,14 @@ kvs_cursor_search_near(WT_CURSOR *wtcursor, int *exact)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
size_t len;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -529,7 +529,7 @@ retry: if ((ret = dbc->get(dbc, key, value, DB_SET_RANGE)) == 0) {
if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
return (WT_NOTFOUND);
- ERET(wtext, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
+ ERET(wt_api, session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
}
static int
@@ -539,13 +539,13 @@ kvs_cursor_insert(WT_CURSOR *wtcursor)
DB *db;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
db = cursor->db;
@@ -566,16 +566,16 @@ kvs_cursor_insert(WT_CURSOR *wtcursor)
* number.
*/
if ((ret = db->put(db, NULL, key, value, DB_APPEND)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "Db.put: %s", db_strerror(ret));
wtcursor->recno = *(db_recno_t *)key->data;
if ((ret = dbc->get(dbc, key, value, DB_SET)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"DbCursor.get: %s", db_strerror(ret));
} else if (cursor->config_overwrite) {
if ((ret = dbc->put(dbc, key, value, DB_KEYFIRST)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"DbCursor.put: %s", db_strerror(ret));
} else {
/*
@@ -586,11 +586,11 @@ kvs_cursor_insert(WT_CURSOR *wtcursor)
db->put(db, NULL, key, value, DB_NOOVERWRITE)) != 0) {
if (ret == DB_KEYEXIST)
return (WT_DUPLICATE_KEY);
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "Db.put: %s", db_strerror(ret));
}
if ((ret = dbc->get(dbc, key, value, DB_SET)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"DbCursor.get: %s", db_strerror(ret));
}
@@ -603,13 +603,13 @@ kvs_cursor_update(WT_CURSOR *wtcursor)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -620,7 +620,7 @@ kvs_cursor_update(WT_CURSOR *wtcursor)
copyin_value(wtcursor);
if ((ret = dbc->put(dbc, key, value, DB_KEYFIRST)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "DbCursor.put: %s", db_strerror(ret));
return (0);
@@ -632,13 +632,13 @@ kvs_cursor_remove(WT_CURSOR *wtcursor)
CURSOR_SOURCE *cursor;
DBC *dbc;
DBT *key, *value;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
key = &cursor->key;
@@ -660,11 +660,11 @@ kvs_cursor_remove(WT_CURSOR *wtcursor)
if ((ret = dbc->get(dbc, key, value, DB_SET)) != 0) {
if (ret == DB_NOTFOUND || ret == DB_KEYEMPTY)
return (WT_NOTFOUND);
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "DbCursor.get: %s", db_strerror(ret));
}
if ((ret = dbc->del(dbc, 0)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "DbCursor.del: %s", db_strerror(ret));
return (0);
@@ -677,32 +677,32 @@ kvs_cursor_close(WT_CURSOR *wtcursor)
DATA_SOURCE *ds;
DB *db;
DBC *dbc;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
WT_SESSION *session;
int ret = 0;
session = wtcursor->session;
cursor = (CURSOR_SOURCE *)wtcursor;
ds = cursor->ds;
- wtext = cursor->wtext;
+ wt_api = cursor->wt_api;
dbc = cursor->dbc;
cursor->dbc = NULL;
if (dbc != NULL && (ret = dbc->close(dbc)) != 0)
- ERET(wtext, session, WT_ERROR,
+ ERET(wt_api, session, WT_ERROR,
"DbCursor.close: %s", db_strerror(ret));
db = cursor->db;
cursor->db = NULL;
if (db != NULL && (ret = db->close(db, 0)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "Db.close: %s", db_strerror(ret));
free(wtcursor);
- if ((ret = writelock(wtext, session, &ds->rwlock)) != 0)
+ if ((ret = writelock(wt_api, session, &ds->rwlock)) != 0)
return (ret);
--ds->open_cursors;
- if ((ret = unlock(wtext, session, &ds->rwlock)) != 0)
+ if ((ret = unlock(wt_api, session, &ds->rwlock)) != 0)
return (ret);
return (0);
@@ -716,32 +716,32 @@ kvs_session_create(WT_DATA_SOURCE *wtds,
DB *db;
DBTYPE type;
WT_CONFIG_ITEM v;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
const char *name;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
/* Check key/value formats */
if ((ret =
- wtext->config_get(wtext, session, config, "key_format", &v)) != 0)
- ERET(wtext, session, ret,
+ wt_api->config_get(wt_api, session, config, "key_format", &v)) != 0)
+ ERET(wt_api, session, ret,
"key_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
type = v.len == 1 && v.str[0] == 'r' ? DB_RECNO : DB_BTREE;
/* Create the Berkeley DB table */
if ((ret = db_create(&db, ds->dbenv, 0)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
if ((ret = db->open(db, NULL, name, NULL, type, DB_CREATE, 0)) != 0)
- ERET(wtext,
+ ERET(wt_api,
session, WT_ERROR, "Db.open: %s", uri, db_strerror(ret));
if ((ret = db->close(db, 0)) != 0)
- ERET(wtext, session, WT_ERROR, "Db.close", db_strerror(ret));
+ ERET(wt_api, session, WT_ERROR, "Db.close", db_strerror(ret));
return (0);
}
@@ -752,30 +752,30 @@ kvs_session_drop(WT_DATA_SOURCE *wtds,
{
DB *db;
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
const char *name;
(void)config; /* Unused parameters */
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0)
return (ret);
if ((ret = db_create(&db, ds->dbenv, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
else if ((ret = db->remove(db, name, NULL, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "Db.remove: %s", db_strerror(ret));
/* db handle is dead */
- ETRET(unlock(wtext, session, &ds->rwlock));
+ ETRET(unlock(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -787,77 +787,77 @@ kvs_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session,
DATA_SOURCE *ds;
DB *db;
WT_CONFIG_ITEM v;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int locked, ret;
const char *name;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
locked = 0;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
/* Allocate the cursor */
if ((cursor = calloc(1, sizeof(CURSOR_SOURCE))) == NULL)
return (os_errno());
cursor->ds = (DATA_SOURCE *)wtds;
- cursor->wtext = wtext;
+ cursor->wt_api = wt_api;
/* Parse configuration */
- if ((ret = wtext->config_get(
- wtext, session, config, "append", &v)) != 0) {
- ESET(wtext, session, ret,
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "append", &v)) != 0) {
+ ESET(wt_api, session, ret,
"append configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
goto err;
}
cursor->config_append = v.val != 0;
- if ((ret = wtext->config_get(
- wtext, session, config, "overwrite", &v)) != 0) {
- ESET(wtext, session, ret,
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "overwrite", &v)) != 0) {
+ ESET(wt_api, session, ret,
"overwrite configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
goto err;
}
cursor->config_overwrite = v.val != 0;
- if ((ret = wtext->config_get(
- wtext, session, config, "key_format", &v)) != 0) {
- ESET(wtext, session, ret,
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "key_format", &v)) != 0) {
+ ESET(wt_api, session, ret,
"key_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
goto err;
}
cursor->config_recno = v.len == 1 && v.str[0] == 'r';
- if ((ret = wtext->config_get(
- wtext, session, config, "value_format", &v)) != 0) {
- ESET(wtext, session, ret,
+ if ((ret = wt_api->config_get(
+ wt_api, session, config, "value_format", &v)) != 0) {
+ ESET(wt_api, session, ret,
"value_format configuration: %s",
- wtext->strerror(wtext, session, ret));
+ wt_api->strerror(wt_api, session, ret));
goto err;
}
cursor->config_bitfield =
v.len == 2 && isdigit((u_char)v.str[0]) && v.str[1] == 't';
- if ((ret = writelock(wtext, session, &ds->rwlock)) != 0)
+ if ((ret = writelock(wt_api, session, &ds->rwlock)) != 0)
goto err;
locked = 1;
/* Open the Berkeley DB cursor */
if ((ret = db_create(&cursor->db, ds->dbenv, 0)) != 0) {
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
goto err;
}
db = cursor->db;
if ((ret = db->open(db, NULL, name, NULL,
cursor->config_recno ? DB_RECNO : DB_BTREE, DB_CREATE, 0)) != 0) {
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "Db.open: %s", db_strerror(ret));
goto err;
}
if ((ret = db->cursor(db, NULL, &cursor->dbc, 0)) != 0) {
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "Db.cursor: %s", db_strerror(ret));
goto err;
}
@@ -882,7 +882,7 @@ err: free(cursor);
}
if (locked)
- ETRET(unlock(wtext, session, &ds->rwlock));
+ ETRET(unlock(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -892,30 +892,30 @@ kvs_session_rename(WT_DATA_SOURCE *wtds, WT_SESSION *session,
{
DATA_SOURCE *ds;
DB *db;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
const char *name;
(void)config; /* Unused parameters */
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0)
return (ret);
if ((ret = db_create(&db, ds->dbenv, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
else if ((ret = db->rename(db, name, NULL, newname, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "Db.rename: %s", db_strerror(ret));
/* db handle is dead */
- ETRET(unlock(wtext, session, &ds->rwlock));
+ ETRET(unlock(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -925,35 +925,35 @@ kvs_session_truncate(WT_DATA_SOURCE *wtds,
{
DATA_SOURCE *ds;
DB *db;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int tret, ret = 0;
const char *name;
(void)config; /* Unused parameters */
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0)
return (ret);
if ((ret = db_create(&db, ds->dbenv, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
else {
if ((ret = db->open(db,
NULL, name, NULL, DB_UNKNOWN, DB_TRUNCATE, 0)) != 0)
- ESET(wtext, session, WT_ERROR,
+ ESET(wt_api, session, WT_ERROR,
"Db.open: %s", db_strerror(ret));
if ((tret = db->close(db, 0)) != 0)
- ESET(wtext, session, WT_ERROR,
+ ESET(wt_api, session, WT_ERROR,
"Db.close: %s", db_strerror(tret));
}
- ETRET(unlock(wtext, session, &ds->rwlock));
+ ETRET(unlock(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -963,30 +963,30 @@ kvs_session_verify(WT_DATA_SOURCE *wtds,
{
DATA_SOURCE *ds;
DB *db;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
const char *name;
(void)config; /* Unused parameters */
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
/* Get the object name */
- if ((ret = uri2name(wtext, session, uri, &name)) != 0)
+ if ((ret = uri2name(wt_api, session, uri, &name)) != 0)
return (ret);
if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0)
return (ret);
if ((ret = db_create(&db, ds->dbenv, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "db_create: %s", db_strerror(ret));
else if ((ret = db->verify(db, name, NULL, NULL, 0)) != 0)
- ESET(wtext, session, WT_ERROR,
+ ESET(wt_api, session, WT_ERROR,
"Db.verify: %s: %s", uri, db_strerror(ret));
/* db handle is dead */
- ETRET(unlock(wtext, session, &ds->rwlock));
+ ETRET(unlock(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -995,18 +995,18 @@ kvs_terminate(WT_DATA_SOURCE *wtds, WT_SESSION *session)
{
DB_ENV *dbenv;
DATA_SOURCE *ds;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
int ret = 0;
ds = (DATA_SOURCE *)wtds;
- wtext = ds->wtext;
+ wt_api = ds->wt_api;
dbenv = ds->dbenv;
if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0)
- ESET(wtext,
+ ESET(wt_api,
session, WT_ERROR, "DbEnv.close: %s", db_strerror(ret));
- ETRET(lock_destroy(wtext, session, &ds->rwlock));
+ ETRET(lock_destroy(wt_api, session, &ds->rwlock));
return (ret);
}
@@ -1033,7 +1033,7 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
};
DATA_SOURCE *ds;
DB_ENV *dbenv;
- WT_EXTENSION_API *wtext;
+ WT_EXTENSION_API *wt_api;
size_t len;
int ret = 0;
const char *home;
@@ -1045,21 +1045,21 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
dbenv = NULL;
path = NULL;
/* Acquire the extension API */
- wtext = connection->get_extension_api(connection);
+ wt_api = connection->get_extension_api(connection);
/* Allocate the local data-source structure. */
if ((ds = calloc(1, sizeof(DATA_SOURCE))) == NULL)
return (os_errno());
- ds->wtext = wtext;
+ ds->wt_api = wt_api;
/* Configure the global lock */
- if ((ret = lock_init(wtext, NULL, &ds->rwlock)) != 0)
+ if ((ret = lock_init(wt_api, NULL, &ds->rwlock)) != 0)
goto err;
ds->wtds = wtds; /* Configure the methods */
/* Berkeley DB environment */
if ((ret = db_env_create(&dbenv, 0)) != 0) {
- ESET(wtext,
+ ESET(wt_api,
NULL, WT_ERROR, "db_env_create: %s", db_strerror(ret));
goto err;
}
@@ -1073,7 +1073,8 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
(void)snprintf(path, len, "%s/KVS", home);
if ((ret = dbenv->open(dbenv, path,
DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_PRIVATE, 0)) != 0) {
- ESET(wtext, NULL, WT_ERROR, "DbEnv.open: %s", db_strerror(ret));
+ ESET(wt_api,
+ NULL, WT_ERROR, "DbEnv.open: %s", db_strerror(ret));
goto err;
}
ds->dbenv = dbenv;
@@ -1081,7 +1082,7 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config)
if ((ret = /* Add the data source */
connection->add_data_source(
connection, "kvsbdb:", (WT_DATA_SOURCE *)ds, NULL)) != 0) {
- ESET(wtext, NULL, ret, "WT_CONNECTION.add_data_source");
+ ESET(wt_api, NULL, ret, "WT_CONNECTION.add_data_source");
goto err;
}
diff --git a/src/third_party/wiredtiger/src/async/async_api.c b/src/third_party/wiredtiger/src/async/async_api.c
index bac4dc01bae..54bcb7cd26c 100644
--- a/src/third_party/wiredtiger/src/async/async_api.c
+++ b/src/third_party/wiredtiger/src/async/async_api.c
@@ -144,7 +144,7 @@ retry:
*/
if (op == NULL || op->state != WT_ASYNCOP_FREE) {
WT_STAT_CONN_INCR(session, async_full);
- WT_RET(EBUSY);
+ return (EBUSY);
}
/*
* Set the state of this op handle as READY for the user to use.
@@ -232,7 +232,7 @@ __async_start(WT_SESSION_IMPL *session)
uint32_t i, session_flags;
conn = S2C(session);
- conn->async_cfg = 1;
+ conn->async_cfg = true;
/*
* Async is on, allocate the WT_ASYNC structure and initialize the ops.
*/
@@ -339,16 +339,16 @@ __wt_async_reconfig(WT_SESSION_IMPL *session, const char *cfg[])
* 2. If async is off, and the user wants it on, start it.
* 3. If not a toggle and async is off, we're done.
*/
- if (conn->async_cfg > 0 && !run) {
+ if (conn->async_cfg && !run) {
/* Case 1 */
WT_TRET(__wt_async_flush(session));
ret = __wt_async_destroy(session);
- conn->async_cfg = 0;
+ conn->async_cfg = false;
return (ret);
- } else if (conn->async_cfg == 0 && run)
+ } else if (!conn->async_cfg && run)
/* Case 2 */
return (__async_start(session));
- else if (conn->async_cfg == 0)
+ else if (!conn->async_cfg)
/* Case 3 */
return (0);
@@ -599,7 +599,8 @@ __wt_async_new_op(WT_SESSION_IMPL *session, const char *uri,
conn = S2C(session);
if (!conn->async_cfg)
- return (ENOTSUP);
+ WT_RET_MSG(
+ session, ENOTSUP, "Asynchronous operations not configured");
op = NULL;
WT_ERR(__async_new_op_alloc(session, uri, config, &op));
diff --git a/src/third_party/wiredtiger/src/async/async_worker.c b/src/third_party/wiredtiger/src/async/async_worker.c
index 401d0616eab..b1bc3902f7c 100644
--- a/src/third_party/wiredtiger/src/async/async_worker.c
+++ b/src/third_party/wiredtiger/src/async/async_worker.c
@@ -216,7 +216,7 @@ __async_worker_execop(WT_SESSION_IMPL *session, WT_ASYNC_OP_IMPL *op,
break;
case WT_AOP_NONE:
WT_RET_MSG(session, EINVAL,
- "Unknown async optype %d\n", op->optype);
+ "Unknown async optype %d", op->optype);
}
return (0);
}
diff --git a/src/third_party/wiredtiger/src/block/block_ckpt.c b/src/third_party/wiredtiger/src/block/block_ckpt.c
index b7ac953cdb1..48522768dc9 100644
--- a/src/third_party/wiredtiger/src/block/block_ckpt.c
+++ b/src/third_party/wiredtiger/src/block/block_ckpt.c
@@ -615,8 +615,6 @@ live_update:
WT_CKPT_FOREACH(ckptbase, ckpt)
if (F_ISSET(ckpt, WT_CKPT_ADD)) {
/*
- * Set the checkpoint size for the live system.
- *
* !!!
* Our caller wants the final checkpoint size. Setting
* the size here violates layering, but the alternative
@@ -624,7 +622,31 @@ live_update:
* cookie into its components, and that's a fair amount
* of work.
*/
- ckpt->ckpt_size = ci->ckpt_size = ckpt_size;
+ ckpt->ckpt_size = ckpt_size;
+
+ /*
+ * Set the rolling checkpoint size for the live system.
+ * The current size includes the current checkpoint's
+ * root page size (root pages are on the checkpoint's
+ * block allocation list as root pages are allocated
+ * with the usual block allocation functions). That's
+ * correct, but we don't want to include it in the size
+ * for the next checkpoint.
+ */
+ ckpt_size -= ci->root_size;
+
+ /*
+ * Additionally, we had a bug for awhile where the live
+ * checkpoint size grew without bound. We can't sanity
+ * check the value, that would require walking the tree
+ * as part of the checkpoint. Bound any bug at the size
+ * of the file.
+ * It isn't practical to assert that the value is within
+ * bounds since databases created with older versions
+ * of WiredTiger (2.8.0) would likely see an error.
+ */
+ ci->ckpt_size =
+ WT_MIN(ckpt_size, (uint64_t)block->size);
WT_ERR(__ckpt_update(session, block, ckpt, ci, true));
}
diff --git a/src/third_party/wiredtiger/src/block/block_ext.c b/src/third_party/wiredtiger/src/block/block_ext.c
index b02fea8707d..26acc8c560f 100644
--- a/src/third_party/wiredtiger/src/block/block_ext.c
+++ b/src/third_party/wiredtiger/src/block/block_ext.c
@@ -344,7 +344,8 @@ __block_off_remove(WT_SESSION_IMPL *session, WT_BLOCK *block,
__block_size_srch(el->sz, ext->size, sstack);
szp = *sstack[0];
if (szp == NULL || szp->size != ext->size)
- return (EINVAL);
+ WT_PANIC_RET(session, EINVAL,
+ "extent not found in by-size list during remove");
__block_off_srch(szp->off, off, astack, true);
ext = *astack[0];
if (ext == NULL || ext->off != off)
diff --git a/src/third_party/wiredtiger/src/bloom/bloom.c b/src/third_party/wiredtiger/src/bloom/bloom.c
index 3afada05dda..dde08d8a982 100644
--- a/src/third_party/wiredtiger/src/bloom/bloom.c
+++ b/src/third_party/wiredtiger/src/bloom/bloom.c
@@ -65,7 +65,9 @@ __bloom_setup(
WT_BLOOM *bloom, uint64_t n, uint64_t m, uint32_t factor, uint32_t k)
{
if (k < 2)
- return (EINVAL);
+ WT_RET_MSG(bloom->session, EINVAL,
+ "bloom filter hash values to be set/tested must be "
+ "greater than 2");
bloom->k = k;
bloom->factor = factor;
@@ -342,7 +344,12 @@ __wt_bloom_intersection(WT_BLOOM *bloom, WT_BLOOM *other)
if (bloom->k != other->k || bloom->factor != other->factor ||
bloom->m != other->m || bloom->n != other->n)
- return (EINVAL);
+ WT_RET_MSG(bloom->session, EINVAL,
+ "bloom filter intersection configuration mismatch: ("
+ "%" PRIu32 "/%" PRIu32 ", %" PRIu32 "/%" PRIu32 ", "
+ "%" PRIu64 "/%" PRIu64 ", %" PRIu64 "/%" PRIu64 ")",
+ bloom->k, other->k, bloom->factor, other->factor,
+ bloom->m, other->m, bloom->n, other->n);
nbytes = __bitstr_size(bloom->m);
for (i = 0; i < nbytes; i++)
diff --git a/src/third_party/wiredtiger/src/btree/bt_cursor.c b/src/third_party/wiredtiger/src/btree/bt_cursor.c
index 3690b41ead4..0454ea7c20e 100644
--- a/src/third_party/wiredtiger/src/btree/bt_cursor.c
+++ b/src/third_party/wiredtiger/src/btree/bt_cursor.c
@@ -37,21 +37,20 @@ __cursor_size_chk(WT_SESSION_IMPL *session, WT_ITEM *kv)
if (kv->size <= WT_GIGABYTE)
return (0);
- /*
- * There are two checks: what we are willing to store in the tree, and
- * what the block manager can actually write.
- */
+ /* Check what we are willing to store in the tree. */
if (kv->size > WT_BTREE_MAX_OBJECT_SIZE)
- ret = EINVAL;
- else {
- size = kv->size;
- ret = bm->write_size(bm, session, &size);
- }
- if (ret != 0)
- WT_RET_MSG(session, ret,
+ WT_RET_MSG(session, EINVAL,
"item size of %" WT_SIZET_FMT " exceeds the maximum "
- "supported size",
+ "supported WiredTiger size of %d",
+ kv->size, WT_BTREE_MAX_OBJECT_SIZE);
+
+ /* Check what the block manager can actually write. */
+ size = kv->size;
+ if ((ret = bm->write_size(bm, session, &size)) != 0)
+ WT_RET_MSG(session, ret,
+ "item size of %" WT_SIZET_FMT " refused by block manager",
kv->size);
+
return (0);
}
@@ -858,7 +857,8 @@ __wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
* value from a column-store, if there were any reason to do so.
*/
if (btree->type != BTREE_ROW)
- WT_RET(ENOTSUP);
+ WT_RET_MSG(session, ENOTSUP,
+ "WT_CURSOR.next_random only supported by row-store tables");
WT_STAT_CONN_INCR(session, cursor_next);
WT_STAT_DATA_INCR(session, cursor_next);
@@ -1217,7 +1217,7 @@ err: if (FLD_ISSET(S2C(session)->log_flags, WT_CONN_LOG_ENABLED))
/*
* __wt_btcur_init --
- * Initialize an cursor used for internal purposes.
+ * Initialize a cursor used for internal purposes.
*/
void
__wt_btcur_init(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
diff --git a/src/third_party/wiredtiger/src/btree/bt_handle.c b/src/third_party/wiredtiger/src/btree/bt_handle.c
index 9591023e163..337a3ea036f 100644
--- a/src/third_party/wiredtiger/src/btree/bt_handle.c
+++ b/src/third_party/wiredtiger/src/btree/bt_handle.c
@@ -271,6 +271,17 @@ __btree_conf(WT_SESSION_IMPL *session, WT_CKPT *ckpt)
else
F_CLR(btree, WT_BTREE_IN_MEMORY | WT_BTREE_NO_EVICTION);
+ WT_RET(__wt_config_gets(session,
+ cfg, "ignore_in_memory_cache_size", &cval));
+ if (cval.val) {
+ if (!F_ISSET(conn, WT_CONN_IN_MEMORY))
+ WT_RET_MSG(session, EINVAL,
+ "ignore_in_memory_cache_size setting is only valid "
+ "with databases configured to run in-memory");
+ F_SET(btree, WT_BTREE_IGNORE_CACHE);
+ } else
+ F_CLR(btree, WT_BTREE_IGNORE_CACHE);
+
WT_RET(__wt_config_gets(session, cfg, "log.enabled", &cval));
if (cval.val)
F_CLR(btree, WT_BTREE_NO_LOGGING);
@@ -353,7 +364,7 @@ __btree_conf(WT_SESSION_IMPL *session, WT_CKPT *ckpt)
WT_RET(__wt_spin_init(session, &btree->flush_lock, "btree flush"));
btree->checkpointing = WT_CKPT_OFF; /* Not checkpointing */
- btree->modified = 0; /* Clean */
+ btree->modified = false; /* Clean */
btree->write_gen = ckpt->write_gen; /* Write generation */
return (0);
diff --git a/src/third_party/wiredtiger/src/btree/bt_io.c b/src/third_party/wiredtiger/src/btree/bt_io.c
index 42c3a849a88..a8645f79dbe 100644
--- a/src/third_party/wiredtiger/src/btree/bt_io.c
+++ b/src/third_party/wiredtiger/src/btree/bt_io.c
@@ -171,6 +171,7 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
uint8_t *addr, size_t *addr_sizep,
bool checkpoint, bool checkpoint_io, bool compressed)
{
+ struct timespec start, stop;
WT_BM *bm;
WT_BTREE *btree;
WT_DECL_ITEM(ctmp);
@@ -356,6 +357,8 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
data_checksum = !compressed;
break;
}
+ if (!F_ISSET(session, WT_SESSION_INTERNAL))
+ __wt_epoch(session, &start);
/* Call the block manager to write the block. */
WT_ERR(checkpoint ?
@@ -363,6 +366,14 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
bm->write(
bm, session, ip, addr, addr_sizep, data_checksum, checkpoint_io));
+ /* Update some statistics now that the write is done */
+ if (!F_ISSET(session, WT_SESSION_INTERNAL)) {
+ __wt_epoch(session, &stop);
+ WT_STAT_CONN_INCR(session, cache_write_app_count);
+ WT_STAT_CONN_INCRV(session, cache_write_app_time,
+ WT_TIMEDIFF_US(stop, start));
+ }
+
WT_STAT_CONN_INCR(session, cache_write);
WT_STAT_DATA_INCR(session, cache_write);
S2C(session)->cache->bytes_written += dsk->mem_size;
diff --git a/src/third_party/wiredtiger/src/btree/bt_read.c b/src/third_party/wiredtiger/src/btree/bt_read.c
index c54eaa69c43..90188498535 100644
--- a/src/third_party/wiredtiger/src/btree/bt_read.c
+++ b/src/third_party/wiredtiger/src/btree/bt_read.c
@@ -327,22 +327,28 @@ __evict_force_check(WT_SESSION_IMPL *session, WT_REF *ref)
if (__wt_hazard_count(session, page) > 1)
return (false);
+ /* If we can do an in-memory split, do it. */
+ if (__wt_leaf_page_can_split(session, page))
+ return (true);
+ if (page->memory_footprint < btree->maxmempage)
+ return (false);
+
+ /* Bump the oldest ID, we're about to do some visibility checks. */
+ WT_IGNORE_RET(__wt_txn_update_oldest(session, 0));
+
/*
- * If we have already tried and the transaction state has not moved on,
- * eviction is highly likely to fail.
+ * Allow some leeway if the transaction ID isn't moving forward since
+ * it is unlikely eviction will be able to evict the page. Don't keep
+ * skipping the page indefinitely or large records can lead to
+ * extremely large memory footprints.
*/
- if (page->modify->last_eviction_id == __wt_txn_oldest_id(session))
+ if (page->modify->update_restored &&
+ page->modify->last_eviction_id == __wt_txn_oldest_id(session))
return (false);
- if (page->memory_footprint < btree->maxmempage)
- return (__wt_leaf_page_can_split(session, page));
-
/* Trigger eviction on the next page release. */
__wt_page_evict_soon(session, ref);
- /* Bump the oldest ID, we're about to do some visibility checks. */
- WT_IGNORE_RET(__wt_txn_update_oldest(session, 0));
-
/* If eviction cannot succeed, don't try. */
return (__wt_page_can_evict(session, ref, NULL));
}
@@ -354,6 +360,7 @@ __evict_force_check(WT_SESSION_IMPL *session, WT_REF *ref)
static int
__page_read(WT_SESSION_IMPL *session, WT_REF *ref)
{
+ struct timespec start, stop;
const WT_PAGE_HEADER *dsk;
WT_BTREE *btree;
WT_DECL_RET;
@@ -401,7 +408,15 @@ __page_read(WT_SESSION_IMPL *session, WT_REF *ref)
* There's an address, read or map the backing disk page and build an
* in-memory version of the page.
*/
+ if (!F_ISSET(session, WT_SESSION_INTERNAL))
+ __wt_epoch(session, &start);
WT_ERR(__wt_bt_read(session, &tmp, addr, addr_size));
+ if (!F_ISSET(session, WT_SESSION_INTERNAL)) {
+ __wt_epoch(session, &stop);
+ WT_STAT_CONN_INCR(session, cache_read_app_count);
+ WT_STAT_CONN_INCRV(session, cache_read_app_time,
+ WT_TIMEDIFF_US(stop, start));
+ }
WT_ERR(__wt_page_inmem(session, ref, tmp.data, tmp.memsize,
WT_DATA_IN_ITEM(&tmp) ?
WT_PAGE_DISK_ALLOC : WT_PAGE_DISK_MAPPED, &page));
diff --git a/src/third_party/wiredtiger/src/btree/bt_split.c b/src/third_party/wiredtiger/src/btree/bt_split.c
index ea667460966..017c820ea29 100644
--- a/src/third_party/wiredtiger/src/btree/bt_split.c
+++ b/src/third_party/wiredtiger/src/btree/bt_split.c
@@ -1582,6 +1582,13 @@ __split_multi_inmem(
*/
page->modify->first_dirty_txn = WT_TXN_FIRST;
+ /*
+ * If the new page is modified, save the oldest ID from reconciliation
+ * to avoid repeatedly attempting eviction on the same page.
+ */
+ page->modify->last_eviction_id = orig->modify->last_eviction_id;
+ page->modify->update_restored = 1;
+
err: /* Free any resources that may have been cached in the cursor. */
WT_TRET(__wt_btcur_close(&cbt, true));
@@ -2245,14 +2252,6 @@ __wt_split_rewrite(WT_SESSION_IMPL *session, WT_REF *ref, WT_MULTI *multi)
WT_ERR(__split_multi_inmem(session, page, multi, new));
/*
- * If the new page is modified, save the oldest ID from reconciliation
- * to avoid repeatedly attempting eviction on the same page.
- */
- if (new->page->modify != NULL)
- new->page->modify->last_eviction_id =
- page->modify->last_eviction_id;
-
- /*
* The rewrite succeeded, we can no longer fail.
*
* Finalize the move, discarding moved update lists from the original
diff --git a/src/third_party/wiredtiger/src/btree/bt_stat.c b/src/third_party/wiredtiger/src/btree/bt_stat.c
index d3ddf33446e..06428b87f6e 100644
--- a/src/third_party/wiredtiger/src/btree/bt_stat.c
+++ b/src/third_party/wiredtiger/src/btree/bt_stat.c
@@ -8,6 +8,7 @@
#include "wt_internal.h"
+static int __stat_tree_walk(WT_SESSION_IMPL *);
static int __stat_page(WT_SESSION_IMPL *, WT_PAGE *, WT_DSRC_STATS **);
static void __stat_page_col_var(WT_SESSION_IMPL *, WT_PAGE *, WT_DSRC_STATS **);
static void __stat_page_row_int(WT_SESSION_IMPL *, WT_PAGE *, WT_DSRC_STATS **);
@@ -23,9 +24,7 @@ __wt_btree_stat_init(WT_SESSION_IMPL *session, WT_CURSOR_STAT *cst)
{
WT_BM *bm;
WT_BTREE *btree;
- WT_DECL_RET;
WT_DSRC_STATS **stats;
- WT_REF *next_walk;
btree = S2BT(session);
bm = btree->bm;
@@ -44,9 +43,29 @@ __wt_btree_stat_init(WT_SESSION_IMPL *session, WT_CURSOR_STAT *cst)
WT_STAT_SET(session, stats, cache_bytes_inuse,
__wt_btree_bytes_inuse(session));
- /* Everything else is really, really expensive. */
- if (!F_ISSET(cst, WT_CONN_STAT_ALL))
- return (0);
+ if (F_ISSET(cst, WT_STAT_TYPE_CACHE_WALK))
+ __wt_curstat_cache_walk(session);
+
+ if (F_ISSET(cst, WT_STAT_TYPE_TREE_WALK))
+ WT_RET(__stat_tree_walk(session));
+
+ return (0);
+}
+
+/*
+ * __stat_tree_walk --
+ * Gather btree statistics that require traversing the tree.
+ */
+static int
+__stat_tree_walk(WT_SESSION_IMPL *session)
+{
+ WT_BTREE *btree;
+ WT_DECL_RET;
+ WT_DSRC_STATS **stats;
+ WT_REF *next_walk;
+
+ btree = S2BT(session);
+ stats = btree->dhandle->stats;
/*
* Clear the statistics we're about to count.
diff --git a/src/third_party/wiredtiger/src/btree/bt_sync.c b/src/third_party/wiredtiger/src/btree/bt_sync.c
index b41179a565d..6d4ad9d0d0f 100644
--- a/src/third_party/wiredtiger/src/btree/bt_sync.c
+++ b/src/third_party/wiredtiger/src/btree/bt_sync.c
@@ -9,6 +9,59 @@
#include "wt_internal.h"
/*
+ * __sync_checkpoint_can_skip --
+ * There are limited conditions under which we can skip writing a dirty
+ * page during checkpoint.
+ */
+static inline bool
+__sync_checkpoint_can_skip(WT_SESSION_IMPL *session, WT_PAGE *page)
+{
+ WT_PAGE_MODIFY *mod;
+ WT_MULTI *multi;
+ WT_TXN *txn;
+ u_int i;
+
+ mod = page->modify;
+ txn = &session->txn;
+
+ /*
+ * We can skip some dirty pages during a checkpoint. The requirements:
+ *
+ * 1. they must be leaf pages,
+ * 2. there is a snapshot transaction active (which is the case in
+ * ordinary application checkpoints but not all internal cases),
+ * 3. the first dirty update on the page is sufficiently recent the
+ * checkpoint transaction would skip them,
+ * 4. there's already an address for every disk block involved.
+ */
+ if (WT_PAGE_IS_INTERNAL(page))
+ return (false);
+ if (!F_ISSET(txn, WT_TXN_HAS_SNAPSHOT))
+ return (false);
+ if (!WT_TXNID_LT(txn->snap_max, mod->first_dirty_txn))
+ return (false);
+
+ /*
+ * The problematic case is when a page was evicted but when there were
+ * unresolved updates and not every block associated with the page has
+ * a disk address. We can't skip such pages because we need a checkpoint
+ * write with valid addresses.
+ *
+ * The page's modification information can change underfoot if the page
+ * is being reconciled, so we'd normally serialize with reconciliation
+ * before reviewing page-modification information. However, checkpoint
+ * is the only valid writer of dirty leaf pages at this point, we skip
+ * the lock.
+ */
+ if (mod->rec_result == WT_PM_REC_MULTIBLOCK)
+ for (multi = mod->mod_multi,
+ i = 0; i < mod->mod_multi_entries; ++multi, ++i)
+ if (multi->addr.addr == NULL)
+ return (false);
+ return (true);
+}
+
+/*
* __sync_file --
* Flush pages for a specific file.
*/
@@ -20,24 +73,23 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
WT_CONNECTION_IMPL *conn;
WT_DECL_RET;
WT_PAGE *page;
- WT_PAGE_MODIFY *mod;
WT_REF *walk;
WT_TXN *txn;
uint64_t internal_bytes, internal_pages, leaf_bytes, leaf_pages;
- uint64_t oldest_id, saved_snap_min;
+ uint64_t oldest_id, saved_pinned_id;
uint32_t flags;
conn = S2C(session);
btree = S2BT(session);
walk = NULL;
txn = &session->txn;
- saved_snap_min = WT_SESSION_TXN_STATE(session)->snap_min;
+ saved_pinned_id = WT_SESSION_TXN_STATE(session)->pinned_id;
flags = WT_READ_CACHE | WT_READ_NO_GEN;
internal_bytes = leaf_bytes = 0;
internal_pages = leaf_pages = 0;
if (WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT))
- WT_RET(__wt_epoch(session, &start));
+ __wt_epoch(session, &start);
switch (syncop) {
case WT_SYNC_WRITE_LEAVES:
@@ -161,29 +213,15 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
* reference and checking modified.
*/
page = walk->page;
- mod = page->modify;
/*
- * Write dirty pages, unless we can be sure they only
- * became dirty after the checkpoint started.
- *
- * We can skip dirty pages if:
- * (1) they are leaf pages;
- * (2) there is a snapshot transaction active (which
- * is the case in ordinary application checkpoints
- * but not all internal cases); and
- * (3) the first dirty update on the page is
- * sufficiently recent that the checkpoint
- * transaction would skip them.
- *
- * Mark the tree dirty: the checkpoint marked it clean
- * and we can't skip future checkpoints until this page
- * is written.
+ * Write dirty pages, if we can't skip them. If we skip
+ * a page, mark the tree dirty. The checkpoint marked it
+ * clean and we can't skip future checkpoints until this
+ * page is written.
*/
- if (!WT_PAGE_IS_INTERNAL(page) &&
- F_ISSET(txn, WT_TXN_HAS_SNAPSHOT) &&
- WT_TXNID_LT(txn->snap_max, mod->first_dirty_txn)) {
- __wt_page_modify_set(session, page);
+ if (__sync_checkpoint_can_skip(session, page)) {
+ __wt_tree_modify_set(session);
continue;
}
@@ -205,15 +243,14 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
}
if (WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT)) {
- WT_ERR(__wt_epoch(session, &end));
+ __wt_epoch(session, &end);
__wt_verbose(session, WT_VERB_CHECKPOINT,
- "__sync_file WT_SYNC_%s wrote:\n\t %" PRIu64
- " bytes, %" PRIu64 " pages of leaves\n\t %" PRIu64
- " bytes, %" PRIu64 " pages of internal\n\t"
- "Took: %" PRIu64 "ms",
+ "__sync_file WT_SYNC_%s wrote: %" PRIu64
+ " leaf pages (%" PRIu64 "B), %" PRIu64
+ " internal pages (%" PRIu64 "B), and took %" PRIu64 "ms",
syncop == WT_SYNC_WRITE_LEAVES ?
"WRITE_LEAVES" : "CHECKPOINT",
- leaf_bytes, leaf_pages, internal_bytes, internal_pages,
+ leaf_pages, leaf_bytes, internal_pages, internal_bytes,
WT_TIMEDIFF_MS(end, start));
}
@@ -226,7 +263,7 @@ err: /* On error, clear any left-over tree walk. */
* snapshot active when we started, release it.
*/
if (txn->isolation == WT_ISO_READ_COMMITTED &&
- saved_snap_min == WT_TXN_NONE)
+ saved_pinned_id == WT_TXN_NONE)
__wt_txn_release_snapshot(session);
/* Clear the checkpoint flag and push the change. */
diff --git a/src/third_party/wiredtiger/src/cache/cache_las.c b/src/third_party/wiredtiger/src/cache/cache_las.c
index 4c338bc6ad9..41f50957809 100644
--- a/src/third_party/wiredtiger/src/cache/cache_las.c
+++ b/src/third_party/wiredtiger/src/cache/cache_las.c
@@ -49,7 +49,7 @@ __wt_las_stats_update(WT_SESSION_IMPL *session)
* don't have to worry about users seeing inconsistent data source
* information.
*/
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) {
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_CLEAR)) {
WT_STAT_SET(session, dstats, cursor_insert, 0);
WT_STAT_SET(session, dstats, cursor_remove, 0);
}
diff --git a/src/third_party/wiredtiger/src/checksum/power8/README.md b/src/third_party/wiredtiger/src/checksum/power8/README.md
index 3e2976650cd..579d841a02c 100644
--- a/src/third_party/wiredtiger/src/checksum/power8/README.md
+++ b/src/third_party/wiredtiger/src/checksum/power8/README.md
@@ -39,7 +39,7 @@ Quick start
- Type make to create the constants (crc32_constants.h)
-- Import the code into your application (crc32.S crc32_wrapper.c
+- Import the code into your application (crc32.sx crc32_wrapper.c
crc32_constants.h ppc-opcode.h) and call the CRC:
```
diff --git a/src/third_party/wiredtiger/src/checksum/power8/crc32.S b/src/third_party/wiredtiger/src/checksum/power8/crc32.sx
index 0b7870668b5..0b7870668b5 100644
--- a/src/third_party/wiredtiger/src/checksum/power8/crc32.S
+++ b/src/third_party/wiredtiger/src/checksum/power8/crc32.sx
diff --git a/src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.S b/src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.sx
index 0f1392b0952..0f1392b0952 100644
--- a/src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.S
+++ b/src/third_party/wiredtiger/src/checksum/zseries/crc32le-vx.sx
diff --git a/src/third_party/wiredtiger/src/config/config_api.c b/src/third_party/wiredtiger/src/config/config_api.c
index 8820b81a410..76a51903588 100644
--- a/src/third_party/wiredtiger/src/config/config_api.c
+++ b/src/third_party/wiredtiger/src/config/config_api.c
@@ -19,9 +19,6 @@ __config_parser_close(WT_CONFIG_PARSER *wt_config_parser)
config_parser = (WT_CONFIG_PARSER_IMPL *)wt_config_parser;
- if (config_parser == NULL)
- return (EINVAL);
-
__wt_free(config_parser->session, config_parser);
return (0);
}
@@ -38,9 +35,6 @@ __config_parser_get(WT_CONFIG_PARSER *wt_config_parser,
config_parser = (WT_CONFIG_PARSER_IMPL *)wt_config_parser;
- if (config_parser == NULL)
- return (EINVAL);
-
return (__wt_config_subgets(config_parser->session,
&config_parser->config_item, key, cval));
}
@@ -57,9 +51,6 @@ __config_parser_next(WT_CONFIG_PARSER *wt_config_parser,
config_parser = (WT_CONFIG_PARSER_IMPL *)wt_config_parser;
- if (config_parser == NULL)
- return (EINVAL);
-
return (__wt_config_next(&config_parser->config, key, cval));
}
diff --git a/src/third_party/wiredtiger/src/config/config_check.c b/src/third_party/wiredtiger/src/config/config_check.c
index d0aa09a96c1..2f372651cb9 100644
--- a/src/third_party/wiredtiger/src/config/config_check.c
+++ b/src/third_party/wiredtiger/src/config/config_check.c
@@ -128,8 +128,7 @@ config_check(WT_SESSION_IMPL *session,
badtype = false;
} else
WT_RET_MSG(session, EINVAL,
- "unknown configuration type: '%s'",
- checks[i].type);
+ "unknown configuration type: '%s'", checks[i].type);
if (badtype)
WT_RET_MSG(session, EINVAL,
diff --git a/src/third_party/wiredtiger/src/config/config_collapse.c b/src/third_party/wiredtiger/src/config/config_collapse.c
index ea956ebfff9..7fe78d06ba7 100644
--- a/src/third_party/wiredtiger/src/config/config_collapse.c
+++ b/src/third_party/wiredtiger/src/config/config_collapse.c
@@ -47,7 +47,7 @@ __wt_config_collapse(
if (k.type != WT_CONFIG_ITEM_STRING &&
k.type != WT_CONFIG_ITEM_ID)
WT_ERR_MSG(session, EINVAL,
- "Invalid configuration key found: '%s'\n", k.str);
+ "Invalid configuration key found: '%s'", k.str);
WT_ERR(__wt_config_get(session, cfg, &k, &v));
/* Include the quotes around string keys/values. */
if (k.type == WT_CONFIG_ITEM_STRING) {
@@ -132,7 +132,7 @@ __config_merge_scan(WT_SESSION_IMPL *session,
if (k.type != WT_CONFIG_ITEM_STRING &&
k.type != WT_CONFIG_ITEM_ID)
WT_ERR_MSG(session, EINVAL,
- "Invalid configuration key found: '%s'\n", k.str);
+ "Invalid configuration key found: '%s'", k.str);
/* Include the quotes around string keys/values. */
if (k.type == WT_CONFIG_ITEM_STRING) {
diff --git a/src/third_party/wiredtiger/src/config/config_def.c b/src/third_party/wiredtiger/src/config/config_def.c
index 7bad5f12a9f..d57bc418c93 100644
--- a/src/third_party/wiredtiger/src/config/config_def.c
+++ b/src/third_party/wiredtiger/src/config/config_def.c
@@ -138,7 +138,8 @@ static const WT_CONFIG_CHECK confchk_WT_CONNECTION_reconfigure[] = {
NULL, NULL,
confchk_wiredtiger_open_shared_cache_subconfigs, 5 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"none\",\"clear\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"none\","
+ "\"clear\",\"tree_walk\"]",
NULL, 0 },
{ "statistics_log", "category",
NULL, NULL,
@@ -246,6 +247,9 @@ static const WT_CONFIG_CHECK confchk_WT_SESSION_create[] = {
{ "format", "string", NULL, "choices=[\"btree\"]", NULL, 0 },
{ "huffman_key", "string", NULL, NULL, NULL, 0 },
{ "huffman_value", "string", NULL, NULL, NULL, 0 },
+ { "ignore_in_memory_cache_size", "boolean",
+ NULL, NULL,
+ NULL, 0 },
{ "immutable", "boolean", NULL, NULL, NULL, 0 },
{ "internal_item_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_max", "int", NULL, "min=0", NULL, 0 },
@@ -295,6 +299,7 @@ static const WT_CONFIG_CHECK confchk_WT_SESSION_drop[] = {
static const WT_CONFIG_CHECK confchk_WT_SESSION_join[] = {
{ "bloom_bit_count", "int", NULL, "min=2,max=1000", NULL, 0 },
+ { "bloom_false_positives", "boolean", NULL, NULL, NULL, 0 },
{ "bloom_hash_count", "int", NULL, "min=2,max=100", NULL, 0 },
{ "compare", "string",
NULL, "choices=[\"eq\",\"ge\",\"gt\",\"le\",\"lt\"]",
@@ -331,7 +336,8 @@ static const WT_CONFIG_CHECK confchk_WT_SESSION_open_cursor[] = {
{ "readonly", "boolean", NULL, NULL, NULL, 0 },
{ "skip_sort_check", "boolean", NULL, NULL, NULL, 0 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"clear\",\"size\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"clear\","
+ "\"size\",\"tree_walk\"]",
NULL, 0 },
{ "target", "list", NULL, NULL, NULL, 0 },
{ NULL, NULL, NULL, NULL, NULL, 0 }
@@ -413,6 +419,9 @@ static const WT_CONFIG_CHECK confchk_file_config[] = {
{ "format", "string", NULL, "choices=[\"btree\"]", NULL, 0 },
{ "huffman_key", "string", NULL, NULL, NULL, 0 },
{ "huffman_value", "string", NULL, NULL, NULL, 0 },
+ { "ignore_in_memory_cache_size", "boolean",
+ NULL, NULL,
+ NULL, 0 },
{ "internal_item_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_truncate", "boolean", NULL, NULL, NULL, 0 },
@@ -471,6 +480,9 @@ static const WT_CONFIG_CHECK confchk_file_meta[] = {
{ "huffman_key", "string", NULL, NULL, NULL, 0 },
{ "huffman_value", "string", NULL, NULL, NULL, 0 },
{ "id", "string", NULL, NULL, NULL, 0 },
+ { "ignore_in_memory_cache_size", "boolean",
+ NULL, NULL,
+ NULL, 0 },
{ "internal_item_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_truncate", "boolean", NULL, NULL, NULL, 0 },
@@ -544,6 +556,9 @@ static const WT_CONFIG_CHECK confchk_lsm_meta[] = {
{ "format", "string", NULL, "choices=[\"btree\"]", NULL, 0 },
{ "huffman_key", "string", NULL, NULL, NULL, 0 },
{ "huffman_value", "string", NULL, NULL, NULL, 0 },
+ { "ignore_in_memory_cache_size", "boolean",
+ NULL, NULL,
+ NULL, 0 },
{ "internal_item_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_max", "int", NULL, "min=0", NULL, 0 },
{ "internal_key_truncate", "boolean", NULL, NULL, NULL, 0 },
@@ -697,7 +712,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open[] = {
NULL, NULL,
confchk_wiredtiger_open_shared_cache_subconfigs, 5 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"none\",\"clear\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"none\","
+ "\"clear\",\"tree_walk\"]",
NULL, 0 },
{ "statistics_log", "category",
NULL, NULL,
@@ -781,7 +797,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_all[] = {
NULL, NULL,
confchk_wiredtiger_open_shared_cache_subconfigs, 5 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"none\",\"clear\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"none\","
+ "\"clear\",\"tree_walk\"]",
NULL, 0 },
{ "statistics_log", "category",
NULL, NULL,
@@ -862,7 +879,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_basecfg[] = {
NULL, NULL,
confchk_wiredtiger_open_shared_cache_subconfigs, 5 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"none\",\"clear\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"none\","
+ "\"clear\",\"tree_walk\"]",
NULL, 0 },
{ "statistics_log", "category",
NULL, NULL,
@@ -941,7 +959,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_usercfg[] = {
NULL, NULL,
confchk_wiredtiger_open_shared_cache_subconfigs, 5 },
{ "statistics", "list",
- NULL, "choices=[\"all\",\"fast\",\"none\",\"clear\"]",
+ NULL, "choices=[\"all\",\"cache_walk\",\"fast\",\"none\","
+ "\"clear\",\"tree_walk\"]",
NULL, 0 },
{ "statistics_log", "category",
NULL, NULL,
@@ -1053,18 +1072,18 @@ static const WT_CONFIG_ENTRY config_entries[] = {
"block_compressor=,cache_resident=false,checksum=uncompressed,"
"colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,"
"name=),exclusive=false,extractor=,format=btree,huffman_key=,"
- "huffman_value=,immutable=false,internal_item_max=0,"
- "internal_key_max=0,internal_key_truncate=true,"
- "internal_page_max=4KB,key_format=u,key_gap=10,leaf_item_max=0,"
- "leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,"
- "log=(enabled=true),lsm=(auto_throttle=true,bloom=true,"
- "bloom_bit_count=16,bloom_config=,bloom_hash_count=8,"
+ "huffman_value=,ignore_in_memory_cache_size=false,immutable=false"
+ ",internal_item_max=0,internal_key_max=0,"
+ "internal_key_truncate=true,internal_page_max=4KB,key_format=u,"
+ "key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,"
+ "leaf_value_max=0,log=(enabled=true),lsm=(auto_throttle=true,"
+ "bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,"
"bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,"
"chunk_size=10MB,merge_max=15,merge_min=0),memory_page_max=5MB,"
"os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,"
"prefix_compression_min=4,source=,split_deepen_min_child=0,"
"split_deepen_per_child=0,split_pct=75,type=file,value_format=u",
- confchk_WT_SESSION_create, 40
+ confchk_WT_SESSION_create, 41
},
{ "WT_SESSION.drop",
"checkpoint_wait=true,force=false,lock_wait=true,"
@@ -1072,9 +1091,10 @@ static const WT_CONFIG_ENTRY config_entries[] = {
confchk_WT_SESSION_drop, 4
},
{ "WT_SESSION.join",
- "bloom_bit_count=16,bloom_hash_count=8,compare=\"eq\",count=,"
- "operation=\"and\",strategy=",
- confchk_WT_SESSION_join, 6
+ "bloom_bit_count=16,bloom_false_positives=false,"
+ "bloom_hash_count=8,compare=\"eq\",count=,operation=\"and\","
+ "strategy=",
+ confchk_WT_SESSION_join, 7
},
{ "WT_SESSION.log_flush",
"sync=on",
@@ -1148,7 +1168,8 @@ static const WT_CONFIG_ENTRY config_entries[] = {
"allocation_size=4KB,app_metadata=,block_allocation=best,"
"block_compressor=,cache_resident=false,checksum=uncompressed,"
"collator=,columns=,dictionary=0,encryption=(keyid=,name=),"
- "format=btree,huffman_key=,huffman_value=,internal_item_max=0,"
+ "format=btree,huffman_key=,huffman_value=,"
+ "ignore_in_memory_cache_size=false,internal_item_max=0,"
"internal_key_max=0,internal_key_truncate=true,"
"internal_page_max=4KB,key_format=u,key_gap=10,leaf_item_max=0,"
"leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,"
@@ -1156,14 +1177,15 @@ static const WT_CONFIG_ENTRY config_entries[] = {
"os_cache_max=0,prefix_compression=false,prefix_compression_min=4"
",split_deepen_min_child=0,split_deepen_per_child=0,split_pct=75,"
"value_format=u",
- confchk_file_config, 33
+ confchk_file_config, 34
},
{ "file.meta",
"allocation_size=4KB,app_metadata=,block_allocation=best,"
"block_compressor=,cache_resident=false,checkpoint=,"
"checkpoint_lsn=,checksum=uncompressed,collator=,columns=,"
"dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key="
- ",huffman_value=,id=,internal_item_max=0,internal_key_max=0,"
+ ",huffman_value=,id=,ignore_in_memory_cache_size=false,"
+ "internal_item_max=0,internal_key_max=0,"
"internal_key_truncate=true,internal_page_max=4KB,key_format=u,"
"key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,"
"leaf_value_max=0,log=(enabled=true),memory_page_max=5MB,"
@@ -1171,7 +1193,7 @@ static const WT_CONFIG_ENTRY config_entries[] = {
"prefix_compression_min=4,split_deepen_min_child=0,"
"split_deepen_per_child=0,split_pct=75,value_format=u,"
"version=(major=0,minor=0)",
- confchk_file_meta, 37
+ confchk_file_meta, 38
},
{ "index.meta",
"app_metadata=,collator=,columns=,extractor=,immutable=false,"
@@ -1183,18 +1205,19 @@ static const WT_CONFIG_ENTRY config_entries[] = {
"block_compressor=,cache_resident=false,checksum=uncompressed,"
"chunks=,collator=,columns=,dictionary=0,encryption=(keyid=,"
"name=),format=btree,huffman_key=,huffman_value=,"
- "internal_item_max=0,internal_key_max=0,"
- "internal_key_truncate=true,internal_page_max=4KB,key_format=u,"
- "key_gap=10,last=,leaf_item_max=0,leaf_key_max=0,"
- "leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),"
- "lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,"
- "bloom_config=,bloom_hash_count=8,bloom_oldest=false,"
- "chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_max=15,"
- "merge_min=0),memory_page_max=5MB,old_chunks=,"
- "os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,"
- "prefix_compression_min=4,split_deepen_min_child=0,"
- "split_deepen_per_child=0,split_pct=75,value_format=u",
- confchk_lsm_meta, 37
+ "ignore_in_memory_cache_size=false,internal_item_max=0,"
+ "internal_key_max=0,internal_key_truncate=true,"
+ "internal_page_max=4KB,key_format=u,key_gap=10,last=,"
+ "leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,"
+ "leaf_value_max=0,log=(enabled=true),lsm=(auto_throttle=true,"
+ "bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,"
+ "bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,"
+ "chunk_size=10MB,merge_max=15,merge_min=0),memory_page_max=5MB,"
+ "old_chunks=,os_cache_dirty_max=0,os_cache_max=0,"
+ "prefix_compression=false,prefix_compression_min=4,"
+ "split_deepen_min_child=0,split_deepen_per_child=0,split_pct=75,"
+ "value_format=u",
+ confchk_lsm_meta, 38
},
{ "table.meta",
"app_metadata=,colgroups=,collator=,columns=,key_format=u,"
diff --git a/src/third_party/wiredtiger/src/conn/conn_api.c b/src/third_party/wiredtiger/src/conn/conn_api.c
index 0951fd4e58c..a6e0f57f02e 100644
--- a/src/third_party/wiredtiger/src/conn/conn_api.c
+++ b/src/third_party/wiredtiger/src/conn/conn_api.c
@@ -789,14 +789,17 @@ __conn_get_extension_api(WT_CONNECTION *wt_conn)
return (&conn->extension_api);
}
+#ifdef HAVE_BUILTIN_EXTENSION_LZ4
+ extern int lz4_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
+#endif
#ifdef HAVE_BUILTIN_EXTENSION_SNAPPY
extern int snappy_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
#endif
#ifdef HAVE_BUILTIN_EXTENSION_ZLIB
extern int zlib_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
#endif
-#ifdef HAVE_BUILTIN_EXTENSION_LZ4
- extern int lz4_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
+#ifdef HAVE_BUILTIN_EXTENSION_ZSTD
+ extern int zstd_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *);
#endif
/*
@@ -808,14 +811,17 @@ __conn_load_default_extensions(WT_CONNECTION_IMPL *conn)
{
WT_UNUSED(conn);
+#ifdef HAVE_BUILTIN_EXTENSION_LZ4
+ WT_RET(lz4_extension_init(&conn->iface, NULL));
+#endif
#ifdef HAVE_BUILTIN_EXTENSION_SNAPPY
WT_RET(snappy_extension_init(&conn->iface, NULL));
#endif
#ifdef HAVE_BUILTIN_EXTENSION_ZLIB
WT_RET(zlib_extension_init(&conn->iface, NULL));
#endif
-#ifdef HAVE_BUILTIN_EXTENSION_LZ4
- WT_RET(lz4_extension_init(&conn->iface, NULL));
+#ifdef HAVE_BUILTIN_EXTENSION_ZSTD
+ WT_RET(zstd_extension_init(&conn->iface, NULL));
#endif
return (0);
}
@@ -1445,7 +1451,7 @@ __conn_single(WT_SESSION_IMPL *session, const char *cfg[])
WT_FH *fh;
size_t len;
wt_off_t size;
- bool bytelock, exist, is_create;
+ bool bytelock, exist, is_create, match;
char buf[256];
conn = S2C(session);
@@ -1471,13 +1477,14 @@ __conn_single(WT_SESSION_IMPL *session, const char *cfg[])
* won't open a database in multiple threads, but we don't want to have
* it fail the first time, but succeed the second.
*/
+ match = false;
TAILQ_FOREACH(t, &__wt_process.connqh, q)
if (t->home != NULL &&
t != conn && strcmp(t->home, conn->home) == 0) {
- ret = EBUSY;
+ match = true;
break;
}
- if (ret != 0)
+ if (match)
WT_ERR_MSG(session, EBUSY,
"WiredTiger database is already being managed by another "
"thread in this process");
@@ -1668,32 +1675,60 @@ __conn_statistics_config(WT_SESSION_IMPL *session, const char *cfg[])
if ((ret = __wt_config_subgets(
session, &cval, "fast", &sval)) == 0 && sval.val != 0) {
- LF_SET(WT_CONN_STAT_FAST);
+ LF_SET(WT_STAT_TYPE_FAST);
++set;
}
WT_RET_NOTFOUND_OK(ret);
if ((ret = __wt_config_subgets(
session, &cval, "all", &sval)) == 0 && sval.val != 0) {
- LF_SET(WT_CONN_STAT_ALL | WT_CONN_STAT_FAST);
+ LF_SET(
+ WT_STAT_TYPE_ALL | WT_STAT_TYPE_CACHE_WALK |
+ WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK);
++set;
}
WT_RET_NOTFOUND_OK(ret);
+ if (set > 1)
+ WT_RET_MSG(session, EINVAL,
+ "Only one of all, fast, none configuration values should "
+ "be specified");
+
+ /*
+ * Now that we've parsed general statistics categories, process
+ * sub-categories.
+ */
+ if ((ret = __wt_config_subgets(
+ session, &cval, "cache_walk", &sval)) == 0 && sval.val != 0)
+ /*
+ * Configuring cache walk statistics implies fast statistics.
+ * Keep that knowledge internal for now - it may change in the
+ * future.
+ */
+ LF_SET(WT_STAT_TYPE_FAST | WT_STAT_TYPE_CACHE_WALK);
+ WT_RET_NOTFOUND_OK(ret);
+
+ if ((ret = __wt_config_subgets(
+ session, &cval, "tree_walk", &sval)) == 0 && sval.val != 0)
+ /*
+ * Configuring tree walk statistics implies fast statistics.
+ * Keep that knowledge internal for now - it may change in the
+ * future.
+ */
+ LF_SET(WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK);
+ WT_RET_NOTFOUND_OK(ret);
+
if ((ret = __wt_config_subgets(
session, &cval, "clear", &sval)) == 0 && sval.val != 0) {
- if (!LF_ISSET(WT_CONN_STAT_FAST | WT_CONN_STAT_ALL))
+ if (!LF_ISSET(WT_STAT_TYPE_ALL | WT_STAT_TYPE_CACHE_WALK |
+ WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK))
WT_RET_MSG(session, EINVAL,
- "the value \"clear\" can be specified only if "
- "either \"all\" or \"fast\" is specified");
- LF_SET(WT_CONN_STAT_CLEAR);
+ "the value \"clear\" can only be specified if "
+ "statistics are enabled");
+ LF_SET(WT_STAT_CLEAR);
}
WT_RET_NOTFOUND_OK(ret);
- if (set > 1)
- WT_RET_MSG(session, EINVAL,
- "only one statistics configuration value may be specified");
-
/* Configuring statistics clears any existing values. */
conn->stat_flags = flags;
@@ -1943,6 +1978,42 @@ __conn_chk_file_system(WT_SESSION_IMPL *session, bool readonly)
}
/*
+ * wiredtiger_dummy_session_init --
+ * Initialize the connection's dummy session.
+ */
+static void
+wiredtiger_dummy_session_init(
+ WT_CONNECTION_IMPL *conn, WT_EVENT_HANDLER *event_handler)
+{
+ WT_SESSION_IMPL *session;
+
+ session = &conn->dummy_session;
+
+ /*
+ * We use a fake session until we can allocate and initialize the real
+ * ones. Initialize the necessary fields (unfortunately, the fields we
+ * initialize have been selected by core dumps, we need to do better).
+ */
+ session->iface.connection = &conn->iface;
+ session->name = "wiredtiger_open";
+
+ /* Standard I/O and error handling first. */
+ __wt_os_stdio(session);
+ __wt_event_handler_set(session, event_handler);
+
+ /* Statistics */
+ session->stat_bucket = 0;
+
+ /*
+ * Set the default session's strerror method. If one of the extensions
+ * being loaded reports an error via the WT_EXTENSION_API strerror
+ * method, but doesn't supply that method a WT_SESSION handle, we'll
+ * use the WT_CONNECTION_IMPL's default session and its strerror method.
+ */
+ session->iface.strerror = __wt_session_strerror;
+}
+
+/*
* wiredtiger_open --
* Main library entry point: open a new connection to a WiredTiger
* database.
@@ -2013,21 +2084,11 @@ wiredtiger_open(const char *home, WT_EVENT_HANDLER *event_handler,
TAILQ_INSERT_TAIL(&__wt_process.connqh, conn, q);
__wt_spin_unlock(NULL, &__wt_process.spinlock);
- session = conn->default_session = &conn->dummy_session;
- session->iface.connection = &conn->iface;
- session->name = "wiredtiger_open";
-
- /* Do standard I/O and error handling first. */
- __wt_os_stdio(session);
- __wt_event_handler_set(session, event_handler);
-
/*
- * Set the default session's strerror method. If one of the extensions
- * being loaded reports an error via the WT_EXTENSION_API strerror
- * method, but doesn't supply that method a WT_SESSION handle, we'll
- * use the WT_CONNECTION_IMPL's default session and its strerror method.
+ * Initialize the fake session used until we can create real sessions.
*/
- conn->default_session->iface.strerror = __wt_session_strerror;
+ wiredtiger_dummy_session_init(conn, event_handler);
+ session = conn->default_session = &conn->dummy_session;
/* Basic initialization of the connection structure. */
WT_ERR(__wt_connection_init(conn));
diff --git a/src/third_party/wiredtiger/src/conn/conn_cache.c b/src/third_party/wiredtiger/src/conn/conn_cache.c
index 6788b1f7f47..fe5f94ea03d 100644
--- a/src/third_party/wiredtiger/src/conn/conn_cache.c
+++ b/src/third_party/wiredtiger/src/conn/conn_cache.c
@@ -42,47 +42,38 @@ __cache_config_local(WT_SESSION_IMPL *session, bool shared, const char *cfg[])
WT_RET(__wt_config_gets(session, cfg, "eviction_trigger", &cval));
cache->eviction_trigger = (u_int)cval.val;
- if (F_ISSET(conn, WT_CONN_IN_MEMORY))
- cache->eviction_checkpoint_target =
- cache->eviction_dirty_target =
- cache->eviction_dirty_trigger = 100U;
- else {
- WT_RET(__wt_config_gets(
- session, cfg, "eviction_checkpoint_target", &cval));
- cache->eviction_checkpoint_target = (u_int)cval.val;
+ WT_RET(__wt_config_gets(
+ session, cfg, "eviction_checkpoint_target", &cval));
+ cache->eviction_checkpoint_target = (u_int)cval.val;
- WT_RET(__wt_config_gets(
- session, cfg, "eviction_dirty_target", &cval));
- cache->eviction_dirty_target = (u_int)cval.val;
+ WT_RET(__wt_config_gets(session, cfg, "eviction_dirty_target", &cval));
+ cache->eviction_dirty_target = (u_int)cval.val;
- /*
- * Don't allow the dirty target to be larger than the overall
- * target.
- */
- if (cache->eviction_dirty_target > cache->eviction_target)
- cache->eviction_dirty_target = cache->eviction_target;
+ /*
+ * Don't allow the dirty target to be larger than the overall
+ * target.
+ */
+ if (cache->eviction_dirty_target > cache->eviction_target)
+ cache->eviction_dirty_target = cache->eviction_target;
- /*
- * Sanity check the checkpoint target: don't allow a value
- * lower than the dirty target.
- */
- if (cache->eviction_checkpoint_target > 0 &&
- cache->eviction_checkpoint_target <
- cache->eviction_dirty_target)
- cache->eviction_checkpoint_target =
- cache->eviction_dirty_target;
+ /*
+ * Sanity check the checkpoint target: don't allow a value
+ * lower than the dirty target.
+ */
+ if (cache->eviction_checkpoint_target > 0 &&
+ cache->eviction_checkpoint_target < cache->eviction_dirty_target)
+ cache->eviction_checkpoint_target =
+ cache->eviction_dirty_target;
- WT_RET(__wt_config_gets(
- session, cfg, "eviction_dirty_trigger", &cval));
- cache->eviction_dirty_trigger = (u_int)cval.val;
+ WT_RET(__wt_config_gets(session, cfg, "eviction_dirty_trigger", &cval));
+ cache->eviction_dirty_trigger = (u_int)cval.val;
- /*
- * Don't allow the dirty trigger to be larger than the overall
- * trigger or we can get stuck with a cache full of dirty data.
- */
- if (cache->eviction_dirty_trigger > cache->eviction_trigger)
- cache->eviction_dirty_trigger = cache->eviction_trigger;
- }
+ /*
+ * Don't allow the dirty trigger to be larger than the overall
+ * trigger or we can get stuck with a cache full of dirty data.
+ */
+ if (cache->eviction_dirty_trigger > cache->eviction_trigger)
+ cache->eviction_dirty_trigger = cache->eviction_trigger;
WT_RET(__wt_config_gets(session, cfg, "eviction.threads_max", &cval));
WT_ASSERT(session, cval.val > 0);
@@ -192,26 +183,26 @@ __wt_cache_create(WT_SESSION_IMPL *session, const char *cfg[])
* get any work done.
*/
if (cache->eviction_target >= cache->eviction_trigger)
- WT_ERR_MSG(session, EINVAL,
+ WT_RET_MSG(session, EINVAL,
"eviction target must be lower than the eviction trigger");
- WT_ERR(__wt_cond_auto_alloc(session, "cache eviction server",
+ WT_RET(__wt_cond_auto_alloc(session, "cache eviction server",
false, 10000, WT_MILLION, &cache->evict_cond));
- WT_ERR(__wt_spin_init(session, &cache->evict_pass_lock, "evict pass"));
- WT_ERR(__wt_spin_init(session,
+ WT_RET(__wt_spin_init(session, &cache->evict_pass_lock, "evict pass"));
+ WT_RET(__wt_spin_init(session,
&cache->evict_queue_lock, "cache eviction queue"));
- WT_ERR(__wt_spin_init(session, &cache->evict_walk_lock, "cache walk"));
+ WT_RET(__wt_spin_init(session, &cache->evict_walk_lock, "cache walk"));
if ((ret = __wt_open_internal_session(conn, "evict pass",
false, WT_SESSION_NO_DATA_HANDLES, &cache->walk_session)) != 0)
- WT_ERR_MSG(NULL, ret,
+ WT_RET_MSG(NULL, ret,
"Failed to create session for eviction walks");
/* Allocate the LRU eviction queue. */
cache->evict_slots = WT_EVICT_WALK_BASE + WT_EVICT_WALK_INCR;
for (i = 0; i < WT_EVICT_QUEUE_MAX; ++i) {
- WT_ERR(__wt_calloc_def(session,
+ WT_RET(__wt_calloc_def(session,
cache->evict_slots, &cache->evict_queues[i].evict_queue));
- WT_ERR(__wt_spin_init(session,
+ WT_RET(__wt_spin_init(session,
&cache->evict_queues[i].evict_lock, "cache eviction"));
}
@@ -227,9 +218,6 @@ __wt_cache_create(WT_SESSION_IMPL *session, const char *cfg[])
*/
__wt_cache_stats_update(session);
return (0);
-
-err: WT_RET(__wt_cache_destroy(session));
- return (ret);
}
/*
diff --git a/src/third_party/wiredtiger/src/conn/conn_ckpt.c b/src/third_party/wiredtiger/src/conn/conn_ckpt.c
index 451b0cd86f6..1d18c128c5b 100644
--- a/src/third_party/wiredtiger/src/conn/conn_ckpt.c
+++ b/src/third_party/wiredtiger/src/conn/conn_ckpt.c
@@ -89,22 +89,36 @@ __ckpt_server(void *arg)
*/
__wt_cond_wait(session, conn->ckpt_cond, conn->ckpt_usecs);
- /* Checkpoint the database. */
- WT_ERR(wt_session->checkpoint(wt_session, NULL));
-
- /* Reset. */
- if (conn->ckpt_logsize) {
- __wt_log_written_reset(session);
- conn->ckpt_signalled = false;
-
- /*
- * In case we crossed the log limit during the
- * checkpoint and the condition variable was already
- * signalled, do a tiny wait to clear it so we don't do
- * another checkpoint immediately.
- */
- __wt_cond_wait(session, conn->ckpt_cond, 1);
- }
+ /*
+ * Checkpoint the database if the connection is marked dirty.
+ * A connection is marked dirty whenever a btree gets marked
+ * dirty, which reflects upon a change in the database that
+ * needs to be checkpointed. Said that, there can be short
+ * instances when a btree gets marked dirty and the connection
+ * is yet to be. We might skip a checkpoint in that short
+ * instance, which is okay because by the next time we get to
+ * checkpoint, the connection would have been marked dirty and
+ * hence the checkpoint will not be skipped this time.
+ */
+ if (conn->modified) {
+ WT_ERR(wt_session->checkpoint(wt_session, NULL));
+
+ /* Reset. */
+ if (conn->ckpt_logsize) {
+ __wt_log_written_reset(session);
+ conn->ckpt_signalled = false;
+
+ /*
+ * In case we crossed the log limit during the
+ * checkpoint and the condition variable was
+ * already signalled, do a tiny wait to clear
+ * it so we don't do another checkpoint
+ * immediately.
+ */
+ __wt_cond_wait(session, conn->ckpt_cond, 1);
+ }
+ } else
+ WT_STAT_CONN_INCR(session, txn_checkpoint_skipped);
}
if (0) {
diff --git a/src/third_party/wiredtiger/src/conn/conn_dhandle.c b/src/third_party/wiredtiger/src/conn/conn_dhandle.c
index ec850c793cc..ac72e330b67 100644
--- a/src/third_party/wiredtiger/src/conn/conn_dhandle.c
+++ b/src/third_party/wiredtiger/src/conn/conn_dhandle.c
@@ -20,6 +20,7 @@ __conn_dhandle_destroy(WT_SESSION_IMPL *session, WT_DATA_HANDLE *dhandle)
__wt_free(session, dhandle->checkpoint);
__wt_free(session, dhandle->handle);
__wt_spin_destroy(session, &dhandle->close_lock);
+ __wt_stat_dsrc_discard(session, dhandle);
__wt_overwrite_and_free(session, dhandle);
}
@@ -54,8 +55,6 @@ __conn_dhandle_alloc(WT_SESSION_IMPL *session,
WT_ERR(__wt_spin_init(
session, &dhandle->close_lock, "data handle close"));
- __wt_stat_dsrc_init(dhandle);
-
if (strcmp(uri, WT_METAFILE_URI) == 0)
F_SET(dhandle, WT_DHANDLE_IS_METADATA);
@@ -310,16 +309,15 @@ __wt_conn_btree_open(
WT_ASSERT(session, !F_ISSET(S2C(session), WT_CONN_CLOSING));
/*
- * If the handle is already open, it has to be closed so it can
- * be reopened with a new configuration.
+ * If the handle is already open, it has to be closed so it can be
+ * reopened with a new configuration.
*
- * This call can return EBUSY if there's an update in the
- * object that's not yet globally visible. That's not a
- * problem because it can only happen when we're switching from
- * a normal handle to a "special" one, so we're returning EBUSY
- * to an attempt to verify or do other special operations. The
- * reverse won't happen because when the handle from a verify
- * or other special operation is closed, there won't be updates
+ * This call can return EBUSY if there's an update in the object that's
+ * not yet globally visible. That's not a problem because it can only
+ * happen when we're switching from a normal handle to a "special" one,
+ * so we're returning EBUSY to an attempt to verify or do other special
+ * operations. The reverse won't happen because when the handle from a
+ * verify or other special operation is closed, there won't be updates
* in the tree that can block the close.
*/
if (F_ISSET(dhandle, WT_DHANDLE_OPEN))
@@ -332,6 +330,16 @@ __wt_conn_btree_open(
/* Set any special flags on the handle. */
F_SET(btree, LF_MASK(WT_BTREE_SPECIAL_FLAGS));
+ /*
+ * Allocate data-source statistics memory. We don't allocate that memory
+ * when allocating the data-handle because not all data handles need
+ * statistics (for example, handles used for checkpoint locking). If we
+ * are reopening the handle, then it may already have statistics memory,
+ * check to avoid the leak.
+ */
+ if (dhandle->stat_array == NULL)
+ WT_ERR(__wt_stat_dsrc_init(session, dhandle));
+
WT_ERR(__wt_btree_open(session, cfg));
/*
@@ -554,8 +562,7 @@ __wt_conn_dhandle_discard_single(
dhandle = session->dhandle;
- if (F_ISSET(dhandle, WT_DHANDLE_OPEN) ||
- (final && F_ISSET(dhandle, WT_DHANDLE_DEAD))) {
+ if (F_ISSET(dhandle, WT_DHANDLE_OPEN)) {
tret = __wt_conn_btree_sync_and_close(session, final, force);
if (final && tret != 0) {
__wt_err(session, tret,
diff --git a/src/third_party/wiredtiger/src/conn/conn_handle.c b/src/third_party/wiredtiger/src/conn/conn_handle.c
index 5ff8b7f798b..3571cc60115 100644
--- a/src/third_party/wiredtiger/src/conn/conn_handle.c
+++ b/src/third_party/wiredtiger/src/conn/conn_handle.c
@@ -48,23 +48,25 @@ __wt_connection_init(WT_CONNECTION_IMPL *conn)
WT_RET(__wt_conn_config_init(session));
/* Statistics. */
- __wt_stat_connection_init(conn);
+ WT_RET(__wt_stat_connection_init(session, conn));
- /* Locks. */
+ /* Spinlocks. */
WT_RET(__wt_spin_init(session, &conn->api_lock, "api"));
- WT_RET(__wt_spin_init(session, &conn->checkpoint_lock, "checkpoint"));
- WT_RET(__wt_spin_init(session, &conn->dhandle_lock, "data handle"));
+ WT_SPIN_INIT_TRACKED(session, &conn->checkpoint_lock, checkpoint);
+ WT_SPIN_INIT_TRACKED(session, &conn->dhandle_lock, handle_list);
WT_RET(__wt_spin_init(session, &conn->encryptor_lock, "encryptor"));
WT_RET(__wt_spin_init(session, &conn->fh_lock, "file list"));
- WT_RET(__wt_rwlock_alloc(session,
- &conn->hot_backup_lock, "hot backup"));
WT_RET(__wt_spin_init(session, &conn->las_lock, "lookaside table"));
- WT_RET(__wt_spin_init(session, &conn->metadata_lock, "metadata"));
+ WT_SPIN_INIT_TRACKED(session, &conn->metadata_lock, metadata);
WT_RET(__wt_spin_init(session, &conn->reconfig_lock, "reconfigure"));
- WT_RET(__wt_spin_init(session, &conn->schema_lock, "schema"));
- WT_RET(__wt_spin_init(session, &conn->table_lock, "table creation"));
+ WT_SPIN_INIT_TRACKED(session, &conn->schema_lock, schema);
+ WT_SPIN_INIT_TRACKED(session, &conn->table_lock, table);
WT_RET(__wt_spin_init(session, &conn->turtle_lock, "turtle file"));
+ /* Read-write locks */
+ WT_RET(__wt_rwlock_alloc(
+ session, &conn->hot_backup_lock, "hot backup"));
+
WT_RET(__wt_calloc_def(session, WT_PAGE_LOCKS, &conn->page_lock));
WT_CACHE_LINE_ALIGNMENT_VERIFY(session, conn->page_lock);
for (i = 0; i < WT_PAGE_LOCKS; ++i)
@@ -159,6 +161,7 @@ __wt_connection_destroy(WT_CONNECTION_IMPL *conn)
__wt_free(session, conn->home);
__wt_free(session, conn->error_prefix);
__wt_free(session, conn->sessions);
+ __wt_stat_connection_discard(session, conn);
__wt_free(NULL, conn);
return (ret);
diff --git a/src/third_party/wiredtiger/src/conn/conn_log.c b/src/third_party/wiredtiger/src/conn/conn_log.c
index 2786526c2fa..8198b3a1a02 100644
--- a/src/third_party/wiredtiger/src/conn/conn_log.c
+++ b/src/third_party/wiredtiger/src/conn/conn_log.c
@@ -87,14 +87,16 @@ __logmgr_config(
if (reconfig &&
((enabled && !FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED)) ||
(!enabled && FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))))
- return (EINVAL);
+ WT_RET_MSG(session, EINVAL,
+ "log manager reconfigure: enabled mismatch with existing "
+ "setting");
/* Logging is incompatible with in-memory */
if (enabled) {
WT_RET(__wt_config_gets(session, cfg, "in_memory", &cval));
if (cval.val != 0)
WT_RET_MSG(session, EINVAL,
- "In memory configuration incompatible with "
+ "In-memory configuration incompatible with "
"log=(enabled=true)");
}
@@ -137,8 +139,7 @@ __logmgr_config(
if (!reconfig) {
WT_RET(__wt_config_gets(session, cfg, "log.file_max", &cval));
conn->log_file_max = (wt_off_t)cval.val;
- WT_STAT_CONN_SET(session,
- log_max_filesize, conn->log_file_max);
+ WT_STAT_CONN_SET(session, log_max_filesize, conn->log_file_max);
}
/*
@@ -839,10 +840,10 @@ __log_server(void *arg)
/* Wait until the next event. */
- WT_ERR(__wt_epoch(session, &start));
+ __wt_epoch(session, &start);
__wt_cond_auto_wait_signal(session,
conn->log_cond, did_work, &signalled);
- WT_ERR(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
timediff = WT_TIMEDIFF_MS(now, start);
}
diff --git a/src/third_party/wiredtiger/src/conn/conn_stat.c b/src/third_party/wiredtiger/src/conn/conn_stat.c
index 66979dfd023..3bcdfd7ecb1 100644
--- a/src/third_party/wiredtiger/src/conn/conn_stat.c
+++ b/src/third_party/wiredtiger/src/conn/conn_stat.c
@@ -130,12 +130,12 @@ __statlog_config(WT_SESSION_IMPL *session, const char **cfg, bool *runp)
WT_RET(__wt_config_gets(session, cfg, "statistics_log.json", &cval));
if (cval.val != 0)
- FLD_SET(conn->stat_flags, WT_CONN_STAT_JSON);
+ FLD_SET(conn->stat_flags, WT_STAT_JSON);
WT_RET(__wt_config_gets(
session, cfg, "statistics_log.on_close", &cval));
if (cval.val != 0)
- FLD_SET(conn->stat_flags, WT_CONN_STAT_ON_CLOSE);
+ FLD_SET(conn->stat_flags, WT_STAT_ON_CLOSE);
/*
* We don't allow the log path to be reconfigured for security reasons.
@@ -206,7 +206,7 @@ __statlog_config(WT_SESSION_IMPL *session, const char **cfg, bool *runp)
#define WT_TIMESTAMP_JSON_DEFAULT "%Y-%m-%dT%H:%M:%S.000Z"
WT_ERR(__wt_config_gets(
session, cfg, "statistics_log.timestamp", &cval));
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_JSON) &&
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_JSON) &&
WT_STRING_MATCH(WT_TIMESTAMP_DEFAULT, cval.str, cval.len))
WT_ERR(__wt_strdup(
session, WT_TIMESTAMP_JSON_DEFAULT, &conn->stat_format));
@@ -264,7 +264,7 @@ __statlog_dump(WT_SESSION_IMPL *session, const char *name, bool conn_stats)
goto err;
}
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_JSON)) {
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_JSON)) {
WT_ERR(__wt_fprintf(session, conn->stat_fs,
"{\"version\":\"%s\",\"localTime\":\"%s\"",
WIREDTIGER_VERSION_STRING, conn->stat_stamp));
@@ -415,7 +415,7 @@ __statlog_log_one(WT_SESSION_IMPL *session, WT_ITEM *path, WT_ITEM *tmp)
conn = S2C(session);
/* Get the current local time of day. */
- WT_RET(__wt_epoch(session, &ts));
+ __wt_epoch(session, &ts);
tm = localtime_r(&ts.tv_sec, &_tm);
/* Create the logging path name for this time of day. */
@@ -469,12 +469,12 @@ __statlog_log_one(WT_SESSION_IMPL *session, WT_ITEM *path, WT_ITEM *tmp)
}
/*
- * __wt_statlog_log_one --
- * Log a set of statistics into the configured statistics log. Requires
- * that the server is not currently running.
+ * __statlog_on_close --
+ * Log a set of statistics at close. Requires the server is not currently
+ * running.
*/
-int
-__wt_statlog_log_one(WT_SESSION_IMPL *session)
+static int
+__statlog_on_close(WT_SESSION_IMPL *session)
{
WT_CONNECTION_IMPL *conn;
WT_DECL_RET;
@@ -482,7 +482,7 @@ __wt_statlog_log_one(WT_SESSION_IMPL *session)
conn = S2C(session);
- if (!FLD_ISSET(conn->stat_flags, WT_CONN_STAT_ON_CLOSE))
+ if (!FLD_ISSET(conn->stat_flags, WT_STAT_ON_CLOSE))
return (0);
if (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
@@ -643,7 +643,7 @@ __wt_statlog_destroy(WT_SESSION_IMPL *session, bool is_close)
/* Log a set of statistics on shutdown if configured. */
if (is_close)
- WT_TRET(__wt_statlog_log_one(session));
+ WT_TRET(__statlog_on_close(session));
/* Discard all configuration information. */
WT_TRET(__stat_config_discard(session));
diff --git a/src/third_party/wiredtiger/src/conn/conn_sweep.c b/src/third_party/wiredtiger/src/conn/conn_sweep.c
index 03593f8951a..dba37fa2eb0 100644
--- a/src/third_party/wiredtiger/src/conn/conn_sweep.c
+++ b/src/third_party/wiredtiger/src/conn/conn_sweep.c
@@ -271,7 +271,7 @@ __sweep_server(void *arg)
/* Wait until the next event. */
__wt_cond_wait(session,
conn->sweep_cond, conn->sweep_interval * WT_MILLION);
- WT_ERR(__wt_seconds(session, &now));
+ __wt_seconds(session, &now);
WT_STAT_CONN_INCR(session, dh_sweeps);
diff --git a/src/third_party/wiredtiger/src/cursor/cur_backup.c b/src/third_party/wiredtiger/src/cursor/cur_backup.c
index 3a3ff7de92b..3585082644f 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_backup.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_backup.c
@@ -31,13 +31,13 @@ __curbackup_next(WT_CURSOR *cursor)
cb = (WT_CURSOR_BACKUP *)cursor;
CURSOR_API_CALL(cursor, session, next, NULL);
- if (cb->list == NULL || cb->list[cb->next].name == NULL) {
+ if (cb->list == NULL || cb->list[cb->next] == NULL) {
F_CLR(cursor, WT_CURSTD_KEY_SET);
WT_ERR(WT_NOTFOUND);
}
- cb->iface.key.data = cb->list[cb->next].name;
- cb->iface.key.size = strlen(cb->list[cb->next].name) + 1;
+ cb->iface.key.data = cb->list[cb->next];
+ cb->iface.key.size = strlen(cb->list[cb->next]) + 1;
++cb->next;
F_SET(cursor, WT_CURSTD_KEY_INT);
@@ -226,9 +226,13 @@ __backup_start(
* holds the lock until it's finished the checkpoint, otherwise we
* could start a hot backup that would race with an already-started
* checkpoint.
+ *
+ * We are holding the checkpoint and schema locks so schema operations
+ * will not see the backup file list until it is complete and valid.
*/
__wt_writelock(session, conn->hot_backup_lock);
conn->hot_backup = true;
+ conn->hot_backup_list = NULL;
__wt_writeunlock(session, conn->hot_backup_lock);
/* We're the lock holder, we own cleanup. */
@@ -293,37 +297,15 @@ err: /* Close the hot backup file. */
if (ret == 0) {
WT_ASSERT(session, dest != NULL);
WT_TRET(__wt_fs_rename(session, WT_BACKUP_TMP, dest, false));
+ __wt_writelock(session, conn->hot_backup_lock);
+ conn->hot_backup_list = cb->list;
+ __wt_writeunlock(session, conn->hot_backup_lock);
}
return (ret);
}
/*
- * __backup_cleanup_handles --
- * Release and free all btree handles held by the backup.
- */
-static int
-__backup_cleanup_handles(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb)
-{
- WT_CURSOR_BACKUP_ENTRY *p;
- WT_DECL_RET;
-
- if (cb->list == NULL)
- return (0);
-
- /* Release the handles, free the file names, free the list itself. */
- for (p = cb->list; p->name != NULL; ++p) {
- if (p->handle != NULL)
- WT_WITH_DHANDLE(session, p->handle,
- WT_TRET(__wt_session_release_btree(session)));
- __wt_free(session, p->name);
- }
-
- __wt_free(session, cb->list);
- return (ret);
-}
-
-/*
* __backup_stop --
* Stop a backup.
*/
@@ -332,11 +314,19 @@ __backup_stop(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb)
{
WT_CONNECTION_IMPL *conn;
WT_DECL_RET;
+ int i;
conn = S2C(session);
- /* Release all btree handles held by the backup. */
- WT_TRET(__backup_cleanup_handles(session, cb));
+ /* Release all btree names held by the backup. */
+ __wt_writelock(session, conn->hot_backup_lock);
+ conn->hot_backup_list = NULL;
+ __wt_writeunlock(session, conn->hot_backup_lock);
+ if (cb->list != NULL) {
+ for (i = 0; cb->list[i] != NULL; ++i)
+ __wt_free(session, cb->list[i]);
+ __wt_free(session, cb->list);
+ }
/* Remove any backup specific file. */
WT_TRET(__wt_backup_file_remove(session));
@@ -513,40 +503,23 @@ static int
__backup_list_append(
WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *uri)
{
- WT_CURSOR_BACKUP_ENTRY *p;
- WT_DATA_HANDLE *old_dhandle;
- WT_DECL_RET;
+ char **p;
const char *name;
/* Leave a NULL at the end to mark the end of the list. */
WT_RET(__wt_realloc_def(session, &cb->list_allocated,
cb->list_next + 2, &cb->list));
p = &cb->list[cb->list_next];
- p[0].name = p[1].name = NULL;
- p[0].handle = p[1].handle = NULL;
+ p[0] = p[1] = NULL;
name = uri;
/*
- * If it's a file in the database, get a handle for the underlying
- * object (this handle blocks schema level operations, for example
- * WT_SESSION.drop or an LSM file discard after level merging).
- *
- * If the handle is busy (e.g., it is being bulk-loaded), silently skip
- * it. We have a special fake checkpoint in the metadata, and recovery
- * will recreate an empty file.
+ * If it's a file in the database we need to remove the prefix.
*/
- if (WT_PREFIX_MATCH(uri, "file:")) {
+ if (WT_PREFIX_MATCH(uri, "file:"))
name += strlen("file:");
- old_dhandle = session->dhandle;
- ret = __wt_session_get_btree(session, uri, NULL, NULL, 0);
- p->handle = session->dhandle;
- session->dhandle = old_dhandle;
- if (ret != 0)
- return (ret == EBUSY ? 0 : ret);
- }
-
/*
* !!!
* Assumes metadata file entries map one-to-one to physical files.
@@ -556,7 +529,7 @@ __backup_list_append(
* that for now, that block manager might not even support physical
* copying of files by applications.
*/
- WT_RET(__wt_strdup(session, name, &p->name));
+ WT_RET(__wt_strdup(session, name, p));
++cb->list_next;
return (0);
diff --git a/src/third_party/wiredtiger/src/cursor/cur_file.c b/src/third_party/wiredtiger/src/cursor/cur_file.c
index e304cf7b775..9fc466f4c76 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_file.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_file.c
@@ -117,12 +117,12 @@ err: API_END_RET(session, ret);
}
/*
- * __curfile_next_random --
+ * __wt_curfile_next_random --
* WT_CURSOR->next method for the btree cursor type when configured with
- * next_random.
+ * next_random. This is exported because it is called directly within LSM.
*/
-static int
-__curfile_next_random(WT_CURSOR *cursor)
+int
+__wt_curfile_next_random(WT_CURSOR *cursor)
{
WT_CURSOR_BTREE *cbt;
WT_DECL_RET;
@@ -473,7 +473,7 @@ __curfile_create(WT_SESSION_IMPL *session,
"column-store objects");
__wt_cursor_set_notsup(cursor);
- cursor->next = __curfile_next_random;
+ cursor->next = __wt_curfile_next_random;
cursor->reset = __curfile_reset;
WT_ERR(__wt_config_gets_def(
diff --git a/src/third_party/wiredtiger/src/cursor/cur_index.c b/src/third_party/wiredtiger/src/cursor/cur_index.c
index ea742cac435..eb5e15ae5c3 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_index.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_index.c
@@ -38,7 +38,9 @@ __curindex_set_value(WT_CURSOR *cursor, ...)
WT_SESSION_IMPL *session;
JOINABLE_CURSOR_API_CALL(cursor, session, set_value, NULL);
- ret = ENOTSUP;
+ WT_ERR_MSG(session, ENOTSUP,
+ "WT_CURSOR.set_value not supported for index cursors");
+
err: cursor->saved_err = ret;
F_CLR(cursor, WT_CURSTD_VALUE_SET);
API_END(session, ret);
diff --git a/src/third_party/wiredtiger/src/cursor/cur_join.c b/src/third_party/wiredtiger/src/cursor/cur_join.c
index 087411febda..806436ebf38 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_join.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_join.c
@@ -38,8 +38,8 @@ __wt_curjoin_joined(WT_CURSOR *cursor)
WT_SESSION_IMPL *session;
session = (WT_SESSION_IMPL *)cursor->session;
- __wt_errx(session, "cursor is being used in a join");
- return (ENOTSUP);
+
+ WT_RET_MSG(session, ENOTSUP, "cursor is being used in a join");
}
/*
@@ -613,8 +613,8 @@ __curjoin_entry_member(WT_SESSION_IMPL *session, WT_CURSOR_JOIN_ENTRY *entry,
if (entry->bloom != NULL) {
/*
* If the item is not in the Bloom filter, we return
- * immediately, otherwise, we still need to check the long
- * way, since it may be a false positive.
+ * immediately, otherwise, we still may need to check the
+ * long way, since it may be a false positive.
*
* If we don't own the Bloom filter, we must be sharing one
* in a previous entry. So the shared filter has already
@@ -623,6 +623,8 @@ __curjoin_entry_member(WT_SESSION_IMPL *session, WT_CURSOR_JOIN_ENTRY *entry,
*/
if (F_ISSET(entry, WT_CURJOIN_ENTRY_OWN_BLOOM))
WT_ERR(__wt_bloom_inmem_get(entry->bloom, key));
+ if (F_ISSET(entry, WT_CURJOIN_ENTRY_FALSE_POSITIVES))
+ return (0);
bloom_found = true;
}
if (entry->subjoin != NULL) {
@@ -671,6 +673,8 @@ __curjoin_entry_member(WT_SESSION_IMPL *session, WT_CURSOR_JOIN_ENTRY *entry,
extract_cursor.entry = entry;
WT_ERR(idx->extractor->extract(idx->extractor,
&session->iface, key, &v, &extract_cursor.iface));
+ __wt_buf_free(session, &extract_cursor.iface.key);
+ __wt_buf_free(session, &extract_cursor.iface.value);
if (!extract_cursor.ismember)
WT_ERR(WT_NOTFOUND);
} else
@@ -1302,11 +1306,15 @@ __wt_curjoin_open(WT_SESSION_IMPL *session,
WT_STATIC_ASSERT(offsetof(WT_CURSOR_JOIN, iface) == 0);
+ if (owner != NULL)
+ WT_RET_MSG(session, EINVAL,
+ "unable to initialize a join cursor with existing owner");
+
if (!WT_PREFIX_SKIP(uri, "join:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, uri, "join:"));
tablename = uri;
if (!WT_PREFIX_SKIP(tablename, "table:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, uri, "table:"));
columns = strchr(tablename, '(');
if (columns == NULL)
@@ -1334,9 +1342,6 @@ __wt_curjoin_open(WT_SESSION_IMPL *session,
WT_ERR(__wt_strdup(session, columns, &cjoin->projection));
}
- if (owner != NULL)
- WT_ERR(EINVAL);
-
WT_ERR(__wt_cursor_init(cursor, uri, owner, cfg, cursorp));
if (0) {
@@ -1442,6 +1447,11 @@ __wt_curjoin_join(WT_SESSION_IMPL *session, WT_CURSOR_JOIN *cjoin,
WT_RET_MSG(session, EINVAL,
"join has incompatible strategy "
"values for the same index");
+ if (LF_MASK(WT_CURJOIN_ENTRY_FALSE_POSITIVES) !=
+ F_MASK(entry, WT_CURJOIN_ENTRY_FALSE_POSITIVES))
+ WT_RET_MSG(session, EINVAL,
+ "join has incompatible bloom_false_positives "
+ "values for the same index");
/*
* Check against other comparisons (we call them endpoints)
diff --git a/src/third_party/wiredtiger/src/cursor/cur_json.c b/src/third_party/wiredtiger/src/cursor/cur_json.c
index bb24e88cba5..093ec3c59ac 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_json.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_json.c
@@ -492,11 +492,9 @@ __wt_json_token(WT_SESSION *wt_session, const char *src, int *toktype,
uc = (const u_char *)src;
if (__wt_hex2byte(&uc[1], &ignored) ||
- __wt_hex2byte(&uc[3], &ignored)) {
- __wt_errx(session,
+ __wt_hex2byte(&uc[3], &ignored))
+ WT_RET_MSG(session, EINVAL,
"invalid Unicode within JSON string");
- return (-1);
- }
src += 4;
}
backslash = false;
@@ -569,7 +567,10 @@ __wt_json_token(WT_SESSION *wt_session, const char *src, int *toktype,
}
*toklen = (size_t)(src - *tokstart);
*toktype = result;
- return (result < 0 ? EINVAL : 0);
+
+ if (result < 0)
+ WT_RET_MSG(session, EINVAL, "illegal token in JSON");
+ return (0);
}
/*
@@ -606,24 +607,20 @@ __wt_json_tokname(int toktype)
static int
json_string_arg(WT_SESSION_IMPL *session, const char **jstr, WT_ITEM *item)
{
- WT_DECL_RET;
int tok;
const char *tokstart;
- WT_RET(__wt_json_token((WT_SESSION *)session, *jstr, &tok, &tokstart,
- &item->size));
+ WT_RET(__wt_json_token(
+ (WT_SESSION *)session, *jstr, &tok, &tokstart, &item->size));
if (tok == 's') {
*jstr = tokstart + item->size;
/* The tokenizer includes the '"' chars */
item->data = tokstart + 1;
item->size -= 2;
- ret = 0;
- } else {
- __wt_errx(session, "expected JSON <string>, got %s",
- __wt_json_tokname(tok));
- ret = EINVAL;
- }
- return (ret);
+ } else
+ WT_RET_MSG(session, EINVAL,
+ "expected JSON <string>, got %s", __wt_json_tokname(tok));
+ return (0);
}
/*
@@ -648,11 +645,9 @@ json_int_arg(WT_SESSION_IMPL *session, const char **jstr, int64_t *ip)
WT_RET_MSG(session, EINVAL,
"JSON <int> extraneous input");
*jstr = tokstart + toksize;
- } else {
- __wt_errx(session, "expected JSON <int>, got %s",
- __wt_json_tokname(tok));
- return (EINVAL);
- }
+ } else
+ WT_RET_MSG(session, EINVAL,
+ "expected JSON <int>, got %s", __wt_json_tokname(tok));
return (0);
}
@@ -678,22 +673,20 @@ json_uint_arg(WT_SESSION_IMPL *session, const char **jstr, uint64_t *up)
WT_RET_MSG(session, EINVAL,
"JSON <int> extraneous input");
*jstr = tokstart + toksize;
- } else {
- __wt_errx(session, "expected unsigned JSON <int>, got %s",
+ } else
+ WT_RET_MSG(session, EINVAL,
+ "expected unsigned JSON <int>, got %s",
__wt_json_tokname(tok));
- return (EINVAL);
- }
return (0);
}
#define JSON_EXPECT_TOKEN_GET(session, jstr, tokval, start, sz) do { \
int __tok; \
WT_RET(__wt_json_token((WT_SESSION *)session, jstr, &__tok, &start, &sz));\
- if (__tok != tokval) { \
- __wt_errx(session, "expected JSON %s, got %s", \
+ if (__tok != tokval) \
+ WT_RET_MSG(session, EINVAL, \
+ "expected JSON %s, got %s", \
__wt_json_tokname(tokval), __wt_json_tokname(__tok)); \
- return (EINVAL); \
- } \
jstr = start + sz; \
} while (0)
@@ -782,11 +775,10 @@ __json_pack_size(
JSON_EXPECT_TOKEN_GET(session, jstr, 's', tokstart, toksize);
WT_RET(__pack_name_next(&packname, &name));
if (toksize - 2 != name.len ||
- strncmp(tokstart + 1, name.str, toksize - 2) != 0) {
- __wt_errx(session, "JSON expected %s name: \"%.*s\"",
+ strncmp(tokstart + 1, name.str, toksize - 2) != 0)
+ WT_RET_MSG(session, EINVAL,
+ "JSON expected %s name: \"%.*s\"",
iskey ? "key" : "value", (int)name.len, name.str);
- return (EINVAL);
- }
JSON_EXPECT_TOKEN(session, jstr, ':');
WT_PACK_JSON_GET(session, pv, jstr);
total += __pack_size(session, &pv);
@@ -861,9 +853,8 @@ __wt_json_strlen(const char *src, size_t srclen)
/*
* __wt_json_strncpy --
- * Copy bytes of string in JSON format to a destination,
- * up to dstlen bytes. If dstlen is greater than the needed size,
- * the result if zero padded.
+ * Copy bytes of string in JSON format to a destination, up to dstlen
+ * bytes. If dstlen is greater than the needed size, the result if zero padded.
*/
int
__wt_json_strncpy(WT_SESSION *wt_session, char **pdst, size_t dstlen,
@@ -884,18 +875,16 @@ __wt_json_strncpy(WT_SESSION *wt_session, char **pdst, size_t dstlen,
if ((ch = *src++) == '\\')
switch (ch = *src++) {
case 'u':
- if (__wt_hex2byte((const u_char *)src, &hi))
- return (EINVAL);
- src += 2;
- if (__wt_hex2byte((const u_char *)src, &lo))
- return (EINVAL);
- src += 2;
- if (hi != 0) {
- __wt_errx(NULL, "Unicode \"%6.6s\""
- " byte out of range in JSON",
+ if (__wt_hex2byte((const u_char *)src, &hi) ||
+ __wt_hex2byte((const u_char *)src + 2, &lo))
+ WT_RET_MSG(session, EINVAL,
+ "invalid Unicode within JSON string");
+ src += 4;
+ if (hi != 0)
+ WT_RET_MSG(session, EINVAL,
+ "Unicode \"%6.6s\" byte out of "
+ "range in JSON",
src - 6);
- return (EINVAL);
- }
*dst++ = (char)lo;
break;
case 'f':
@@ -920,7 +909,8 @@ __wt_json_strncpy(WT_SESSION *wt_session, char **pdst, size_t dstlen,
*dst++ = ch;
}
if (src != srcend)
- return (ENOMEM);
+ WT_RET_MSG(session,
+ ENOMEM, "JSON string copy destination buffer too small");
*pdst = dst;
while (dst < dstend)
*dst++ = '\0';
diff --git a/src/third_party/wiredtiger/src/cursor/cur_metadata.c b/src/third_party/wiredtiger/src/cursor/cur_metadata.c
index fd00acdf0ab..10e2fdf28be 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_metadata.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_metadata.c
@@ -48,7 +48,10 @@ __schema_source_config(WT_SESSION_IMPL *session,
WT_ERR(__wt_buf_fmt(session, buf, "%.*s", (int)cval.len, cval.str));
srch->set_key(srch, buf->data);
if ((ret = srch->search(srch)) == WT_NOTFOUND)
- WT_ERR(EINVAL);
+ WT_ERR_MSG(session, EINVAL,
+ "metadata information for source configuration \"%s\" "
+ "not found",
+ (char *)buf->data);
WT_ERR(ret);
WT_ERR(srch->get_value(srch, &v));
WT_ERR(__wt_strdup(session, v, result));
diff --git a/src/third_party/wiredtiger/src/cursor/cur_stat.c b/src/third_party/wiredtiger/src/cursor/cur_stat.c
index 700cc366ff0..5fde64c74ca 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_stat.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_stat.c
@@ -354,7 +354,7 @@ __curstat_conn_init(WT_SESSION_IMPL *session, WT_CURSOR_STAT *cst)
*/
__wt_conn_stat_init(session);
__wt_stat_connection_aggregate(conn->stats, &cst->u.conn_stats);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
__wt_stat_connection_clear_all(conn->stats);
cst->stats = (int64_t *)&cst->u.conn_stats;
@@ -380,10 +380,11 @@ __curstat_file_init(WT_SESSION_IMPL *session,
* If we are only getting the size of the file, we don't need to open
* the tree.
*/
- if (F_ISSET(cst, WT_CONN_STAT_SIZE)) {
+ if (F_ISSET(cst, WT_STAT_TYPE_SIZE)) {
filename = uri;
if (!WT_PREFIX_SKIP(filename, "file:"))
- return (EINVAL);
+ return (
+ __wt_unexpected_object_type(session, uri, "file:"));
__wt_stat_dsrc_init_single(&cst->u.dsrc_stats);
WT_RET(__wt_block_manager_named_size(session, filename, &size));
cst->u.dsrc_stats.block_size = size;
@@ -401,7 +402,7 @@ __curstat_file_init(WT_SESSION_IMPL *session,
if ((ret = __wt_btree_stat_init(session, cst)) == 0) {
__wt_stat_dsrc_init_single(&cst->u.dsrc_stats);
__wt_stat_dsrc_aggregate(dhandle->stats, &cst->u.dsrc_stats);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
__wt_stat_dsrc_clear_all(dhandle->stats);
__wt_curstat_dsrc_final(cst);
}
@@ -604,50 +605,79 @@ __wt_curstat_open(WT_SESSION_IMPL *session,
if ((ret = __wt_config_gets(session, cfg, "statistics", &cval)) == 0) {
if ((ret = __wt_config_subgets(
session, &cval, "all", &sval)) == 0 && sval.val != 0) {
- if (!FLD_ISSET(conn->stat_flags, WT_CONN_STAT_ALL))
+ if (!FLD_ISSET(conn->stat_flags, WT_STAT_TYPE_ALL))
goto config_err;
- F_SET(cst, WT_CONN_STAT_ALL | WT_CONN_STAT_FAST);
+ F_SET(cst, WT_STAT_TYPE_ALL | WT_STAT_TYPE_CACHE_WALK |
+ WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK);
}
WT_ERR_NOTFOUND_OK(ret);
if ((ret = __wt_config_subgets(
session, &cval, "fast", &sval)) == 0 && sval.val != 0) {
- if (F_ISSET(cst, WT_CONN_STAT_ALL))
+ if (F_ISSET(cst, WT_STAT_TYPE_ALL))
WT_ERR_MSG(session, EINVAL,
- "only one statistics configuration value "
- "may be specified");
- F_SET(cst, WT_CONN_STAT_FAST);
+ "Only one of all, fast, none "
+ "configuration values should be specified");
+ F_SET(cst, WT_STAT_TYPE_FAST);
}
WT_ERR_NOTFOUND_OK(ret);
+
+ if ((ret = __wt_config_subgets(session,
+ &cval, "cache_walk", &sval)) == 0 && sval.val != 0) {
+ /*
+ * Configuring cache walk statistics implies fast
+ * statistics. Keep that knowledge internal for now -
+ * it may change in the future.
+ */
+ F_SET(cst, WT_STAT_TYPE_CACHE_WALK | WT_STAT_TYPE_FAST);
+ }
+ WT_ERR_NOTFOUND_OK(ret);
+
+ if ((ret = __wt_config_subgets(session,
+ &cval, "tree_walk", &sval)) == 0 && sval.val != 0) {
+ /*
+ * Configuring tree walk statistics implies fast
+ * statistics. Keep that knowledge internal for now -
+ * it may change in the future.
+ */
+ F_SET(cst, WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK);
+ }
+ WT_ERR_NOTFOUND_OK(ret);
+
if ((ret = __wt_config_subgets(
session, &cval, "size", &sval)) == 0 && sval.val != 0) {
- if (F_ISSET(cst, WT_CONN_STAT_FAST | WT_CONN_STAT_ALL))
+ if (F_ISSET(cst, WT_STAT_TYPE_FAST | WT_STAT_TYPE_ALL))
WT_ERR_MSG(session, EINVAL,
- "only one statistics configuration value "
- "may be specified");
- F_SET(cst, WT_CONN_STAT_SIZE);
+ "Only one of all, fast, none "
+ "configuration values should be specified");
+ F_SET(cst, WT_STAT_TYPE_SIZE);
}
WT_ERR_NOTFOUND_OK(ret);
if ((ret = __wt_config_subgets(
session, &cval, "clear", &sval)) == 0 && sval.val != 0) {
- if (F_ISSET(cst, WT_CONN_STAT_SIZE))
+ if (F_ISSET(cst, WT_STAT_TYPE_SIZE))
WT_ERR_MSG(session, EINVAL,
"clear is incompatible with size "
"statistics");
- F_SET(cst, WT_CONN_STAT_CLEAR);
+ F_SET(cst, WT_STAT_CLEAR);
}
WT_ERR_NOTFOUND_OK(ret);
/* If no configuration, use the connection's configuration. */
if (cst->flags == 0) {
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_ALL))
- F_SET(cst, WT_CONN_STAT_ALL);
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_FAST))
- F_SET(cst, WT_CONN_STAT_FAST);
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_TYPE_ALL))
+ F_SET(cst, WT_STAT_TYPE_ALL);
+ if (FLD_ISSET(
+ conn->stat_flags, WT_STAT_TYPE_CACHE_WALK))
+ F_SET(cst, WT_STAT_TYPE_CACHE_WALK);
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_TYPE_FAST))
+ F_SET(cst, WT_STAT_TYPE_FAST);
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_TYPE_TREE_WALK))
+ F_SET(cst, WT_STAT_TYPE_TREE_WALK);
}
/* If the connection configures clear, so do we. */
- if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR))
- F_SET(cst, WT_CONN_STAT_CLEAR);
+ if (FLD_ISSET(conn->stat_flags, WT_STAT_CLEAR))
+ F_SET(cst, WT_STAT_CLEAR);
}
/*
@@ -670,9 +700,9 @@ __wt_curstat_open(WT_SESSION_IMPL *session,
/*
* Do the initial statistics snapshot: there won't be cursor operations
- * to trigger initialization when aggregating statistics for upper-level
- * objects like tables, we need to a valid set of statistics when before
- * the open returns.
+ * to trigger initialization with aggregating statistics for upper-level
+ * objects like tables so we need a valid set of statistics before the
+ * open returns.
*/
WT_ERR(__wt_curstat_init(session, uri, other, cst->cfg, cst));
cst->notinitialized = false;
diff --git a/src/third_party/wiredtiger/src/cursor/cur_table.c b/src/third_party/wiredtiger/src/cursor/cur_table.c
index 1b93b27f564..502d93639a8 100644
--- a/src/third_party/wiredtiger/src/cursor/cur_table.c
+++ b/src/third_party/wiredtiger/src/cursor/cur_table.c
@@ -757,13 +757,36 @@ err: API_END_RET(session, ret);
}
/*
+ * __curtable_complete --
+ * Return failure if the table is not yet fully created.
+ */
+static int
+__curtable_complete(WT_SESSION_IMPL *session, WT_TABLE *table)
+{
+ WT_DECL_RET;
+ bool complete;
+
+ if (table->cg_complete)
+ return (0);
+
+ /* If the table is incomplete, wait on the table lock and recheck. */
+ complete = false;
+ WT_WITH_TABLE_LOCK(session, ret, complete = table->cg_complete);
+ WT_RET(ret);
+ if (!complete)
+ WT_RET_MSG(session, EINVAL,
+ "'%s' not available until all column groups are created",
+ table->name);
+ return (0);
+}
+
+/*
* __curtable_open_colgroups --
* Open cursors on column groups for a table cursor.
*/
static int
__curtable_open_colgroups(WT_CURSOR_TABLE *ctable, const char *cfg_arg[])
{
- WT_DECL_RET;
WT_SESSION_IMPL *session;
WT_TABLE *table;
WT_CURSOR **cp;
@@ -775,21 +798,11 @@ __curtable_open_colgroups(WT_CURSOR_TABLE *ctable, const char *cfg_arg[])
cfg_arg[0], cfg_arg[1], "dump=\"\",readonly=0", NULL, NULL
};
u_int i;
- bool complete;
session = (WT_SESSION_IMPL *)ctable->iface.session;
table = ctable->table;
- /* If the table is incomplete, wait on the table lock and recheck. */
- complete = table->cg_complete;
- if (!complete) {
- WT_WITH_TABLE_LOCK(session, ret, complete = table->cg_complete);
- WT_RET(ret);
- }
- if (!complete)
- WT_RET_MSG(session, EINVAL,
- "Can't use '%s' until all column groups are created",
- table->name);
+ WT_RET(__curtable_complete(session, table)); /* completeness check */
WT_RET(__wt_calloc_def(session,
WT_COLGROUPS(table), &ctable->cg_cursors));
@@ -879,7 +892,7 @@ __wt_curtable_open(WT_SESSION_IMPL *session,
tablename = uri;
if (!WT_PREFIX_SKIP(tablename, "table:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, uri, "table:"));
columns = strchr(tablename, '(');
if (columns == NULL)
size = strlen(tablename);
@@ -887,6 +900,8 @@ __wt_curtable_open(WT_SESSION_IMPL *session,
size = WT_PTRDIFF(columns, tablename);
WT_RET(__wt_schema_get_table(session, tablename, size, false, &table));
+ WT_RET(__curtable_complete(session, table)); /* completeness check */
+
if (table->is_simple) {
/* Just return a cursor on the underlying data source. */
ret = __wt_open_cursor(session,
diff --git a/src/third_party/wiredtiger/src/docs/build-posix.dox b/src/third_party/wiredtiger/src/docs/build-posix.dox
index 4889bf931c9..3e7f8f37acd 100644
--- a/src/third_party/wiredtiger/src/docs/build-posix.dox
+++ b/src/third_party/wiredtiger/src/docs/build-posix.dox
@@ -150,10 +150,14 @@ Configure WiredTiger to support the \c verbose configuration string to
Configure WiredTiger for <a href="http://www.zlib.net/">zlib</a>
compression; see @ref compression for more information.
+@par \c --enable-zstd
+Configure WiredTiger for <a href="https://github.com/facebook/zstd">Zstd</a>
+compression; see @ref compression for more information.
+
@par <code>--with-builtins</code>
Configure WiredTiger to include support for extensions in the main library.
This avoids requiring additional libraries for supported extensions. Currently
-supported options are \c lz4, \c snappy and \c zlib.
+supported options are \c lz4, \c snappy, \c zlib and \c zstd.
@par <code>--with-python-prefix</code>
Configure WiredTiger to install Python libraries to a non-standard Python
diff --git a/src/third_party/wiredtiger/src/docs/compression.dox b/src/third_party/wiredtiger/src/docs/compression.dox
index 0be96835760..74bed5c6f68 100644
--- a/src/third_party/wiredtiger/src/docs/compression.dox
+++ b/src/third_party/wiredtiger/src/docs/compression.dox
@@ -1,7 +1,7 @@
/*! @m_page{{c,java},compression,Compressors}
This section explains how to configure WiredTiger's builtin support for
-the lz4, snappy and zlib compression engines.
+the lz4, snappy, zlib and zstd compression engines.
@section compression_lz4 Using LZ4 compression
@@ -85,11 +85,53 @@ an extension. For example, with the WiredTiger library installed in
@snippet ex_all.c Configure zlib extension
+The default compression level for the zlib compression is
+\c Z_DEFAULT_COMPRESSION (see the zlib documentation for further
+information); compression can be configured to other levels using the
+additional configuration argument \c compression_level.
+
+@snippet ex_all.c Configure zlib extension with compression level
+
Finally, when creating the WiredTiger object, set \c block_compressor
to \c zlib:
@snippet ex_all.c Create a zlib compressed table
+@section compression_zstd Using Zstd compression
+
+To use the builtin support for Facebook's
+<a href="https://github.com/facebook/zstd">Zstd</a>
+compression, first check that Zstd is installed in include and library
+directories searched by the compiler. Once Zstd is installed, you can
+enable Zstd using the \c --enable-zstd option to configure.
+
+If Zstd is installed in a location not normally searched by the
+compiler toolchain, you'll need to modify the \c CPPFLAGS and \c LDFLAGS
+to indicate these locations. For example, with the Zstd includes and
+libraries installed in \c /usr/local/include and \c /usr/local/lib, you
+would run configure with the following additional arguments:
+
+@code
+--enable-zstd CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/include"
+@endcode
+
+When opening the WiredTiger database, load the Zstd shared library as
+an extension. For example, with the WiredTiger library installed in
+\c /usr/local/lib, you would use the following extension:
+
+@snippet ex_all.c Configure zstd extension
+
+The default compression level for the zstd compression is 3; compression
+can be configured to other levels using the additional configuration
+argument \c compression_level.
+
+@snippet ex_all.c Configure zstd extension with compression level
+
+Finally, when creating the WiredTiger object, set \c block_compressor
+to \c zstd:
+
+@snippet ex_all.c Create a zstd compressed table
+
@section compression_upgrading Upgrading compression engines
WiredTiger does not store information with file blocks to identify the
diff --git a/src/third_party/wiredtiger/src/docs/file-formats.dox b/src/third_party/wiredtiger/src/docs/file-formats.dox
index 8346024953a..d8990aca7a6 100644
--- a/src/third_party/wiredtiger/src/docs/file-formats.dox
+++ b/src/third_party/wiredtiger/src/docs/file-formats.dox
@@ -110,7 +110,7 @@ considered. (See @subpage_single huffman for details.)
compressing blocks of the backing object's file. The cost is additional
CPU and memory use when reading and writing pages to disk. Note the
additional CPU cost of block compression can be high, and should be
-considered. (See @ref compression for details.)
+considered. (See @x_ref compression_formats for details.)
Block compression is disabled by default.
@@ -146,7 +146,7 @@ Huffman encoding can be high, and should be considered.
compressing blocks of the backing object's file. The cost is additional
CPU and memory use when reading and writing pages to disk. Note the
additional CPU cost of block compression can be high, and should be
-considered. (See @ref compression for details.)
+considered. (See @x_ref compression_formats for details.)
Block compression is disabled by default.
@@ -157,7 +157,7 @@ compression: block compression.
compressing blocks of the backing object's file. The cost is additional
CPU and memory use when reading and writing pages to disk. Note the
additional CPU cost of block compression can be high, and should be
-considered. (See @ref compression for details.)
+considered. (See @x_ref compression_formats for details.)
Block compression is disabled by default.
diff --git a/src/third_party/wiredtiger/src/docs/spell.ok b/src/third_party/wiredtiger/src/docs/spell.ok
index a2ef7658ec6..2413cbc93fb 100644
--- a/src/third_party/wiredtiger/src/docs/spell.ok
+++ b/src/third_party/wiredtiger/src/docs/spell.ok
@@ -95,6 +95,7 @@ WiredTigerStat
WiredTigerTestCase
Yann
Za
+Zstd
aR
abstime
ack'ed
@@ -345,6 +346,7 @@ nolock
nolocking
nommap
nop
+noraw
nosql
nosync
notgranted
@@ -507,3 +509,4 @@ xa
yieldcpu
zlib
zseries
+zstd
diff --git a/src/third_party/wiredtiger/src/docs/style/header-web.html b/src/third_party/wiredtiger/src/docs/style/header-web.html
new file mode 100644
index 00000000000..63b22bb763e
--- /dev/null
+++ b/src/third_party/wiredtiger/src/docs/style/header-web.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath$tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath$jquery.js"></script>
+<script type="text/javascript" src="$relpath$dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath$$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<div id="titlearea">
+<table cellspacing="0" cellpadding="0">
+ <tbody>
+ <tr style="height: 56px;">
+ <!--BEGIN PROJECT_LOGO-->
+ <td id="projectlogo"><a href="http://wiredtiger.com/"><img alt="Logo" src="$relpath$$projectlogo" alt="WiredTiger" /></a></td>
+ <!--END PROJECT_LOGO-->
+ <!--BEGIN PROJECT_NAME-->
+ <td style="padding-left: 0.5em;">
+ <div id="projectname">
+ <!--BEGIN PROJECT_NUMBER--><span id="projectnumber"><!--$projectnumber--></span><!--END PROJECT_NUMBER-->
+ <!--BEGIN PROJECT_BRIEF-->
+ <div id="projectbrief">
+
+ <script type="text/javascript">
+ window.addEventListener("load",function(){
+ document.getElementById("version_select").options.namedItem("$projectbrief").selected=true;
+ },false);
+ </script>
+
+ <select id="version_select" onchange="window.location=window.location.href.replace('$projectbrief',this.value);">
+ <option name="$projectbrief" value="$projectbrief">$projectnumber</option>
+ <option name="stable" value="stable">Latest stable release</option>
+ <option name="develop" value="develop">Current develop branch</option>
+ <option name="mongodb-3.2" value="mongodb-3.2">MongoDB 3.2</option>
+ <option name="mongodb-3.4" value="mongodb-3.4">MongoDB 3.4</option>
+ </select>
+ </div><!--END PROJECT_BRIEF-->
+ </div>
+ </td>
+ <!--END PROJECT_NAME-->
+ <!--BEGIN !PROJECT_NAME-->
+ <!--BEGIN PROJECT_BRIEF-->
+ <td style="padding-left: 0.5em;">
+ <div id="projectbrief">$projectbrief</div>
+ </td>
+ <!--END PROJECT_BRIEF-->
+ <!--END !PROJECT_NAME-->
+ <!--BEGIN DISABLE_INDEX-->
+ <!--BEGIN SEARCHENGINE-->
+ <td>$searchbox</td>
+ <!--END SEARCHENGINE-->
+ <!--END DISABLE_INDEX-->
+ </tr>
+ </tbody>
+</table>
+</div>
+<div class="banner">
+ <a href="https://github.com/wiredtiger/wiredtiger">Fork me on GitHub</a>
+ <a class="last" href="http://groups.google.com/group/wiredtiger-users">Join my user group</a>
+</div>
+<!--END TITLEAREA-->
+<!-- end header part -->
diff --git a/src/third_party/wiredtiger/src/docs/style/header.html b/src/third_party/wiredtiger/src/docs/style/header.html
index 0c045a750d1..422268b7a47 100644
--- a/src/third_party/wiredtiger/src/docs/style/header.html
+++ b/src/third_party/wiredtiger/src/docs/style/header.html
@@ -30,13 +30,13 @@ $extrastylesheet
<div id="projectname">
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
- <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
+ <!--BEGIN PROJECT_BRIEF--><div id="projectbrief"><!-- $projectbrief --></div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
- <div id="projectbrief">$projectbrief</div>
+ <div id="projectbrief"><!-- $projectbrief --></div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
diff --git a/src/third_party/wiredtiger/src/docs/tools/doxfilter.py b/src/third_party/wiredtiger/src/docs/tools/doxfilter.py
index b2d5f857df1..f1c3308c689 100755
--- a/src/third_party/wiredtiger/src/docs/tools/doxfilter.py
+++ b/src/third_party/wiredtiger/src/docs/tools/doxfilter.py
@@ -98,6 +98,9 @@ def process_lang(lang, lines):
subpage_pat = re.compile(r'@subpage\s+(\w*)')
subpage_rep = r'@subpage \1' + lang_suffix
exref_pat = re.compile(r'@ex_ref{ex_([^.]*)[.]c}')
+ # Add some ability to have non-language references
+ x_ref_pat = re.compile(r'@x_ref\s+(\w*)')
+ x_ref_rep = r'@ref \1'
if lang == 'c':
exref_rep = r'@ex_ref{ex_\1' + lang_ext + '}'
else:
@@ -118,6 +121,7 @@ def process_lang(lang, lines):
line = re.sub(snip_pat, snip_rep, line)
line = re.sub(mpage_pat, mpage_rep, line)
line = re.sub(subpage_pat, subpage_rep, line)
+ line = re.sub(x_ref_pat, x_ref_rep, line)
if '@m_if' in line:
m = re.search(mif_pat, line)
if not m:
diff --git a/src/third_party/wiredtiger/src/docs/tune-compression.dox b/src/third_party/wiredtiger/src/docs/tune-compression.dox
index bb675337a0d..8db2151aa76 100644
--- a/src/third_party/wiredtiger/src/docs/tune-compression.dox
+++ b/src/third_party/wiredtiger/src/docs/tune-compression.dox
@@ -2,7 +2,7 @@
WiredTiger includes a number of optional compression techniques. Configuring
compression generally decreases on-disk and in-memory resource requirements
-and the amount of I/O, and increases CPU cost when rows are read and written.
+and the amount of I/O, and increases CPU cost when data are read and written.
Configuring compression may change application throughput. For example,
in applications using solid-state drives (where I/O is less expensive),
@@ -19,7 +19,44 @@ An example of turning on row-store or column-store dictionary compression:
@snippet ex_all.c Configure dictionary compression on
+@section compression_formats Block Compression Formats
+WiredTiger provides two methods of compressing your data when using block
+compression: the raw and noraw methods. These methods change how WiredTiger
+works to fit data into the blocks that are stored on disk.
+
+@subsection noraw_compression Noraw Compression
+Noraw compression is the traditional compression model where a fixed
+amount of data is given to the compression system, then turned into a
+compressed block of data. The amount of data chosen to compress is the
+data needed to fill the uncompressed block. Thus when compressed, the block will
+be smaller than the normal data size and the sizes written to disk will often
+vary depending on how compressible the data being stored is. Algorithms
+using noraw compression include zlib-noraw, lz4-noraw and snappy.
+
+@subsection raw_compression Raw Compression
+WiredTiger's raw compression takes advantage of compressors that provide a
+streaming compression API. Using the streaming API WiredTiger will try to fit
+as much data as possible into one block. This means that blocks created
+with raw compression should be of similar size. Using a streaming compression
+method should also make for less overhead in compression, as the setup and
+initial work for compressing is done fewer times compared to the amount of
+data stored. Algorithms using raw compression include zlib, lz4.
+
+@subsection to_raw_or_noraw Choosing between Raw and Noraw Compression
+When looking at which compression method to use the biggest consideration is
+that raw compression will normally provide higher compression levels while
+using more CPU for compression.
+
+An additional consideration is that raw compression may provide a performance
+advantage in workloads where data is accessed sequentially. That is because
+more data is generally packed into each block on disk. Conversely, noraw
+compression may perform better for workloads with random access patterns
+because each block will tend to be smaller and require less work to read and
+decompress.
+
See @ref file_formats_compression for more information on available
compression techniques.
+See @ref compression for information on how to configure and enable compression.
+
*/
diff --git a/src/third_party/wiredtiger/src/docs/wtperf.dox b/src/third_party/wiredtiger/src/docs/wtperf.dox
index a49d0d9f871..83aadf8a776 100644
--- a/src/third_party/wiredtiger/src/docs/wtperf.dox
+++ b/src/third_party/wiredtiger/src/docs/wtperf.dox
@@ -150,33 +150,27 @@ number of async worker threads
@par checkpoint_interval (unsigned int, default=120)
checkpoint every interval seconds during the workload phase.
@par checkpoint_stress_rate (unsigned int, default=0)
-checkpoint every rate operations during the populate phase in the
-populate thread(s), 0 to disable
+checkpoint every rate operations during the populate phase in the populate thread(s), 0 to disable
@par checkpoint_threads (unsigned int, default=0)
number of checkpoint threads
-@par conn_config (string, default=create)
+@par conn_config (string, default="create")
connection configuration string
+@par close_conn (boolean, default=true)
+properly close connection at end of test. Setting to false does not sync data to disk and can result in lost data after test exits.
@par compact (boolean, default=false)
post-populate compact for LSM merging activity
-@par compression (string, default=none)
-compression extension. Allowed configuration values are: 'none',
-'lz4', 'snappy', 'zlib'
+@par compression (string, default="none")
+compression extension. Allowed configuration values are: 'none', 'lz4', 'snappy', 'zlib', 'zstd'
@par create (boolean, default=true)
do population phase; false to use existing database
@par database_count (unsigned int, default=1)
-number of WiredTiger databases to use. Each database will execute the
-workload using a separate home directory and complete set of worker
-threads
-@par drop_tables (unsigned int, default=0)
-Whether to drop all tables at the end of the run, and report time
-taken to do the drop.
+number of WiredTiger databases to use. Each database will execute the workload using a separate home directory and complete set of worker threads
+@par drop_tables (boolean, default=false)
+Whether to drop all tables at the end of the run, and report time taken to do the drop.
@par icount (unsigned int, default=5000)
-number of records to initially populate. If multiple tables are
-configured the count is spread evenly across all tables.
+number of records to initially populate. If multiple tables are configured the count is spread evenly across all tables.
@par idle_table_cycle (unsigned int, default=0)
-Enable regular create and drop of idle tables, value is the maximum
-number of seconds a create or drop is allowed before flagging an
-error. Default 0 which means disabled.
+Enable regular create and drop of idle tables, value is the maximum number of seconds a create or drop is allowed before flagging an error. Default 0 which means disabled.
@par index (boolean, default=false)
Whether to create an index on the value field.
@par insert_rmw (boolean, default=false)
@@ -188,28 +182,21 @@ perform partial logging on first table only.
@par log_like_table (boolean, default=false)
Append all modification operations to another shared table.
@par min_throughput (unsigned int, default=0)
-notify if any throughput measured is less than this amount. Aborts or
-prints warning based on min_throughput_fatal setting. Requires
-sample_interval to be configured
+notify if any throughput measured is less than this amount. Aborts or prints warning based on min_throughput_fatal setting. Requires sample_interval to be configured
@par min_throughput_fatal (boolean, default=false)
print warning (false) or abort (true) of min_throughput failure.
@par max_latency (unsigned int, default=0)
-notify if any latency measured exceeds this number of
-milliseconds.Aborts or prints warning based on min_throughput_fatal
-setting. Requires sample_interval to be configured
+notify if any latency measured exceeds this number of milliseconds. Aborts or prints warning based on min_throughput_fatal setting. Requires sample_interval to be configured
@par max_latency_fatal (boolean, default=false)
print warning (false) or abort (true) of max_latency failure.
@par pareto (unsigned int, default=0)
-use pareto distribution for random numbers. Zero to disable, otherwise
-a percentage indicating how aggressive the distribution should be.
+use pareto distribution for random numbers. Zero to disable, otherwise a percentage indicating how aggressive the distribution should be.
@par populate_ops_per_txn (unsigned int, default=0)
-number of operations to group into each transaction in the populate
-phase, zero for auto-commit
+number of operations to group into each transaction in the populate phase, zero for auto-commit
@par populate_threads (unsigned int, default=1)
number of populate threads, 1 for bulk load
@par random_range (unsigned int, default=0)
-if non zero choose a value from within this range as the key for
-insert operations
+if non zero choose a value from within this range as the key for insert operations
@par random_value (boolean, default=false)
generate random content for the value
@par range_partition (boolean, default=false)
@@ -217,9 +204,7 @@ partition data by range (vs hash)
@par read_range (unsigned int, default=0)
scan a range of keys after each search
@par readonly (boolean, default=false)
-reopen the connection between populate and workload phases in readonly
-mode. Requires reopen_connection turned on (default). Requires that
-read be the only workload specified
+reopen the connection between populate and workload phases in readonly mode. Requires reopen_connection turned on (default). Requires that read be the only workload specified
@par reopen_connection (boolean, default=true)
close and reopen the connection between populate and workload phases
@par report_interval (unsigned int, default=2)
@@ -231,40 +216,22 @@ total workload seconds
@par sample_interval (unsigned int, default=0)
performance logging every interval seconds, 0 to disable
@par sample_rate (unsigned int, default=50)
-how often the latency of operations is measured. One for every
-operation,two for every second operation, three for every third
-operation etc.
-@par sess_config (string, default=)
+how often the latency of operations is measured. One for every operation, two for every second operation, three for every third operation etc.
+@par sess_config (string, default="")
session configuration string
@par session_count_idle (unsigned int, default=0)
number of idle sessions to create. Default 0.
-@par table_config (string, default=key_format=S,value_format=S,type=lsm,exclusive=true,allocation_size=4kb,internal_page_max=64kb,leaf_page_max=4kb,split_pct=100)
+@par table_config (string, default="key_format=S,value_format=S,type=lsm,exclusive=true, allocation_size=4kb,internal_page_max=64kb,leaf_page_max=4kb, split_pct=100")
table configuration string
@par table_count (unsigned int, default=1)
-number of tables to run operations over. Keys are divided evenly over
-the tables. Cursors are held open on all tables. Default 1, maximum
-99999.
+number of tables to run operations over. Keys are divided evenly over the tables. Cursors are held open on all tables. Default 1, maximum 99999.
@par table_count_idle (unsigned int, default=0)
number of tables to create, that won't be populated. Default 0.
-@par threads (string, default=)
-workload configuration: each 'count' entry is the total number of
-threads, and the 'insert', 'read' and 'update' entries are the ratios
-of insert, read and update operations done by each worker thread; If a
-throttle value is provided each thread will do a maximum of that
-number of operations per second; multiple workload configurations may
-be specified per threads configuration; for example, a more complex
-threads configuration might be
-'threads=((count=2,reads=1)(count=8,reads=1,inserts=2,updates=1))'
-which would create 2 threads doing nothing but reads and 8 threads
-each doing 50% inserts and 25% reads and updates. Allowed
-configuration values are 'count', 'throttle', 'update_delta', 'reads',
-'inserts', 'updates', 'truncate', 'truncate_pct' and 'truncate_count'.
-There are also behavior modifiers, supported modifiers are
-'ops_per_txn'
-@par transaction_config (string, default=)
-transaction configuration string, relevant when populate_opts_per_txn
-is nonzero
-@par table_name (string, default=test)
+@par threads (string, default="")
+workload configuration: each 'count' entry is the total number of threads, and the 'insert', 'read' and 'update' entries are the ratios of insert, read and update operations done by each worker thread; If a throttle value is provided each thread will do a maximum of that number of operations per second; multiple workload configurations may be specified per threads configuration; for example, a more complex threads configuration might be 'threads=((count=2,reads=1)(count=8,reads=1,inserts=2,updates=1))' which would create 2 threads doing nothing but reads and 8 threads each doing 50% inserts and 25% reads and updates. Allowed configuration values are 'count', 'throttle', 'update_delta', 'reads', 'inserts', 'updates', 'truncate', 'truncate_pct' and 'truncate_count'. There are also behavior modifiers, supported modifiers are 'ops_per_txn'
+@par transaction_config (string, default="")
+WT_SESSION.begin_transaction configuration string, applied during the populate phase when populate_ops_per_txn is nonzero
+@par table_name (string, default="test")
table name
@par truncate_single_ops (boolean, default=false)
Implement truncate via cursor remove instead of session API
diff --git a/src/third_party/wiredtiger/src/evict/evict_lru.c b/src/third_party/wiredtiger/src/evict/evict_lru.c
index acc81f566a5..909fe813f98 100644
--- a/src/third_party/wiredtiger/src/evict/evict_lru.c
+++ b/src/third_party/wiredtiger/src/evict/evict_lru.c
@@ -233,10 +233,10 @@ __wt_evict_thread_run(WT_SESSION_IMPL *session, WT_THREAD *thread)
#ifdef HAVE_DIAGNOSTIC
/*
- * Ensure the cache stuck timer is initialized when starting eviction
+ * Ensure the cache stuck timer is initialized when starting eviction.
*/
if (thread->id == 0)
- WT_ERR(__wt_epoch(session, &cache->stuck_ts));
+ __wt_epoch(session, &cache->stuck_ts);
#endif
while (F_ISSET(conn, WT_CONN_EVICTION_RUN) &&
@@ -350,10 +350,10 @@ __evict_server(WT_SESSION_IMPL *session, bool *did_work)
} else if (cache->pages_evicted != cache->pages_evict) {
cache->pages_evicted = cache->pages_evict;
#ifdef HAVE_DIAGNOSTIC
- WT_RET(__wt_epoch(session, &cache->stuck_ts));
+ __wt_epoch(session, &cache->stuck_ts);
} else {
/* After being stuck for 5 minutes, give up. */
- WT_RET(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
if (WT_TIMEDIFF_SEC(now, cache->stuck_ts) > 300) {
ret = ETIMEDOUT;
__wt_err(session, ret,
@@ -465,16 +465,16 @@ __evict_update_work(WT_SESSION_IMPL *session)
*/
bytes_max = conn->cache_size + 1;
bytes_inuse = __wt_cache_bytes_inuse(cache);
- if (bytes_inuse > (cache->eviction_target * bytes_max) / 100)
- F_SET(cache, WT_CACHE_EVICT_CLEAN);
if (__wt_eviction_clean_needed(session, NULL))
F_SET(cache, WT_CACHE_EVICT_CLEAN | WT_CACHE_EVICT_CLEAN_HARD);
+ else if (bytes_inuse > (cache->eviction_target * bytes_max) / 100)
+ F_SET(cache, WT_CACHE_EVICT_CLEAN);
dirty_inuse = __wt_cache_dirty_leaf_inuse(cache);
- if (dirty_inuse > (cache->eviction_dirty_target * bytes_max) / 100)
- F_SET(cache, WT_CACHE_EVICT_DIRTY);
if (__wt_eviction_dirty_needed(session, NULL))
F_SET(cache, WT_CACHE_EVICT_DIRTY | WT_CACHE_EVICT_DIRTY_HARD);
+ else if (dirty_inuse > (cache->eviction_dirty_target * bytes_max) / 100)
+ F_SET(cache, WT_CACHE_EVICT_DIRTY);
/*
* If application threads are blocked by the total volume of data in
@@ -506,12 +506,6 @@ __evict_update_work(WT_SESSION_IMPL *session)
F_CLR(cache, WT_CACHE_EVICT_CLEAN | WT_CACHE_EVICT_CLEAN_HARD);
}
- /* If threads are blocked by eviction we should be looking for pages. */
- WT_ASSERT(session, !F_ISSET(cache, WT_CACHE_EVICT_CLEAN_HARD) ||
- F_ISSET(cache, WT_CACHE_EVICT_CLEAN));
- WT_ASSERT(session, !F_ISSET(cache, WT_CACHE_EVICT_DIRTY_HARD) ||
- F_ISSET(cache, WT_CACHE_EVICT_DIRTY));
-
WT_STAT_CONN_SET(session, cache_eviction_state,
F_MASK(cache, WT_CACHE_EVICT_MASK));
@@ -543,7 +537,7 @@ __evict_pass(WT_SESSION_IMPL *session)
/* Evict pages from the cache. */
for (loop = 0; cache->pass_intr == 0; loop++) {
- WT_RET(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
if (loop == 0)
prev = now;
@@ -554,6 +548,7 @@ __evict_pass(WT_SESSION_IMPL *session)
* does need to do some work.
*/
__wt_cache_read_gen_incr(session);
+ ++cache->evict_pass_gen;
/*
* Update the oldest ID: we use it to decide whether pages are
@@ -895,12 +890,11 @@ __evict_lru_walk(WT_SESSION_IMPL *session)
/* Fill the next queue (that isn't the urgent queue). */
queue = cache->evict_fill_queue;
other_queue = cache->evict_queues + (1 - (queue - cache->evict_queues));
+ cache->evict_fill_queue = other_queue;
/* If this queue is full, try the other one. */
if (__evict_queue_full(queue) && !__evict_queue_full(other_queue))
queue = other_queue;
- cache->evict_fill_queue =
- &cache->evict_queues[1 - (queue - cache->evict_queues)];
/*
* If both queues are full and haven't been empty on recent refills,
@@ -916,23 +910,6 @@ __evict_lru_walk(WT_SESSION_IMPL *session)
WT_RET_NOTFOUND_OK(ret);
/*
- * If we found no pages at all during the walk, something is wrong.
- * Be more aggressive next time.
- *
- * Continue on to sort the queue, in case there are pages left from a
- * previous walk.
- */
- if (ret == WT_NOTFOUND) {
- if (F_ISSET(cache,
- WT_CACHE_EVICT_CLEAN_HARD | WT_CACHE_EVICT_DIRTY_HARD))
- cache->evict_aggressive_score = WT_MIN(
- cache->evict_aggressive_score + WT_EVICT_SCORE_BUMP,
- WT_EVICT_SCORE_MAX);
- WT_STAT_CONN_SET(session, cache_eviction_aggressive_set,
- cache->evict_aggressive_score);
- }
-
- /*
* If the queue we are filling is empty, pages are being requested
* faster than they are being queued.
*/
@@ -1062,7 +1039,7 @@ __evict_walk(WT_SESSION_IMPL *session, WT_EVICT_QUEUE *queue)
WT_CONNECTION_IMPL *conn;
WT_DATA_HANDLE *dhandle;
WT_DECL_RET;
- u_int max_entries, retries, slot, start_slot, spins;
+ u_int max_entries, retries, slot, spins, start_slot, total_candidates;
bool dhandle_locked, incr;
conn = S2C(session);
@@ -1079,6 +1056,14 @@ __evict_walk(WT_SESSION_IMPL *session, WT_EVICT_QUEUE *queue)
start_slot = slot = queue->evict_entries;
max_entries = WT_MIN(slot + WT_EVICT_WALK_INCR, cache->evict_slots);
+ /*
+ * Another pathological case: if there are only a tiny number of
+ * candidate pages in cache, don't put all of them on one queue.
+ */
+ total_candidates = (u_int)(F_ISSET(cache, WT_CACHE_EVICT_CLEAN) ?
+ __wt_cache_pages_inuse(cache) : cache->pages_dirty_leaf);
+ max_entries = WT_MIN(max_entries, 1 + total_candidates / 2);
+
retry: while (slot < max_entries) {
/*
* If another thread is waiting on the eviction server to clear
@@ -1282,8 +1267,8 @@ __evict_push_candidate(WT_SESSION_IMPL *session,
* Get a few page eviction candidates from a single underlying file.
*/
static int
-__evict_walk_file(WT_SESSION_IMPL *session,
- WT_EVICT_QUEUE *queue, u_int max_entries, u_int *slotp)
+__evict_walk_file(WT_SESSION_IMPL *session, WT_EVICT_QUEUE *queue,
+ u_int max_entries, u_int *slotp)
{
WT_BTREE *btree;
WT_CACHE *cache;
@@ -1294,7 +1279,7 @@ __evict_walk_file(WT_SESSION_IMPL *session,
WT_PAGE_MODIFY *mod;
WT_REF *ref;
WT_TXN_GLOBAL *txn_global;
- uint64_t btree_inuse, bytes_per_slot, cache_inuse;
+ uint64_t btree_inuse, bytes_per_slot, cache_inuse, min_pages;
uint64_t pages_seen, pages_queued, refs_walked;
uint32_t remaining_slots, total_slots, walk_flags;
uint32_t target_pages_clean, target_pages_dirty, target_pages;
@@ -1367,6 +1352,16 @@ __evict_walk_file(WT_SESSION_IMPL *session,
FLD_SET(walk_flags, WT_READ_PREV);
/*
+ * Examine at least a reasonable number of pages before deciding
+ * whether to give up. When we are only looking for dirty pages,
+ * search the tree for longer.
+ */
+ min_pages = 10 * target_pages;
+ if (F_ISSET(cache, WT_CACHE_EVICT_DIRTY) &&
+ !F_ISSET(cache, WT_CACHE_EVICT_CLEAN))
+ min_pages *= 10;
+
+ /*
* Get some more eviction candidate pages.
*
* !!! Take care terminating this loop.
@@ -1388,9 +1383,10 @@ __evict_walk_file(WT_SESSION_IMPL *session,
* no good eviction candidates can be found. Abandon the walk
* if we get into that situation.
*/
- give_up = !__wt_cache_aggressive(session) && pages_seen > 100 &&
+ give_up = !__wt_cache_aggressive(session) &&
+ pages_seen > min_pages &&
(pages_queued == 0 || (pages_seen / pages_queued) >
- (10 * total_slots / target_pages));
+ (min_pages / target_pages));
if (give_up)
break;
@@ -1410,6 +1406,7 @@ __evict_walk_file(WT_SESSION_IMPL *session,
page = ref->page;
modified = __wt_page_is_modified(page);
+ page->evict_pass_gen = cache->evict_pass_gen;
/*
* Use the EVICT_LRU flag to avoid putting pages onto the list
@@ -1450,15 +1447,15 @@ __evict_walk_file(WT_SESSION_IMPL *session,
if (modified && !F_ISSET(cache, WT_CACHE_EVICT_DIRTY))
continue;
- /* If eviction gets aggressive, anything else is fair game. */
- if (__wt_cache_aggressive(session))
- goto fast;
-
/* Limit internal pages to 50% of the total. */
if (WT_PAGE_IS_INTERNAL(page) &&
internal_pages >= (int)(evict - start) / 2)
continue;
+ /* If eviction gets aggressive, anything else is fair game. */
+ if (__wt_cache_aggressive(session))
+ goto fast;
+
/*
* If the oldest transaction hasn't changed since the last time
* this page was written, it's unlikely we can make progress.
@@ -1508,19 +1505,22 @@ fast: /* If the page can't be evicted, give up. */
btree->evict_walk_period = 0;
/*
- * If we happen to end up on the root page, clear it. We have to track
- * hazard pointers, and the root page complicates that calculation.
+ * If we happen to end up on the root page or a page requiring urgent
+ * eviction, clear it. We have to track hazard pointers, and the root
+ * page complicates that calculation.
*
* Likewise if we found no new candidates during the walk: there is no
- * point keeping a page pinned, since it may be the only candidate in an
- * idle tree.
+ * point keeping a page pinned, since it may be the only candidate in
+ * an idle tree.
*
* If we land on a page requiring forced eviction, move on to the next
* page: we want this page evicted as quickly as possible.
*/
if ((ref = btree->evict_ref) != NULL) {
/* Give up the walk occasionally. */
- if (__wt_ref_is_root(ref) || evict == start || give_up)
+ if (__wt_ref_is_root(ref) || evict == start || give_up ||
+ ref->page->read_gen == WT_READGEN_OLDEST ||
+ ref->page->memory_footprint >= btree->splitmempage)
WT_RET(__evict_clear_walk(session, restarts == 0));
else if (ref->page->read_gen == WT_READGEN_OLDEST)
WT_RET_NOTFOUND_OK(__wt_tree_walk_count(
@@ -1543,17 +1543,17 @@ __evict_get_ref(
WT_SESSION_IMPL *session, bool is_server, WT_BTREE **btreep, WT_REF **refp)
{
WT_CACHE *cache;
- WT_DECL_RET;
WT_EVICT_ENTRY *evict;
WT_EVICT_QUEUE *queue, *other_queue, *urgent_queue;
uint32_t candidates;
- bool is_app, urgent_ok;
+ bool is_app, server_only, urgent_ok;
cache = S2C(session)->cache;
is_app = !F_ISSET(session, WT_SESSION_INTERNAL);
+ server_only = is_server && !WT_EVICT_HAS_WORKERS(session);
urgent_ok = (!is_app && !is_server) ||
!WT_EVICT_HAS_WORKERS(session) ||
- __wt_cache_aggressive(session);
+ (is_app && __wt_cache_aggressive(session));
urgent_queue = cache->evict_urgent_queue;
*btreep = NULL;
*refp = NULL;
@@ -1569,7 +1569,8 @@ __evict_get_ref(
}
/*
- * The server repopulates whenever the other queue is not full.
+ * The server repopulates whenever the other queue is not full, as long
+ * as at least one page has been evicted out of the current queue.
*
* Note that there are pathological cases where there are only enough
* eviction candidates in the cache to fill one queue. In that case,
@@ -1577,18 +1578,14 @@ __evict_get_ref(
* Such cases are extremely rare in real applications.
*/
if (is_server &&
+ (!urgent_ok || __evict_queue_empty(urgent_queue, false)) &&
+ !__evict_queue_full(cache->evict_current_queue) &&
+ !__evict_queue_full(cache->evict_fill_queue) &&
(cache->evict_empty_score > WT_EVICT_SCORE_CUTOFF ||
- __evict_queue_empty(cache->evict_fill_queue, false))) {
- while ((ret = __wt_spin_trylock(
- session, &cache->evict_queue_lock)) == EBUSY)
- if ((!urgent_ok ||
- __evict_queue_empty(urgent_queue, false)) &&
- !__evict_queue_full(cache->evict_fill_queue))
- return (WT_NOTFOUND);
+ __evict_queue_empty(cache->evict_fill_queue, false)))
+ return (WT_NOTFOUND);
- WT_RET(ret);
- } else
- __wt_spin_lock(session, &cache->evict_queue_lock);
+ __wt_spin_lock(session, &cache->evict_queue_lock);
/* Check the urgent queue first. */
if (urgent_ok && !__evict_queue_empty(urgent_queue, false))
@@ -1596,17 +1593,15 @@ __evict_get_ref(
else {
/*
* Check if the current queue needs to change.
- * The current queue could have changed while we waited for
- * the lock.
*
* The server will only evict half of the pages before looking
- * for more. The remainder are left to eviction workers (if any
- * configured), or application threads if necessary.
+ * for more, but should only switch queues if there are no
+ * other eviction workers.
*/
queue = cache->evict_current_queue;
other_queue = cache->evict_other_queue;
- if (__evict_queue_empty(queue, is_server) &&
- !__evict_queue_empty(other_queue, is_server)) {
+ if (__evict_queue_empty(queue, server_only) &&
+ !__evict_queue_empty(other_queue, server_only)) {
cache->evict_current_queue = other_queue;
cache->evict_other_queue = queue;
}
@@ -1715,15 +1710,19 @@ __evict_get_ref(
static int
__evict_page(WT_SESSION_IMPL *session, bool is_server)
{
+ struct timespec enter, leave;
WT_BTREE *btree;
WT_CACHE *cache;
WT_DECL_RET;
WT_REF *ref;
+ bool app_timer;
WT_RET(__evict_get_ref(session, is_server, &btree, &ref));
WT_ASSERT(session, ref->state == WT_REF_LOCKED);
+ app_timer = false;
cache = S2C(session)->cache;
+
/*
* An internal session flags either the server itself or an eviction
* worker thread.
@@ -1739,6 +1738,10 @@ __evict_page(WT_SESSION_IMPL *session, bool is_server)
WT_STAT_CONN_INCR(session, cache_eviction_app_dirty);
WT_STAT_CONN_INCR(session, cache_eviction_app);
cache->app_evicts++;
+ if (WT_STAT_ENABLED(session)) {
+ app_timer = true;
+ __wt_epoch(session, &enter);
+ }
}
/*
@@ -1756,6 +1759,11 @@ __evict_page(WT_SESSION_IMPL *session, bool is_server)
(void)__wt_atomic_subv32(&btree->evict_busy, 1);
+ if (app_timer) {
+ __wt_epoch(session, &leave);
+ WT_STAT_CONN_INCRV(session,
+ application_evict_time, WT_TIMEDIFF_US(leave, enter));
+ }
return (ret);
}
@@ -1767,6 +1775,7 @@ __evict_page(WT_SESSION_IMPL *session, bool is_server)
int
__wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
{
+ struct timespec enter, leave;
WT_CACHE *cache;
WT_CONNECTION_IMPL *conn;
WT_DECL_RET;
@@ -1792,9 +1801,11 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
/* Wake the eviction server if we need to do work. */
__wt_evict_server_wake(session);
- init_evict_count = cache->pages_evict;
+ /* Track how long application threads spend doing eviction. */
+ if (WT_STAT_ENABLED(session) && !F_ISSET(session, WT_SESSION_INTERNAL))
+ __wt_epoch(session, &enter);
- for (;;) {
+ for (init_evict_count = cache->pages_evict;; ret = 0) {
/*
* A pathological case: if we're the oldest transaction in the
* system and the eviction server is stuck trying to find space,
@@ -1804,7 +1815,7 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
if (__wt_cache_stuck(session) && __wt_txn_am_oldest(session)) {
--cache->evict_aggressive_score;
WT_STAT_CONN_INCR(session, txn_fail_cache);
- return (WT_ROLLBACK);
+ WT_ERR(WT_ROLLBACK);
}
/*
@@ -1816,7 +1827,7 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
* limit the work to 5 evictions and return. If that's not the
* case, we can do more.
*/
- if (!busy && txn_state->snap_min != WT_TXN_NONE &&
+ if (!busy && txn_state->pinned_id != WT_TXN_NONE &&
txn_global->current != txn_global->oldest_id)
busy = true;
max_pages_evicted = busy ? 5 : 20;
@@ -1825,7 +1836,7 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
if (!__wt_eviction_needed(session, busy, &pct_full) ||
(pct_full < 100 &&
cache->pages_evict > init_evict_count + max_pages_evicted))
- return (0);
+ break;
/*
* Don't make application threads participate in scrubbing for
@@ -1842,7 +1853,7 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
switch (ret = __evict_page(session, false)) {
case 0:
if (busy)
- return (0);
+ goto err;
/* FALLTHROUGH */
case EBUSY:
break;
@@ -1853,9 +1864,18 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
cache->app_waits++;
break;
default:
- return (ret);
+ goto err;
}
}
+
+err: if (WT_STAT_ENABLED(session) &&
+ !F_ISSET(session, WT_SESSION_INTERNAL)) {
+ __wt_epoch(session, &leave);
+ WT_STAT_CONN_INCRV(session,
+ application_cache_time, WT_TIMEDIFF_US(leave, enter));
+ }
+
+ return (ret);
/* NOTREACHED */
}
diff --git a/src/third_party/wiredtiger/src/evict/evict_page.c b/src/third_party/wiredtiger/src/evict/evict_page.c
index 092f80cc000..3d1557e027e 100644
--- a/src/third_party/wiredtiger/src/evict/evict_page.c
+++ b/src/third_party/wiredtiger/src/evict/evict_page.c
@@ -31,23 +31,14 @@ __evict_exclusive_clear(WT_SESSION_IMPL *session, WT_REF *ref)
static inline int
__evict_exclusive(WT_SESSION_IMPL *session, WT_REF *ref)
{
- int loops;
-
WT_ASSERT(session, ref->state == WT_REF_LOCKED);
/*
* Check for a hazard pointer indicating another thread is using the
* page, meaning the page cannot be evicted.
*/
- for (loops = 0; loops < 10; loops++) {
- if (__wt_page_hazard_check(session, ref->page) == NULL)
- return (0);
- if (ref->page->read_gen != WT_READGEN_OLDEST &&
- ref->page->memory_footprint <
- S2BT(session)->split_deepen_min_child)
- break;
- __wt_sleep(0, WT_THOUSAND);
- }
+ if (__wt_page_hazard_check(session, ref->page) == NULL)
+ return (0);
WT_STAT_DATA_INCR(session, cache_eviction_hazard);
WT_STAT_CONN_INCR(session, cache_eviction_hazard);
diff --git a/src/third_party/wiredtiger/src/evict/evict_stat.c b/src/third_party/wiredtiger/src/evict/evict_stat.c
new file mode 100644
index 00000000000..2dd3b1e83a0
--- /dev/null
+++ b/src/third_party/wiredtiger/src/evict/evict_stat.c
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2014-2016 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * See the file LICENSE for redistribution information.
+ */
+
+#include "wt_internal.h"
+
+/*
+ * __evict_stat_walk --
+ * Walk all the pages in cache for a dhandle gathering stats information
+ */
+static void
+__evict_stat_walk(WT_SESSION_IMPL *session)
+{
+ WT_BTREE *btree;
+ WT_PAGE *page;
+ WT_REF *next_walk;
+ uint64_t dsk_size, gen_gap, size;
+ uint64_t written_size_cnt, written_size_sum;
+ uint64_t gen_gap_cnt, gen_gap_max, gen_gap_sum;
+ uint64_t max_pagesize, min_written_size;
+ uint64_t num_memory, num_queued, num_not_queueable, num_smaller_allocsz;
+ uint64_t pages_clean, pages_dirty, pages_internal, pages_leaf;
+ uint64_t seen_count, walk_count;
+
+ btree = S2BT(session);
+ next_walk = NULL;
+ written_size_cnt = written_size_sum = 0;
+ gen_gap_cnt = gen_gap_max = gen_gap_sum = 0;
+ max_pagesize = 0;
+ num_memory = num_queued = num_not_queueable = num_smaller_allocsz = 0;
+ pages_clean = pages_dirty = pages_internal = pages_leaf = 0;
+ seen_count = walk_count = 0;
+ min_written_size = UINT64_MAX;
+
+ while (__wt_tree_walk_count(session, &next_walk, &walk_count,
+ WT_READ_CACHE | WT_READ_NO_EVICT | WT_READ_NO_WAIT) == 0 &&
+ next_walk != NULL) {
+ ++seen_count;
+ page = next_walk->page;
+ size = page->memory_footprint;
+
+ if (__wt_page_is_modified(page))
+ ++pages_dirty;
+ else
+ ++pages_clean;
+
+ if (!__wt_ref_is_root(next_walk) &&
+ !__wt_page_can_evict(session, next_walk, NULL))
+ ++num_not_queueable;
+
+ if (F_ISSET_ATOMIC(page, WT_PAGE_EVICT_LRU))
+ ++num_queued;
+
+ if (size > max_pagesize)
+ max_pagesize = size;
+
+ dsk_size = page->dsk != NULL ? page->dsk->mem_size : 0;
+ if (dsk_size != 0) {
+ if (dsk_size < btree->allocsize)
+ ++num_smaller_allocsz;
+ if (dsk_size < min_written_size)
+ min_written_size = dsk_size;
+ ++written_size_cnt;
+ written_size_sum += dsk_size;
+ } else
+ ++num_memory;
+
+ if (WT_PAGE_IS_INTERNAL(page))
+ ++pages_internal;
+ else
+ ++pages_leaf;
+
+ /* Skip root pages since they are never considered */
+ if (__wt_ref_is_root(next_walk))
+ continue;
+
+ gen_gap =
+ S2C(session)->cache->evict_pass_gen - page->evict_pass_gen;
+ if (gen_gap > gen_gap_max)
+ gen_gap_max = gen_gap;
+ gen_gap_sum += gen_gap;
+ ++gen_gap_cnt;
+ }
+
+ WT_STAT_DATA_SET(session, cache_state_avg_written_size,
+ written_size_cnt == 0 ? 0 : written_size_sum / written_size_cnt);
+ WT_STAT_DATA_SET(session, cache_state_gen_avg_gap,
+ gen_gap_cnt == 0 ? 0 : gen_gap_sum / gen_gap_cnt);
+
+ WT_STAT_DATA_SET(session, cache_state_gen_max_gap, gen_gap_max);
+ WT_STAT_DATA_SET(session, cache_state_max_pagesize, max_pagesize);
+ WT_STAT_DATA_SET(session,
+ cache_state_min_written_size, min_written_size);
+ WT_STAT_DATA_SET(session, cache_state_memory, num_memory);
+ WT_STAT_DATA_SET(session, cache_state_queued, num_queued);
+ WT_STAT_DATA_SET(session, cache_state_not_queueable, num_not_queueable);
+ WT_STAT_DATA_SET(session,
+ cache_state_smaller_alloc_size, num_smaller_allocsz);
+ WT_STAT_DATA_SET(session, cache_state_pages, walk_count);
+ WT_STAT_DATA_SET(session, cache_state_pages_clean, pages_clean);
+ WT_STAT_DATA_SET(session, cache_state_pages_dirty, pages_dirty);
+ WT_STAT_DATA_SET(session, cache_state_pages_internal, pages_internal);
+ WT_STAT_DATA_SET(session, cache_state_pages_leaf, pages_leaf);
+ WT_STAT_DATA_SET(session,
+ cache_state_refs_skipped, walk_count - seen_count);
+}
+
+/*
+ * __wt_curstat_cache_walk --
+ * Initialize the statistics for a cache cache_walk pass.
+ */
+void
+__wt_curstat_cache_walk(WT_SESSION_IMPL *session)
+{
+ WT_BTREE *btree;
+ WT_CONNECTION_IMPL *conn;
+ WT_PAGE_INDEX *root_idx;
+
+ btree = S2BT(session);
+ conn = S2C(session);
+
+ /* Set statistics that don't require walking the cache. */
+ WT_STAT_DATA_SET(session,
+ cache_state_gen_current, conn->cache->evict_pass_gen);
+
+ /* Root page statistics */
+ root_idx = WT_INTL_INDEX_GET_SAFE(btree->root.page);
+ WT_STAT_DATA_SET(session,
+ cache_state_root_entries, root_idx->entries);
+ WT_STAT_DATA_SET(session,
+ cache_state_root_size, btree->root.page->memory_footprint);
+
+ WT_WITH_HANDLE_LIST_LOCK(session, __evict_stat_walk(session));
+}
diff --git a/src/third_party/wiredtiger/src/include/api.h b/src/third_party/wiredtiger/src/include/api.h
index e1b2f8edaf3..2783d17f825 100644
--- a/src/third_party/wiredtiger/src/include/api.h
+++ b/src/third_party/wiredtiger/src/include/api.h
@@ -139,7 +139,9 @@
(s) = (WT_SESSION_IMPL *)(cur)->session; \
TXN_API_CALL_NOCONF(s, WT_CURSOR, n, cur, \
((bt) == NULL) ? NULL : ((WT_BTREE *)(bt))->dhandle); \
- if (F_ISSET(S2C(s), WT_CONN_IN_MEMORY) && __wt_cache_full(s)) \
+ if (F_ISSET(S2C(s), WT_CONN_IN_MEMORY) && \
+ !F_ISSET((WT_BTREE *)(bt), WT_BTREE_IGNORE_CACHE) && \
+ __wt_cache_full(s)) \
WT_ERR(WT_CACHE_FULL);
#define JOINABLE_CURSOR_UPDATE_API_CALL(cur, s, n, bt) \
diff --git a/src/third_party/wiredtiger/src/include/btmem.h b/src/third_party/wiredtiger/src/include/btmem.h
index b4ca937e7ed..84c91097a99 100644
--- a/src/third_party/wiredtiger/src/include/btmem.h
+++ b/src/third_party/wiredtiger/src/include/btmem.h
@@ -430,6 +430,8 @@ struct __wt_page_modify {
#define WT_PM_REC_MULTIBLOCK 2 /* Reconciliation: multiple blocks */
#define WT_PM_REC_REPLACE 3 /* Reconciliation: single block */
uint8_t rec_result; /* Reconciliation state */
+
+ uint8_t update_restored; /* Page created by restoring updates */
};
/*
@@ -619,6 +621,8 @@ struct __wt_page {
#define WT_READGEN_START_VALUE 100
#define WT_READGEN_STEP 100
uint64_t read_gen;
+ /* The evict pass generation for the page */
+ uint64_t evict_pass_gen;
size_t memory_footprint; /* Memory attached to the page */
diff --git a/src/third_party/wiredtiger/src/include/btree.h b/src/third_party/wiredtiger/src/include/btree.h
index cfaf59e70e1..713d46ae85f 100644
--- a/src/third_party/wiredtiger/src/include/btree.h
+++ b/src/third_party/wiredtiger/src/include/btree.h
@@ -119,7 +119,7 @@ struct __wt_btree {
uint64_t last_recno; /* Column-store last record number */
WT_REF root; /* Root page reference */
- int modified; /* If the tree ever modified */
+ bool modified; /* If the tree ever modified */
bool bulk_load_ok; /* Bulk-load is a possibility */
WT_BM *bm; /* Block manager reference */
@@ -154,18 +154,19 @@ struct __wt_btree {
WT_SPINLOCK flush_lock; /* Lock to flush the tree's pages */
/* Flags values up to 0xff are reserved for WT_DHANDLE_* */
-#define WT_BTREE_BULK 0x00100 /* Bulk-load handle */
-#define WT_BTREE_IN_MEMORY 0x00200 /* Cache-resident object */
-#define WT_BTREE_LOOKASIDE 0x00400 /* Look-aside table */
-#define WT_BTREE_NO_CHECKPOINT 0x00800 /* Disable checkpoints */
-#define WT_BTREE_NO_EVICTION 0x01000 /* Disable eviction */
-#define WT_BTREE_NO_LOGGING 0x02000 /* Disable logging */
-#define WT_BTREE_NO_RECONCILE 0x04000 /* Allow splits, even with no evict */
-#define WT_BTREE_REBALANCE 0x08000 /* Handle is for rebalance */
-#define WT_BTREE_SALVAGE 0x10000 /* Handle is for salvage */
-#define WT_BTREE_SKIP_CKPT 0x20000 /* Handle skipped checkpoint */
-#define WT_BTREE_UPGRADE 0x40000 /* Handle is for upgrade */
-#define WT_BTREE_VERIFY 0x80000 /* Handle is for verify */
+#define WT_BTREE_BULK 0x000100 /* Bulk-load handle */
+#define WT_BTREE_IGNORE_CACHE 0x000200 /* Cache-resident object */
+#define WT_BTREE_IN_MEMORY 0x000400 /* Cache-resident object */
+#define WT_BTREE_LOOKASIDE 0x000800 /* Look-aside table */
+#define WT_BTREE_NO_CHECKPOINT 0x001000 /* Disable checkpoints */
+#define WT_BTREE_NO_EVICTION 0x002000 /* Disable eviction */
+#define WT_BTREE_NO_LOGGING 0x004000 /* Disable logging */
+#define WT_BTREE_NO_RECONCILE 0x008000 /* Allow splits, even with no evict */
+#define WT_BTREE_REBALANCE 0x010000 /* Handle is for rebalance */
+#define WT_BTREE_SALVAGE 0x020000 /* Handle is for salvage */
+#define WT_BTREE_SKIP_CKPT 0x040000 /* Handle skipped checkpoint */
+#define WT_BTREE_UPGRADE 0x080000 /* Handle is for upgrade */
+#define WT_BTREE_VERIFY 0x100000 /* Handle is for verify */
uint32_t flags;
};
diff --git a/src/third_party/wiredtiger/src/include/btree.i b/src/third_party/wiredtiger/src/include/btree.i
index a9ce4f754a9..e48189d50ea 100644
--- a/src/third_party/wiredtiger/src/include/btree.i
+++ b/src/third_party/wiredtiger/src/include/btree.i
@@ -485,6 +485,38 @@ __wt_page_only_modify_set(WT_SESSION_IMPL *session, WT_PAGE *page)
}
/*
+ * __wt_tree_modify_set --
+ * Mark the tree dirty.
+ */
+static inline void
+__wt_tree_modify_set(WT_SESSION_IMPL *session)
+{
+ /*
+ * Test before setting the dirty flag, it's a hot cache line.
+ *
+ * The tree's modified flag is cleared by the checkpoint thread: set it
+ * and insert a barrier before dirtying the page. (I don't think it's
+ * a problem if the tree is marked dirty with all the pages clean, it
+ * might result in an extra checkpoint that doesn't do any work but it
+ * shouldn't cause problems; regardless, let's play it safe.)
+ */
+ if (!S2BT(session)->modified) {
+ /* Assert we never dirty a checkpoint handle. */
+ WT_ASSERT(session, session->dhandle->checkpoint == NULL);
+
+ S2BT(session)->modified = true;
+ WT_FULL_BARRIER();
+ }
+
+ /*
+ * The btree may already be marked dirty while the connection is still
+ * clean; mark the connection dirty outside the test of the btree state.
+ */
+ if (!S2C(session)->modified)
+ S2C(session)->modified = true;
+}
+
+/*
* __wt_page_modify_clear --
* Clean a modified page.
*/
@@ -513,22 +545,9 @@ __wt_page_modify_set(WT_SESSION_IMPL *session, WT_PAGE *page)
/*
* Mark the tree dirty (even if the page is already marked dirty), newly
* created pages to support "empty" files are dirty, but the file isn't
- * marked dirty until there's a real change needing to be written. Test
- * before setting the dirty flag, it's a hot cache line.
- *
- * The tree's modified flag is cleared by the checkpoint thread: set it
- * and insert a barrier before dirtying the page. (I don't think it's
- * a problem if the tree is marked dirty with all the pages clean, it
- * might result in an extra checkpoint that doesn't do any work but it
- * shouldn't cause problems; regardless, let's play it safe.)
+ * marked dirty until there's a real change needing to be written.
*/
- if (S2BT(session)->modified == 0) {
- /* Assert we never dirty a checkpoint handle. */
- WT_ASSERT(session, session->dhandle->checkpoint == NULL);
-
- S2BT(session)->modified = 1;
- WT_FULL_BARRIER();
- }
+ __wt_tree_modify_set(session);
__wt_page_only_modify_set(session, page);
}
@@ -1159,15 +1178,7 @@ __wt_leaf_page_can_split(WT_SESSION_IMPL *session, WT_PAGE *page)
* There is no point doing an in-memory split unless there is a lot of
* data in the last skiplist on the page. Split if there are enough
* items and the skiplist does not fit within a single disk page.
- *
- * Rather than scanning the whole list, walk a higher level, which
- * gives a sample of the items -- at level 0 we have all the items, at
- * level 1 we have 1/4 and at level 2 we have 1/16th. If we see more
- * than 30 items and more data than would fit in a disk page, split.
*/
-#define WT_MIN_SPLIT_DEPTH 2
-#define WT_MIN_SPLIT_COUNT 30
-#define WT_MIN_SPLIT_MULTIPLIER 16 /* At level 2, we see 1/16th entries */
ins_head = page->type == WT_PAGE_ROW_LEAF ?
(page->pg_row_entries == 0 ?
@@ -1176,8 +1187,40 @@ __wt_leaf_page_can_split(WT_SESSION_IMPL *session, WT_PAGE *page)
WT_COL_APPEND(page);
if (ins_head == NULL)
return (false);
+
+ /*
+ * In the extreme case, where the page is much larger than the maximum
+ * size, split as soon as there are 5 items on the page.
+ */
+#define WT_MAX_SPLIT_COUNT 5
+ if (page->memory_footprint > btree->maxleafpage * 2) {
+ for (count = 0, ins = ins_head->head[0];
+ ins != NULL;
+ ins = ins->next[0]) {
+ if (++count < WT_MAX_SPLIT_COUNT)
+ continue;
+
+ WT_STAT_CONN_INCR(session, cache_inmem_splittable);
+ WT_STAT_DATA_INCR(session, cache_inmem_splittable);
+ return (true);
+ }
+
+ return (false);
+ }
+
+ /*
+ * Rather than scanning the whole list, walk a higher level, which
+ * gives a sample of the items -- at level 0 we have all the items, at
+ * level 1 we have 1/4 and at level 2 we have 1/16th. If we see more
+ * than 30 items and more data than would fit in a disk page, split.
+ */
+#define WT_MIN_SPLIT_DEPTH 2
+#define WT_MIN_SPLIT_COUNT 30
+#define WT_MIN_SPLIT_MULTIPLIER 16 /* At level 2, we see 1/16th entries */
+
for (count = 0, size = 0, ins = ins_head->head[WT_MIN_SPLIT_DEPTH];
- ins != NULL; ins = ins->next[WT_MIN_SPLIT_DEPTH]) {
+ ins != NULL;
+ ins = ins->next[WT_MIN_SPLIT_DEPTH]) {
count += WT_MIN_SPLIT_MULTIPLIER;
size += WT_MIN_SPLIT_MULTIPLIER *
(WT_INSERT_KEY_SIZE(ins) + WT_UPDATE_MEMSIZE(ins->upd));
@@ -1382,9 +1425,11 @@ __wt_page_swap_func(
* page not having been released, and that's not the case.
*/
if (LF_ISSET(WT_READ_NOTFOUND_OK) && ret == WT_NOTFOUND)
- return (EINVAL);
+ WT_RET_MSG(session,
+ EINVAL, "page-release WT_NOTFOUND error mapped to EINVAL");
if (LF_ISSET(WT_READ_RESTART_OK) && ret == WT_RESTART)
- return (EINVAL);
+ WT_RET_MSG(session,
+ EINVAL, "page-release WT_RESTART error mapped to EINVAL");
return (ret);
}
diff --git a/src/third_party/wiredtiger/src/include/cache.h b/src/third_party/wiredtiger/src/include/cache.h
index b24b625aec4..9a2b83b5b57 100644
--- a/src/third_party/wiredtiger/src/include/cache.h
+++ b/src/third_party/wiredtiger/src/include/cache.h
@@ -91,6 +91,7 @@ struct __wt_cache {
uint64_t read_gen; /* Current page read generation */
uint64_t read_gen_oldest; /* Oldest read generation the eviction
* server saw in its last queue load */
+ uint64_t evict_pass_gen; /* Number of eviction passes */
/*
* Eviction thread information.
diff --git a/src/third_party/wiredtiger/src/include/cache.i b/src/third_party/wiredtiger/src/include/cache.i
index 4255d04ec37..17ab39e97d2 100644
--- a/src/third_party/wiredtiger/src/include/cache.i
+++ b/src/third_party/wiredtiger/src/include/cache.i
@@ -355,7 +355,7 @@ __wt_cache_eviction_check(WT_SESSION_IMPL *session, bool busy, bool *didworkp)
txn_state = WT_SESSION_TXN_STATE(session);
busy = busy || txn_state->id != WT_TXN_NONE ||
session->nhazard > 0 ||
- (txn_state->snap_min != WT_TXN_NONE &&
+ (txn_state->pinned_id != WT_TXN_NONE &&
txn_global->current != txn_global->oldest_id);
/*
diff --git a/src/third_party/wiredtiger/src/include/connection.h b/src/third_party/wiredtiger/src/include/connection.h
index e19ad684b24..7d3d07a6abc 100644
--- a/src/third_party/wiredtiger/src/include/connection.h
+++ b/src/third_party/wiredtiger/src/include/connection.h
@@ -269,7 +269,8 @@ struct __wt_connection_impl {
WT_TXN_GLOBAL txn_global; /* Global transaction state */
WT_RWLOCK *hot_backup_lock; /* Hot backup serialization */
- bool hot_backup;
+ bool hot_backup; /* Hot backup in progress */
+ char **hot_backup_list; /* Hot backup file list */
WT_SESSION_IMPL *ckpt_session; /* Checkpoint thread session */
wt_thread_t ckpt_tid; /* Checkpoint thread */
@@ -285,20 +286,14 @@ struct __wt_connection_impl {
uint64_t ckpt_time_recent; /* Checkpoint time recent/total */
uint64_t ckpt_time_total;
-#define WT_CONN_STAT_ALL 0x01 /* "all" statistics configured */
-#define WT_CONN_STAT_CLEAR 0x02 /* clear after gathering */
-#define WT_CONN_STAT_FAST 0x04 /* "fast" statistics configured */
-#define WT_CONN_STAT_JSON 0x08 /* output JSON format */
-#define WT_CONN_STAT_ON_CLOSE 0x10 /* output statistics on close */
-#define WT_CONN_STAT_SIZE 0x20 /* "size" statistics configured */
- uint32_t stat_flags;
+ uint32_t stat_flags; /* Options declared in flags.py */
/* Connection statistics */
WT_CONNECTION_STATS *stats[WT_COUNTER_SLOTS];
- WT_CONNECTION_STATS stat_array[WT_COUNTER_SLOTS];
+ WT_CONNECTION_STATS *stat_array;
WT_ASYNC *async; /* Async structure */
- int async_cfg; /* Global async configuration */
+ bool async_cfg; /* Global async configuration */
uint32_t async_size; /* Async op array size */
uint32_t async_workers; /* Number of async workers */
@@ -352,6 +347,12 @@ struct __wt_connection_impl {
WT_SESSION_IMPL *meta_ckpt_session;/* Metadata checkpoint session */
+ /*
+ * Is there a data/schema change that needs to be the part of a
+ * checkpoint.
+ */
+ bool modified;
+
WT_SESSION_IMPL *sweep_session; /* Handle sweep session */
wt_thread_t sweep_tid; /* Handle sweep thread */
int sweep_tid_set; /* Handle sweep thread set */
diff --git a/src/third_party/wiredtiger/src/include/cursor.h b/src/third_party/wiredtiger/src/include/cursor.h
index f1fa4d193ac..d522abc2a56 100644
--- a/src/third_party/wiredtiger/src/include/cursor.h
+++ b/src/third_party/wiredtiger/src/include/cursor.h
@@ -59,10 +59,6 @@
0 /* uint32_t flags */ \
}
-struct __wt_cursor_backup_entry {
- char *name; /* File name */
- WT_DATA_HANDLE *handle; /* Handle */
-};
struct __wt_cursor_backup {
WT_CURSOR iface;
@@ -70,7 +66,7 @@ struct __wt_cursor_backup {
WT_FSTREAM *bfs; /* Backup file stream */
uint32_t maxid; /* Maximum log file ID seen */
- WT_CURSOR_BACKUP_ENTRY *list; /* List of files to be copied. */
+ char **list; /* List of files to be copied. */
size_t list_allocated;
size_t list_next;
@@ -365,9 +361,11 @@ struct __wt_cursor_join_entry {
uint32_t bloom_hash_count; /* hash functions in bloom */
uint64_t count; /* approx number of matches */
-#define WT_CURJOIN_ENTRY_BLOOM 0x01 /* use a bloom filter */
-#define WT_CURJOIN_ENTRY_DISJUNCTION 0x02 /* endpoints are or-ed */
-#define WT_CURJOIN_ENTRY_OWN_BLOOM 0x04 /* this entry owns the bloom */
+#define WT_CURJOIN_ENTRY_BLOOM 0x01 /* use a bloom filter */
+#define WT_CURJOIN_ENTRY_DISJUNCTION 0x02 /* endpoints are or-ed */
+#define WT_CURJOIN_ENTRY_FALSE_POSITIVES 0x04 /* after bloom filter do not
+ * filter false positives */
+#define WT_CURJOIN_ENTRY_OWN_BLOOM 0x08 /* this entry owns the bloom */
uint8_t flags;
WT_CURSOR_JOIN_ENDPOINT *ends; /* reference endpoints */
@@ -467,7 +465,7 @@ struct __wt_cursor_stat {
uint64_t v; /* Current stats value */
WT_ITEM pv; /* Current stats value (string) */
- /* Uses the same values as WT_CONNECTION::stat_flags field */
+ /* Options declared in flags.py, shared by WT_CONNECTION::stat_flags */
uint32_t flags;
};
diff --git a/src/third_party/wiredtiger/src/include/dhandle.h b/src/third_party/wiredtiger/src/include/dhandle.h
index 9a11594c893..001eb268f86 100644
--- a/src/third_party/wiredtiger/src/include/dhandle.h
+++ b/src/third_party/wiredtiger/src/include/dhandle.h
@@ -75,7 +75,7 @@ struct __wt_data_handle {
/* Data-source statistics */
WT_DSRC_STATS *stats[WT_COUNTER_SLOTS];
- WT_DSRC_STATS stat_array[WT_COUNTER_SLOTS];
+ WT_DSRC_STATS *stat_array;
/* Flags values over 0xff are reserved for WT_BTREE_* */
#define WT_DHANDLE_DEAD 0x01 /* Dead, awaiting discard */
diff --git a/src/third_party/wiredtiger/src/include/extern.h b/src/third_party/wiredtiger/src/include/extern.h
index 5444b2e9f14..6234f2f6bc5 100644
--- a/src/third_party/wiredtiger/src/include/extern.h
+++ b/src/third_party/wiredtiger/src/include/extern.h
@@ -270,7 +270,6 @@ extern int __wt_connection_open(WT_CONNECTION_IMPL *conn, const char *cfg[]) WT_
extern int __wt_connection_close(WT_CONNECTION_IMPL *conn) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_connection_workers(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_conn_stat_init(WT_SESSION_IMPL *session);
-extern int __wt_statlog_log_one(WT_SESSION_IMPL *session) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_statlog_create(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_statlog_destroy(WT_SESSION_IMPL *session, bool is_close) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_sweep_config(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -282,6 +281,7 @@ extern int __wt_curbulk_init(WT_SESSION_IMPL *session, WT_CURSOR_BULK *cbulk, bo
extern int __wt_curconfig_open(WT_SESSION_IMPL *session, const char *uri, const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curds_open( WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_DATA_SOURCE *dsrc, WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curdump_create(WT_CURSOR *child, WT_CURSOR *owner, WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_curfile_next_random(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curfile_update_check(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curfile_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_curindex_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -352,6 +352,7 @@ extern void __wt_evict_priority_clear(WT_SESSION_IMPL *session);
extern int __wt_cache_dump(WT_SESSION_IMPL *session, const char *ofile) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_page_release_evict(WT_SESSION_IMPL *session, WT_REF *ref) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_evict(WT_SESSION_IMPL *session, WT_REF *ref, bool closing) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_curstat_cache_walk(WT_SESSION_IMPL *session);
extern void __wt_log_ckpt(WT_SESSION_IMPL *session, WT_LSN *ckp_lsn);
extern int __wt_log_flush_lsn(WT_SESSION_IMPL *session, WT_LSN *lsn, bool start) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_log_background(WT_SESSION_IMPL *session, WT_LSN *lsn);
@@ -582,6 +583,7 @@ extern int __wt_curstat_table_init(WT_SESSION_IMPL *session, const char *uri, co
extern int __wt_schema_truncate( WT_SESSION_IMPL *session, const char *uri, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_range_truncate(WT_CURSOR *start, WT_CURSOR *stop) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_schema_range_truncate( WT_SESSION_IMPL *session, WT_CURSOR *start, WT_CURSOR *stop) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_schema_backup_check(WT_SESSION_IMPL *session, const char *name) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern WT_DATA_SOURCE *__wt_schema_get_source(WT_SESSION_IMPL *session, const char *name);
extern int __wt_str_name_check(WT_SESSION_IMPL *session, const char *str) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_name_check(WT_SESSION_IMPL *session, const char *str, size_t len) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -636,6 +638,7 @@ extern int __wt_panic(WT_SESSION_IMPL *session) WT_GCC_FUNC_DECL_ATTRIBUTE((cold
extern int __wt_illegal_value(WT_SESSION_IMPL *session, const char *name) WT_GCC_FUNC_DECL_ATTRIBUTE((cold)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_object_unsupported(WT_SESSION_IMPL *session, const char *uri) WT_GCC_FUNC_DECL_ATTRIBUTE((cold)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_bad_object_type(WT_SESSION_IMPL *session, const char *uri) WT_GCC_FUNC_DECL_ATTRIBUTE((cold)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_unexpected_object_type( WT_SESSION_IMPL *session, const char *uri, const char *expect) WT_GCC_FUNC_DECL_ATTRIBUTE((cold)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_library_init(void) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_breakpoint(void);
extern void __wt_attach(WT_SESSION_IMPL *session);
@@ -664,6 +667,7 @@ extern int __wt_huffman_encode(WT_SESSION_IMPL *session, void *huffman_arg, cons
extern int __wt_huffman_decode(WT_SESSION_IMPL *session, void *huffman_arg, const uint8_t *from_arg, size_t from_len, WT_ITEM *to_buf) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_rwlock_alloc( WT_SESSION_IMPL *session, WT_RWLOCK **rwlockp, const char *name) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_try_readlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_readlock_spin(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock);
extern void __wt_readlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock);
extern void __wt_readunlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock);
extern int __wt_try_writelock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -677,7 +681,7 @@ extern uint32_t __wt_log2_int(uint32_t n);
extern bool __wt_ispo2(uint32_t v);
extern uint32_t __wt_rduppo2(uint32_t n, uint32_t po2);
extern void __wt_random_init(WT_RAND_STATE volatile *rnd_state);
-extern int __wt_random_init_seed( WT_SESSION_IMPL *session, WT_RAND_STATE volatile *rnd_state) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_random_init_seed( WT_SESSION_IMPL *session, WT_RAND_STATE volatile *rnd_state);
extern uint32_t __wt_random(WT_RAND_STATE volatile *rnd_state);
extern int __wt_buf_grow_worker(WT_SESSION_IMPL *session, WT_ITEM *buf, size_t size) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_buf_fmt(WT_SESSION_IMPL *session, WT_ITEM *buf, const char *fmt, ...) WT_GCC_FUNC_DECL_ATTRIBUTE((format (printf, 3, 4))) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -695,14 +699,16 @@ extern void *__wt_ext_scr_alloc( WT_EXTENSION_API *wt_api, WT_SESSION *wt_sessio
extern void __wt_ext_scr_free(WT_EXTENSION_API *wt_api, WT_SESSION *wt_session, void *p);
extern int __wt_stat_dsrc_desc(WT_CURSOR_STAT *cst, int slot, const char **p) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_stat_dsrc_init_single(WT_DSRC_STATS *stats);
-extern void __wt_stat_dsrc_init(WT_DATA_HANDLE *handle);
+extern int __wt_stat_dsrc_init( WT_SESSION_IMPL *session, WT_DATA_HANDLE *handle) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_stat_dsrc_discard( WT_SESSION_IMPL *session, WT_DATA_HANDLE *handle);
extern void __wt_stat_dsrc_clear_single(WT_DSRC_STATS *stats);
extern void __wt_stat_dsrc_clear_all(WT_DSRC_STATS **stats);
extern void __wt_stat_dsrc_aggregate_single( WT_DSRC_STATS *from, WT_DSRC_STATS *to);
extern void __wt_stat_dsrc_aggregate( WT_DSRC_STATS **from, WT_DSRC_STATS *to);
extern int __wt_stat_connection_desc(WT_CURSOR_STAT *cst, int slot, const char **p) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_stat_connection_init_single(WT_CONNECTION_STATS *stats);
-extern void __wt_stat_connection_init(WT_CONNECTION_IMPL *handle);
+extern int __wt_stat_connection_init( WT_SESSION_IMPL *session, WT_CONNECTION_IMPL *handle) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_stat_connection_discard( WT_SESSION_IMPL *session, WT_CONNECTION_IMPL *handle);
extern void __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats);
extern void __wt_stat_connection_clear_all(WT_CONNECTION_STATS **stats);
extern void __wt_stat_connection_aggregate( WT_CONNECTION_STATS **from, WT_CONNECTION_STATS *to);
@@ -723,7 +729,7 @@ extern int __wt_txn_config(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_F
extern void __wt_txn_release(WT_SESSION_IMPL *session);
extern int __wt_txn_commit(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_txn_rollback(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
-extern int __wt_txn_init(WT_SESSION_IMPL *session) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_txn_init(WT_SESSION_IMPL *session, WT_SESSION_IMPL *session_ret) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_txn_stats_update(WT_SESSION_IMPL *session);
extern void __wt_txn_destroy(WT_SESSION_IMPL *session);
extern int __wt_txn_global_init(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
@@ -741,7 +747,7 @@ extern int __wt_ext_transaction_visible( WT_EXTENSION_API *wt_api, WT_SESSION *w
extern void __wt_txn_op_free(WT_SESSION_IMPL *session, WT_TXN_OP *op);
extern int __wt_txn_log_op(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_txn_log_commit(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
-extern int __wt_txn_checkpoint_logread( WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end, WT_LSN *ckpt_lsn) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern int __wt_txn_checkpoint_logread(WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end, WT_LSN *ckpt_lsn) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_txn_checkpoint_log( WT_SESSION_IMPL *session, bool full, uint32_t flags, WT_LSN *lsnp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_txn_truncate_log( WT_SESSION_IMPL *session, WT_CURSOR_BTREE *start, WT_CURSOR_BTREE *stop) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_txn_truncate_end(WT_SESSION_IMPL *session);
diff --git a/src/third_party/wiredtiger/src/include/extern_posix.h b/src/third_party/wiredtiger/src/include/extern_posix.h
index d2f74d2ffe4..fd94ef0ddf2 100644
--- a/src/third_party/wiredtiger/src/include/extern_posix.h
+++ b/src/third_party/wiredtiger/src/include/extern_posix.h
@@ -27,5 +27,5 @@ extern void __wt_sleep(uint64_t seconds, uint64_t micro_seconds);
extern int __wt_thread_create(WT_SESSION_IMPL *session, wt_thread_t *tidret, WT_THREAD_CALLBACK(*func)(void *), void *arg) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_thread_join(WT_SESSION_IMPL *session, wt_thread_t tid) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_thread_id(char *buf, size_t buflen);
-extern int __wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp);
extern void __wt_yield(void);
diff --git a/src/third_party/wiredtiger/src/include/extern_win.h b/src/third_party/wiredtiger/src/include/extern_win.h
index 8c2b19056e0..f06ee881ece 100644
--- a/src/third_party/wiredtiger/src/include/extern_win.h
+++ b/src/third_party/wiredtiger/src/include/extern_win.h
@@ -25,7 +25,7 @@ extern void __wt_sleep(uint64_t seconds, uint64_t micro_seconds);
extern int __wt_thread_create(WT_SESSION_IMPL *session, wt_thread_t *tidret, WT_THREAD_CALLBACK(*func)(void *), void *arg) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_thread_join(WT_SESSION_IMPL *session, wt_thread_t tid) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern void __wt_thread_id(char *buf, size_t buflen);
-extern int __wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
+extern void __wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp);
extern int __wt_to_utf16_string( WT_SESSION_IMPL *session, const char*utf8, WT_ITEM **outbuf) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern int __wt_to_utf8_string( WT_SESSION_IMPL *session, const wchar_t*wide, WT_ITEM **outbuf) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
extern DWORD __wt_getlasterror(void);
diff --git a/src/third_party/wiredtiger/src/include/flags.h b/src/third_party/wiredtiger/src/include/flags.h
index 5d718da473d..b0d167525b2 100644
--- a/src/third_party/wiredtiger/src/include/flags.h
+++ b/src/third_party/wiredtiger/src/include/flags.h
@@ -70,6 +70,14 @@
#define WT_SESSION_NO_SCHEMA_LOCK 0x00020000
#define WT_SESSION_QUIET_CORRUPT_FILE 0x00040000
#define WT_SESSION_SERVER_ASYNC 0x00080000
+#define WT_STAT_CLEAR 0x00000001
+#define WT_STAT_JSON 0x00000002
+#define WT_STAT_ON_CLOSE 0x00000004
+#define WT_STAT_TYPE_ALL 0x00000008
+#define WT_STAT_TYPE_CACHE_WALK 0x00000010
+#define WT_STAT_TYPE_FAST 0x00000020
+#define WT_STAT_TYPE_SIZE 0x00000040
+#define WT_STAT_TYPE_TREE_WALK 0x00000080
#define WT_TXN_LOG_CKPT_CLEANUP 0x00000001
#define WT_TXN_LOG_CKPT_PREPARE 0x00000002
#define WT_TXN_LOG_CKPT_START 0x00000004
diff --git a/src/third_party/wiredtiger/src/include/lsm.h b/src/third_party/wiredtiger/src/include/lsm.h
index 2550ca444c1..b433e4c3c44 100644
--- a/src/third_party/wiredtiger/src/include/lsm.h
+++ b/src/third_party/wiredtiger/src/include/lsm.h
@@ -31,6 +31,17 @@ struct __wt_lsm_worker_args {
};
/*
+ * WT_LSM_CURSOR_CHUNK --
+ * Iterator struct containing all the LSM cursor access points for a chunk.
+ */
+struct __wt_lsm_cursor_chunk {
+ WT_BLOOM *bloom; /* Bloom filter handle for each chunk.*/
+ WT_CURSOR *cursor; /* Cursor handle for each chunk. */
+ uint64_t count; /* Number of items in chunk */
+ uint64_t switch_txn; /* Switch txn for each chunk */
+};
+
+/*
* WT_CURSOR_LSM --
* An LSM cursor.
*/
@@ -43,17 +54,12 @@ struct __wt_cursor_lsm {
u_int nchunks; /* Number of chunks in the cursor */
u_int nupdates; /* Updates needed (including
snapshot isolation checks). */
- WT_BLOOM **blooms; /* Bloom filter handles. */
- size_t bloom_alloc;
-
- WT_CURSOR **cursors; /* Cursor handles. */
- size_t cursor_alloc;
-
- WT_CURSOR *current; /* The current cursor for iteration */
+ WT_CURSOR *current; /* The current cursor for iteration */
WT_LSM_CHUNK *primary_chunk; /* The current primary chunk */
- uint64_t *switch_txn; /* Switch txn for each chunk */
- size_t txnid_alloc;
+ WT_LSM_CURSOR_CHUNK **chunks; /* Array of LSM cursor units */
+ size_t chunks_alloc; /* Current size iterators array */
+ size_t chunks_count; /* Current number of iterators */
u_int update_count; /* Updates performed. */
diff --git a/src/third_party/wiredtiger/src/include/meta.h b/src/third_party/wiredtiger/src/include/meta.h
index 63c79dbc72e..6d4a167a8e5 100644
--- a/src/third_party/wiredtiger/src/include/meta.h
+++ b/src/third_party/wiredtiger/src/include/meta.h
@@ -29,9 +29,8 @@
#define WT_LAS_URI "file:WiredTigerLAS.wt" /* Lookaside table URI*/
/*
- * Pre computed hash for the metadata file. Used to optimize comparisons
- * against the metafile URI. The validity is checked on connection open
- * when diagnostic is enabled.
+ * Optimize comparisons against the metafile URI, flag handles that reference
+ * the metadata file.
*/
#define WT_IS_METADATA(session, dh) \
F_ISSET((dh), WT_DHANDLE_IS_METADATA)
diff --git a/src/third_party/wiredtiger/src/include/misc.i b/src/third_party/wiredtiger/src/include/misc.i
index f267c7afc91..befd480e085 100644
--- a/src/third_party/wiredtiger/src/include/misc.i
+++ b/src/third_party/wiredtiger/src/include/misc.i
@@ -33,16 +33,14 @@ __wt_strdup(WT_SESSION_IMPL *session, const char *str, void *retp)
* __wt_seconds --
* Return the seconds since the Epoch.
*/
-static inline int
+static inline void
__wt_seconds(WT_SESSION_IMPL *session, time_t *timep)
{
struct timespec t;
- WT_RET(__wt_epoch(session, &t));
+ __wt_epoch(session, &t);
*timep = t.tv_sec;
-
- return (0);
}
/*
diff --git a/src/third_party/wiredtiger/src/include/mutex.h b/src/third_party/wiredtiger/src/include/mutex.h
index f0f8173bad4..84f015d6b67 100644
--- a/src/third_party/wiredtiger/src/include/mutex.h
+++ b/src/third_party/wiredtiger/src/include/mutex.h
@@ -43,7 +43,7 @@ typedef union { /* Read/write lock */
uint16_t writers; /* Now serving for writers */
uint16_t readers; /* Now serving for readers */
uint16_t next; /* Next available ticket number */
- uint16_t __notused; /* Padding */
+ uint16_t writers_active;/* Count of active writers */
} s;
} wt_rwlock_t;
@@ -74,6 +74,16 @@ struct __wt_rwlock {
struct WT_COMPILER_TYPE_ALIGN(WT_CACHE_LINE_ALIGNMENT) __wt_spinlock {
volatile int lock;
+
+ /*
+ * We track acquisitions and time spent waiting for some locks. For
+ * performance reasons and to make it possible to write generic code
+ * that tracks statistics for different locks, we store the offset
+ * of the statistics fields to be updated during lock acquisition.
+ */
+ int16_t stat_count_off; /* acquisitions offset */
+ int16_t stat_app_usecs_off; /* waiting application threads offset */
+ int16_t stat_int_usecs_off; /* waiting server threads offset */
};
#elif SPINLOCK_TYPE == SPINLOCK_PTHREAD_MUTEX ||\
@@ -83,7 +93,17 @@ struct WT_COMPILER_TYPE_ALIGN(WT_CACHE_LINE_ALIGNMENT) __wt_spinlock {
struct WT_COMPILER_TYPE_ALIGN(WT_CACHE_LINE_ALIGNMENT) __wt_spinlock {
wt_mutex_t lock;
- const char *name; /* Statistics: mutex name */
+ const char *name; /* Mutex name */
+
+ /*
+ * We track acquisitions and time spent waiting for some locks. For
+ * performance reasons and to make it possible to write generic code
+ * that tracks statistics for different locks, we store the offset
+ * of the statistics fields to be updated during lock acquisition.
+ */
+ int16_t stat_count_off; /* acquisitions offset */
+ int16_t stat_app_usecs_off; /* waiting application threads offset */
+ int16_t stat_int_usecs_off; /* waiting server threads offset */
int8_t initialized; /* Lock initialized, for cleanup */
};
diff --git a/src/third_party/wiredtiger/src/include/mutex.i b/src/third_party/wiredtiger/src/include/mutex.i
index cb1847d9991..a6309e0976b 100644
--- a/src/third_party/wiredtiger/src/include/mutex.i
+++ b/src/third_party/wiredtiger/src/include/mutex.i
@@ -32,6 +32,7 @@ __wt_spin_init(WT_SESSION_IMPL *session, WT_SPINLOCK *t, const char *name)
WT_UNUSED(name);
t->lock = 0;
+ t->stat_count_off = t->stat_app_usecs_off = t->stat_int_usecs_off = -1;
return (0);
}
@@ -111,6 +112,7 @@ __wt_spin_init(WT_SESSION_IMPL *session, WT_SPINLOCK *t, const char *name)
#endif
t->name = name;
+ t->stat_count_off = t->stat_app_usecs_off = t->stat_int_usecs_off = -1;
t->initialized = 1;
WT_UNUSED(session);
@@ -255,3 +257,46 @@ __wt_spin_unlock(WT_SESSION_IMPL *session, WT_SPINLOCK *t)
#error Unknown spinlock type
#endif
+
+/*
+ * WT_SPIN_INIT_TRACKED --
+ * Spinlock initialization, with tracking.
+ *
+ * Implemented as a macro so we can pass in a statistics field and convert
+ * it into a statistics structure array offset.
+ */
+#define WT_SPIN_INIT_TRACKED(session, t, name) do { \
+ WT_RET(__wt_spin_init(session, t, #name)); \
+ (t)->stat_count_off = (int16_t)WT_STATS_FIELD_TO_OFFSET( \
+ S2C(session)->stats, lock_##name##_count); \
+ (t)->stat_app_usecs_off = (int16_t)WT_STATS_FIELD_TO_OFFSET( \
+ S2C(session)->stats, lock_##name##_wait_application); \
+ (t)->stat_int_usecs_off = (int16_t)WT_STATS_FIELD_TO_OFFSET( \
+ S2C(session)->stats, lock_##name##_wait_internal); \
+} while (0)
+
+/*
+ * __wt_spin_lock_track --
+ * Spinlock acquisition, with tracking.
+ */
+static inline void
+__wt_spin_lock_track(WT_SESSION_IMPL *session, WT_SPINLOCK *t)
+{
+ struct timespec enter, leave;
+ int64_t **stats;
+
+ if (t->stat_count_off != -1 && WT_STAT_ENABLED(session)) {
+ __wt_epoch(session, &enter);
+ __wt_spin_lock(session, t);
+ __wt_epoch(session, &leave);
+ stats = (int64_t **)S2C(session)->stats;
+ stats[session->stat_bucket][t->stat_count_off]++;
+ if (F_ISSET(session, WT_SESSION_INTERNAL))
+ stats[session->stat_bucket][t->stat_int_usecs_off] +=
+ (int64_t)WT_TIMEDIFF_US(leave, enter);
+ else
+ stats[session->stat_bucket][t->stat_app_usecs_off] +=
+ (int64_t)WT_TIMEDIFF_US(leave, enter);
+ } else
+ __wt_spin_lock(session, t);
+}
diff --git a/src/third_party/wiredtiger/src/include/schema.h b/src/third_party/wiredtiger/src/include/schema.h
index f93c596e2ca..6a5ce67a867 100644
--- a/src/third_party/wiredtiger/src/include/schema.h
+++ b/src/third_party/wiredtiger/src/include/schema.h
@@ -86,11 +86,11 @@ struct __wt_table {
if (F_ISSET(session, (flag))) { \
op; \
} else { \
- __wt_spin_lock(session, (lock)); \
+ __wt_spin_lock_track(session, lock); \
F_SET(session, (flag)); \
op; \
F_CLR(session, (flag)); \
- __wt_spin_unlock(session, (lock)); \
+ __wt_spin_unlock(session, lock); \
} \
} while (0)
@@ -102,11 +102,11 @@ struct __wt_table {
ret = 0; \
if (!F_ISSET(session, (flag)) && \
F_ISSET(session, WT_SESSION_LOCK_NO_WAIT)) { \
- if ((ret = __wt_spin_trylock(session, (lock))) == 0) { \
+ if ((ret = __wt_spin_trylock(session, lock)) == 0) { \
F_SET(session, (flag)); \
op; \
F_CLR(session, (flag)); \
- __wt_spin_unlock(session, (lock)); \
+ __wt_spin_unlock(session, lock); \
} \
} else \
WT_WITH_LOCK_WAIT(session, lock, flag, op); \
diff --git a/src/third_party/wiredtiger/src/include/session.h b/src/third_party/wiredtiger/src/include/session.h
index aa51dae58c4..3f9f495c134 100644
--- a/src/third_party/wiredtiger/src/include/session.h
+++ b/src/third_party/wiredtiger/src/include/session.h
@@ -147,6 +147,9 @@ struct WT_COMPILER_TYPE_ALIGN(WT_CACHE_LINE_ALIGNMENT) __wt_session_impl {
void *reconcile; /* Reconciliation support */
int (*reconcile_cleanup)(WT_SESSION_IMPL *);
+ /* Sessions have an associated statistics bucket based on its ID. */
+ u_int stat_bucket; /* Statistics bucket offset */
+
uint32_t flags;
/*
diff --git a/src/third_party/wiredtiger/src/include/stat.h b/src/third_party/wiredtiger/src/include/stat.h
index cd0cae16826..e53414fc0c9 100644
--- a/src/third_party/wiredtiger/src/include/stat.h
+++ b/src/third_party/wiredtiger/src/include/stat.h
@@ -79,9 +79,9 @@
* those structures regardless of the specific statistic structure we're working
* with, by translating statistics structure field names to structure offsets.
*
- * Translate a statistic's value name to an offset.
+ * Translate a statistic's value name to an offset in the array.
*/
-#define WT_STATS_FIELD_TO_SLOT(stats, fld) \
+#define WT_STATS_FIELD_TO_OFFSET(stats, fld) \
(int)(&(stats)[0]->fld - (int64_t *)(stats)[0])
/*
@@ -140,38 +140,54 @@ __wt_stats_clear(void *stats_arg, int slot)
#define WT_STAT_ENABLED(session) (S2C(session)->stat_flags != 0)
#define WT_STAT_READ(stats, fld) \
- __wt_stats_aggregate(stats, WT_STATS_FIELD_TO_SLOT(stats, fld))
+ __wt_stats_aggregate(stats, WT_STATS_FIELD_TO_OFFSET(stats, fld))
#define WT_STAT_WRITE(session, stats, fld, v) do { \
if (WT_STAT_ENABLED(session)) \
(stats)->fld = (int64_t)(v); \
} while (0)
-#define WT_STAT_DECRV(session, stats, fld, value) do { \
+#define WT_STAT_DECRV_BASE(session, stat, fld, value) do { \
if (WT_STAT_ENABLED(session)) \
- (stats)[WT_STATS_SLOT_ID(session)]->fld -= (int64_t)(value); \
+ (stat)->fld -= (int64_t)(value); \
} while (0)
-#define WT_STAT_DECRV_ATOMIC(session, stats, fld, value) do { \
+#define WT_STAT_DECRV_ATOMIC_BASE(session, stat, fld, value) do { \
+ if (WT_STAT_ENABLED(session)) \
+ __wt_atomic_subi64(&(stat)->fld, (int64_t)(value)); \
+} while (0)
+#define WT_STAT_INCRV_BASE(session, stat, fld, value) do { \
+ if (WT_STAT_ENABLED(session)) \
+ (stat)->fld += (int64_t)(value); \
+} while (0)
+#define WT_STAT_INCRV_ATOMIC_BASE(session, stat, fld, value) do { \
if (WT_STAT_ENABLED(session)) \
- __wt_atomic_subi64(&(stats)[WT_STATS_SLOT_ID(session)]->fld, \
- (int64_t)(value)); \
+ __wt_atomic_addi64(&(stat)->fld, (int64_t)(value)); \
+} while (0)
+
+#define WT_STAT_DECRV(session, stats, fld, value) do { \
+ WT_STAT_DECRV_BASE( \
+ session, (stats)[(session)->stat_bucket], fld, value); \
+} while (0)
+#define WT_STAT_DECRV_ATOMIC(session, stats, fld, value) do { \
+ WT_STAT_DECRV_ATOMIC_BASE( \
+ session, (stats)[(session)->stat_bucket], fld, value); \
} while (0)
#define WT_STAT_DECR(session, stats, fld) \
WT_STAT_DECRV(session, stats, fld, 1)
+
#define WT_STAT_INCRV(session, stats, fld, value) do { \
- if (WT_STAT_ENABLED(session)) \
- (stats)[WT_STATS_SLOT_ID(session)]->fld += (int64_t)(value); \
+ WT_STAT_INCRV_BASE( \
+ session, (stats)[(session)->stat_bucket], fld, value); \
} while (0)
#define WT_STAT_INCRV_ATOMIC(session, stats, fld, value) do { \
- if (WT_STAT_ENABLED(session)) \
- __wt_atomic_addi64(&(stats)[WT_STATS_SLOT_ID(session)]->fld, \
- (int64_t)(value)); \
+ WT_STAT_INCRV_ATOMIC_BASE( \
+ session, (stats)[(session)->stat_bucket], fld, value); \
} while (0)
#define WT_STAT_INCR(session, stats, fld) \
WT_STAT_INCRV(session, stats, fld, 1)
#define WT_STAT_SET(session, stats, fld, value) do { \
if (WT_STAT_ENABLED(session)) { \
__wt_stats_clear(stats, \
- WT_STATS_FIELD_TO_SLOT(stats, fld)); \
+ WT_STATS_FIELD_TO_OFFSET(stats, fld)); \
(stats)[0]->fld = (int64_t)(value); \
} \
} while (0)
@@ -179,18 +195,24 @@ __wt_stats_clear(void *stats_arg, int slot)
/*
* Update connection handle statistics if statistics gathering is enabled.
*/
-#define WT_STAT_CONN_DECR(session, fld) \
- WT_STAT_DECR(session, S2C(session)->stats, fld)
-#define WT_STAT_CONN_DECR_ATOMIC(session, fld) \
- WT_STAT_DECRV_ATOMIC(session, S2C(session)->stats, fld, 1)
#define WT_STAT_CONN_DECRV(session, fld, value) \
- WT_STAT_DECRV(session, S2C(session)->stats, fld, value)
-#define WT_STAT_CONN_INCR(session, fld) \
- WT_STAT_INCR(session, S2C(session)->stats, fld)
-#define WT_STAT_CONN_INCR_ATOMIC(session, fld) \
- WT_STAT_INCRV_ATOMIC(session, S2C(session)->stats, fld, 1)
+ WT_STAT_DECRV_BASE(session, \
+ S2C(session)->stats[(session)->stat_bucket], fld, value)
+#define WT_STAT_CONN_DECR_ATOMIC(session, fld) \
+ WT_STAT_DECRV_ATOMIC_BASE(session, \
+ S2C(session)->stats[(session)->stat_bucket], fld, 1)
+#define WT_STAT_CONN_DECR(session, fld) \
+ WT_STAT_CONN_DECRV(session, fld, 1)
+
#define WT_STAT_CONN_INCRV(session, fld, value) \
- WT_STAT_INCRV(session, S2C(session)->stats, fld, value)
+ WT_STAT_INCRV_BASE(session, \
+ S2C(session)->stats[(session)->stat_bucket], fld, value)
+#define WT_STAT_CONN_INCR_ATOMIC(session, fld) \
+ WT_STAT_INCRV_ATOMIC_BASE(session, \
+ S2C(session)->stats[(session)->stat_bucket], fld, 1)
+#define WT_STAT_CONN_INCR(session, fld) \
+ WT_STAT_CONN_INCRV(session, fld, 1)
+
#define WT_STAT_CONN_SET(session, fld, value) \
WT_STAT_SET(session, S2C(session)->stats, fld, value)
@@ -203,21 +225,24 @@ __wt_stats_clear(void *stats_arg, int slot)
* necessary until everything is converted to using data-source handles.
*/
#define WT_STAT_DATA_DECRV(session, fld, value) do { \
- if ((session)->dhandle != NULL) \
+ if ((session)->dhandle != NULL && \
+ (session)->dhandle->stat_array != NULL) \
WT_STAT_DECRV( \
session, (session)->dhandle->stats, fld, value); \
} while (0)
#define WT_STAT_DATA_DECR(session, fld) \
WT_STAT_DATA_DECRV(session, fld, 1)
#define WT_STAT_DATA_INCRV(session, fld, value) do { \
- if ((session)->dhandle != NULL) \
+ if ((session)->dhandle != NULL && \
+ (session)->dhandle->stat_array != NULL) \
WT_STAT_INCRV( \
session, (session)->dhandle->stats, fld, value); \
} while (0)
#define WT_STAT_DATA_INCR(session, fld) \
WT_STAT_DATA_INCRV(session, fld, 1)
#define WT_STAT_DATA_SET(session, fld, value) do { \
- if ((session)->dhandle != NULL) \
+ if ((session)->dhandle != NULL && \
+ (session)->dhandle->stat_array != NULL) \
WT_STAT_SET( \
session, (session)->dhandle->stats, fld, value); \
} while (0)
@@ -263,6 +288,10 @@ struct __wt_connection_stats {
int64_t block_byte_write_checkpoint;
int64_t block_map_read;
int64_t block_byte_map_read;
+ int64_t cache_read_app_count;
+ int64_t cache_read_app_time;
+ int64_t cache_write_app_count;
+ int64_t cache_write_app_time;
int64_t cache_bytes_image;
int64_t cache_bytes_inuse;
int64_t cache_bytes_other;
@@ -356,6 +385,21 @@ struct __wt_connection_stats {
int64_t dh_sweeps;
int64_t dh_session_handles;
int64_t dh_session_sweeps;
+ int64_t lock_checkpoint_count;
+ int64_t lock_checkpoint_wait_application;
+ int64_t lock_checkpoint_wait_internal;
+ int64_t lock_handle_list_count;
+ int64_t lock_handle_list_wait_application;
+ int64_t lock_handle_list_wait_internal;
+ int64_t lock_metadata_count;
+ int64_t lock_metadata_wait_application;
+ int64_t lock_metadata_wait_internal;
+ int64_t lock_schema_count;
+ int64_t lock_schema_wait_application;
+ int64_t lock_schema_wait_internal;
+ int64_t lock_table_count;
+ int64_t lock_table_wait_application;
+ int64_t lock_table_wait_internal;
int64_t log_slot_switch_busy;
int64_t log_slot_closes;
int64_t log_slot_races;
@@ -420,6 +464,8 @@ struct __wt_connection_stats {
int64_t thread_fsync_active;
int64_t thread_read_active;
int64_t thread_write_active;
+ int64_t application_evict_time;
+ int64_t application_cache_time;
int64_t page_busy_blocked;
int64_t page_forcible_evict_blocked;
int64_t page_locked_blocked;
@@ -437,6 +483,7 @@ struct __wt_connection_stats {
int64_t txn_checkpoint_scrub_time;
int64_t txn_checkpoint_time_total;
int64_t txn_checkpoint;
+ int64_t txn_checkpoint_skipped;
int64_t txn_fail_cache;
int64_t txn_checkpoint_fsync_post;
int64_t txn_checkpoint_fsync_post_duration;
@@ -515,6 +562,24 @@ struct __wt_dsrc_stats {
int64_t cache_write;
int64_t cache_write_restore;
int64_t cache_eviction_clean;
+ int64_t cache_state_gen_avg_gap;
+ int64_t cache_state_avg_written_size;
+ int64_t cache_state_pages_clean;
+ int64_t cache_state_gen_current;
+ int64_t cache_state_pages_dirty;
+ int64_t cache_state_root_entries;
+ int64_t cache_state_pages_internal;
+ int64_t cache_state_pages_leaf;
+ int64_t cache_state_gen_max_gap;
+ int64_t cache_state_max_pagesize;
+ int64_t cache_state_min_written_size;
+ int64_t cache_state_smaller_alloc_size;
+ int64_t cache_state_memory;
+ int64_t cache_state_queued;
+ int64_t cache_state_not_queueable;
+ int64_t cache_state_refs_skipped;
+ int64_t cache_state_root_size;
+ int64_t cache_state_pages;
int64_t compress_read;
int64_t compress_write;
int64_t compress_write_fail;
diff --git a/src/third_party/wiredtiger/src/include/txn.h b/src/third_party/wiredtiger/src/include/txn.h
index 2e41ae8620d..8128e8e4cc2 100644
--- a/src/third_party/wiredtiger/src/include/txn.h
+++ b/src/third_party/wiredtiger/src/include/txn.h
@@ -49,9 +49,9 @@
WT_ASSERT((s), (s)->txn.forced_iso > 0); \
(s)->txn.forced_iso--; \
WT_ASSERT((s), txn_state->id == saved_state.id && \
- (txn_state->snap_min == saved_state.snap_min || \
- saved_state.snap_min == WT_TXN_NONE)); \
- txn_state->snap_min = saved_state.snap_min; \
+ (txn_state->pinned_id == saved_state.pinned_id || \
+ saved_state.pinned_id == WT_TXN_NONE)); \
+ txn_state->pinned_id = saved_state.pinned_id; \
} while (0)
struct __wt_named_snapshot {
@@ -59,14 +59,14 @@ struct __wt_named_snapshot {
TAILQ_ENTRY(__wt_named_snapshot) q;
- uint64_t snap_min, snap_max;
+ uint64_t pinned_id, snap_min, snap_max;
uint64_t *snapshot;
uint32_t snapshot_count;
};
struct WT_COMPILER_TYPE_ALIGN(WT_CACHE_LINE_ALIGNMENT) __wt_txn_state {
volatile uint64_t id;
- volatile uint64_t snap_min;
+ volatile uint64_t pinned_id;
};
struct __wt_txn_global {
diff --git a/src/third_party/wiredtiger/src/include/txn.i b/src/third_party/wiredtiger/src/include/txn.i
index 1a8851a9a2a..cf7e2eafc65 100644
--- a/src/third_party/wiredtiger/src/include/txn.i
+++ b/src/third_party/wiredtiger/src/include/txn.i
@@ -309,7 +309,7 @@ __wt_txn_idle_cache_check(WT_SESSION_IMPL *session)
* WT_TXN_HAS_SNAPSHOT.
*/
if (F_ISSET(txn, WT_TXN_RUNNING) &&
- !F_ISSET(txn, WT_TXN_HAS_ID) && txn_state->snap_min == WT_TXN_NONE)
+ !F_ISSET(txn, WT_TXN_HAS_ID) && txn_state->pinned_id == WT_TXN_NONE)
WT_RET(__wt_cache_eviction_check(session, false, NULL));
return (0);
@@ -480,8 +480,8 @@ __wt_txn_cursor_op(WT_SESSION_IMPL *session)
* positioned on a value, it can't be freed.
*/
if (txn->isolation == WT_ISO_READ_UNCOMMITTED) {
- if (txn_state->snap_min == WT_TXN_NONE)
- txn_state->snap_min = txn_global->last_running;
+ if (txn_state->pinned_id == WT_TXN_NONE)
+ txn_state->pinned_id = txn_global->last_running;
} else if (!F_ISSET(txn, WT_TXN_HAS_SNAPSHOT))
WT_RET(__wt_txn_get_snapshot(session));
diff --git a/src/third_party/wiredtiger/src/include/wiredtiger.in b/src/third_party/wiredtiger/src/include/wiredtiger.in
index f4763a113f1..665e8eaf4b0 100644
--- a/src/third_party/wiredtiger/src/include/wiredtiger.in
+++ b/src/third_party/wiredtiger/src/include/wiredtiger.in
@@ -962,8 +962,9 @@ struct __wt_session {
* where appropriate (for example\, a cache size statistic is not
* cleared\, while the count of cursor insert operations will be
* cleared). See @ref statistics for more information., a list\, with
- * values chosen from the following options: \c "all"\, \c "fast"\, \c
- * "clear"\, \c "size"; default empty.}
+ * values chosen from the following options: \c "all"\, \c
+ * "cache_walk"\, \c "fast"\, \c "clear"\, \c "size"\, \c "tree_walk";
+ * default empty.}
* @config{target, if non-empty\, backup the list of objects; valid only
* for a backup data source., a list of strings; default empty.}
* @configend
@@ -1004,9 +1005,9 @@ struct __wt_session {
* @config{block_compressor, configure a compressor for file blocks.
* Permitted values are \c "none" or custom compression engine name
* created with WT_CONNECTION::add_compressor. If WiredTiger has
- * builtin support for \c "snappy"\, \c "lz4" or \c "zlib" compression\,
- * these names are also available. See @ref compression for more
- * information., a string; default \c none.}
+ * builtin support for \c "lz4"\, \c "snappy"\, \c "zlib" or \c "zstd"
+ * compression\, these names are also available. See @ref compression
+ * for more information., a string; default \c none.}
* @config{cache_resident, do not ever evict the object's pages from
* cache. Not compatible with LSM tables; see @ref
* tuning_cache_resident for more information., a boolean flag; default
@@ -1069,6 +1070,11 @@ struct __wt_session {
* Permitted values are \c "none"\, \c "english"\, \c "utf8<file>" or \c
* "utf16<file>". See @ref huffman for more information., a string;
* default \c none.}
+ * @config{ignore_in_memory_cache_size, allow update and insert
+ * operations to proceed even if the cache is already at capacity. Only
+ * valid in conjunction with in-memory databases. Should be used with
+ * caution - this configuration allows WiredTiger to consume memory over
+ * the configured cache limit., a boolean flag; default \c false.}
* @config{immutable, configure the index to be immutable - that is an
* index is not changed by any update to a record in the table., a
* boolean flag; default \c false.}
@@ -1278,6 +1284,9 @@ struct __wt_session {
* @configstart{WT_SESSION.join, see dist/api_data.py}
* @config{bloom_bit_count, the number of bits used per item for the
* bloom filter., an integer between 2 and 1000; default \c 16.}
+ * @config{bloom_false_positives, return all values that pass the bloom
+ * filter\, without eliminating any false positives., a boolean flag;
+ * default \c false.}
* @config{bloom_hash_count, the number of hash values per item for the
* bloom filter., an integer between 2 and 100; default \c 8.}
* @config{compare, modifies the set of items to be returned so that the
@@ -1815,14 +1824,13 @@ struct __wt_connection {
* default \c 5.}
* @config{eviction_dirty_target, perform eviction in worker threads
* when the cache contains at least this much dirty content\, expressed
- * as a percentage of the total cache size. Ignored if \c in_memory is
- * \c true., an integer between 1 and 99; default \c 5.}
+ * as a percentage of the total cache size., an integer between 1 and
+ * 99; default \c 5.}
* @config{eviction_dirty_trigger, trigger application threads to
* perform eviction when the cache contains at least this much dirty
* content\, expressed as a percentage of the total cache size. This
- * setting only alters behavior if it is lower than eviction_trigger.
- * Ignored if \c in_memory is \c true., an integer between 1 and 99;
- * default \c 20.}
+ * setting only alters behavior if it is lower than eviction_trigger.,
+ * an integer between 1 and 99; default \c 20.}
* @config{eviction_target, perform eviction in worker threads when the
* cache contains at least this much content\, expressed as a percentage
* of the total cache size. Must be less than \c eviction_trigger., an
@@ -1899,8 +1907,9 @@ struct __wt_connection {
* reset each time a statistics cursor is used to gather statistics\, as
* well as each time statistics are logged using the \c statistics_log
* configuration. See @ref statistics for more information., a list\,
- * with values chosen from the following options: \c "all"\, \c "fast"\,
- * \c "none"\, \c "clear"; default \c none.}
+ * with values chosen from the following options: \c "all"\, \c
+ * "cache_walk"\, \c "fast"\, \c "none"\, \c "clear"\, \c "tree_walk";
+ * default \c none.}
* @config{statistics_log = (, log any statistics the database is
* configured to maintain\, to a file. See @ref statistics for more
* information. Enabling the statistics log server uses a session from
@@ -2281,13 +2290,12 @@ struct __wt_connection {
* is \c true., an integer between 0 and 99; default \c 5.}
* @config{eviction_dirty_target, perform eviction in worker threads when the
* cache contains at least this much dirty content\, expressed as a percentage
- * of the total cache size. Ignored if \c in_memory is \c true., an integer
- * between 1 and 99; default \c 5.}
+ * of the total cache size., an integer between 1 and 99; default \c 5.}
* @config{eviction_dirty_trigger, trigger application threads to perform
* eviction when the cache contains at least this much dirty content\, expressed
* as a percentage of the total cache size. This setting only alters behavior
- * if it is lower than eviction_trigger. Ignored if \c in_memory is \c true.,
- * an integer between 1 and 99; default \c 20.}
+ * if it is lower than eviction_trigger., an integer between 1 and 99; default
+ * \c 20.}
* @config{eviction_target, perform eviction in worker threads when the cache
* contains at least this much content\, expressed as a percentage of the total
* cache size. Must be less than \c eviction_trigger., an integer between 10
@@ -2333,11 +2341,11 @@ struct __wt_connection {
* @config{&nbsp;&nbsp;&nbsp;&nbsp;compressor, configure a compressor for log
* records. Permitted values are \c "none" or custom compression engine name
* created with WT_CONNECTION::add_compressor. If WiredTiger has builtin
- * support for \c "snappy"\, \c "lz4" or \c "zlib" compression\, these names are
- * also available. See @ref compression for more information., a string;
- * default \c none.}
- * @config{&nbsp;&nbsp;&nbsp;&nbsp;enabled, enable logging
- * subsystem., a boolean flag; default \c false.}
+ * support for \c "lz4"\, \c "snappy"\, \c "zlib" or \c "zstd" compression\,
+ * these names are also available. See @ref compression for more information.,
+ * a string; default \c none.}
+ * @config{&nbsp;&nbsp;&nbsp;&nbsp;enabled, enable
+ * logging subsystem., a boolean flag; default \c false.}
* @config{&nbsp;&nbsp;&nbsp;&nbsp;file_max, the maximum size of log files., an
* integer between 100KB and 2GB; default \c 100MB.}
* @config{&nbsp;&nbsp;&nbsp;&nbsp;path, the name of a directory into which log
@@ -2403,8 +2411,9 @@ struct __wt_connection {
* statistics are reset each time a statistics cursor is used to gather
* statistics\, as well as each time statistics are logged using the \c
* statistics_log configuration. See @ref statistics for more information., a
- * list\, with values chosen from the following options: \c "all"\, \c "fast"\,
- * \c "none"\, \c "clear"; default \c none.}
+ * list\, with values chosen from the following options: \c "all"\, \c
+ * "cache_walk"\, \c "fast"\, \c "none"\, \c "clear"\, \c "tree_walk"; default
+ * \c none.}
* @config{statistics_log = (, log any statistics the database is configured to
* maintain\, to a file. See @ref statistics for more information. Enabling
* the statistics log server uses a session from the configured session_max., a
@@ -4191,13 +4200,48 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
/*! @} */
-/*******************************************
- * Statistic reference.
- *******************************************/
/*!
* @addtogroup wt
* @{
*/
+
+/*!
+ * @name Log record and operation types
+ * @anchor log_types
+ * @{
+ */
+/*
+ * NOTE: The values of these record types and operations must
+ * never change because they're written into the log. Append
+ * any new records or operations to the appropriate set.
+ */
+/*! checkpoint */
+#define WT_LOGREC_CHECKPOINT 0
+/*! transaction commit */
+#define WT_LOGREC_COMMIT 1
+/*! file sync */
+#define WT_LOGREC_FILE_SYNC 2
+/*! message */
+#define WT_LOGREC_MESSAGE 3
+/*! invalid operation */
+#define WT_LOGOP_INVALID 0
+/*! column put */
+#define WT_LOGOP_COL_PUT 1
+/*! column remove */
+#define WT_LOGOP_COL_REMOVE 2
+/*! column truncate */
+#define WT_LOGOP_COL_TRUNCATE 3
+/*! row put */
+#define WT_LOGOP_ROW_PUT 4
+/*! row remove */
+#define WT_LOGOP_ROW_REMOVE 5
+/*! row truncate */
+#define WT_LOGOP_ROW_TRUNCATE 6
+/*! @} */
+
+/*******************************************
+ * Statistic reference.
+ *******************************************/
/*
* DO NOT EDIT: automatically built by dist/api_stat.py.
* Statistics section: BEGIN
@@ -4274,384 +4318,437 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
#define WT_STAT_CONN_BLOCK_MAP_READ 1029
/*! block-manager: mapped bytes read */
#define WT_STAT_CONN_BLOCK_BYTE_MAP_READ 1030
+/*! cache: application threads page read from disk to cache count */
+#define WT_STAT_CONN_CACHE_READ_APP_COUNT 1031
+/*! cache: application threads page read from disk to cache time (usecs) */
+#define WT_STAT_CONN_CACHE_READ_APP_TIME 1032
+/*! cache: application threads page write from cache to disk count */
+#define WT_STAT_CONN_CACHE_WRITE_APP_COUNT 1033
+/*! cache: application threads page write from cache to disk time (usecs) */
+#define WT_STAT_CONN_CACHE_WRITE_APP_TIME 1034
/*! cache: bytes belonging to page images in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_IMAGE 1031
+#define WT_STAT_CONN_CACHE_BYTES_IMAGE 1035
/*! cache: bytes currently in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_INUSE 1032
+#define WT_STAT_CONN_CACHE_BYTES_INUSE 1036
/*! cache: bytes not belonging to page images in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_OTHER 1033
+#define WT_STAT_CONN_CACHE_BYTES_OTHER 1037
/*! cache: bytes read into cache */
-#define WT_STAT_CONN_CACHE_BYTES_READ 1034
+#define WT_STAT_CONN_CACHE_BYTES_READ 1038
/*! cache: bytes written from cache */
-#define WT_STAT_CONN_CACHE_BYTES_WRITE 1035
+#define WT_STAT_CONN_CACHE_BYTES_WRITE 1039
/*! cache: checkpoint blocked page eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_CHECKPOINT 1036
+#define WT_STAT_CONN_CACHE_EVICTION_CHECKPOINT 1040
/*! cache: eviction calls to get a page */
-#define WT_STAT_CONN_CACHE_EVICTION_GET_REF 1037
+#define WT_STAT_CONN_CACHE_EVICTION_GET_REF 1041
/*! cache: eviction calls to get a page found queue empty */
-#define WT_STAT_CONN_CACHE_EVICTION_GET_REF_EMPTY 1038
+#define WT_STAT_CONN_CACHE_EVICTION_GET_REF_EMPTY 1042
/*! cache: eviction calls to get a page found queue empty after locking */
-#define WT_STAT_CONN_CACHE_EVICTION_GET_REF_EMPTY2 1039
+#define WT_STAT_CONN_CACHE_EVICTION_GET_REF_EMPTY2 1043
/*! cache: eviction currently operating in aggressive mode */
-#define WT_STAT_CONN_CACHE_EVICTION_AGGRESSIVE_SET 1040
+#define WT_STAT_CONN_CACHE_EVICTION_AGGRESSIVE_SET 1044
/*! cache: eviction empty score */
-#define WT_STAT_CONN_CACHE_EVICTION_EMPTY_SCORE 1041
+#define WT_STAT_CONN_CACHE_EVICTION_EMPTY_SCORE 1045
/*! cache: eviction server candidate queue empty when topping up */
-#define WT_STAT_CONN_CACHE_EVICTION_QUEUE_EMPTY 1042
+#define WT_STAT_CONN_CACHE_EVICTION_QUEUE_EMPTY 1046
/*! cache: eviction server candidate queue not empty when topping up */
-#define WT_STAT_CONN_CACHE_EVICTION_QUEUE_NOT_EMPTY 1043
+#define WT_STAT_CONN_CACHE_EVICTION_QUEUE_NOT_EMPTY 1047
/*! cache: eviction server evicting pages */
-#define WT_STAT_CONN_CACHE_EVICTION_SERVER_EVICTING 1044
+#define WT_STAT_CONN_CACHE_EVICTION_SERVER_EVICTING 1048
/*!
* cache: eviction server slept, because we did not make progress with
* eviction
*/
-#define WT_STAT_CONN_CACHE_EVICTION_SERVER_SLEPT 1045
+#define WT_STAT_CONN_CACHE_EVICTION_SERVER_SLEPT 1049
/*! cache: eviction server unable to reach eviction goal */
-#define WT_STAT_CONN_CACHE_EVICTION_SLOW 1046
+#define WT_STAT_CONN_CACHE_EVICTION_SLOW 1050
/*! cache: eviction state */
-#define WT_STAT_CONN_CACHE_EVICTION_STATE 1047
+#define WT_STAT_CONN_CACHE_EVICTION_STATE 1051
/*! cache: eviction walks abandoned */
-#define WT_STAT_CONN_CACHE_EVICTION_WALKS_ABANDONED 1048
+#define WT_STAT_CONN_CACHE_EVICTION_WALKS_ABANDONED 1052
/*! cache: eviction worker thread evicting pages */
-#define WT_STAT_CONN_CACHE_EVICTION_WORKER_EVICTING 1049
+#define WT_STAT_CONN_CACHE_EVICTION_WORKER_EVICTING 1053
/*! cache: failed eviction of pages that exceeded the in-memory maximum */
-#define WT_STAT_CONN_CACHE_EVICTION_FORCE_FAIL 1050
+#define WT_STAT_CONN_CACHE_EVICTION_FORCE_FAIL 1054
/*! cache: files with active eviction walks */
-#define WT_STAT_CONN_CACHE_EVICTION_WALKS_ACTIVE 1051
+#define WT_STAT_CONN_CACHE_EVICTION_WALKS_ACTIVE 1055
/*! cache: files with new eviction walks started */
-#define WT_STAT_CONN_CACHE_EVICTION_WALKS_STARTED 1052
+#define WT_STAT_CONN_CACHE_EVICTION_WALKS_STARTED 1056
/*! cache: hazard pointer blocked page eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_HAZARD 1053
+#define WT_STAT_CONN_CACHE_EVICTION_HAZARD 1057
/*! cache: hazard pointer check calls */
-#define WT_STAT_CONN_CACHE_HAZARD_CHECKS 1054
+#define WT_STAT_CONN_CACHE_HAZARD_CHECKS 1058
/*! cache: hazard pointer check entries walked */
-#define WT_STAT_CONN_CACHE_HAZARD_WALKS 1055
+#define WT_STAT_CONN_CACHE_HAZARD_WALKS 1059
/*! cache: hazard pointer maximum array length */
-#define WT_STAT_CONN_CACHE_HAZARD_MAX 1056
+#define WT_STAT_CONN_CACHE_HAZARD_MAX 1060
/*! cache: in-memory page passed criteria to be split */
-#define WT_STAT_CONN_CACHE_INMEM_SPLITTABLE 1057
+#define WT_STAT_CONN_CACHE_INMEM_SPLITTABLE 1061
/*! cache: in-memory page splits */
-#define WT_STAT_CONN_CACHE_INMEM_SPLIT 1058
+#define WT_STAT_CONN_CACHE_INMEM_SPLIT 1062
/*! cache: internal pages evicted */
-#define WT_STAT_CONN_CACHE_EVICTION_INTERNAL 1059
+#define WT_STAT_CONN_CACHE_EVICTION_INTERNAL 1063
/*! cache: internal pages split during eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_SPLIT_INTERNAL 1060
+#define WT_STAT_CONN_CACHE_EVICTION_SPLIT_INTERNAL 1064
/*! cache: leaf pages split during eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_SPLIT_LEAF 1061
+#define WT_STAT_CONN_CACHE_EVICTION_SPLIT_LEAF 1065
/*! cache: lookaside table insert calls */
-#define WT_STAT_CONN_CACHE_LOOKASIDE_INSERT 1062
+#define WT_STAT_CONN_CACHE_LOOKASIDE_INSERT 1066
/*! cache: lookaside table remove calls */
-#define WT_STAT_CONN_CACHE_LOOKASIDE_REMOVE 1063
+#define WT_STAT_CONN_CACHE_LOOKASIDE_REMOVE 1067
/*! cache: maximum bytes configured */
-#define WT_STAT_CONN_CACHE_BYTES_MAX 1064
+#define WT_STAT_CONN_CACHE_BYTES_MAX 1068
/*! cache: maximum page size at eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_MAXIMUM_PAGE_SIZE 1065
+#define WT_STAT_CONN_CACHE_EVICTION_MAXIMUM_PAGE_SIZE 1069
/*! cache: modified pages evicted */
-#define WT_STAT_CONN_CACHE_EVICTION_DIRTY 1066
+#define WT_STAT_CONN_CACHE_EVICTION_DIRTY 1070
/*! cache: modified pages evicted by application threads */
-#define WT_STAT_CONN_CACHE_EVICTION_APP_DIRTY 1067
+#define WT_STAT_CONN_CACHE_EVICTION_APP_DIRTY 1071
/*! cache: overflow pages read into cache */
-#define WT_STAT_CONN_CACHE_READ_OVERFLOW 1068
+#define WT_STAT_CONN_CACHE_READ_OVERFLOW 1072
/*! cache: overflow values cached in memory */
-#define WT_STAT_CONN_CACHE_OVERFLOW_VALUE 1069
+#define WT_STAT_CONN_CACHE_OVERFLOW_VALUE 1073
/*! cache: page split during eviction deepened the tree */
-#define WT_STAT_CONN_CACHE_EVICTION_DEEPEN 1070
+#define WT_STAT_CONN_CACHE_EVICTION_DEEPEN 1074
/*! cache: page written requiring lookaside records */
-#define WT_STAT_CONN_CACHE_WRITE_LOOKASIDE 1071
+#define WT_STAT_CONN_CACHE_WRITE_LOOKASIDE 1075
/*! cache: pages currently held in the cache */
-#define WT_STAT_CONN_CACHE_PAGES_INUSE 1072
+#define WT_STAT_CONN_CACHE_PAGES_INUSE 1076
/*! cache: pages evicted because they exceeded the in-memory maximum */
-#define WT_STAT_CONN_CACHE_EVICTION_FORCE 1073
+#define WT_STAT_CONN_CACHE_EVICTION_FORCE 1077
/*! cache: pages evicted because they had chains of deleted items */
-#define WT_STAT_CONN_CACHE_EVICTION_FORCE_DELETE 1074
+#define WT_STAT_CONN_CACHE_EVICTION_FORCE_DELETE 1078
/*! cache: pages evicted by application threads */
-#define WT_STAT_CONN_CACHE_EVICTION_APP 1075
+#define WT_STAT_CONN_CACHE_EVICTION_APP 1079
/*! cache: pages queued for eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED 1076
+#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED 1080
/*! cache: pages queued for urgent eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED_URGENT 1077
+#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED_URGENT 1081
/*! cache: pages queued for urgent eviction during walk */
-#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED_OLDEST 1078
+#define WT_STAT_CONN_CACHE_EVICTION_PAGES_QUEUED_OLDEST 1082
/*! cache: pages read into cache */
-#define WT_STAT_CONN_CACHE_READ 1079
+#define WT_STAT_CONN_CACHE_READ 1083
/*! cache: pages read into cache requiring lookaside entries */
-#define WT_STAT_CONN_CACHE_READ_LOOKASIDE 1080
+#define WT_STAT_CONN_CACHE_READ_LOOKASIDE 1084
/*! cache: pages requested from the cache */
-#define WT_STAT_CONN_CACHE_PAGES_REQUESTED 1081
+#define WT_STAT_CONN_CACHE_PAGES_REQUESTED 1085
/*! cache: pages seen by eviction walk */
-#define WT_STAT_CONN_CACHE_EVICTION_PAGES_SEEN 1082
+#define WT_STAT_CONN_CACHE_EVICTION_PAGES_SEEN 1086
/*! cache: pages selected for eviction unable to be evicted */
-#define WT_STAT_CONN_CACHE_EVICTION_FAIL 1083
+#define WT_STAT_CONN_CACHE_EVICTION_FAIL 1087
/*! cache: pages walked for eviction */
-#define WT_STAT_CONN_CACHE_EVICTION_WALK 1084
+#define WT_STAT_CONN_CACHE_EVICTION_WALK 1088
/*! cache: pages written from cache */
-#define WT_STAT_CONN_CACHE_WRITE 1085
+#define WT_STAT_CONN_CACHE_WRITE 1089
/*! cache: pages written requiring in-memory restoration */
-#define WT_STAT_CONN_CACHE_WRITE_RESTORE 1086
+#define WT_STAT_CONN_CACHE_WRITE_RESTORE 1090
/*! cache: percentage overhead */
-#define WT_STAT_CONN_CACHE_OVERHEAD 1087
+#define WT_STAT_CONN_CACHE_OVERHEAD 1091
/*! cache: tracked bytes belonging to internal pages in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_INTERNAL 1088
+#define WT_STAT_CONN_CACHE_BYTES_INTERNAL 1092
/*! cache: tracked bytes belonging to leaf pages in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_LEAF 1089
+#define WT_STAT_CONN_CACHE_BYTES_LEAF 1093
/*! cache: tracked dirty bytes in the cache */
-#define WT_STAT_CONN_CACHE_BYTES_DIRTY 1090
+#define WT_STAT_CONN_CACHE_BYTES_DIRTY 1094
/*! cache: tracked dirty pages in the cache */
-#define WT_STAT_CONN_CACHE_PAGES_DIRTY 1091
+#define WT_STAT_CONN_CACHE_PAGES_DIRTY 1095
/*! cache: unmodified pages evicted */
-#define WT_STAT_CONN_CACHE_EVICTION_CLEAN 1092
+#define WT_STAT_CONN_CACHE_EVICTION_CLEAN 1096
/*! connection: auto adjusting condition resets */
-#define WT_STAT_CONN_COND_AUTO_WAIT_RESET 1093
+#define WT_STAT_CONN_COND_AUTO_WAIT_RESET 1097
/*! connection: auto adjusting condition wait calls */
-#define WT_STAT_CONN_COND_AUTO_WAIT 1094
+#define WT_STAT_CONN_COND_AUTO_WAIT 1098
/*! connection: files currently open */
-#define WT_STAT_CONN_FILE_OPEN 1095
+#define WT_STAT_CONN_FILE_OPEN 1099
/*! connection: memory allocations */
-#define WT_STAT_CONN_MEMORY_ALLOCATION 1096
+#define WT_STAT_CONN_MEMORY_ALLOCATION 1100
/*! connection: memory frees */
-#define WT_STAT_CONN_MEMORY_FREE 1097
+#define WT_STAT_CONN_MEMORY_FREE 1101
/*! connection: memory re-allocations */
-#define WT_STAT_CONN_MEMORY_GROW 1098
+#define WT_STAT_CONN_MEMORY_GROW 1102
/*! connection: pthread mutex condition wait calls */
-#define WT_STAT_CONN_COND_WAIT 1099
+#define WT_STAT_CONN_COND_WAIT 1103
/*! connection: pthread mutex shared lock read-lock calls */
-#define WT_STAT_CONN_RWLOCK_READ 1100
+#define WT_STAT_CONN_RWLOCK_READ 1104
/*! connection: pthread mutex shared lock write-lock calls */
-#define WT_STAT_CONN_RWLOCK_WRITE 1101
+#define WT_STAT_CONN_RWLOCK_WRITE 1105
/*! connection: total fsync I/Os */
-#define WT_STAT_CONN_FSYNC_IO 1102
+#define WT_STAT_CONN_FSYNC_IO 1106
/*! connection: total read I/Os */
-#define WT_STAT_CONN_READ_IO 1103
+#define WT_STAT_CONN_READ_IO 1107
/*! connection: total write I/Os */
-#define WT_STAT_CONN_WRITE_IO 1104
+#define WT_STAT_CONN_WRITE_IO 1108
/*! cursor: cursor create calls */
-#define WT_STAT_CONN_CURSOR_CREATE 1105
+#define WT_STAT_CONN_CURSOR_CREATE 1109
/*! cursor: cursor insert calls */
-#define WT_STAT_CONN_CURSOR_INSERT 1106
+#define WT_STAT_CONN_CURSOR_INSERT 1110
/*! cursor: cursor next calls */
-#define WT_STAT_CONN_CURSOR_NEXT 1107
+#define WT_STAT_CONN_CURSOR_NEXT 1111
/*! cursor: cursor prev calls */
-#define WT_STAT_CONN_CURSOR_PREV 1108
+#define WT_STAT_CONN_CURSOR_PREV 1112
/*! cursor: cursor remove calls */
-#define WT_STAT_CONN_CURSOR_REMOVE 1109
+#define WT_STAT_CONN_CURSOR_REMOVE 1113
/*! cursor: cursor reset calls */
-#define WT_STAT_CONN_CURSOR_RESET 1110
+#define WT_STAT_CONN_CURSOR_RESET 1114
/*! cursor: cursor restarted searches */
-#define WT_STAT_CONN_CURSOR_RESTART 1111
+#define WT_STAT_CONN_CURSOR_RESTART 1115
/*! cursor: cursor search calls */
-#define WT_STAT_CONN_CURSOR_SEARCH 1112
+#define WT_STAT_CONN_CURSOR_SEARCH 1116
/*! cursor: cursor search near calls */
-#define WT_STAT_CONN_CURSOR_SEARCH_NEAR 1113
+#define WT_STAT_CONN_CURSOR_SEARCH_NEAR 1117
/*! cursor: cursor update calls */
-#define WT_STAT_CONN_CURSOR_UPDATE 1114
+#define WT_STAT_CONN_CURSOR_UPDATE 1118
/*! cursor: truncate calls */
-#define WT_STAT_CONN_CURSOR_TRUNCATE 1115
+#define WT_STAT_CONN_CURSOR_TRUNCATE 1119
/*! data-handle: connection data handles currently active */
-#define WT_STAT_CONN_DH_CONN_HANDLE_COUNT 1116
+#define WT_STAT_CONN_DH_CONN_HANDLE_COUNT 1120
/*! data-handle: connection sweep candidate became referenced */
-#define WT_STAT_CONN_DH_SWEEP_REF 1117
+#define WT_STAT_CONN_DH_SWEEP_REF 1121
/*! data-handle: connection sweep dhandles closed */
-#define WT_STAT_CONN_DH_SWEEP_CLOSE 1118
+#define WT_STAT_CONN_DH_SWEEP_CLOSE 1122
/*! data-handle: connection sweep dhandles removed from hash list */
-#define WT_STAT_CONN_DH_SWEEP_REMOVE 1119
+#define WT_STAT_CONN_DH_SWEEP_REMOVE 1123
/*! data-handle: connection sweep time-of-death sets */
-#define WT_STAT_CONN_DH_SWEEP_TOD 1120
+#define WT_STAT_CONN_DH_SWEEP_TOD 1124
/*! data-handle: connection sweeps */
-#define WT_STAT_CONN_DH_SWEEPS 1121
+#define WT_STAT_CONN_DH_SWEEPS 1125
/*! data-handle: session dhandles swept */
-#define WT_STAT_CONN_DH_SESSION_HANDLES 1122
+#define WT_STAT_CONN_DH_SESSION_HANDLES 1126
/*! data-handle: session sweep attempts */
-#define WT_STAT_CONN_DH_SESSION_SWEEPS 1123
+#define WT_STAT_CONN_DH_SESSION_SWEEPS 1127
+/*! lock: checkpoint lock acquisitions */
+#define WT_STAT_CONN_LOCK_CHECKPOINT_COUNT 1128
+/*! lock: checkpoint lock application thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_CHECKPOINT_WAIT_APPLICATION 1129
+/*! lock: checkpoint lock internal thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_CHECKPOINT_WAIT_INTERNAL 1130
+/*! lock: handle-list lock acquisitions */
+#define WT_STAT_CONN_LOCK_HANDLE_LIST_COUNT 1131
+/*! lock: handle-list lock application thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_HANDLE_LIST_WAIT_APPLICATION 1132
+/*! lock: handle-list lock internal thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_HANDLE_LIST_WAIT_INTERNAL 1133
+/*! lock: metadata lock acquisitions */
+#define WT_STAT_CONN_LOCK_METADATA_COUNT 1134
+/*! lock: metadata lock application thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_METADATA_WAIT_APPLICATION 1135
+/*! lock: metadata lock internal thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_METADATA_WAIT_INTERNAL 1136
+/*! lock: schema lock acquisitions */
+#define WT_STAT_CONN_LOCK_SCHEMA_COUNT 1137
+/*! lock: schema lock application thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_SCHEMA_WAIT_APPLICATION 1138
+/*! lock: schema lock internal thread wait time (usecs) */
+#define WT_STAT_CONN_LOCK_SCHEMA_WAIT_INTERNAL 1139
+/*! lock: table lock acquisitions */
+#define WT_STAT_CONN_LOCK_TABLE_COUNT 1140
+/*!
+ * lock: table lock application thread time waiting for the table lock
+ * (usecs)
+ */
+#define WT_STAT_CONN_LOCK_TABLE_WAIT_APPLICATION 1141
+/*!
+ * lock: table lock internal thread time waiting for the table lock
+ * (usecs)
+ */
+#define WT_STAT_CONN_LOCK_TABLE_WAIT_INTERNAL 1142
/*! log: busy returns attempting to switch slots */
-#define WT_STAT_CONN_LOG_SLOT_SWITCH_BUSY 1124
+#define WT_STAT_CONN_LOG_SLOT_SWITCH_BUSY 1143
/*! log: consolidated slot closures */
-#define WT_STAT_CONN_LOG_SLOT_CLOSES 1125
+#define WT_STAT_CONN_LOG_SLOT_CLOSES 1144
/*! log: consolidated slot join races */
-#define WT_STAT_CONN_LOG_SLOT_RACES 1126
+#define WT_STAT_CONN_LOG_SLOT_RACES 1145
/*! log: consolidated slot join transitions */
-#define WT_STAT_CONN_LOG_SLOT_TRANSITIONS 1127
+#define WT_STAT_CONN_LOG_SLOT_TRANSITIONS 1146
/*! log: consolidated slot joins */
-#define WT_STAT_CONN_LOG_SLOT_JOINS 1128
+#define WT_STAT_CONN_LOG_SLOT_JOINS 1147
/*! log: consolidated slot unbuffered writes */
-#define WT_STAT_CONN_LOG_SLOT_UNBUFFERED 1129
+#define WT_STAT_CONN_LOG_SLOT_UNBUFFERED 1148
/*! log: log bytes of payload data */
-#define WT_STAT_CONN_LOG_BYTES_PAYLOAD 1130
+#define WT_STAT_CONN_LOG_BYTES_PAYLOAD 1149
/*! log: log bytes written */
-#define WT_STAT_CONN_LOG_BYTES_WRITTEN 1131
+#define WT_STAT_CONN_LOG_BYTES_WRITTEN 1150
/*! log: log files manually zero-filled */
-#define WT_STAT_CONN_LOG_ZERO_FILLS 1132
+#define WT_STAT_CONN_LOG_ZERO_FILLS 1151
/*! log: log flush operations */
-#define WT_STAT_CONN_LOG_FLUSH 1133
+#define WT_STAT_CONN_LOG_FLUSH 1152
/*! log: log force write operations */
-#define WT_STAT_CONN_LOG_FORCE_WRITE 1134
+#define WT_STAT_CONN_LOG_FORCE_WRITE 1153
/*! log: log force write operations skipped */
-#define WT_STAT_CONN_LOG_FORCE_WRITE_SKIP 1135
+#define WT_STAT_CONN_LOG_FORCE_WRITE_SKIP 1154
/*! log: log records compressed */
-#define WT_STAT_CONN_LOG_COMPRESS_WRITES 1136
+#define WT_STAT_CONN_LOG_COMPRESS_WRITES 1155
/*! log: log records not compressed */
-#define WT_STAT_CONN_LOG_COMPRESS_WRITE_FAILS 1137
+#define WT_STAT_CONN_LOG_COMPRESS_WRITE_FAILS 1156
/*! log: log records too small to compress */
-#define WT_STAT_CONN_LOG_COMPRESS_SMALL 1138
+#define WT_STAT_CONN_LOG_COMPRESS_SMALL 1157
/*! log: log release advances write LSN */
-#define WT_STAT_CONN_LOG_RELEASE_WRITE_LSN 1139
+#define WT_STAT_CONN_LOG_RELEASE_WRITE_LSN 1158
/*! log: log scan operations */
-#define WT_STAT_CONN_LOG_SCANS 1140
+#define WT_STAT_CONN_LOG_SCANS 1159
/*! log: log scan records requiring two reads */
-#define WT_STAT_CONN_LOG_SCAN_REREADS 1141
+#define WT_STAT_CONN_LOG_SCAN_REREADS 1160
/*! log: log server thread advances write LSN */
-#define WT_STAT_CONN_LOG_WRITE_LSN 1142
+#define WT_STAT_CONN_LOG_WRITE_LSN 1161
/*! log: log server thread write LSN walk skipped */
-#define WT_STAT_CONN_LOG_WRITE_LSN_SKIP 1143
+#define WT_STAT_CONN_LOG_WRITE_LSN_SKIP 1162
/*! log: log sync operations */
-#define WT_STAT_CONN_LOG_SYNC 1144
+#define WT_STAT_CONN_LOG_SYNC 1163
/*! log: log sync time duration (usecs) */
-#define WT_STAT_CONN_LOG_SYNC_DURATION 1145
+#define WT_STAT_CONN_LOG_SYNC_DURATION 1164
/*! log: log sync_dir operations */
-#define WT_STAT_CONN_LOG_SYNC_DIR 1146
+#define WT_STAT_CONN_LOG_SYNC_DIR 1165
/*! log: log sync_dir time duration (usecs) */
-#define WT_STAT_CONN_LOG_SYNC_DIR_DURATION 1147
+#define WT_STAT_CONN_LOG_SYNC_DIR_DURATION 1166
/*! log: log write operations */
-#define WT_STAT_CONN_LOG_WRITES 1148
+#define WT_STAT_CONN_LOG_WRITES 1167
/*! log: logging bytes consolidated */
-#define WT_STAT_CONN_LOG_SLOT_CONSOLIDATED 1149
+#define WT_STAT_CONN_LOG_SLOT_CONSOLIDATED 1168
/*! log: maximum log file size */
-#define WT_STAT_CONN_LOG_MAX_FILESIZE 1150
+#define WT_STAT_CONN_LOG_MAX_FILESIZE 1169
/*! log: number of pre-allocated log files to create */
-#define WT_STAT_CONN_LOG_PREALLOC_MAX 1151
+#define WT_STAT_CONN_LOG_PREALLOC_MAX 1170
/*! log: pre-allocated log files not ready and missed */
-#define WT_STAT_CONN_LOG_PREALLOC_MISSED 1152
+#define WT_STAT_CONN_LOG_PREALLOC_MISSED 1171
/*! log: pre-allocated log files prepared */
-#define WT_STAT_CONN_LOG_PREALLOC_FILES 1153
+#define WT_STAT_CONN_LOG_PREALLOC_FILES 1172
/*! log: pre-allocated log files used */
-#define WT_STAT_CONN_LOG_PREALLOC_USED 1154
+#define WT_STAT_CONN_LOG_PREALLOC_USED 1173
/*! log: records processed by log scan */
-#define WT_STAT_CONN_LOG_SCAN_RECORDS 1155
+#define WT_STAT_CONN_LOG_SCAN_RECORDS 1174
/*! log: total in-memory size of compressed records */
-#define WT_STAT_CONN_LOG_COMPRESS_MEM 1156
+#define WT_STAT_CONN_LOG_COMPRESS_MEM 1175
/*! log: total log buffer size */
-#define WT_STAT_CONN_LOG_BUFFER_SIZE 1157
+#define WT_STAT_CONN_LOG_BUFFER_SIZE 1176
/*! log: total size of compressed records */
-#define WT_STAT_CONN_LOG_COMPRESS_LEN 1158
+#define WT_STAT_CONN_LOG_COMPRESS_LEN 1177
/*! log: written slots coalesced */
-#define WT_STAT_CONN_LOG_SLOT_COALESCED 1159
+#define WT_STAT_CONN_LOG_SLOT_COALESCED 1178
/*! log: yields waiting for previous log file close */
-#define WT_STAT_CONN_LOG_CLOSE_YIELDS 1160
+#define WT_STAT_CONN_LOG_CLOSE_YIELDS 1179
/*! reconciliation: fast-path pages deleted */
-#define WT_STAT_CONN_REC_PAGE_DELETE_FAST 1161
+#define WT_STAT_CONN_REC_PAGE_DELETE_FAST 1180
/*! reconciliation: page reconciliation calls */
-#define WT_STAT_CONN_REC_PAGES 1162
+#define WT_STAT_CONN_REC_PAGES 1181
/*! reconciliation: page reconciliation calls for eviction */
-#define WT_STAT_CONN_REC_PAGES_EVICTION 1163
+#define WT_STAT_CONN_REC_PAGES_EVICTION 1182
/*! reconciliation: pages deleted */
-#define WT_STAT_CONN_REC_PAGE_DELETE 1164
+#define WT_STAT_CONN_REC_PAGE_DELETE 1183
/*! reconciliation: split bytes currently awaiting free */
-#define WT_STAT_CONN_REC_SPLIT_STASHED_BYTES 1165
+#define WT_STAT_CONN_REC_SPLIT_STASHED_BYTES 1184
/*! reconciliation: split objects currently awaiting free */
-#define WT_STAT_CONN_REC_SPLIT_STASHED_OBJECTS 1166
+#define WT_STAT_CONN_REC_SPLIT_STASHED_OBJECTS 1185
/*! session: open cursor count */
-#define WT_STAT_CONN_SESSION_CURSOR_OPEN 1167
+#define WT_STAT_CONN_SESSION_CURSOR_OPEN 1186
/*! session: open session count */
-#define WT_STAT_CONN_SESSION_OPEN 1168
+#define WT_STAT_CONN_SESSION_OPEN 1187
/*! session: table compact failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_COMPACT_FAIL 1169
+#define WT_STAT_CONN_SESSION_TABLE_COMPACT_FAIL 1188
/*! session: table compact successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_COMPACT_SUCCESS 1170
+#define WT_STAT_CONN_SESSION_TABLE_COMPACT_SUCCESS 1189
/*! session: table create failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_CREATE_FAIL 1171
+#define WT_STAT_CONN_SESSION_TABLE_CREATE_FAIL 1190
/*! session: table create successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_CREATE_SUCCESS 1172
+#define WT_STAT_CONN_SESSION_TABLE_CREATE_SUCCESS 1191
/*! session: table drop failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_DROP_FAIL 1173
+#define WT_STAT_CONN_SESSION_TABLE_DROP_FAIL 1192
/*! session: table drop successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_DROP_SUCCESS 1174
+#define WT_STAT_CONN_SESSION_TABLE_DROP_SUCCESS 1193
/*! session: table rebalance failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_REBALANCE_FAIL 1175
+#define WT_STAT_CONN_SESSION_TABLE_REBALANCE_FAIL 1194
/*! session: table rebalance successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_REBALANCE_SUCCESS 1176
+#define WT_STAT_CONN_SESSION_TABLE_REBALANCE_SUCCESS 1195
/*! session: table rename failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_RENAME_FAIL 1177
+#define WT_STAT_CONN_SESSION_TABLE_RENAME_FAIL 1196
/*! session: table rename successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_RENAME_SUCCESS 1178
+#define WT_STAT_CONN_SESSION_TABLE_RENAME_SUCCESS 1197
/*! session: table salvage failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_SALVAGE_FAIL 1179
+#define WT_STAT_CONN_SESSION_TABLE_SALVAGE_FAIL 1198
/*! session: table salvage successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_SALVAGE_SUCCESS 1180
+#define WT_STAT_CONN_SESSION_TABLE_SALVAGE_SUCCESS 1199
/*! session: table truncate failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_TRUNCATE_FAIL 1181
+#define WT_STAT_CONN_SESSION_TABLE_TRUNCATE_FAIL 1200
/*! session: table truncate successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_TRUNCATE_SUCCESS 1182
+#define WT_STAT_CONN_SESSION_TABLE_TRUNCATE_SUCCESS 1201
/*! session: table verify failed calls */
-#define WT_STAT_CONN_SESSION_TABLE_VERIFY_FAIL 1183
+#define WT_STAT_CONN_SESSION_TABLE_VERIFY_FAIL 1202
/*! session: table verify successful calls */
-#define WT_STAT_CONN_SESSION_TABLE_VERIFY_SUCCESS 1184
+#define WT_STAT_CONN_SESSION_TABLE_VERIFY_SUCCESS 1203
/*! thread-state: active filesystem fsync calls */
-#define WT_STAT_CONN_THREAD_FSYNC_ACTIVE 1185
+#define WT_STAT_CONN_THREAD_FSYNC_ACTIVE 1204
/*! thread-state: active filesystem read calls */
-#define WT_STAT_CONN_THREAD_READ_ACTIVE 1186
+#define WT_STAT_CONN_THREAD_READ_ACTIVE 1205
/*! thread-state: active filesystem write calls */
-#define WT_STAT_CONN_THREAD_WRITE_ACTIVE 1187
+#define WT_STAT_CONN_THREAD_WRITE_ACTIVE 1206
+/*! thread-yield: application thread time evicting (usecs) */
+#define WT_STAT_CONN_APPLICATION_EVICT_TIME 1207
+/*! thread-yield: application thread time waiting for cache (usecs) */
+#define WT_STAT_CONN_APPLICATION_CACHE_TIME 1208
/*! thread-yield: page acquire busy blocked */
-#define WT_STAT_CONN_PAGE_BUSY_BLOCKED 1188
+#define WT_STAT_CONN_PAGE_BUSY_BLOCKED 1209
/*! thread-yield: page acquire eviction blocked */
-#define WT_STAT_CONN_PAGE_FORCIBLE_EVICT_BLOCKED 1189
+#define WT_STAT_CONN_PAGE_FORCIBLE_EVICT_BLOCKED 1210
/*! thread-yield: page acquire locked blocked */
-#define WT_STAT_CONN_PAGE_LOCKED_BLOCKED 1190
+#define WT_STAT_CONN_PAGE_LOCKED_BLOCKED 1211
/*! thread-yield: page acquire read blocked */
-#define WT_STAT_CONN_PAGE_READ_BLOCKED 1191
+#define WT_STAT_CONN_PAGE_READ_BLOCKED 1212
/*! thread-yield: page acquire time sleeping (usecs) */
-#define WT_STAT_CONN_PAGE_SLEEP 1192
+#define WT_STAT_CONN_PAGE_SLEEP 1213
/*! transaction: number of named snapshots created */
-#define WT_STAT_CONN_TXN_SNAPSHOTS_CREATED 1193
+#define WT_STAT_CONN_TXN_SNAPSHOTS_CREATED 1214
/*! transaction: number of named snapshots dropped */
-#define WT_STAT_CONN_TXN_SNAPSHOTS_DROPPED 1194
+#define WT_STAT_CONN_TXN_SNAPSHOTS_DROPPED 1215
/*! transaction: transaction begins */
-#define WT_STAT_CONN_TXN_BEGIN 1195
+#define WT_STAT_CONN_TXN_BEGIN 1216
/*! transaction: transaction checkpoint currently running */
-#define WT_STAT_CONN_TXN_CHECKPOINT_RUNNING 1196
+#define WT_STAT_CONN_TXN_CHECKPOINT_RUNNING 1217
/*! transaction: transaction checkpoint generation */
-#define WT_STAT_CONN_TXN_CHECKPOINT_GENERATION 1197
+#define WT_STAT_CONN_TXN_CHECKPOINT_GENERATION 1218
/*! transaction: transaction checkpoint max time (msecs) */
-#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_MAX 1198
+#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_MAX 1219
/*! transaction: transaction checkpoint min time (msecs) */
-#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_MIN 1199
+#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_MIN 1220
/*! transaction: transaction checkpoint most recent time (msecs) */
-#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_RECENT 1200
+#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_RECENT 1221
/*! transaction: transaction checkpoint scrub dirty target */
-#define WT_STAT_CONN_TXN_CHECKPOINT_SCRUB_TARGET 1201
+#define WT_STAT_CONN_TXN_CHECKPOINT_SCRUB_TARGET 1222
/*! transaction: transaction checkpoint scrub time (msecs) */
-#define WT_STAT_CONN_TXN_CHECKPOINT_SCRUB_TIME 1202
+#define WT_STAT_CONN_TXN_CHECKPOINT_SCRUB_TIME 1223
/*! transaction: transaction checkpoint total time (msecs) */
-#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_TOTAL 1203
+#define WT_STAT_CONN_TXN_CHECKPOINT_TIME_TOTAL 1224
/*! transaction: transaction checkpoints */
-#define WT_STAT_CONN_TXN_CHECKPOINT 1204
+#define WT_STAT_CONN_TXN_CHECKPOINT 1225
+/*!
+ * transaction: transaction checkpoints skipped because database was
+ * clean
+ */
+#define WT_STAT_CONN_TXN_CHECKPOINT_SKIPPED 1226
/*! transaction: transaction failures due to cache overflow */
-#define WT_STAT_CONN_TXN_FAIL_CACHE 1205
+#define WT_STAT_CONN_TXN_FAIL_CACHE 1227
/*!
* transaction: transaction fsync calls for checkpoint after allocating
* the transaction ID
*/
-#define WT_STAT_CONN_TXN_CHECKPOINT_FSYNC_POST 1206
+#define WT_STAT_CONN_TXN_CHECKPOINT_FSYNC_POST 1228
/*!
* transaction: transaction fsync duration for checkpoint after
* allocating the transaction ID (usecs)
*/
-#define WT_STAT_CONN_TXN_CHECKPOINT_FSYNC_POST_DURATION 1207
+#define WT_STAT_CONN_TXN_CHECKPOINT_FSYNC_POST_DURATION 1229
/*! transaction: transaction range of IDs currently pinned */
-#define WT_STAT_CONN_TXN_PINNED_RANGE 1208
+#define WT_STAT_CONN_TXN_PINNED_RANGE 1230
/*! transaction: transaction range of IDs currently pinned by a checkpoint */
-#define WT_STAT_CONN_TXN_PINNED_CHECKPOINT_RANGE 1209
+#define WT_STAT_CONN_TXN_PINNED_CHECKPOINT_RANGE 1231
/*!
* transaction: transaction range of IDs currently pinned by named
* snapshots
*/
-#define WT_STAT_CONN_TXN_PINNED_SNAPSHOT_RANGE 1210
+#define WT_STAT_CONN_TXN_PINNED_SNAPSHOT_RANGE 1232
/*! transaction: transaction sync calls */
-#define WT_STAT_CONN_TXN_SYNC 1211
+#define WT_STAT_CONN_TXN_SYNC 1233
/*! transaction: transactions committed */
-#define WT_STAT_CONN_TXN_COMMIT 1212
+#define WT_STAT_CONN_TXN_COMMIT 1234
/*! transaction: transactions rolled back */
-#define WT_STAT_CONN_TXN_ROLLBACK 1213
+#define WT_STAT_CONN_TXN_ROLLBACK 1235
/*!
* @}
@@ -4709,28 +4806,28 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
/*! btree: btree checkpoint generation */
#define WT_STAT_DSRC_BTREE_CHECKPOINT_GENERATION 2022
/*!
- * btree: column-store fixed-size leaf pages, only reported if
- * statistics=all is set
+ * btree: column-store fixed-size leaf pages, only reported if tree_walk
+ * or all statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_COLUMN_FIX 2023
/*!
- * btree: column-store internal pages, only reported if statistics=all is
- * set
+ * btree: column-store internal pages, only reported if tree_walk or all
+ * statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_COLUMN_INTERNAL 2024
/*!
* btree: column-store variable-size RLE encoded values, only reported if
- * statistics=all is set
+ * tree_walk or all statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_COLUMN_RLE 2025
/*!
* btree: column-store variable-size deleted values, only reported if
- * statistics=all is set
+ * tree_walk or all statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_COLUMN_DELETED 2026
/*!
* btree: column-store variable-size leaf pages, only reported if
- * statistics=all is set
+ * tree_walk or all statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_COLUMN_VARIABLE 2027
/*! btree: fixed-record size */
@@ -4748,20 +4845,26 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
/*! btree: maximum tree depth */
#define WT_STAT_DSRC_BTREE_MAXIMUM_DEPTH 2034
/*!
- * btree: number of key/value pairs, only reported if statistics=all is
- * set
+ * btree: number of key/value pairs, only reported if tree_walk or all
+ * statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_ENTRIES 2035
-/*! btree: overflow pages, only reported if statistics=all is set */
+/*!
+ * btree: overflow pages, only reported if tree_walk or all statistics
+ * are enabled
+ */
#define WT_STAT_DSRC_BTREE_OVERFLOW 2036
/*! btree: pages rewritten by compaction */
#define WT_STAT_DSRC_BTREE_COMPACT_REWRITE 2037
/*!
- * btree: row-store internal pages, only reported if statistics=all is
- * set
+ * btree: row-store internal pages, only reported if tree_walk or all
+ * statistics are enabled
*/
#define WT_STAT_DSRC_BTREE_ROW_INTERNAL 2038
-/*! btree: row-store leaf pages, only reported if statistics=all is set */
+/*!
+ * btree: row-store leaf pages, only reported if tree_walk or all
+ * statistics are enabled
+ */
#define WT_STAT_DSRC_BTREE_ROW_LEAF 2039
/*! cache: bytes currently in the cache */
#define WT_STAT_DSRC_CACHE_BYTES_INUSE 2040
@@ -4807,87 +4910,179 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
#define WT_STAT_DSRC_CACHE_WRITE_RESTORE 2060
/*! cache: unmodified pages evicted */
#define WT_STAT_DSRC_CACHE_EVICTION_CLEAN 2061
+/*!
+ * cache_walk: Average difference between current eviction generation
+ * when the page was last considered, only reported if cache_walk or all
+ * statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_GEN_AVG_GAP 2062
+/*!
+ * cache_walk: Average on-disk page image size seen, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_AVG_WRITTEN_SIZE 2063
+/*!
+ * cache_walk: Clean pages currently in cache, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_PAGES_CLEAN 2064
+/*!
+ * cache_walk: Current eviction generation, only reported if cache_walk
+ * or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_GEN_CURRENT 2065
+/*!
+ * cache_walk: Dirty pages currently in cache, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_PAGES_DIRTY 2066
+/*!
+ * cache_walk: Entries in the root page, only reported if cache_walk or
+ * all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_ROOT_ENTRIES 2067
+/*!
+ * cache_walk: Internal pages currently in cache, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_PAGES_INTERNAL 2068
+/*!
+ * cache_walk: Leaf pages currently in cache, only reported if cache_walk
+ * or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_PAGES_LEAF 2069
+/*!
+ * cache_walk: Maximum difference between current eviction generation
+ * when the page was last considered, only reported if cache_walk or all
+ * statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_GEN_MAX_GAP 2070
+/*!
+ * cache_walk: Maximum page size seen, only reported if cache_walk or all
+ * statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_MAX_PAGESIZE 2071
+/*!
+ * cache_walk: Minimum on-disk page image size seen, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_MIN_WRITTEN_SIZE 2072
+/*!
+ * cache_walk: On-disk page image sizes smaller than a single allocation
+ * unit, only reported if cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_SMALLER_ALLOC_SIZE 2073
+/*!
+ * cache_walk: Pages created in memory and never written, only reported
+ * if cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_MEMORY 2074
+/*!
+ * cache_walk: Pages currently queued for eviction, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_QUEUED 2075
+/*!
+ * cache_walk: Pages that could not be queued for eviction, only reported
+ * if cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_NOT_QUEUEABLE 2076
+/*!
+ * cache_walk: Refs skipped during cache traversal, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_REFS_SKIPPED 2077
+/*!
+ * cache_walk: Size of the root page, only reported if cache_walk or all
+ * statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_ROOT_SIZE 2078
+/*!
+ * cache_walk: Total number of pages currently in cache, only reported if
+ * cache_walk or all statistics are enabled
+ */
+#define WT_STAT_DSRC_CACHE_STATE_PAGES 2079
/*! compression: compressed pages read */
-#define WT_STAT_DSRC_COMPRESS_READ 2062
+#define WT_STAT_DSRC_COMPRESS_READ 2080
/*! compression: compressed pages written */
-#define WT_STAT_DSRC_COMPRESS_WRITE 2063
+#define WT_STAT_DSRC_COMPRESS_WRITE 2081
/*! compression: page written failed to compress */
-#define WT_STAT_DSRC_COMPRESS_WRITE_FAIL 2064
+#define WT_STAT_DSRC_COMPRESS_WRITE_FAIL 2082
/*! compression: page written was too small to compress */
-#define WT_STAT_DSRC_COMPRESS_WRITE_TOO_SMALL 2065
+#define WT_STAT_DSRC_COMPRESS_WRITE_TOO_SMALL 2083
/*! compression: raw compression call failed, additional data available */
-#define WT_STAT_DSRC_COMPRESS_RAW_FAIL_TEMPORARY 2066
+#define WT_STAT_DSRC_COMPRESS_RAW_FAIL_TEMPORARY 2084
/*! compression: raw compression call failed, no additional data available */
-#define WT_STAT_DSRC_COMPRESS_RAW_FAIL 2067
+#define WT_STAT_DSRC_COMPRESS_RAW_FAIL 2085
/*! compression: raw compression call succeeded */
-#define WT_STAT_DSRC_COMPRESS_RAW_OK 2068
+#define WT_STAT_DSRC_COMPRESS_RAW_OK 2086
/*! cursor: bulk-loaded cursor-insert calls */
-#define WT_STAT_DSRC_CURSOR_INSERT_BULK 2069
+#define WT_STAT_DSRC_CURSOR_INSERT_BULK 2087
/*! cursor: create calls */
-#define WT_STAT_DSRC_CURSOR_CREATE 2070
+#define WT_STAT_DSRC_CURSOR_CREATE 2088
/*! cursor: cursor-insert key and value bytes inserted */
-#define WT_STAT_DSRC_CURSOR_INSERT_BYTES 2071
+#define WT_STAT_DSRC_CURSOR_INSERT_BYTES 2089
/*! cursor: cursor-remove key bytes removed */
-#define WT_STAT_DSRC_CURSOR_REMOVE_BYTES 2072
+#define WT_STAT_DSRC_CURSOR_REMOVE_BYTES 2090
/*! cursor: cursor-update value bytes updated */
-#define WT_STAT_DSRC_CURSOR_UPDATE_BYTES 2073
+#define WT_STAT_DSRC_CURSOR_UPDATE_BYTES 2091
/*! cursor: insert calls */
-#define WT_STAT_DSRC_CURSOR_INSERT 2074
+#define WT_STAT_DSRC_CURSOR_INSERT 2092
/*! cursor: next calls */
-#define WT_STAT_DSRC_CURSOR_NEXT 2075
+#define WT_STAT_DSRC_CURSOR_NEXT 2093
/*! cursor: prev calls */
-#define WT_STAT_DSRC_CURSOR_PREV 2076
+#define WT_STAT_DSRC_CURSOR_PREV 2094
/*! cursor: remove calls */
-#define WT_STAT_DSRC_CURSOR_REMOVE 2077
+#define WT_STAT_DSRC_CURSOR_REMOVE 2095
/*! cursor: reset calls */
-#define WT_STAT_DSRC_CURSOR_RESET 2078
+#define WT_STAT_DSRC_CURSOR_RESET 2096
/*! cursor: restarted searches */
-#define WT_STAT_DSRC_CURSOR_RESTART 2079
+#define WT_STAT_DSRC_CURSOR_RESTART 2097
/*! cursor: search calls */
-#define WT_STAT_DSRC_CURSOR_SEARCH 2080
+#define WT_STAT_DSRC_CURSOR_SEARCH 2098
/*! cursor: search near calls */
-#define WT_STAT_DSRC_CURSOR_SEARCH_NEAR 2081
+#define WT_STAT_DSRC_CURSOR_SEARCH_NEAR 2099
/*! cursor: truncate calls */
-#define WT_STAT_DSRC_CURSOR_TRUNCATE 2082
+#define WT_STAT_DSRC_CURSOR_TRUNCATE 2100
/*! cursor: update calls */
-#define WT_STAT_DSRC_CURSOR_UPDATE 2083
+#define WT_STAT_DSRC_CURSOR_UPDATE 2101
/*! reconciliation: dictionary matches */
-#define WT_STAT_DSRC_REC_DICTIONARY 2084
+#define WT_STAT_DSRC_REC_DICTIONARY 2102
/*! reconciliation: fast-path pages deleted */
-#define WT_STAT_DSRC_REC_PAGE_DELETE_FAST 2085
+#define WT_STAT_DSRC_REC_PAGE_DELETE_FAST 2103
/*!
* reconciliation: internal page key bytes discarded using suffix
* compression
*/
-#define WT_STAT_DSRC_REC_SUFFIX_COMPRESSION 2086
+#define WT_STAT_DSRC_REC_SUFFIX_COMPRESSION 2104
/*! reconciliation: internal page multi-block writes */
-#define WT_STAT_DSRC_REC_MULTIBLOCK_INTERNAL 2087
+#define WT_STAT_DSRC_REC_MULTIBLOCK_INTERNAL 2105
/*! reconciliation: internal-page overflow keys */
-#define WT_STAT_DSRC_REC_OVERFLOW_KEY_INTERNAL 2088
+#define WT_STAT_DSRC_REC_OVERFLOW_KEY_INTERNAL 2106
/*! reconciliation: leaf page key bytes discarded using prefix compression */
-#define WT_STAT_DSRC_REC_PREFIX_COMPRESSION 2089
+#define WT_STAT_DSRC_REC_PREFIX_COMPRESSION 2107
/*! reconciliation: leaf page multi-block writes */
-#define WT_STAT_DSRC_REC_MULTIBLOCK_LEAF 2090
+#define WT_STAT_DSRC_REC_MULTIBLOCK_LEAF 2108
/*! reconciliation: leaf-page overflow keys */
-#define WT_STAT_DSRC_REC_OVERFLOW_KEY_LEAF 2091
+#define WT_STAT_DSRC_REC_OVERFLOW_KEY_LEAF 2109
/*! reconciliation: maximum blocks required for a page */
-#define WT_STAT_DSRC_REC_MULTIBLOCK_MAX 2092
+#define WT_STAT_DSRC_REC_MULTIBLOCK_MAX 2110
/*! reconciliation: overflow values written */
-#define WT_STAT_DSRC_REC_OVERFLOW_VALUE 2093
+#define WT_STAT_DSRC_REC_OVERFLOW_VALUE 2111
/*! reconciliation: page checksum matches */
-#define WT_STAT_DSRC_REC_PAGE_MATCH 2094
+#define WT_STAT_DSRC_REC_PAGE_MATCH 2112
/*! reconciliation: page reconciliation calls */
-#define WT_STAT_DSRC_REC_PAGES 2095
+#define WT_STAT_DSRC_REC_PAGES 2113
/*! reconciliation: page reconciliation calls for eviction */
-#define WT_STAT_DSRC_REC_PAGES_EVICTION 2096
+#define WT_STAT_DSRC_REC_PAGES_EVICTION 2114
/*! reconciliation: pages deleted */
-#define WT_STAT_DSRC_REC_PAGE_DELETE 2097
+#define WT_STAT_DSRC_REC_PAGE_DELETE 2115
/*! session: object compaction */
-#define WT_STAT_DSRC_SESSION_COMPACT 2098
+#define WT_STAT_DSRC_SESSION_COMPACT 2116
/*! session: open cursor count */
-#define WT_STAT_DSRC_SESSION_CURSOR_OPEN 2099
+#define WT_STAT_DSRC_SESSION_CURSOR_OPEN 2117
/*! transaction: update conflicts */
-#define WT_STAT_DSRC_TXN_UPDATE_CONFLICT 2100
+#define WT_STAT_DSRC_TXN_UPDATE_CONFLICT 2118
/*!
* @}
@@ -4910,42 +5105,6 @@ extern int wiredtiger_extension_terminate(WT_CONNECTION *connection);
* Statistics section: END
* DO NOT EDIT: automatically built by dist/api_stat.py.
*/
-/*!
- * @name Log record and operation types
- * @anchor log_types
- * @{
- */
-/*
- * DO NOT EDIT: automatically built by dist/log.py.
- * Log record declarations: BEGIN
- */
-/*! invalid operation */
-#define WT_LOGOP_INVALID 0
-/*! checkpoint */
-#define WT_LOGREC_CHECKPOINT 0
-/*! transaction commit */
-#define WT_LOGREC_COMMIT 1
-/*! file sync */
-#define WT_LOGREC_FILE_SYNC 2
-/*! message */
-#define WT_LOGREC_MESSAGE 3
-/*! column put */
-#define WT_LOGOP_COL_PUT 1
-/*! column remove */
-#define WT_LOGOP_COL_REMOVE 2
-/*! column truncate */
-#define WT_LOGOP_COL_TRUNCATE 3
-/*! row put */
-#define WT_LOGOP_ROW_PUT 4
-/*! row remove */
-#define WT_LOGOP_ROW_REMOVE 5
-/*! row truncate */
-#define WT_LOGOP_ROW_TRUNCATE 6
-/*
- * Log record declarations: END
- * DO NOT EDIT: automatically built by dist/log.py.
- */
-/*! @} */
/*! @} */
#undef __F
diff --git a/src/third_party/wiredtiger/src/include/wt_internal.h b/src/third_party/wiredtiger/src/include/wt_internal.h
index 4e6699ab9d1..dc3e3a0b6c0 100644
--- a/src/third_party/wiredtiger/src/include/wt_internal.h
+++ b/src/third_party/wiredtiger/src/include/wt_internal.h
@@ -126,8 +126,6 @@ struct __wt_connection_stats;
typedef struct __wt_connection_stats WT_CONNECTION_STATS;
struct __wt_cursor_backup;
typedef struct __wt_cursor_backup WT_CURSOR_BACKUP;
-struct __wt_cursor_backup_entry;
- typedef struct __wt_cursor_backup_entry WT_CURSOR_BACKUP_ENTRY;
struct __wt_cursor_btree;
typedef struct __wt_cursor_btree WT_CURSOR_BTREE;
struct __wt_cursor_bulk;
@@ -216,6 +214,8 @@ struct __wt_logslot;
typedef struct __wt_logslot WT_LOGSLOT;
struct __wt_lsm_chunk;
typedef struct __wt_lsm_chunk WT_LSM_CHUNK;
+struct __wt_lsm_cursor_chunk;
+ typedef struct __wt_lsm_cursor_chunk WT_LSM_CURSOR_CHUNK;
struct __wt_lsm_data_source;
typedef struct __wt_lsm_data_source WT_LSM_DATA_SOURCE;
struct __wt_lsm_manager;
diff --git a/src/third_party/wiredtiger/src/log/log.c b/src/third_party/wiredtiger/src/log/log.c
index b0c789f0f9e..96b593ec706 100644
--- a/src/third_party/wiredtiger/src/log/log.c
+++ b/src/third_party/wiredtiger/src/log/log.c
@@ -21,6 +21,60 @@ static int __log_write_internal(
#define WT_LOG_OPEN_VERIFY 0x02
/*
+ * __log_wait_for_earlier_slot --
+ * Wait for write_lsn to catch up to this slot.
+ */
+static void
+__log_wait_for_earlier_slot(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
+{
+ WT_CONNECTION_IMPL *conn;
+ WT_LOG *log;
+ int yield_count;
+
+ conn = S2C(session);
+ log = conn->log;
+ yield_count = 0;
+
+ while (__wt_log_cmp(&log->write_lsn, &slot->slot_release_lsn) != 0) {
+ /*
+ * If we're on a locked path and the write LSN is not advancing,
+ * unlock in case an earlier thread is trying to switch its
+ * slot and complete its operation.
+ */
+ if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
+ __wt_spin_unlock(session, &log->log_slot_lock);
+ __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
+ if (++yield_count < WT_THOUSAND)
+ __wt_yield();
+ else
+ __wt_cond_wait(session, log->log_write_cond, 200);
+ if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
+ __wt_spin_lock(session, &log->log_slot_lock);
+ }
+}
+
+/*
+ * __log_fs_write --
+ * Wrapper when writing to a log file. If we're writing to a new log
+ * file for the first time wait for writes to the previous log file.
+ */
+static int
+__log_fs_write(WT_SESSION_IMPL *session,
+ WT_LOGSLOT *slot, wt_off_t offset, size_t len, const void *buf)
+{
+ /*
+ * If we're writing into a new log file, we have to wait for all
+ * writes to the previous log file to complete otherwise there could
+ * be a hole at the end of the previous log file that we cannot detect.
+ */
+ if (slot->slot_release_lsn.l.file < slot->slot_start_lsn.l.file) {
+ __log_wait_for_earlier_slot(session, slot);
+ WT_RET(__wt_log_force_sync(session, &slot->slot_release_lsn));
+ }
+ return (__wt_write(session, slot->slot_fh, offset, len, buf));
+}
+
+/*
* __wt_log_ckpt --
* Record the given LSN as the checkpoint LSN and signal the archive
* thread as needed.
@@ -128,9 +182,9 @@ __wt_log_force_sync(WT_SESSION_IMPL *session, WT_LSN *min_lsn)
"log_force_sync: sync directory %s to LSN %" PRIu32
"/%" PRIu32,
log->log_dir_fh->name, min_lsn->l.file, min_lsn->l.offset);
- WT_ERR(__wt_epoch(session, &fsync_start));
+ __wt_epoch(session, &fsync_start);
WT_ERR(__wt_fsync(session, log->log_dir_fh, true));
- WT_ERR(__wt_epoch(session, &fsync_stop));
+ __wt_epoch(session, &fsync_stop);
fsync_duration_usecs = WT_TIMEDIFF_US(fsync_stop, fsync_start);
log->sync_dir_lsn = *min_lsn;
WT_STAT_CONN_INCR(session, log_sync_dir);
@@ -152,9 +206,9 @@ __wt_log_force_sync(WT_SESSION_IMPL *session, WT_LSN *min_lsn)
__wt_verbose(session, WT_VERB_LOG,
"log_force_sync: sync %s to LSN %" PRIu32 "/%" PRIu32,
log_fh->name, min_lsn->l.file, min_lsn->l.offset);
- WT_ERR(__wt_epoch(session, &fsync_start));
+ __wt_epoch(session, &fsync_start);
WT_ERR(__wt_fsync(session, log_fh, true));
- WT_ERR(__wt_epoch(session, &fsync_stop));
+ __wt_epoch(session, &fsync_stop);
fsync_duration_usecs = WT_TIMEDIFF_US(fsync_stop, fsync_start);
log->sync_lsn = *min_lsn;
WT_STAT_CONN_INCR(session, log_sync);
@@ -576,7 +630,7 @@ __log_fill(WT_SESSION_IMPL *session,
/*
* If this is a force or unbuffered write, write it now.
*/
- WT_ERR(__wt_write(session, myslot->slot->slot_fh,
+ WT_ERR(__log_fs_write(session, myslot->slot,
myslot->offset + myslot->slot->slot_start_offset,
record->size, record->mem));
@@ -1352,13 +1406,11 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
WT_LSN sync_lsn;
int64_t release_buffered, release_bytes;
uint64_t fsync_duration_usecs;
- int yield_count;
bool locked;
conn = S2C(session);
log = conn->log;
locked = false;
- yield_count = 0;
if (freep != NULL)
*freep = 1;
release_buffered = WT_LOG_SLOT_RELEASED_BUFFERED(slot->slot_state);
@@ -1379,8 +1431,7 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
/* Write the buffered records */
if (release_buffered != 0)
- WT_ERR(__wt_write(session,
- slot->slot_fh, slot->slot_start_offset,
+ WT_ERR(__log_fs_write(session, slot, slot->slot_start_offset,
(size_t)release_buffered, slot->slot_buf.mem));
/*
@@ -1411,22 +1462,7 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
* be holes in the log file.
*/
WT_STAT_CONN_INCR(session, log_release_write_lsn);
- while (__wt_log_cmp(&log->write_lsn, &slot->slot_release_lsn) != 0) {
- /*
- * If we're on a locked path and the write LSN is not advancing,
- * unlock in case an earlier thread is trying to switch its
- * slot and complete its operation.
- */
- if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
- __wt_spin_unlock(session, &log->log_slot_lock);
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
- if (++yield_count < WT_THOUSAND)
- __wt_yield();
- else
- __wt_cond_wait(session, log->log_write_cond, 200);
- if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
- __wt_spin_lock(session, &log->log_slot_lock);
- }
+ __log_wait_for_earlier_slot(session, slot);
log->write_start_lsn = slot->slot_start_lsn;
log->write_lsn = slot->slot_end_lsn;
@@ -1478,9 +1514,9 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
"/%" PRIu32,
log->log_dir_fh->name,
sync_lsn.l.file, sync_lsn.l.offset);
- WT_ERR(__wt_epoch(session, &fsync_start));
+ __wt_epoch(session, &fsync_start);
WT_ERR(__wt_fsync(session, log->log_dir_fh, true));
- WT_ERR(__wt_epoch(session, &fsync_stop));
+ __wt_epoch(session, &fsync_stop);
fsync_duration_usecs =
WT_TIMEDIFF_US(fsync_stop, fsync_start);
log->sync_dir_lsn = sync_lsn;
@@ -1500,9 +1536,9 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
log->log_fh->name,
sync_lsn.l.file, sync_lsn.l.offset);
WT_STAT_CONN_INCR(session, log_sync);
- WT_ERR(__wt_epoch(session, &fsync_start));
+ __wt_epoch(session, &fsync_start);
WT_ERR(__wt_fsync(session, log->log_fh, true));
- WT_ERR(__wt_epoch(session, &fsync_stop));
+ __wt_epoch(session, &fsync_stop);
fsync_duration_usecs =
WT_TIMEDIFF_US(fsync_stop, fsync_start);
WT_STAT_CONN_INCRV(session,
diff --git a/src/third_party/wiredtiger/src/log/log_auto.c b/src/third_party/wiredtiger/src/log/log_auto.c
index 34bccd0ede4..c2b38184405 100644
--- a/src/third_party/wiredtiger/src/log/log_auto.c
+++ b/src/third_party/wiredtiger/src/log/log_auto.c
@@ -119,11 +119,13 @@ __wt_logop_col_put_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, uint64_t *recnop, WT_ITEM *valuep)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIru);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, recnop, valuep));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, recnop, valuep)) != 0)
+ WT_RET_MSG(session, ret, "logop_col_put: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_COL_PUT);
*pp += size;
@@ -192,11 +194,13 @@ __wt_logop_col_remove_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, uint64_t *recnop)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIr);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, recnop));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, recnop)) != 0)
+ WT_RET_MSG(session, ret, "logop_col_remove: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_COL_REMOVE);
*pp += size;
@@ -252,11 +256,13 @@ __wt_logop_col_truncate_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, uint64_t *startp, uint64_t *stopp)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIrr);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, startp, stopp));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, startp, stopp)) != 0)
+ WT_RET_MSG(session, ret, "logop_col_truncate: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_COL_TRUNCATE);
*pp += size;
@@ -315,11 +321,13 @@ __wt_logop_row_put_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, WT_ITEM *keyp, WT_ITEM *valuep)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIuu);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, keyp, valuep));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, keyp, valuep)) != 0)
+ WT_RET_MSG(session, ret, "logop_row_put: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_ROW_PUT);
*pp += size;
@@ -394,11 +402,13 @@ __wt_logop_row_remove_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, WT_ITEM *keyp)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIu);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, keyp));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, keyp)) != 0)
+ WT_RET_MSG(session, ret, "logop_row_remove: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_ROW_REMOVE);
*pp += size;
@@ -464,11 +474,13 @@ __wt_logop_row_truncate_unpack(
WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
uint32_t *fileidp, WT_ITEM *startp, WT_ITEM *stopp, uint32_t *modep)
{
+ WT_DECL_RET;
const char *fmt = WT_UNCHECKED_STRING(IIIuuI);
uint32_t optype, size;
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
- &optype, &size, fileidp, startp, stopp, modep));
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ &optype, &size, fileidp, startp, stopp, modep)) != 0)
+ WT_RET_MSG(session, ret, "logop_row_truncate: unpack failure");
WT_ASSERT(session, optype == WT_LOGOP_ROW_TRUNCATE);
*pp += size;
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_cursor.c b/src/third_party/wiredtiger/src/lsm/lsm_cursor.c
index e98f59e7b05..c20673563d9 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_cursor.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_cursor.c
@@ -10,7 +10,7 @@
#define WT_FORALL_CURSORS(clsm, c, i) \
for ((i) = (clsm)->nchunks; (i) > 0;) \
- if (((c) = (clsm)->cursors[--i]) != NULL)
+ if (((c) = (clsm)->chunks[--i]->cursor) != NULL)
#define WT_LSM_CURCMP(s, lsm_tree, c1, c2, cmp) \
__wt_compare(s, (lsm_tree)->collator, &(c1)->key, &(c2)->key, &cmp)
@@ -18,6 +18,7 @@
static int __clsm_lookup(WT_CURSOR_LSM *, WT_ITEM *);
static int __clsm_open_cursors(WT_CURSOR_LSM *, bool, u_int, uint32_t);
static int __clsm_reset_cursors(WT_CURSOR_LSM *, WT_CURSOR *);
+static int __clsm_search_near(WT_CURSOR *cursor, int *exactp);
/*
* __wt_clsm_request_switch --
@@ -109,7 +110,7 @@ __clsm_enter_update(WT_CURSOR_LSM *clsm)
primary = NULL;
have_primary = false;
} else {
- primary = clsm->cursors[clsm->nchunks - 1];
+ primary = clsm->chunks[clsm->nchunks - 1]->cursor;
primary_chunk = clsm->primary_chunk;
WT_ASSERT(session, F_ISSET(&session->txn, WT_TXN_HAS_ID));
have_primary = (primary != NULL && primary_chunk != NULL &&
@@ -165,8 +166,7 @@ __clsm_enter(WT_CURSOR_LSM *clsm, bool reset, bool update)
WT_LSM_TREE *lsm_tree;
WT_SESSION_IMPL *session;
WT_TXN *txn;
- uint64_t *switch_txnp;
- uint64_t snap_min;
+ uint64_t i, pinned_id , switch_txn;
lsm_tree = clsm->lsm_tree;
session = (WT_SESSION_IMPL *)clsm->iface.session;
@@ -226,8 +226,8 @@ __clsm_enter(WT_CURSOR_LSM *clsm, bool reset, bool update)
* that overlaps with our snapshot is a potential
* conflict.
*
- * Note that the global snap_min is correct here: it
- * tracks concurrent transactions excluding special
+ * Note that the pinned ID is correct here: it tracks
+ * concurrent transactions excluding special
* transactions such as checkpoint (which we can't
* conflict with because checkpoint only writes the
* metadata, which is not an LSM tree).
@@ -237,17 +237,18 @@ __clsm_enter(WT_CURSOR_LSM *clsm, bool reset, bool update)
F_ISSET(clsm, WT_CLSM_OPEN_SNAPSHOT)) {
WT_ASSERT(session,
F_ISSET(txn, WT_TXN_HAS_SNAPSHOT));
- snap_min =
- WT_SESSION_TXN_STATE(session)->snap_min;
- for (switch_txnp =
- &clsm->switch_txn[clsm->nchunks - 2];
+ pinned_id =
+ WT_SESSION_TXN_STATE(session)->pinned_id;
+ for (i = clsm->nchunks - 2;
clsm->nupdates < clsm->nchunks;
- clsm->nupdates++, switch_txnp--) {
- if (WT_TXNID_LT(*switch_txnp, snap_min))
+ clsm->nupdates++, i--) {
+ switch_txn =
+ clsm->chunks[i]->switch_txn;
+ if (WT_TXNID_LT(switch_txn, pinned_id))
break;
WT_ASSERT(session,
!__wt_txn_visible_all(
- session, *switch_txnp));
+ session, switch_txn));
}
}
}
@@ -378,7 +379,7 @@ __clsm_close_cursors(WT_CURSOR_LSM *clsm, u_int start, u_int end)
WT_CURSOR *c;
u_int i;
- if (clsm->cursors == NULL || clsm->nchunks == 0)
+ if (clsm->chunks == NULL || clsm->nchunks == 0)
return (0);
/*
@@ -387,12 +388,12 @@ __clsm_close_cursors(WT_CURSOR_LSM *clsm, u_int start, u_int end)
* careful with unsigned integer wrapping.
*/
for (i = start; i < end; i++) {
- if ((c = (clsm)->cursors[i]) != NULL) {
- clsm->cursors[i] = NULL;
+ if ((c = (clsm)->chunks[i]->cursor) != NULL) {
+ clsm->chunks[i]->cursor = NULL;
WT_RET(c->close(c));
}
- if ((bloom = clsm->blooms[i]) != NULL) {
- clsm->blooms[i] = NULL;
+ if ((bloom = clsm->chunks[i]->bloom) != NULL) {
+ clsm->chunks[i]->bloom = NULL;
WT_RET(__wt_bloom_close(bloom));
}
}
@@ -401,6 +402,45 @@ __clsm_close_cursors(WT_CURSOR_LSM *clsm, u_int start, u_int end)
}
/*
+ * __clsm_resize_chunks --
+ * Allocates an array of unit objects for each chunk.
+ */
+static int
+__clsm_resize_chunks(
+ WT_SESSION_IMPL *session, WT_CURSOR_LSM *clsm, u_int nchunks)
+{
+ WT_DECL_RET;
+ WT_LSM_CURSOR_CHUNK *chunk;
+
+ /* Don't allocate more iterators if we don't need them. */
+ if (clsm->chunks_count >= nchunks) {
+ return (ret);
+ }
+
+ WT_RET(__wt_realloc_def(session, &clsm->chunks_alloc, nchunks,
+ &clsm->chunks));
+ for (; clsm->chunks_count < nchunks; clsm->chunks_count++) {
+ WT_RET(__wt_calloc_one(session, &chunk));
+ clsm->chunks[clsm->chunks_count] = chunk;
+ }
+ return (ret);
+}
+
+/*
+ * __clsm_free_chunks --
+ * Allocates an array of unit objects for each chunk.
+ */
+static void
+__clsm_free_chunks(WT_SESSION_IMPL *session, WT_CURSOR_LSM *clsm)
+{
+ size_t i;
+ for (i = 0; i < clsm->chunks_count; i++) {
+ __wt_free(session, clsm->chunks[i]);
+ }
+ __wt_free(session, clsm->chunks);
+}
+
+/*
* __clsm_open_cursors --
* Open cursors for the current set of files.
*/
@@ -409,7 +449,7 @@ __clsm_open_cursors(
WT_CURSOR_LSM *clsm, bool update, u_int start_chunk, uint32_t start_id)
{
WT_BTREE *btree;
- WT_CURSOR *c, **cp, *primary;
+ WT_CURSOR *c, *cursor, *primary;
WT_DECL_RET;
WT_LSM_CHUNK *chunk;
WT_LSM_TREE *lsm_tree;
@@ -422,6 +462,7 @@ __clsm_open_cursors(
bool locked;
c = &clsm->iface;
+ cursor = NULL;
session = (WT_SESSION_IMPL *)c->session;
txn = &session->txn;
chunk = NULL;
@@ -465,7 +506,7 @@ __clsm_open_cursors(
retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
nchunks = clsm->nchunks;
ngood = 0;
-
+ WT_ERR(__clsm_resize_chunks(session, clsm, nchunks));
/*
* We may have raced with another merge completing. Check that
* we're starting at the right offset in the chunk array.
@@ -486,16 +527,13 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
WT_ASSERT(session, start_chunk + nchunks <= lsm_tree->nchunks);
} else {
nchunks = lsm_tree->nchunks;
+ WT_ERR(__clsm_resize_chunks(session, clsm, nchunks));
/*
* If we are only opening the cursor for updates, only open the
* primary chunk, plus any other chunks that might be required
* to detect snapshot isolation conflicts.
*/
- if (F_ISSET(clsm, WT_CLSM_OPEN_SNAPSHOT))
- WT_ERR(__wt_realloc_def(session,
- &clsm->txnid_alloc, nchunks,
- &clsm->switch_txn));
if (F_ISSET(clsm, WT_CLSM_OPEN_READ))
ngood = nupdates = 0;
else if (F_ISSET(clsm, WT_CLSM_OPEN_SNAPSHOT)) {
@@ -504,11 +542,11 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
* chunk are globally visible. Copy the maximum
* transaction IDs into the cursor as we go.
*/
- for (ngood = nchunks - 1, nupdates = 1;
- ngood > 0;
+ for (ngood = nchunks - 1, nupdates = 1; ngood > 0;
ngood--, nupdates++) {
chunk = lsm_tree->chunk[ngood - 1];
- clsm->switch_txn[ngood - 1] = chunk->switch_txn;
+ clsm->chunks[ngood - 1]->switch_txn =
+ chunk->switch_txn;
if (__wt_txn_visible_all(
session, chunk->switch_txn))
break;
@@ -519,21 +557,20 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
}
/* Check how many cursors are already open. */
- for (cp = clsm->cursors + ngood;
- ngood < clsm->nchunks && ngood < nchunks;
- cp++, ngood++) {
+ for (; ngood < clsm->nchunks && ngood < nchunks; ngood++) {
chunk = lsm_tree->chunk[ngood];
+ cursor = clsm->chunks[ngood]->cursor;
/* If the cursor isn't open yet, we're done. */
- if (*cp == NULL)
+ if (cursor == NULL)
break;
/* Easy case: the URIs don't match. */
- if (strcmp((*cp)->uri, chunk->uri) != 0)
+ if (strcmp(cursor->uri, chunk->uri) != 0)
break;
/* Make sure the checkpoint config matches. */
- checkpoint = ((WT_CURSOR_BTREE *)*cp)->
+ checkpoint = ((WT_CURSOR_BTREE *)cursor)->
btree->dhandle->checkpoint;
if (checkpoint == NULL &&
F_ISSET(chunk, WT_LSM_CHUNK_ONDISK) &&
@@ -541,7 +578,7 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
break;
/* Make sure the Bloom config matches. */
- if (clsm->blooms[ngood] == NULL &&
+ if (clsm->chunks[ngood]->bloom == NULL &&
F_ISSET(chunk, WT_LSM_CHUNK_BLOOM))
break;
}
@@ -559,7 +596,7 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
* full, we may block while closing a cursor. Save the
* generation number and retry if it has changed under us.
*/
- if (clsm->cursors != NULL && ngood < clsm->nchunks) {
+ if (clsm->chunks != NULL && ngood < clsm->nchunks) {
close_range_start = ngood;
close_range_end = clsm->nchunks;
} else if (!F_ISSET(clsm, WT_CLSM_OPEN_READ) && nupdates > 0 ) {
@@ -591,28 +628,23 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
clsm->current = NULL;
}
- WT_ERR(__wt_realloc_def(session,
- &clsm->bloom_alloc, nchunks, &clsm->blooms));
- WT_ERR(__wt_realloc_def(session,
- &clsm->cursor_alloc, nchunks, &clsm->cursors));
-
clsm->nchunks = nchunks;
/* Open the cursors for chunks that have changed. */
- for (i = ngood, cp = clsm->cursors + i; i != nchunks; i++, cp++) {
+ for (i = ngood; i != nchunks; i++) {
chunk = lsm_tree->chunk[i + start_chunk];
/* Copy the maximum transaction ID. */
if (F_ISSET(clsm, WT_CLSM_OPEN_SNAPSHOT))
- clsm->switch_txn[i] = chunk->switch_txn;
+ clsm->chunks[i]->switch_txn = chunk->switch_txn;
/*
* Read from the checkpoint if the file has been written.
* Once all cursors switch, the in-memory tree can be evicted.
*/
- WT_ASSERT(session, *cp == NULL);
+ WT_ASSERT(session, clsm->chunks[i]->cursor == NULL);
ret = __wt_open_cursor(session, chunk->uri, c,
(F_ISSET(chunk, WT_LSM_CHUNK_ONDISK) && !chunk->empty) ?
- ckpt_cfg : NULL, cp);
+ ckpt_cfg : NULL, &clsm->chunks[i]->cursor);
/*
* XXX kludge: we may have an empty chunk where no checkpoint
@@ -620,8 +652,8 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
* chunk instead.
*/
if (ret == WT_NOTFOUND && F_ISSET(chunk, WT_LSM_CHUNK_ONDISK)) {
- ret = __wt_open_cursor(
- session, chunk->uri, c, NULL, cp);
+ ret = __wt_open_cursor(session,
+ chunk->uri, c, NULL, &clsm->chunks[i]->cursor);
if (ret == 0)
chunk->empty = 1;
}
@@ -634,25 +666,31 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
* write conflicts with concurrent updates.
*/
if (i != nchunks - 1)
- (*cp)->insert = __wt_curfile_update_check;
+ clsm->chunks[i]->cursor->insert =
+ __wt_curfile_update_check;
if (!F_ISSET(clsm, WT_CLSM_MERGE) &&
F_ISSET(chunk, WT_LSM_CHUNK_BLOOM))
WT_ERR(__wt_bloom_open(session, chunk->bloom_uri,
lsm_tree->bloom_bit_count,
lsm_tree->bloom_hash_count,
- c, &clsm->blooms[i]));
+ c, &clsm->chunks[i]->bloom));
/* Child cursors always use overwrite and raw mode. */
- F_SET(*cp, WT_CURSTD_OVERWRITE | WT_CURSTD_RAW);
+ F_SET(clsm->chunks[i]->cursor,
+ WT_CURSTD_OVERWRITE | WT_CURSTD_RAW);
}
+ /* Setup the count values for each chunk in the chunks*/
+ for (i = 0; i != clsm->nchunks; i++)
+ clsm->chunks[i]->count = lsm_tree->chunk[i]->count;
+
/* The last chunk is our new primary. */
if (chunk != NULL &&
!F_ISSET(chunk, WT_LSM_CHUNK_ONDISK) &&
chunk->switch_txn == WT_TXN_NONE) {
clsm->primary_chunk = chunk;
- primary = clsm->cursors[clsm->nchunks - 1];
+ primary = clsm->chunks[clsm->nchunks - 1]->cursor;
/*
* Disable eviction for the in-memory chunk. Also clear the
* bulk load flag here, otherwise eviction will be enabled by
@@ -672,17 +710,19 @@ err:
#ifdef HAVE_DIAGNOSTIC
/* Check that all cursors are open as expected. */
if (ret == 0 && F_ISSET(clsm, WT_CLSM_OPEN_READ)) {
- for (i = 0, cp = clsm->cursors; i != clsm->nchunks; cp++, i++) {
+ for (i = 0; i != clsm->nchunks; i++) {
+ cursor = clsm->chunks[i]->cursor;
chunk = lsm_tree->chunk[i + start_chunk];
- /* Make sure the cursor is open. */
- WT_ASSERT(session, *cp != NULL);
+ /* Make sure the first cursor is open. */
+ WT_ASSERT(session, cursor != NULL);
/* Easy case: the URIs should match. */
- WT_ASSERT(session, strcmp((*cp)->uri, chunk->uri) == 0);
+ WT_ASSERT(
+ session, strcmp(cursor->uri, chunk->uri) == 0);
/* Make sure the checkpoint config matches. */
- checkpoint = ((WT_CURSOR_BTREE *)*cp)->
+ checkpoint = ((WT_CURSOR_BTREE *)cursor)->
btree->dhandle->checkpoint;
WT_ASSERT(session,
(F_ISSET(chunk, WT_LSM_CHUNK_ONDISK) &&
@@ -693,7 +733,8 @@ err:
WT_ASSERT(session,
(F_ISSET(chunk, WT_LSM_CHUNK_BLOOM) &&
!F_ISSET(clsm, WT_CLSM_MERGE)) ?
- clsm->blooms[i] != NULL : clsm->blooms[i] == NULL);
+ clsm->chunks[i]->bloom != NULL :
+ clsm->chunks[i]->bloom == NULL);
}
}
#endif
@@ -902,6 +943,96 @@ err: __clsm_leave(clsm);
}
/*
+ * __clsm_random_chunk --
+ * Pick a chunk at random, weighted by the size of all chunks. Weighting
+ * proportional to documents avoids biasing towards small chunks. Then return
+ * the cursor on the chunk we have picked.
+ */
+static int
+__clsm_random_chunk(WT_SESSION_IMPL *session,
+ WT_CURSOR_LSM *clsm, WT_CURSOR **cursor)
+{
+ uint64_t checked_docs, i, rand_doc, total_docs;
+
+ /*
+ * If the tree is empty we cannot do a random lookup, so return a
+ * WT_NOTFOUND.
+ */
+ if (clsm->nchunks == 0)
+ return (WT_NOTFOUND);
+ for (total_docs = i = 0; i < clsm->nchunks; i++) {
+ total_docs += clsm->chunks[i]->count;
+ }
+ if (total_docs == 0)
+ return (WT_NOTFOUND);
+
+ rand_doc = __wt_random(&session->rnd) % total_docs;
+
+ for (checked_docs = i = 0; i < clsm->nchunks; i++) {
+ checked_docs += clsm->chunks[i]->count;
+ if (rand_doc <= checked_docs) {
+ *cursor = clsm->chunks[i]->cursor;
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * __clsm_next_random --
+ * WT_CURSOR->next method for the LSM cursor type when configured with
+ * next_random.
+ */
+static int
+__clsm_next_random(WT_CURSOR *cursor)
+{
+ WT_CURSOR_LSM *clsm;
+ WT_CURSOR *c;
+ WT_DECL_RET;
+ WT_SESSION_IMPL *session;
+ int exact;
+
+ c = NULL;
+ clsm = (WT_CURSOR_LSM *)cursor;
+
+ CURSOR_API_CALL(cursor, session, next, NULL);
+ WT_CURSOR_NOVALUE(cursor);
+ WT_ERR(__clsm_enter(clsm, false, false));
+
+ for (;;) {
+ WT_ERR(__clsm_random_chunk(session, clsm, &c));
+ /*
+ * This call to next_random on the chunk can potentially end in
+ * WT_NOTFOUND if the chunk we picked is empty. We want to retry
+ * in that case.
+ */
+ ret = __wt_curfile_next_random(c);
+ if (ret == WT_NOTFOUND)
+ continue;
+
+ WT_ERR(ret);
+ F_SET(cursor, WT_CURSTD_KEY_INT);
+ WT_ERR(c->get_key(c, &cursor->key));
+ /*
+ * Search near the current key to resolve any tombstones
+ * and position to a valid document. If we see a
+ * WT_NOTFOUND here that is valid, as the tree has no
+ * documents visible to us.
+ */
+ WT_ERR(__clsm_search_near(cursor, &exact));
+ break;
+ }
+
+ /* We have found a valid doc. Set that we are now positioned */
+ if (0) {
+err: F_CLR(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ }
+ __clsm_leave(clsm);
+ API_END(session, ret);
+ return (ret);
+}
+
+/*
* __clsm_prev --
* WT_CURSOR->prev method for the LSM cursor type.
*/
@@ -1072,7 +1203,7 @@ __clsm_lookup(WT_CURSOR_LSM *clsm, WT_ITEM *value)
WT_FORALL_CURSORS(clsm, c, i) {
/* If there is a Bloom filter, see if we can skip the read. */
bloom = NULL;
- if ((bloom = clsm->blooms[i]) != NULL) {
+ if ((bloom = clsm->chunks[i]->bloom) != NULL) {
if (!have_hash) {
__wt_bloom_hash(bloom, &cursor->key, &bhash);
have_hash = true;
@@ -1259,7 +1390,12 @@ __clsm_search_near(WT_CURSOR *cursor, int *exactp)
*/
F_CLR(cursor, WT_CURSTD_KEY_SET);
F_SET(cursor, WT_CURSTD_KEY_INT);
- if ((ret = cursor->next(cursor)) == 0) {
+ /*
+ * We call __clsm_next here as we want to advance
+ * forward. If we are a random LSM cursor calling next
+ * on the cursor will not advance as we intend.
+ */
+ if ((ret = __clsm_next(cursor)) == 0) {
cmp = 1;
deleted = false;
}
@@ -1268,7 +1404,11 @@ __clsm_search_near(WT_CURSOR *cursor, int *exactp)
}
if (deleted) {
clsm->current = NULL;
- WT_ERR(cursor->prev(cursor));
+ /*
+ * We call prev directly here as cursor->prev may be "invalid"
+ * if this is a random cursor.
+ */
+ WT_ERR(__clsm_prev(cursor));
cmp = -1;
}
*exactp = cmp;
@@ -1312,7 +1452,7 @@ __clsm_put(WT_SESSION_IMPL *session, WT_CURSOR_LSM *clsm,
* Clear the existing cursor position. Don't clear the primary cursor:
* we're about to use it anyway.
*/
- primary = clsm->cursors[clsm->nchunks - 1];
+ primary = clsm->chunks[clsm->nchunks - 1]->cursor;
WT_RET(__clsm_reset_cursors(clsm, primary));
/* If necessary, set the position for future scans. */
@@ -1322,12 +1462,12 @@ __clsm_put(WT_SESSION_IMPL *session, WT_CURSOR_LSM *clsm,
for (i = 0, slot = clsm->nchunks - 1; i < clsm->nupdates; i++, slot--) {
/* Check if we need to keep updating old chunks. */
if (i > 0 &&
- __wt_txn_visible(session, clsm->switch_txn[slot])) {
+ __wt_txn_visible(session, clsm->chunks[slot]->switch_txn)) {
clsm->nupdates = i;
break;
}
- c = clsm->cursors[slot];
+ c = clsm->chunks[slot]->cursor;
c->set_key(c, key);
c->set_value(c, value);
WT_RET((position && i == 0) ? c->update(c) : c->insert(c));
@@ -1485,9 +1625,7 @@ __wt_clsm_close(WT_CURSOR *cursor)
clsm = (WT_CURSOR_LSM *)cursor;
CURSOR_API_CALL(cursor, session, close, NULL);
WT_TRET(__clsm_close_cursors(clsm, 0, clsm->nchunks));
- __wt_free(session, clsm->blooms);
- __wt_free(session, clsm->cursors);
- __wt_free(session, clsm->switch_txn);
+ __clsm_free_chunks(session, clsm);
/* In case we were somehow left positioned, clear that. */
__clsm_leave(clsm);
@@ -1540,7 +1678,7 @@ __wt_clsm_open(WT_SESSION_IMPL *session,
lsm_tree = NULL;
if (!WT_PREFIX_MATCH(uri, "lsm:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, uri, "lsm:"));
if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY))
WT_RET_MSG(session, EINVAL,
@@ -1588,6 +1726,13 @@ __wt_clsm_open(WT_SESSION_IMPL *session,
*/
clsm->dsk_gen = 0;
+ /* If the next_random option is set, configure a random cursor */
+ WT_ERR(__wt_config_gets_def(session, cfg, "next_random", 0, &cval));
+ if (cval.val != 0) {
+ __wt_cursor_set_notsup(cursor);
+ cursor->next = __clsm_next_random;
+ }
+
WT_ERR(__wt_cursor_init(cursor, cursor->uri, owner, cfg, cursorp));
if (bulk)
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_cursor_bulk.c b/src/third_party/wiredtiger/src/lsm/lsm_cursor_bulk.c
index 607ca0c9705..319426de3f0 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_cursor_bulk.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_cursor_bulk.c
@@ -28,9 +28,8 @@ __clsm_close_bulk(WT_CURSOR *cursor)
session = (WT_SESSION_IMPL *)clsm->iface.session;
/* Close the bulk cursor to ensure the chunk is written to disk. */
- bulk_cursor = clsm->cursors[0];
+ bulk_cursor = clsm->chunks[0]->cursor;
WT_RET(bulk_cursor->close(bulk_cursor));
- clsm->cursors[0] = NULL;
clsm->nchunks = 0;
/* Set ondisk, and flush the metadata */
@@ -75,7 +74,7 @@ __clsm_insert_bulk(WT_CURSOR *cursor)
WT_ASSERT(session, lsm_tree->nchunks == 1 && clsm->nchunks == 1);
++chunk->count;
chunk->size += cursor->key.size + cursor->value.size;
- bulk_cursor = *clsm->cursors;
+ bulk_cursor = clsm->chunks[0]->cursor;
bulk_cursor->set_key(bulk_cursor, &cursor->key);
bulk_cursor->set_value(bulk_cursor, &cursor->value);
WT_RET(bulk_cursor->insert(bulk_cursor));
@@ -124,11 +123,10 @@ __wt_clsm_open_bulk(WT_CURSOR_LSM *clsm, const char *cfg[])
* for a bloom filter - it makes cleanup simpler. Cleaned up by
* cursor close on error.
*/
- WT_RET(__wt_calloc_one(session, &clsm->blooms));
- clsm->bloom_alloc = 1;
- WT_RET(__wt_calloc_one(session, &clsm->cursors));
- clsm->cursor_alloc = 1;
- clsm->nchunks = 1;
+ WT_RET(
+ __wt_realloc_def(session, &clsm->chunks_alloc, 1, &clsm->chunks));
+ WT_RET(__wt_calloc_one(session, &clsm->chunks[0]));
+ clsm->chunks_count = clsm->nchunks = 1;
/*
* Open a bulk cursor on the first chunk in the tree - take a read
@@ -139,7 +137,7 @@ __wt_clsm_open_bulk(WT_CURSOR_LSM *clsm, const char *cfg[])
*/
WT_RET(__wt_open_cursor(session,
lsm_tree->chunk[0]->uri, &clsm->iface, cfg, &bulk_cursor));
- clsm->cursors[0] = bulk_cursor;
+ clsm->chunks[0]->cursor = bulk_cursor;
/* LSM cursors are always raw */
F_SET(bulk_cursor, WT_CURSTD_RAW);
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_manager.c b/src/third_party/wiredtiger/src/lsm/lsm_manager.c
index 5a5140b9c3a..cbd83a5cd30 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_manager.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_manager.c
@@ -55,6 +55,7 @@ __lsm_general_worker_start(WT_SESSION_IMPL *session)
* as many worker threads as are required to keep up with demand.
*/
WT_ASSERT(session, manager->lsm_workers > 0);
+ WT_ASSERT(session, manager->lsm_workers < manager->lsm_workers_max);
for (; manager->lsm_workers < manager->lsm_workers_max;
manager->lsm_workers++) {
worker_args =
@@ -116,17 +117,18 @@ __lsm_stop_workers(WT_SESSION_IMPL *session)
{
WT_LSM_MANAGER *manager;
WT_LSM_WORKER_ARGS *worker_args;
- uint32_t i;
manager = &S2C(session)->lsm_manager;
/*
- * Start at the end of the list of threads and stop them until we
- * have the desired number. We want to keep all active threads
- * packed at the front of the worker array.
+ * Start at the end of the list of threads and stop them until we have
+ * the desired number. We want to keep all active threads packed at the
+ * front of the worker array.
*/
- WT_ASSERT(session, manager->lsm_workers != 0);
- for (i = manager->lsm_workers - 1; i >= manager->lsm_workers_max; i--) {
- worker_args = &manager->lsm_worker_cookies[i];
+ WT_ASSERT(session, manager->lsm_workers > manager->lsm_workers_max);
+ for (; manager->lsm_workers > manager->lsm_workers_max;
+ manager->lsm_workers--) {
+ worker_args =
+ &manager->lsm_worker_cookies[manager->lsm_workers - 1];
/*
* Clear this worker's flag so it stops.
*/
@@ -136,7 +138,6 @@ __lsm_stop_workers(WT_SESSION_IMPL *session)
worker_args->tid = 0;
worker_args->type = 0;
worker_args->flags = 0;
- manager->lsm_workers--;
/*
* We do not clear the session because they are allocated
* statically when the connection was opened.
@@ -392,7 +393,7 @@ __lsm_manager_run_server(WT_SESSION_IMPL *session)
TAILQ_FOREACH(lsm_tree, &S2C(session)->lsmqh, q) {
if (!lsm_tree->active)
continue;
- WT_ERR(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
pushms = lsm_tree->work_push_ts.tv_sec == 0 ? 0 :
WT_TIMEDIFF_MS(now, lsm_tree->work_push_ts);
fillms = 3 * lsm_tree->chunk_fill_ms;
@@ -651,7 +652,7 @@ __wt_lsm_manager_push_entry(WT_SESSION_IMPL *session,
return (0);
}
- WT_RET(__wt_epoch(session, &lsm_tree->work_push_ts));
+ __wt_epoch(session, &lsm_tree->work_push_ts);
WT_RET(__wt_calloc_one(session, &entry));
entry->type = type;
entry->flags = flags;
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_merge.c b/src/third_party/wiredtiger/src/lsm/lsm_merge.c
index 4bbfcfd4411..493855d489a 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_merge.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_merge.c
@@ -54,7 +54,7 @@ __lsm_merge_aggressive_clear(WT_LSM_TREE *lsm_tree)
* __lsm_merge_aggressive_update --
* Update the merge aggressiveness for an LSM tree.
*/
-static int
+static void
__lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
{
struct timespec now;
@@ -72,7 +72,7 @@ __lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
if (!lsm_tree->modified ||
F_ISSET(lsm_tree, WT_LSM_TREE_COMPACTING)) {
lsm_tree->merge_aggressiveness = 10;
- return (0);
+ return;
}
/*
@@ -81,7 +81,7 @@ __lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
*/
if (lsm_tree->chunks_flushed <= lsm_tree->merge_min) {
__lsm_merge_aggressive_clear(lsm_tree);
- return (0);
+ return;
}
/*
@@ -91,10 +91,10 @@ __lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
*/
if (!F_ISSET(lsm_tree, WT_LSM_TREE_AGGRESSIVE_TIMER)) {
F_SET(lsm_tree, WT_LSM_TREE_AGGRESSIVE_TIMER);
- return (__wt_epoch(session, &lsm_tree->merge_aggressive_ts));
+ __wt_epoch(session, &lsm_tree->merge_aggressive_ts);
}
- WT_RET(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
msec_since_last_merge =
WT_TIMEDIFF_MS(now, lsm_tree->merge_aggressive_ts);
@@ -113,7 +113,7 @@ __lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
* generates a variable load.
*/
if (msec_since_last_merge < msec_to_create_merge)
- return (0);
+ return;
/*
* Bump how aggressively we look for merges based on how long since
@@ -134,7 +134,6 @@ __lsm_merge_aggressive_update(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree)
msec_since_last_merge, lsm_tree->chunk_fill_ms);
lsm_tree->merge_aggressiveness = new_aggressive;
}
- return (0);
}
/*
@@ -326,7 +325,7 @@ retry_find:
goto retry_find;
}
/* Consider getting aggressive if no merge was found */
- WT_RET(__lsm_merge_aggressive_update(session, lsm_tree));
+ __lsm_merge_aggressive_update(session, lsm_tree);
return (WT_NOTFOUND);
}
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_stat.c b/src/third_party/wiredtiger/src/lsm/lsm_stat.c
index f4f5a0acce8..3fe3ca1ba81 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_stat.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_stat.c
@@ -42,11 +42,11 @@ __curstat_lsm_init(
if (cst->flags != 0) {
(void)snprintf(config, sizeof(config),
"statistics=(%s%s%s%s)",
- F_ISSET(cst, WT_CONN_STAT_ALL) ? "all," : "",
- F_ISSET(cst, WT_CONN_STAT_CLEAR) ? "clear," : "",
- !F_ISSET(cst, WT_CONN_STAT_ALL) &&
- F_ISSET(cst, WT_CONN_STAT_FAST) ? "fast," : "",
- F_ISSET(cst, WT_CONN_STAT_SIZE) ? "size," : "");
+ F_ISSET(cst, WT_STAT_TYPE_ALL) ? "all," : "",
+ F_ISSET(cst, WT_STAT_CLEAR) ? "clear," : "",
+ !F_ISSET(cst, WT_STAT_TYPE_ALL) &&
+ F_ISSET(cst, WT_STAT_TYPE_FAST) ? "fast," : "",
+ F_ISSET(cst, WT_STAT_TYPE_SIZE) ? "size," : "");
cfg[1] = disk_cfg[1] = config;
}
@@ -132,26 +132,26 @@ __curstat_lsm_init(
/* Include, and optionally clear, LSM-level specific information. */
WT_STAT_WRITE(session, stats, bloom_miss, lsm_tree->bloom_miss);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->bloom_miss = 0;
WT_STAT_WRITE(session, stats, bloom_hit, lsm_tree->bloom_hit);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->bloom_hit = 0;
WT_STAT_WRITE(session,
stats, bloom_false_positive, lsm_tree->bloom_false_positive);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->bloom_false_positive = 0;
WT_STAT_WRITE(session,
stats, lsm_lookup_no_bloom, lsm_tree->lsm_lookup_no_bloom);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->lsm_lookup_no_bloom = 0;
WT_STAT_WRITE(session,
stats, lsm_checkpoint_throttle, lsm_tree->lsm_checkpoint_throttle);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->lsm_checkpoint_throttle = 0;
WT_STAT_WRITE(session,
stats, lsm_merge_throttle, lsm_tree->lsm_merge_throttle);
- if (F_ISSET(cst, WT_CONN_STAT_CLEAR))
+ if (F_ISSET(cst, WT_STAT_CLEAR))
lsm_tree->lsm_merge_throttle = 0;
__wt_curstat_dsrc_final(cst);
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_tree.c b/src/third_party/wiredtiger/src/lsm/lsm_tree.c
index db9fd581110..0054dcd1583 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_tree.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_tree.c
@@ -265,7 +265,7 @@ __wt_lsm_tree_setup_chunk(
WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree, WT_LSM_CHUNK *chunk)
{
WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_SCHEMA));
- WT_RET(__wt_epoch(session, &chunk->create_ts));
+ __wt_epoch(session, &chunk->create_ts);
WT_RET(__wt_lsm_tree_chunk_name(
session, lsm_tree, chunk->id, &chunk->uri));
@@ -496,7 +496,7 @@ __lsm_tree_open(WT_SESSION_IMPL *session,
lsm_tree->queue_ref = 0;
/* Set a flush timestamp as a baseline. */
- WT_ERR(__wt_epoch(session, &lsm_tree->last_flush_ts));
+ __wt_epoch(session, &lsm_tree->last_flush_ts);
/* Now the tree is setup, make it visible to others. */
TAILQ_INSERT_HEAD(&S2C(session)->lsmqh, lsm_tree, q);
@@ -1139,7 +1139,7 @@ __wt_lsm_compact(WT_SESSION_IMPL *session, const char *name, bool *skipp)
return (0);
}
- WT_ERR(__wt_seconds(session, &begin));
+ __wt_seconds(session, &begin);
/*
* Compacting has two distinct phases.
@@ -1267,7 +1267,7 @@ __wt_lsm_compact(WT_SESSION_IMPL *session, const char *name, bool *skipp)
break;
}
__wt_sleep(1, 0);
- WT_ERR(__wt_seconds(session, &end));
+ __wt_seconds(session, &end);
if (session->compact->max_time > 0 &&
session->compact->max_time < (uint64_t)(end - begin)) {
WT_ERR(ETIMEDOUT);
diff --git a/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c b/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c
index 72bcf56b3c4..917104031fc 100644
--- a/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c
+++ b/src/third_party/wiredtiger/src/lsm/lsm_work_unit.c
@@ -358,7 +358,7 @@ __wt_lsm_checkpoint_chunk(WT_SESSION_IMPL *session,
WT_ERR(__wt_lsm_tree_set_chunk_size(session, chunk));
/* Update the flush timestamp to help track ongoing progress. */
- WT_ERR(__wt_epoch(session, &lsm_tree->last_flush_ts));
+ __wt_epoch(session, &lsm_tree->last_flush_ts);
++lsm_tree->chunks_flushed;
/* Lock the tree, mark the chunk as on disk and update the metadata. */
diff --git a/src/third_party/wiredtiger/src/meta/meta_ckpt.c b/src/third_party/wiredtiger/src/meta/meta_ckpt.c
index 2b7719c3241..b985104c2eb 100644
--- a/src/third_party/wiredtiger/src/meta/meta_ckpt.c
+++ b/src/third_party/wiredtiger/src/meta/meta_ckpt.c
@@ -424,7 +424,7 @@ __wt_meta_ckptlist_set(WT_SESSION_IMPL *session,
* guaranteed, a time_t has to be an arithmetic type,
* but not an integral type.
*/
- WT_ERR(__wt_seconds(session, &secs));
+ __wt_seconds(session, &secs);
ckpt->sec = (uintmax_t)secs;
}
if (strcmp(ckpt->name, WT_CHECKPOINT) == 0)
diff --git a/src/third_party/wiredtiger/src/os_posix/os_mtx_cond.c b/src/third_party/wiredtiger/src/os_posix/os_mtx_cond.c
index b25bb8c25d1..d4ee532a2af 100644
--- a/src/third_party/wiredtiger/src/os_posix/os_mtx_cond.c
+++ b/src/third_party/wiredtiger/src/os_posix/os_mtx_cond.c
@@ -63,7 +63,7 @@ __wt_cond_wait_signal(
locked = true;
if (usecs > 0) {
- WT_ERR(__wt_epoch(session, &ts));
+ __wt_epoch(session, &ts);
ts.tv_sec += (time_t)
(((uint64_t)ts.tv_nsec + WT_THOUSAND * usecs) / WT_BILLION);
ts.tv_nsec = (long)
@@ -103,27 +103,30 @@ void
__wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond)
{
WT_DECL_RET;
- bool locked;
-
- locked = false;
__wt_verbose(session, WT_VERB_MUTEX, "signal %s", cond->name);
- /* Fast path if already signalled. */
- if (cond->waiters == -1)
- return;
+ /*
+ * Our callers are often setting flags to cause a thread to exit. Add
+ * a barrier to ensure the flags are seen by the threads.
+ */
+ WT_WRITE_BARRIER();
- if (cond->waiters > 0 || !__wt_atomic_casi32(&cond->waiters, 0, -1)) {
- WT_ERR(pthread_mutex_lock(&cond->mtx));
- locked = true;
- WT_ERR(pthread_cond_broadcast(&cond->cond));
- }
+ /*
+ * Fast path if we are in (or can enter), a state where the next waiter
+ * will return immediately as already signaled.
+ */
+ if (cond->waiters == -1 ||
+ (cond->waiters == 0 && __wt_atomic_casi32(&cond->waiters, 0, -1)))
+ return;
-err: if (locked)
- WT_TRET(pthread_mutex_unlock(&cond->mtx));
+ WT_ERR(pthread_mutex_lock(&cond->mtx));
+ ret = pthread_cond_broadcast(&cond->cond);
+ WT_TRET(pthread_mutex_unlock(&cond->mtx));
if (ret == 0)
return;
+err:
WT_PANIC_MSG(session, ret, "pthread_cond_broadcast: %s", cond->name);
}
diff --git a/src/third_party/wiredtiger/src/os_posix/os_time.c b/src/third_party/wiredtiger/src/os_posix/os_time.c
index b1b22a8e684..719e214696b 100644
--- a/src/third_party/wiredtiger/src/os_posix/os_time.c
+++ b/src/third_party/wiredtiger/src/os_posix/os_time.c
@@ -12,26 +12,35 @@
* __wt_epoch --
* Return the time since the Epoch.
*/
-int
+void
__wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp)
{
WT_DECL_RET;
+ /*
+ * This function doesn't return an error, but panics on failure (which
+ * should never happen, it's done this way to simplify error handling
+ * in the caller). However, some compilers complain about using garbage
+ * values. Initializing the values avoids the complaint.
+ */
+ tsp->tv_sec = 0;
+ tsp->tv_nsec = 0;
+
#if defined(HAVE_CLOCK_GETTIME)
- WT_SYSCALL(clock_gettime(CLOCK_REALTIME, tsp), ret);
+ WT_SYSCALL_RETRY(clock_gettime(CLOCK_REALTIME, tsp), ret);
if (ret == 0)
- return (0);
- WT_RET_MSG(session, ret, "clock_gettime");
+ return;
+ WT_PANIC_MSG(session, ret, "clock_gettime");
#elif defined(HAVE_GETTIMEOFDAY)
struct timeval v;
- WT_SYSCALL(gettimeofday(&v, NULL), ret);
+ WT_SYSCALL_RETRY(gettimeofday(&v, NULL), ret);
if (ret == 0) {
tsp->tv_sec = v.tv_sec;
tsp->tv_nsec = v.tv_usec * WT_THOUSAND;
- return (0);
+ return;
}
- WT_RET_MSG(session, ret, "gettimeofday");
+ WT_PANIC_MSG(session, ret, "gettimeofday");
#else
NO TIME-OF-DAY IMPLEMENTATION: see src/os_posix/os_time.c
#endif
diff --git a/src/third_party/wiredtiger/src/os_win/os_mtx_cond.c b/src/third_party/wiredtiger/src/os_win/os_mtx_cond.c
index 41acec3ab4c..79c62ccd7f2 100644
--- a/src/third_party/wiredtiger/src/os_win/os_mtx_cond.c
+++ b/src/third_party/wiredtiger/src/os_win/os_mtx_cond.c
@@ -123,22 +123,23 @@ __wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond)
__wt_verbose(session, WT_VERB_MUTEX, "signal %s", cond->name);
- /* Fast path if already signalled. */
- if (cond->waiters == -1)
- return;
-
- if (cond->waiters > 0 || !__wt_atomic_casi32(&cond->waiters, 0, -1)) {
- EnterCriticalSection(&cond->mtx);
- locked = true;
- WakeAllConditionVariable(&cond->cond);
- }
+ /*
+ * Our callers are often setting flags to cause a thread to exit. Add
+ * a barrier to ensure the flags are seen by the threads.
+ */
+ WT_WRITE_BARRIER();
- if (locked)
- LeaveCriticalSection(&cond->mtx);
- if (ret == 0)
+ /*
+ * Fast path if we are in (or can enter), a state where the next waiter
+ * will return immediately as already signaled.
+ */
+ if (cond->waiters == -1 ||
+ (cond->waiters == 0 && __wt_atomic_casi32(&cond->waiters, 0, -1)))
return;
- WT_PANIC_MSG(session, ret, "WakeAllConditionVariable: %s", cond->name);
+ EnterCriticalSection(&cond->mtx);
+ WakeAllConditionVariable(&cond->cond);
+ LeaveCriticalSection(&cond->mtx);
}
/*
@@ -149,7 +150,6 @@ int
__wt_cond_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp)
{
WT_CONDVAR *cond;
- WT_DECL_RET;
cond = *condp;
if (cond == NULL)
@@ -159,5 +159,5 @@ __wt_cond_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp)
DeleteCriticalSection(&cond->mtx);
__wt_free(session, *condp);
- return (ret);
+ return (0);
}
diff --git a/src/third_party/wiredtiger/src/os_win/os_time.c b/src/third_party/wiredtiger/src/os_win/os_time.c
index e784b5d8a36..6aa5b3719f6 100644
--- a/src/third_party/wiredtiger/src/os_win/os_time.c
+++ b/src/third_party/wiredtiger/src/os_win/os_time.c
@@ -12,11 +12,11 @@
* __wt_epoch --
* Return the time since the Epoch.
*/
-int
+void
__wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp)
{
- uint64_t ns100;
FILETIME time;
+ uint64_t ns100;
WT_UNUSED(session);
@@ -26,8 +26,6 @@ __wt_epoch(WT_SESSION_IMPL *session, struct timespec *tsp)
- 116444736000000000LL;
tsp->tv_sec = ns100 / 10000000;
tsp->tv_nsec = (long)((ns100 % 10000000) * 100);
-
- return (0);
}
/*
diff --git a/src/third_party/wiredtiger/src/reconcile/rec_write.c b/src/third_party/wiredtiger/src/reconcile/rec_write.c
index 9c38c535301..fe288beed15 100644
--- a/src/third_party/wiredtiger/src/reconcile/rec_write.c
+++ b/src/third_party/wiredtiger/src/reconcile/rec_write.c
@@ -451,19 +451,18 @@ __wt_reconcile(WT_SESSION_IMPL *session,
}
/*
- * When application threads perform eviction, don't cache block manager
- * or reconciliation structures (even across calls), we can have a
- * significant number of application threads doing eviction at the same
- * time with large items. We ignore checkpoints, once the checkpoint
- * completes, all unnecessary session resources will be discarded.
+ * When threads perform eviction, don't cache block manager or
+ * reconciliation structures (even across calls), we can have a
+ * significant number of threads doing eviction at the same time with
+ * large items. We ignore checkpoints, once the checkpoint completes,
+ * all unnecessary session resources will be discarded.
*
- * Even in application threads doing checkpoints or in internal threads
- * doing any reconciliation, clean up reconciliation resources. Some
- * workloads have millions of boundary structures in a reconciliation
- * and we don't want to tie that memory down, even across calls.
+ * Even in application threads doing checkpoints, clean up
+ * reconciliation resources. Some workloads have millions of boundary
+ * structures in a reconciliation and we don't want to tie that memory
+ * down, even across calls.
*/
- if (WT_SESSION_IS_CHECKPOINT(session) ||
- F_ISSET(session, WT_SESSION_INTERNAL))
+ if (WT_SESSION_IS_CHECKPOINT(session))
__rec_bnd_cleanup(session, r, false);
else {
/*
@@ -548,8 +547,11 @@ __rec_write_status(WT_SESSION_IMPL *session, WT_RECONCILE *r, WT_PAGE *page)
btree = S2BT(session);
mod = page->modify;
- /* Check for a lookaside table and checkpoint collision. */
- if (__rec_las_checkpoint_test(session, r))
+ /*
+ * If we have used the lookaside table, check for a lookaside table and
+ * checkpoint collision.
+ */
+ if (r->cache_write_lookaside && __rec_las_checkpoint_test(session, r))
return (EBUSY);
/*
@@ -564,10 +566,12 @@ __rec_write_status(WT_SESSION_IMPL *session, WT_RECONCILE *r, WT_PAGE *page)
* barrier after the change for clarity (the requirement is the
* flag be set before a subsequent checkpoint reads it, and
* as the current checkpoint is waiting on this reconciliation
- * to complete, there's no risk of that happening)
+ * to complete, there's no risk of that happening).
*/
- btree->modified = 1;
+ btree->modified = true;
WT_FULL_BARRIER();
+ if (!S2C(session)->modified)
+ S2C(session)->modified = true;
/*
* Eviction should only be here if following the save/restore
@@ -3335,7 +3339,7 @@ supd_check_complete:
__wt_verbose(session, WT_VERB_SPLIT,
"Reconciliation creating a page with %" PRIu32
" entries, memory footprint %" WT_SIZET_FMT
- ", page count %" PRIu32 ", %s, split state: %d\n",
+ ", page count %" PRIu32 ", %s, split state: %d",
r->entries, r->page->memory_footprint, r->bnd_next,
F_ISSET(r, WT_EVICTING) ? "evict" : "checkpoint",
r->bnd_state);
diff --git a/src/third_party/wiredtiger/src/schema/schema_create.c b/src/third_party/wiredtiger/src/schema/schema_create.c
index ed88e687a19..020d5e72c13 100644
--- a/src/third_party/wiredtiger/src/schema/schema_create.c
+++ b/src/third_party/wiredtiger/src/schema/schema_create.c
@@ -70,7 +70,7 @@ __create_file(WT_SESSION_IMPL *session,
filename = uri;
if (!WT_PREFIX_SKIP(filename, "file:"))
- WT_RET_MSG(session, EINVAL, "Expected a 'file:' URI: %s", uri);
+ return (__wt_unexpected_object_type(session, uri, "file:"));
/* Check if the file already exists. */
if (!is_metadata && (ret =
@@ -193,7 +193,8 @@ __create_colgroup(WT_SESSION_IMPL *session,
tablename = name;
if (!WT_PREFIX_SKIP(tablename, "colgroup:"))
- return (EINVAL);
+ return (
+ __wt_unexpected_object_type(session, name, "colgroup:"));
cgname = strchr(tablename, ':');
if (cgname != NULL) {
tlen = (size_t)(cgname - tablename);
@@ -384,7 +385,7 @@ __create_index(WT_SESSION_IMPL *session,
tablename = name;
if (!WT_PREFIX_SKIP(tablename, "index:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, name, "index:"));
idxname = strchr(tablename, ':');
if (idxname == NULL)
WT_RET_MSG(session, EINVAL, "Invalid index name, "
@@ -570,7 +571,7 @@ __create_table(WT_SESSION_IMPL *session,
tablename = name;
if (!WT_PREFIX_SKIP(tablename, "table:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, name, "table:"));
if ((ret = __wt_schema_get_table(session,
tablename, strlen(tablename), false, &table)) == 0) {
diff --git a/src/third_party/wiredtiger/src/schema/schema_drop.c b/src/third_party/wiredtiger/src/schema/schema_drop.c
index ead8cc45c62..65c955cf1e9 100644
--- a/src/third_party/wiredtiger/src/schema/schema_drop.c
+++ b/src/third_party/wiredtiger/src/schema/schema_drop.c
@@ -26,8 +26,9 @@ __drop_file(
filename = uri;
if (!WT_PREFIX_SKIP(filename, "file:"))
- return (EINVAL);
+ return (__wt_unexpected_object_type(session, uri, "file:"));
+ WT_RET(__wt_schema_backup_check(session, filename));
/* Close all btree handles associated with this file. */
WT_WITH_HANDLE_LIST_LOCK(session,
ret = __wt_conn_dhandle_close_all(session, uri, force));
diff --git a/src/third_party/wiredtiger/src/schema/schema_rename.c b/src/third_party/wiredtiger/src/schema/schema_rename.c
index bc92c882117..f512482c162 100644
--- a/src/third_party/wiredtiger/src/schema/schema_rename.c
+++ b/src/third_party/wiredtiger/src/schema/schema_rename.c
@@ -24,11 +24,14 @@ __rename_file(
newvalue = oldvalue = NULL;
filename = uri;
+ if (!WT_PREFIX_SKIP(filename, "file:"))
+ return (__wt_unexpected_object_type(session, uri, "file:"));
newfile = newuri;
- if (!WT_PREFIX_SKIP(filename, "file:") ||
- !WT_PREFIX_SKIP(newfile, "file:"))
- return (EINVAL);
+ if (!WT_PREFIX_SKIP(newfile, "file:"))
+ return (__wt_unexpected_object_type(session, newuri, "file:"));
+ WT_RET(__wt_schema_backup_check(session, filename));
+ WT_RET(__wt_schema_backup_check(session, newfile));
/* Close any btree handles in the file. */
WT_WITH_HANDLE_LIST_LOCK(session,
ret = __wt_conn_dhandle_close_all(session, uri, false));
diff --git a/src/third_party/wiredtiger/src/schema/schema_stat.c b/src/third_party/wiredtiger/src/schema/schema_stat.c
index 1cd39d97364..345f9164e9b 100644
--- a/src/third_party/wiredtiger/src/schema/schema_stat.c
+++ b/src/third_party/wiredtiger/src/schema/schema_stat.c
@@ -137,7 +137,7 @@ __wt_curstat_table_init(WT_SESSION_IMPL *session,
* If only gathering table size statistics, try a fast path that
* avoids the schema and table list locks.
*/
- if (F_ISSET(cst, WT_CONN_STAT_SIZE)) {
+ if (F_ISSET(cst, WT_STAT_TYPE_SIZE)) {
WT_RET(__curstat_size_only(session, uri, &was_fast, cst));
if (was_fast)
return (0);
diff --git a/src/third_party/wiredtiger/src/schema/schema_util.c b/src/third_party/wiredtiger/src/schema/schema_util.c
index d1c84dc8d85..433224a868e 100644
--- a/src/third_party/wiredtiger/src/schema/schema_util.c
+++ b/src/third_party/wiredtiger/src/schema/schema_util.c
@@ -9,6 +9,45 @@
#include "wt_internal.h"
/*
+ * __wt_schema_backup_check --
+ * Check if a backup cursor is open and give an error if the schema
+ * operation will conflict. This is called after the schema operations
+ * have taken the schema lock so no hot backup cursor can be created until
+ * this is done.
+ */
+int
+__wt_schema_backup_check(WT_SESSION_IMPL *session, const char *name)
+{
+ WT_CONNECTION_IMPL *conn;
+ WT_DECL_RET;
+ int i;
+ char **backup_list;
+
+ conn = S2C(session);
+ if (!conn->hot_backup)
+ return (0);
+ __wt_readlock(session, conn->hot_backup_lock);
+ /*
+ * There is a window at the end of a backup where the list has been
+ * cleared from the connection but the flag is still set. It is safe
+ * to drop at that point.
+ */
+ if (!conn->hot_backup ||
+ (backup_list = conn->hot_backup_list) == NULL) {
+ __wt_readunlock(session, conn->hot_backup_lock);
+ return (0);
+ }
+ for (i = 0; backup_list[i] != NULL; ++i) {
+ if (strcmp(backup_list[i], name) == 0) {
+ ret = EBUSY;
+ break;
+ }
+ }
+ __wt_readunlock(session, conn->hot_backup_lock);
+ return (ret);
+}
+
+/*
* __wt_schema_get_source --
* Find a matching data source or report an error.
*/
diff --git a/src/third_party/wiredtiger/src/session/session_api.c b/src/third_party/wiredtiger/src/session/session_api.c
index 0d3fcad3184..3afea383b08 100644
--- a/src/third_party/wiredtiger/src/session/session_api.c
+++ b/src/third_party/wiredtiger/src/session/session_api.c
@@ -68,9 +68,10 @@ __wt_session_copy_values(WT_SESSION_IMPL *session)
* unless the cursor is reading from a checkpoint.
*/
WT_TXN_STATE *txn_state = WT_SESSION_TXN_STATE(session);
- WT_ASSERT(session, txn_state->snap_min != WT_TXN_NONE ||
- (WT_PREFIX_MATCH(cursor->uri, "file:") &&
- F_ISSET((WT_CURSOR_BTREE *)cursor, WT_CBT_NO_TXN)));
+ WT_ASSERT(session,
+ txn_state->pinned_id != WT_TXN_NONE ||
+ (WT_PREFIX_MATCH(cursor->uri, "file:") &&
+ F_ISSET((WT_CURSOR_BTREE *)cursor, WT_CBT_NO_TXN)));
#endif
F_CLR(cursor, WT_CURSTD_VALUE_INT);
@@ -919,8 +920,7 @@ __session_join(WT_SESSION *wt_session, WT_CURSOR *join_cursor,
"table for join cursor does not match table for "
"ref_cursor");
if (F_ISSET(ref_cursor, WT_CURSTD_JOINED))
- WT_ERR_MSG(session, EINVAL,
- "cursor already used in a join");
+ WT_ERR_MSG(session, EINVAL, "cursor already used in a join");
/* "ge" is the default */
range = WT_CURJOIN_END_GT | WT_CURJOIN_END_EQ;
@@ -936,7 +936,9 @@ __session_join(WT_SESSION *wt_session, WT_CURSOR *join_cursor,
else if (WT_STRING_MATCH("eq", cval.str, cval.len))
range = WT_CURJOIN_END_EQ;
else if (!WT_STRING_MATCH("ge", cval.str, cval.len))
- WT_ERR(EINVAL);
+ WT_ERR_MSG(session, EINVAL,
+ "compare=%.*s not supported",
+ (int)cval.len, cval.str);
}
WT_ERR(__wt_config_gets(session, cfg, "count", &cval));
if (cval.len != 0)
@@ -947,12 +949,13 @@ __session_join(WT_SESSION *wt_session, WT_CURSOR *join_cursor,
if (WT_STRING_MATCH("bloom", cval.str, cval.len))
LF_SET(WT_CURJOIN_ENTRY_BLOOM);
else if (!WT_STRING_MATCH("default", cval.str, cval.len))
- WT_ERR(EINVAL);
+ WT_ERR_MSG(session, EINVAL,
+ "strategy=%.*s not supported",
+ (int)cval.len, cval.str);
}
WT_ERR(__wt_config_gets(session, cfg, "bloom_bit_count", &cval));
if ((uint64_t)cval.val > UINT32_MAX)
- WT_ERR_MSG(session, EINVAL,
- "bloom_bit_count: value too large");
+ WT_ERR_MSG(session, EINVAL, "bloom_bit_count: value too large");
bloom_bit_count = (uint32_t)cval.val;
WT_ERR(__wt_config_gets(session, cfg, "bloom_hash_count", &cval));
if ((uint64_t)cval.val > UINT32_MAX)
@@ -962,6 +965,10 @@ __session_join(WT_SESSION *wt_session, WT_CURSOR *join_cursor,
if (LF_ISSET(WT_CURJOIN_ENTRY_BLOOM) && count == 0)
WT_ERR_MSG(session, EINVAL,
"count must be nonzero when strategy=bloom");
+ WT_ERR(__wt_config_gets_def(
+ session, cfg, "bloom_false_positives", 0, &cval));
+ if (cval.val != 0)
+ LF_SET(WT_CURJOIN_ENTRY_FALSE_POSITIVES);
WT_ERR(__wt_config_gets(session, cfg, "operation", &cval));
if (cval.len != 0 && WT_STRING_MATCH("or", cval.str, cval.len))
@@ -1006,7 +1013,9 @@ __session_salvage(WT_SESSION *wt_session, const char *uri, const char *config)
SESSION_API_CALL(session, salvage, config, cfg);
if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY))
- WT_ERR(ENOTSUP);
+ WT_ERR_MSG(session, ENOTSUP,
+ "WT_SESSION.salvage not supported for in-memory "
+ "configurations");
/* Block out checkpoints to avoid spurious EBUSY errors. */
WT_WITH_CHECKPOINT_LOCK(session, ret,
@@ -1310,7 +1319,9 @@ __session_verify(WT_SESSION *wt_session, const char *uri, const char *config)
SESSION_API_CALL(session, verify, config, cfg);
if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY))
- WT_ERR(ENOTSUP);
+ WT_ERR_MSG(session, ENOTSUP,
+ "WT_SESSION.verify not supported for in-memory "
+ "configurations");
/* Block out checkpoints to avoid spurious EBUSY errors. */
WT_WITH_CHECKPOINT_LOCK(session, ret,
@@ -1417,10 +1428,10 @@ __session_transaction_pinned_range(WT_SESSION *wt_session, uint64_t *prange)
/* Assign pinned to the lesser of id or snap_min */
if (txn_state->id != WT_TXN_NONE &&
- WT_TXNID_LT(txn_state->id, txn_state->snap_min))
+ WT_TXNID_LT(txn_state->id, txn_state->pinned_id))
pinned = txn_state->id;
else
- pinned = txn_state->snap_min;
+ pinned = txn_state->pinned_id;
if (pinned == WT_TXN_NONE)
*prange = 0;
@@ -1494,14 +1505,14 @@ __session_transaction_sync(WT_SESSION *wt_session, const char *config)
if (timeout_ms == 0)
WT_ERR(ETIMEDOUT);
- WT_ERR(__wt_epoch(session, &start));
+ __wt_epoch(session, &start);
/*
* Keep checking the LSNs until we find it is stable or we reach
* our timeout.
*/
while (__wt_log_cmp(&session->bg_sync_lsn, &log->sync_lsn) > 0) {
__wt_cond_signal(session, conn->log_file_cond);
- WT_ERR(__wt_epoch(session, &now));
+ __wt_epoch(session, &now);
waited_ms = WT_TIMEDIFF_MS(now, start);
if (forever || waited_ms < timeout_ms)
/*
@@ -1555,7 +1566,9 @@ __session_checkpoint(WT_SESSION *wt_session, const char *config)
SESSION_API_CALL(session, checkpoint, config, cfg);
if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY))
- WT_ERR(ENOTSUP);
+ WT_ERR_MSG(session, ENOTSUP,
+ "WT_SESSION.checkpoint not supported for in-memory "
+ "configurations");
/*
* Checkpoints require a snapshot to write a transactionally consistent
@@ -1756,11 +1769,13 @@ __open_session(WT_CONNECTION_IMPL *conn,
if (i >= conn->session_cnt) /* Defend against off-by-one errors. */
conn->session_cnt = i + 1;
- session_ret->id = i;
session_ret->iface =
F_ISSET(conn, WT_CONN_READONLY) ? stds_readonly : stds;
session_ret->iface.connection = &conn->iface;
+ session_ret->name = NULL;
+ session_ret->id = i;
+
WT_ERR(__wt_cond_alloc(session, "session", false, &session_ret->cond));
if (WT_SESSION_FIRST_USE(session_ret))
@@ -1776,10 +1791,10 @@ __open_session(WT_CONNECTION_IMPL *conn,
* Allocate the table hash array as well.
*/
if (session_ret->dhhash == NULL)
- WT_ERR(__wt_calloc(session_ret, WT_HASH_ARRAY_SIZE,
+ WT_ERR(__wt_calloc(session, WT_HASH_ARRAY_SIZE,
sizeof(struct __dhandles_hash), &session_ret->dhhash));
if (session_ret->tablehash == NULL)
- WT_ERR(__wt_calloc(session_ret, WT_HASH_ARRAY_SIZE,
+ WT_ERR(__wt_calloc(session, WT_HASH_ARRAY_SIZE,
sizeof(struct __tables_hash), &session_ret->tablehash));
for (i = 0; i < WT_HASH_ARRAY_SIZE; i++) {
TAILQ_INIT(&session_ret->dhhash[i]);
@@ -1788,7 +1803,7 @@ __open_session(WT_CONNECTION_IMPL *conn,
/* Initialize transaction support: default to read-committed. */
session_ret->isolation = WT_ISO_READ_COMMITTED;
- WT_ERR(__wt_txn_init(session_ret));
+ WT_ERR(__wt_txn_init(session, session_ret));
/*
* The session's hazard pointer memory isn't discarded during normal
@@ -1807,6 +1822,9 @@ __open_session(WT_CONNECTION_IMPL *conn,
*/
session_ret->hazard_size = 0;
+ /* Cache the offset of this session's statistics bucket. */
+ session_ret->stat_bucket = WT_STATS_SLOT_ID(session);
+
/*
* Configuration: currently, the configuration for open_session is the
* same as session.reconfigure, so use that function.
@@ -1815,8 +1833,6 @@ __open_session(WT_CONNECTION_IMPL *conn,
WT_ERR(
__session_reconfigure((WT_SESSION *)session_ret, config));
- session_ret->name = NULL;
-
/*
* Publish: make the entry visible to server threads. There must be a
* barrier for two reasons, to ensure structure fields are set before
diff --git a/src/third_party/wiredtiger/src/session/session_compact.c b/src/third_party/wiredtiger/src/session/session_compact.c
index f03d5d34bac..66635007723 100644
--- a/src/third_party/wiredtiger/src/session/session_compact.c
+++ b/src/third_party/wiredtiger/src/session/session_compact.c
@@ -179,17 +179,16 @@ __compact_handle_append(WT_SESSION_IMPL *session, const char *cfg[])
* Check if the timeout has been exceeded.
*/
static int
-__session_compact_check_timeout(
- WT_SESSION_IMPL *session, struct timespec begin)
+__session_compact_check_timeout(WT_SESSION_IMPL *session, struct timespec begin)
{
struct timespec end;
if (session->compact->max_time == 0)
return (0);
- WT_RET(__wt_epoch(session, &end));
+ __wt_epoch(session, &end);
if (session->compact->max_time < WT_TIMEDIFF_SEC(end, begin))
- WT_RET(ETIMEDOUT);
+ return (ETIMEDOUT);
return (0);
}
@@ -219,7 +218,7 @@ __compact_file(WT_SESSION_IMPL *session, const char *cfg[])
session, t, "target=(\"%s\"),force=1", dhandle->name));
checkpoint_cfg[1] = t->data;
- WT_ERR(__wt_epoch(session, &start_time));
+ __wt_epoch(session, &start_time);
/*
* We compact 10% of the file on each pass (but the overall size of the
diff --git a/src/third_party/wiredtiger/src/session/session_dhandle.c b/src/third_party/wiredtiger/src/session/session_dhandle.c
index e76407567bc..725854c6001 100644
--- a/src/third_party/wiredtiger/src/session/session_dhandle.c
+++ b/src/third_party/wiredtiger/src/session/session_dhandle.c
@@ -8,8 +8,6 @@
#include "wt_internal.h"
-static int __session_dhandle_sweep(WT_SESSION_IMPL *);
-
/*
* __session_add_dhandle --
* Add a handle to the session's cache.
@@ -371,7 +369,7 @@ __wt_session_close_cache(WT_SESSION_IMPL *session)
* __session_dhandle_sweep --
* Discard any session dhandles that are not open.
*/
-static int
+static void
__session_dhandle_sweep(WT_SESSION_IMPL *session)
{
WT_CONNECTION_IMPL *conn;
@@ -385,9 +383,9 @@ __session_dhandle_sweep(WT_SESSION_IMPL *session)
* Periodically sweep for dead handles; if we've swept recently, don't
* do it again.
*/
- WT_RET(__wt_seconds(session, &now));
+ __wt_seconds(session, &now);
if (difftime(now, session->last_sweep) < conn->sweep_interval)
- return (0);
+ return;
session->last_sweep = now;
WT_STAT_CONN_INCR(session, dh_session_sweeps);
@@ -408,7 +406,6 @@ __session_dhandle_sweep(WT_SESSION_IMPL *session)
}
dhandle_cache = dhandle_cache_next;
}
- return (0);
}
/*
@@ -446,7 +443,7 @@ __session_get_dhandle(
}
/* Sweep the handle list to remove any dead handles. */
- WT_RET(__session_dhandle_sweep(session));
+ __session_dhandle_sweep(session);
/*
* We didn't find a match in the session cache, search the shared
diff --git a/src/third_party/wiredtiger/src/support/err.c b/src/third_party/wiredtiger/src/support/err.c
index 8bfac250b3a..e48225c2c13 100644
--- a/src/third_party/wiredtiger/src/support/err.c
+++ b/src/third_party/wiredtiger/src/support/err.c
@@ -162,7 +162,6 @@ __wt_eventv(WT_SESSION_IMPL *session, bool msg_event, int error,
WT_SESSION *wt_session;
struct timespec ts;
size_t len, remain, wlen;
- int prefix_cnt;
const char *err, *prefix;
char *end, *p, tid[128];
@@ -211,44 +210,32 @@ __wt_eventv(WT_SESSION_IMPL *session, bool msg_event, int error,
* name, and the session's name. Write them as a comma-separate list,
* followed by a colon.
*/
- prefix_cnt = 0;
- if (__wt_epoch(session, &ts) == 0) {
- __wt_thread_id(tid, sizeof(tid));
- remain = WT_PTRDIFF(end, p);
- wlen = (size_t)snprintf(p, remain,
- "[%" PRIuMAX ":%" PRIuMAX "][%s]",
- (uintmax_t)ts.tv_sec,
- (uintmax_t)ts.tv_nsec / WT_THOUSAND, tid);
- p = wlen >= remain ? end : p + wlen;
- prefix_cnt = 1;
- }
+ __wt_epoch(session, &ts);
+ __wt_thread_id(tid, sizeof(tid));
+ remain = WT_PTRDIFF(end, p);
+ wlen = (size_t)snprintf(p, remain, "[%" PRIuMAX ":%" PRIuMAX "][%s]",
+ (uintmax_t)ts.tv_sec, (uintmax_t)ts.tv_nsec / WT_THOUSAND, tid);
+ p = wlen >= remain ? end : p + wlen;
+
if ((prefix = S2C(session)->error_prefix) != NULL) {
remain = WT_PTRDIFF(end, p);
- wlen = (size_t)snprintf(p, remain,
- "%s%s", prefix_cnt == 0 ? "" : ", ", prefix);
+ wlen = (size_t)snprintf(p, remain, ", %s", prefix);
p = wlen >= remain ? end : p + wlen;
- prefix_cnt = 1;
}
prefix = session->dhandle == NULL ? NULL : session->dhandle->name;
if (prefix != NULL) {
remain = WT_PTRDIFF(end, p);
- wlen = (size_t)snprintf(p, remain,
- "%s%s", prefix_cnt == 0 ? "" : ", ", prefix);
+ wlen = (size_t)snprintf(p, remain, ", %s", prefix);
p = wlen >= remain ? end : p + wlen;
- prefix_cnt = 1;
}
if ((prefix = session->name) != NULL) {
remain = WT_PTRDIFF(end, p);
- wlen = (size_t)snprintf(p, remain,
- "%s%s", prefix_cnt == 0 ? "" : ", ", prefix);
- p = wlen >= remain ? end : p + wlen;
- prefix_cnt = 1;
- }
- if (prefix_cnt != 0) {
- remain = WT_PTRDIFF(end, p);
- wlen = (size_t)snprintf(p, remain, ": ");
+ wlen = (size_t)snprintf(p, remain, ", %s", prefix);
p = wlen >= remain ? end : p + wlen;
}
+ remain = WT_PTRDIFF(end, p);
+ wlen = (size_t)snprintf(p, remain, ": ");
+ p = wlen >= remain ? end : p + wlen;
if (file_name != NULL) {
remain = WT_PTRDIFF(end, p);
@@ -575,3 +562,16 @@ __wt_bad_object_type(WT_SESSION_IMPL *session, const char *uri)
WT_RET_MSG(session, ENOTSUP, "unknown object type: %s", uri);
}
+
+/*
+ * __wt_unexpected_object_type --
+ * Print a standard error message when given an unexpected object type.
+ */
+int
+__wt_unexpected_object_type(
+ WT_SESSION_IMPL *session, const char *uri, const char *expect)
+ WT_GCC_FUNC_ATTRIBUTE((cold))
+{
+ WT_RET_MSG(session,
+ EINVAL, "uri %s doesn't match expected \"%s\"", uri, expect);
+}
diff --git a/src/third_party/wiredtiger/src/support/hazard.c b/src/third_party/wiredtiger/src/support/hazard.c
index 46b63ac6129..b1978dbe97a 100644
--- a/src/third_party/wiredtiger/src/support/hazard.c
+++ b/src/third_party/wiredtiger/src/support/hazard.c
@@ -121,13 +121,11 @@ __wt_hazard_set(WT_SESSION_IMPL *session, WT_REF *ref, bool *busyp
return (0);
}
- __wt_errx(session,
- "session %p: hazard pointer table full", (void *)session);
#ifdef HAVE_DIAGNOSTIC
__hazard_dump(session);
#endif
-
- return (ENOMEM);
+ WT_RET_MSG(session, ENOMEM,
+ "session %p: hazard pointer table full", (void *)session);
}
/*
diff --git a/src/third_party/wiredtiger/src/support/huffman.c b/src/third_party/wiredtiger/src/support/huffman.c
index 3ec282915ca..afc785b39a9 100644
--- a/src/third_party/wiredtiger/src/support/huffman.c
+++ b/src/third_party/wiredtiger/src/support/huffman.c
@@ -823,7 +823,8 @@ __wt_huffman_decode(WT_SESSION_IMPL *session, void *huffman_arg,
* where that's not true.
*/
if (from_len_bits < len) /* corrupted */
- WT_ERR(EINVAL);
+ WT_ERR_MSG(session, EINVAL,
+ "huffman decompression detected input corruption");
from_len_bits -= len;
WT_ASSERT(session,
diff --git a/src/third_party/wiredtiger/src/support/mtx_rw.c b/src/third_party/wiredtiger/src/support/mtx_rw.c
index fefa346ac14..ea18f556257 100644
--- a/src/third_party/wiredtiger/src/support/mtx_rw.c
+++ b/src/third_party/wiredtiger/src/support/mtx_rw.c
@@ -167,6 +167,32 @@ __wt_try_readlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
}
/*
+ * __wt_readlock_spin --
+ * Spin to get a read lock: only yield the CPU if the lock is held
+ * exclusive.
+ */
+void
+__wt_readlock_spin(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
+{
+ wt_rwlock_t *l;
+
+ l = &rwlock->rwlock;
+
+ /*
+ * Try to get the lock in a single operation if it is available to
+ * readers. This avoids the situation where multiple readers arrive
+ * concurrently and have to line up in order to enter the lock. For
+ * read-heavy workloads it can make a significant difference.
+ */
+ while (__wt_try_readlock(session, rwlock) != 0) {
+ if (l->s.writers_active > 0)
+ __wt_yield();
+ else
+ WT_PAUSE();
+ }
+}
+
+/*
* __wt_readlock --
* Get a shared lock.
*/
@@ -192,17 +218,14 @@ __wt_readlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
for (pause_cnt = 0; ticket != l->s.readers;) {
/*
* We failed to get the lock; pause before retrying and if we've
- * paused enough, sleep so we don't burn CPU to no purpose. This
+ * paused enough, yield so we don't burn CPU to no purpose. This
* situation happens if there are more threads than cores in the
* system and we're thrashing on shared resources.
- *
- * Don't sleep long when waiting on a read lock, hopefully we're
- * waiting on another read thread to increment the reader count.
*/
if (++pause_cnt < WT_THOUSAND)
WT_PAUSE();
else
- __wt_sleep(0, 10);
+ __wt_yield();
}
/*
@@ -264,6 +287,7 @@ __wt_try_writelock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
/* The replacement lock value is a result of allocating a new ticket. */
++new.s.next;
+ ++new.s.writers_active;
return (__wt_atomic_cas64(&l->u, old.u, new.u) ? 0 : EBUSY);
}
@@ -288,6 +312,7 @@ __wt_writelock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
* lock.
*/
ticket = __wt_atomic_fetch_add16(&l->s.next, 1);
+ (void)__wt_atomic_add16(&l->s.writers_active, 1);
for (pause_cnt = 0; ticket != l->s.writers;) {
/*
* We failed to get the lock; pause before retrying and if we've
@@ -319,14 +344,15 @@ __wt_writeunlock(WT_SESSION_IMPL *session, WT_RWLOCK *rwlock)
WT_UNUSED(session);
+ l = &rwlock->rwlock;
+ (void)__wt_atomic_sub16(&l->s.writers_active, 1);
+
/*
* Ensure that all updates made while the lock was held are visible to
* the next thread to acquire the lock.
*/
WT_WRITE_BARRIER();
- l = &rwlock->rwlock;
-
new = *l;
/*
diff --git a/src/third_party/wiredtiger/src/support/rand.c b/src/third_party/wiredtiger/src/support/rand.c
index d2e4cd27aab..025b18e4ed3 100644
--- a/src/third_party/wiredtiger/src/support/rand.c
+++ b/src/third_party/wiredtiger/src/support/rand.c
@@ -66,20 +66,18 @@ __wt_random_init(WT_RAND_STATE volatile * rnd_state)
* threads and we want each thread to initialize its own random state based
* on a different random seed.
*/
-int
+void
__wt_random_init_seed(
WT_SESSION_IMPL *session, WT_RAND_STATE volatile * rnd_state)
{
struct timespec ts;
WT_RAND_STATE rnd;
- WT_RET(__wt_epoch(session, &ts));
+ __wt_epoch(session, &ts);
M_W(rnd) = (uint32_t)(ts.tv_nsec + 521288629);
M_Z(rnd) = (uint32_t)(ts.tv_nsec + 362436069);
*rnd_state = rnd;
-
- return (0);
}
/*
diff --git a/src/third_party/wiredtiger/src/support/stat.c b/src/third_party/wiredtiger/src/support/stat.c
index 7150223e6cb..5acd9fc713f 100644
--- a/src/third_party/wiredtiger/src/support/stat.c
+++ b/src/third_party/wiredtiger/src/support/stat.c
@@ -65,6 +65,24 @@ static const char * const __stats_dsrc_desc[] = {
"cache: pages written from cache",
"cache: pages written requiring in-memory restoration",
"cache: unmodified pages evicted",
+ "cache_walk: Average difference between current eviction generation when the page was last considered",
+ "cache_walk: Average on-disk page image size seen",
+ "cache_walk: Clean pages currently in cache",
+ "cache_walk: Current eviction generation",
+ "cache_walk: Dirty pages currently in cache",
+ "cache_walk: Entries in the root page",
+ "cache_walk: Internal pages currently in cache",
+ "cache_walk: Leaf pages currently in cache",
+ "cache_walk: Maximum difference between current eviction generation when the page was last considered",
+ "cache_walk: Maximum page size seen",
+ "cache_walk: Minimum on-disk page image size seen",
+ "cache_walk: On-disk page image sizes smaller than a single allocation unit",
+ "cache_walk: Pages created in memory and never written",
+ "cache_walk: Pages currently queued for eviction",
+ "cache_walk: Pages that could not be queued for eviction",
+ "cache_walk: Refs skipped during cache traversal",
+ "cache_walk: Size of the root page",
+ "cache_walk: Total number of pages currently in cache",
"compression: compressed pages read",
"compression: compressed pages written",
"compression: page written failed to compress",
@@ -120,15 +138,27 @@ __wt_stat_dsrc_init_single(WT_DSRC_STATS *stats)
memset(stats, 0, sizeof(*stats));
}
-void
-__wt_stat_dsrc_init(WT_DATA_HANDLE *handle)
+int
+__wt_stat_dsrc_init(
+ WT_SESSION_IMPL *session, WT_DATA_HANDLE *handle)
{
int i;
+ WT_RET(__wt_calloc(session, (size_t)WT_COUNTER_SLOTS,
+ sizeof(*handle->stat_array), &handle->stat_array));
+
for (i = 0; i < WT_COUNTER_SLOTS; ++i) {
handle->stats[i] = &handle->stat_array[i];
__wt_stat_dsrc_init_single(handle->stats[i]);
}
+ return (0);
+}
+
+void
+__wt_stat_dsrc_discard(
+ WT_SESSION_IMPL *session, WT_DATA_HANDLE *handle)
+{
+ __wt_free(session, handle->stat_array);
}
void
@@ -196,6 +226,24 @@ __wt_stat_dsrc_clear_single(WT_DSRC_STATS *stats)
stats->cache_write = 0;
stats->cache_write_restore = 0;
stats->cache_eviction_clean = 0;
+ /* not clearing cache_state_gen_avg_gap */
+ /* not clearing cache_state_avg_written_size */
+ /* not clearing cache_state_pages_clean */
+ /* not clearing cache_state_gen_current */
+ /* not clearing cache_state_pages_dirty */
+ /* not clearing cache_state_root_entries */
+ /* not clearing cache_state_pages_internal */
+ /* not clearing cache_state_pages_leaf */
+ /* not clearing cache_state_gen_max_gap */
+ /* not clearing cache_state_max_pagesize */
+ /* not clearing cache_state_min_written_size */
+ /* not clearing cache_state_smaller_alloc_size */
+ /* not clearing cache_state_memory */
+ /* not clearing cache_state_queued */
+ /* not clearing cache_state_not_queueable */
+ /* not clearing cache_state_refs_skipped */
+ /* not clearing cache_state_root_size */
+ /* not clearing cache_state_pages */
stats->compress_read = 0;
stats->compress_write = 0;
stats->compress_write_fail = 0;
@@ -325,6 +373,27 @@ __wt_stat_dsrc_aggregate_single(
to->cache_write += from->cache_write;
to->cache_write_restore += from->cache_write_restore;
to->cache_eviction_clean += from->cache_eviction_clean;
+ to->cache_state_gen_avg_gap += from->cache_state_gen_avg_gap;
+ to->cache_state_avg_written_size +=
+ from->cache_state_avg_written_size;
+ to->cache_state_pages_clean += from->cache_state_pages_clean;
+ to->cache_state_gen_current += from->cache_state_gen_current;
+ to->cache_state_pages_dirty += from->cache_state_pages_dirty;
+ to->cache_state_root_entries += from->cache_state_root_entries;
+ to->cache_state_pages_internal += from->cache_state_pages_internal;
+ to->cache_state_pages_leaf += from->cache_state_pages_leaf;
+ to->cache_state_gen_max_gap += from->cache_state_gen_max_gap;
+ to->cache_state_max_pagesize += from->cache_state_max_pagesize;
+ to->cache_state_min_written_size +=
+ from->cache_state_min_written_size;
+ to->cache_state_smaller_alloc_size +=
+ from->cache_state_smaller_alloc_size;
+ to->cache_state_memory += from->cache_state_memory;
+ to->cache_state_queued += from->cache_state_queued;
+ to->cache_state_not_queueable += from->cache_state_not_queueable;
+ to->cache_state_refs_skipped += from->cache_state_refs_skipped;
+ to->cache_state_root_size += from->cache_state_root_size;
+ to->cache_state_pages += from->cache_state_pages;
to->compress_read += from->compress_read;
to->compress_write += from->compress_write;
to->compress_write_fail += from->compress_write_fail;
@@ -467,6 +536,39 @@ __wt_stat_dsrc_aggregate(
to->cache_write += WT_STAT_READ(from, cache_write);
to->cache_write_restore += WT_STAT_READ(from, cache_write_restore);
to->cache_eviction_clean += WT_STAT_READ(from, cache_eviction_clean);
+ to->cache_state_gen_avg_gap +=
+ WT_STAT_READ(from, cache_state_gen_avg_gap);
+ to->cache_state_avg_written_size +=
+ WT_STAT_READ(from, cache_state_avg_written_size);
+ to->cache_state_pages_clean +=
+ WT_STAT_READ(from, cache_state_pages_clean);
+ to->cache_state_gen_current +=
+ WT_STAT_READ(from, cache_state_gen_current);
+ to->cache_state_pages_dirty +=
+ WT_STAT_READ(from, cache_state_pages_dirty);
+ to->cache_state_root_entries +=
+ WT_STAT_READ(from, cache_state_root_entries);
+ to->cache_state_pages_internal +=
+ WT_STAT_READ(from, cache_state_pages_internal);
+ to->cache_state_pages_leaf +=
+ WT_STAT_READ(from, cache_state_pages_leaf);
+ to->cache_state_gen_max_gap +=
+ WT_STAT_READ(from, cache_state_gen_max_gap);
+ to->cache_state_max_pagesize +=
+ WT_STAT_READ(from, cache_state_max_pagesize);
+ to->cache_state_min_written_size +=
+ WT_STAT_READ(from, cache_state_min_written_size);
+ to->cache_state_smaller_alloc_size +=
+ WT_STAT_READ(from, cache_state_smaller_alloc_size);
+ to->cache_state_memory += WT_STAT_READ(from, cache_state_memory);
+ to->cache_state_queued += WT_STAT_READ(from, cache_state_queued);
+ to->cache_state_not_queueable +=
+ WT_STAT_READ(from, cache_state_not_queueable);
+ to->cache_state_refs_skipped +=
+ WT_STAT_READ(from, cache_state_refs_skipped);
+ to->cache_state_root_size +=
+ WT_STAT_READ(from, cache_state_root_size);
+ to->cache_state_pages += WT_STAT_READ(from, cache_state_pages);
to->compress_read += WT_STAT_READ(from, compress_read);
to->compress_write += WT_STAT_READ(from, compress_write);
to->compress_write_fail += WT_STAT_READ(from, compress_write_fail);
@@ -549,6 +651,10 @@ static const char * const __stats_connection_desc[] = {
"block-manager: bytes written for checkpoint",
"block-manager: mapped blocks read",
"block-manager: mapped bytes read",
+ "cache: application threads page read from disk to cache count",
+ "cache: application threads page read from disk to cache time (usecs)",
+ "cache: application threads page write from cache to disk count",
+ "cache: application threads page write from cache to disk time (usecs)",
"cache: bytes belonging to page images in the cache",
"cache: bytes currently in the cache",
"cache: bytes not belonging to page images in the cache",
@@ -642,6 +748,21 @@ static const char * const __stats_connection_desc[] = {
"data-handle: connection sweeps",
"data-handle: session dhandles swept",
"data-handle: session sweep attempts",
+ "lock: checkpoint lock acquisitions",
+ "lock: checkpoint lock application thread wait time (usecs)",
+ "lock: checkpoint lock internal thread wait time (usecs)",
+ "lock: handle-list lock acquisitions",
+ "lock: handle-list lock application thread wait time (usecs)",
+ "lock: handle-list lock internal thread wait time (usecs)",
+ "lock: metadata lock acquisitions",
+ "lock: metadata lock application thread wait time (usecs)",
+ "lock: metadata lock internal thread wait time (usecs)",
+ "lock: schema lock acquisitions",
+ "lock: schema lock application thread wait time (usecs)",
+ "lock: schema lock internal thread wait time (usecs)",
+ "lock: table lock acquisitions",
+ "lock: table lock application thread time waiting for the table lock (usecs)",
+ "lock: table lock internal thread time waiting for the table lock (usecs)",
"log: busy returns attempting to switch slots",
"log: consolidated slot closures",
"log: consolidated slot join races",
@@ -706,6 +827,8 @@ static const char * const __stats_connection_desc[] = {
"thread-state: active filesystem fsync calls",
"thread-state: active filesystem read calls",
"thread-state: active filesystem write calls",
+ "thread-yield: application thread time evicting (usecs)",
+ "thread-yield: application thread time waiting for cache (usecs)",
"thread-yield: page acquire busy blocked",
"thread-yield: page acquire eviction blocked",
"thread-yield: page acquire locked blocked",
@@ -723,6 +846,7 @@ static const char * const __stats_connection_desc[] = {
"transaction: transaction checkpoint scrub time (msecs)",
"transaction: transaction checkpoint total time (msecs)",
"transaction: transaction checkpoints",
+ "transaction: transaction checkpoints skipped because database was clean",
"transaction: transaction failures due to cache overflow",
"transaction: transaction fsync calls for checkpoint after allocating the transaction ID",
"transaction: transaction fsync duration for checkpoint after allocating the transaction ID (usecs)",
@@ -748,15 +872,27 @@ __wt_stat_connection_init_single(WT_CONNECTION_STATS *stats)
memset(stats, 0, sizeof(*stats));
}
-void
-__wt_stat_connection_init(WT_CONNECTION_IMPL *handle)
+int
+__wt_stat_connection_init(
+ WT_SESSION_IMPL *session, WT_CONNECTION_IMPL *handle)
{
int i;
+ WT_RET(__wt_calloc(session, (size_t)WT_COUNTER_SLOTS,
+ sizeof(*handle->stat_array), &handle->stat_array));
+
for (i = 0; i < WT_COUNTER_SLOTS; ++i) {
handle->stats[i] = &handle->stat_array[i];
__wt_stat_connection_init_single(handle->stats[i]);
}
+ return (0);
+}
+
+void
+__wt_stat_connection_discard(
+ WT_SESSION_IMPL *session, WT_CONNECTION_IMPL *handle)
+{
+ __wt_free(session, handle->stat_array);
}
void
@@ -793,6 +929,10 @@ __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats)
stats->block_byte_write_checkpoint = 0;
stats->block_map_read = 0;
stats->block_byte_map_read = 0;
+ stats->cache_read_app_count = 0;
+ stats->cache_read_app_time = 0;
+ stats->cache_write_app_count = 0;
+ stats->cache_write_app_time = 0;
/* not clearing cache_bytes_image */
/* not clearing cache_bytes_inuse */
/* not clearing cache_bytes_other */
@@ -886,6 +1026,21 @@ __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats)
stats->dh_sweeps = 0;
stats->dh_session_handles = 0;
stats->dh_session_sweeps = 0;
+ stats->lock_checkpoint_count = 0;
+ stats->lock_checkpoint_wait_application = 0;
+ stats->lock_checkpoint_wait_internal = 0;
+ stats->lock_handle_list_count = 0;
+ stats->lock_handle_list_wait_application = 0;
+ stats->lock_handle_list_wait_internal = 0;
+ stats->lock_metadata_count = 0;
+ stats->lock_metadata_wait_application = 0;
+ stats->lock_metadata_wait_internal = 0;
+ stats->lock_schema_count = 0;
+ stats->lock_schema_wait_application = 0;
+ stats->lock_schema_wait_internal = 0;
+ stats->lock_table_count = 0;
+ stats->lock_table_wait_application = 0;
+ stats->lock_table_wait_internal = 0;
stats->log_slot_switch_busy = 0;
stats->log_slot_closes = 0;
stats->log_slot_races = 0;
@@ -950,6 +1105,8 @@ __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats)
/* not clearing thread_fsync_active */
/* not clearing thread_read_active */
/* not clearing thread_write_active */
+ stats->application_evict_time = 0;
+ stats->application_cache_time = 0;
stats->page_busy_blocked = 0;
stats->page_forcible_evict_blocked = 0;
stats->page_locked_blocked = 0;
@@ -967,6 +1124,7 @@ __wt_stat_connection_clear_single(WT_CONNECTION_STATS *stats)
/* not clearing txn_checkpoint_scrub_time */
/* not clearing txn_checkpoint_time_total */
stats->txn_checkpoint = 0;
+ stats->txn_checkpoint_skipped = 0;
stats->txn_fail_cache = 0;
stats->txn_checkpoint_fsync_post = 0;
/* not clearing txn_checkpoint_fsync_post_duration */
@@ -1030,6 +1188,11 @@ __wt_stat_connection_aggregate(
WT_STAT_READ(from, block_byte_write_checkpoint);
to->block_map_read += WT_STAT_READ(from, block_map_read);
to->block_byte_map_read += WT_STAT_READ(from, block_byte_map_read);
+ to->cache_read_app_count += WT_STAT_READ(from, cache_read_app_count);
+ to->cache_read_app_time += WT_STAT_READ(from, cache_read_app_time);
+ to->cache_write_app_count +=
+ WT_STAT_READ(from, cache_write_app_count);
+ to->cache_write_app_time += WT_STAT_READ(from, cache_write_app_time);
to->cache_bytes_image += WT_STAT_READ(from, cache_bytes_image);
to->cache_bytes_inuse += WT_STAT_READ(from, cache_bytes_inuse);
to->cache_bytes_other += WT_STAT_READ(from, cache_bytes_other);
@@ -1156,6 +1319,33 @@ __wt_stat_connection_aggregate(
to->dh_sweeps += WT_STAT_READ(from, dh_sweeps);
to->dh_session_handles += WT_STAT_READ(from, dh_session_handles);
to->dh_session_sweeps += WT_STAT_READ(from, dh_session_sweeps);
+ to->lock_checkpoint_count +=
+ WT_STAT_READ(from, lock_checkpoint_count);
+ to->lock_checkpoint_wait_application +=
+ WT_STAT_READ(from, lock_checkpoint_wait_application);
+ to->lock_checkpoint_wait_internal +=
+ WT_STAT_READ(from, lock_checkpoint_wait_internal);
+ to->lock_handle_list_count +=
+ WT_STAT_READ(from, lock_handle_list_count);
+ to->lock_handle_list_wait_application +=
+ WT_STAT_READ(from, lock_handle_list_wait_application);
+ to->lock_handle_list_wait_internal +=
+ WT_STAT_READ(from, lock_handle_list_wait_internal);
+ to->lock_metadata_count += WT_STAT_READ(from, lock_metadata_count);
+ to->lock_metadata_wait_application +=
+ WT_STAT_READ(from, lock_metadata_wait_application);
+ to->lock_metadata_wait_internal +=
+ WT_STAT_READ(from, lock_metadata_wait_internal);
+ to->lock_schema_count += WT_STAT_READ(from, lock_schema_count);
+ to->lock_schema_wait_application +=
+ WT_STAT_READ(from, lock_schema_wait_application);
+ to->lock_schema_wait_internal +=
+ WT_STAT_READ(from, lock_schema_wait_internal);
+ to->lock_table_count += WT_STAT_READ(from, lock_table_count);
+ to->lock_table_wait_application +=
+ WT_STAT_READ(from, lock_table_wait_application);
+ to->lock_table_wait_internal +=
+ WT_STAT_READ(from, lock_table_wait_internal);
to->log_slot_switch_busy += WT_STAT_READ(from, log_slot_switch_busy);
to->log_slot_closes += WT_STAT_READ(from, log_slot_closes);
to->log_slot_races += WT_STAT_READ(from, log_slot_races);
@@ -1242,6 +1432,10 @@ __wt_stat_connection_aggregate(
to->thread_fsync_active += WT_STAT_READ(from, thread_fsync_active);
to->thread_read_active += WT_STAT_READ(from, thread_read_active);
to->thread_write_active += WT_STAT_READ(from, thread_write_active);
+ to->application_evict_time +=
+ WT_STAT_READ(from, application_evict_time);
+ to->application_cache_time +=
+ WT_STAT_READ(from, application_cache_time);
to->page_busy_blocked += WT_STAT_READ(from, page_busy_blocked);
to->page_forcible_evict_blocked +=
WT_STAT_READ(from, page_forcible_evict_blocked);
@@ -1270,6 +1464,8 @@ __wt_stat_connection_aggregate(
to->txn_checkpoint_time_total +=
WT_STAT_READ(from, txn_checkpoint_time_total);
to->txn_checkpoint += WT_STAT_READ(from, txn_checkpoint);
+ to->txn_checkpoint_skipped +=
+ WT_STAT_READ(from, txn_checkpoint_skipped);
to->txn_fail_cache += WT_STAT_READ(from, txn_fail_cache);
to->txn_checkpoint_fsync_post +=
WT_STAT_READ(from, txn_checkpoint_fsync_post);
diff --git a/src/third_party/wiredtiger/src/support/thread_group.c b/src/third_party/wiredtiger/src/support/thread_group.c
index f5ddabad7d4..a866d2d01c5 100644
--- a/src/third_party/wiredtiger/src/support/thread_group.c
+++ b/src/third_party/wiredtiger/src/support/thread_group.c
@@ -60,7 +60,7 @@ __thread_group_grow(
while (group->current_threads < new_count) {
thread = group->threads[group->current_threads++];
__wt_verbose(session, WT_VERB_THREAD_GROUP,
- "Starting utility thread: %p:%"PRIu32"\n",
+ "Starting utility thread: %p:%" PRIu32,
(void *)group, thread->id);
F_SET(thread, WT_THREAD_RUN);
WT_ASSERT(session, thread->session != NULL);
@@ -100,7 +100,7 @@ __thread_group_shrink(WT_SESSION_IMPL *session,
/* Wake threads to ensure they notice the state change */
if (thread->tid != 0) {
__wt_verbose(session, WT_VERB_THREAD_GROUP,
- "Stopping utility thread: %p:%"PRIu32"\n",
+ "Stopping utility thread: %p:%" PRIu32,
(void *)group, thread->id);
F_CLR(thread, WT_THREAD_RUN);
__wt_cond_signal(session, group->wait_cond);
@@ -224,7 +224,7 @@ __wt_thread_group_resize(
__wt_verbose(session, WT_VERB_THREAD_GROUP,
"Resize thread group: %p, from min: %" PRIu32 " -> %" PRIu32
- " from max: %" PRIu32 " -> %" PRIu32 "\n",
+ " from max: %" PRIu32 " -> %" PRIu32,
(void *)group, group->min, new_min, group->max, new_max);
__wt_writelock(session, group->lock);
@@ -253,7 +253,7 @@ __wt_thread_group_create(
cond_alloced = false;
__wt_verbose(session, WT_VERB_THREAD_GROUP,
- "Creating thread group: %p\n", (void *)group);
+ "Creating thread group: %p", (void *)group);
WT_RET(__wt_rwlock_alloc(session, &group->lock, "Thread group"));
WT_ERR(__wt_cond_alloc(
@@ -286,7 +286,7 @@ __wt_thread_group_destroy(WT_SESSION_IMPL *session, WT_THREAD_GROUP *group)
WT_DECL_RET;
__wt_verbose(session, WT_VERB_THREAD_GROUP,
- "Destroying thread group: %p\n", (void *)group);
+ "Destroying thread group: %p", (void *)group);
WT_ASSERT(session, __wt_rwlock_islocked(session, group->lock));
diff --git a/src/third_party/wiredtiger/src/txn/txn.c b/src/third_party/wiredtiger/src/txn/txn.c
index 01e0fbbb634..1c47f94bb60 100644
--- a/src/third_party/wiredtiger/src/txn/txn.c
+++ b/src/third_party/wiredtiger/src/txn/txn.c
@@ -96,11 +96,11 @@ __wt_txn_release_snapshot(WT_SESSION_IMPL *session)
txn_state = WT_SESSION_TXN_STATE(session);
WT_ASSERT(session,
- txn_state->snap_min == WT_TXN_NONE ||
+ txn_state->pinned_id == WT_TXN_NONE ||
session->txn.isolation == WT_ISO_READ_UNCOMMITTED ||
- !__wt_txn_visible_all(session, txn_state->snap_min));
+ !__wt_txn_visible_all(session, txn_state->pinned_id));
- txn_state->snap_min = WT_TXN_NONE;
+ txn_state->pinned_id = WT_TXN_NONE;
F_CLR(txn, WT_TXN_HAS_SNAPSHOT);
}
@@ -112,12 +112,11 @@ int
__wt_txn_get_snapshot(WT_SESSION_IMPL *session)
{
WT_CONNECTION_IMPL *conn;
- WT_DECL_RET;
WT_TXN *txn;
WT_TXN_GLOBAL *txn_global;
WT_TXN_STATE *s, *txn_state;
uint64_t current_id, id;
- uint64_t prev_oldest_id, snap_min;
+ uint64_t prev_oldest_id, pinned_id;
uint32_t i, n, session_cnt;
conn = S2C(session);
@@ -126,30 +125,24 @@ __wt_txn_get_snapshot(WT_SESSION_IMPL *session)
txn_state = WT_SESSION_TXN_STATE(session);
n = 0;
- /*
- * Spin waiting for the lock: the sleeps in our blocking readlock
- * implementation are too slow for scanning the transaction table.
- */
- while ((ret =
- __wt_try_readlock(session, txn_global->scan_rwlock)) == EBUSY)
- WT_PAUSE();
- WT_RET(ret);
+ /* We're going to scan the table: wait for the lock. */
+ __wt_readlock_spin(session, txn_global->scan_rwlock);
- current_id = snap_min = txn_global->current;
+ current_id = pinned_id = txn_global->current;
prev_oldest_id = txn_global->oldest_id;
/*
* Include the checkpoint transaction, if one is running: we should
* ignore any uncommitted changes the checkpoint has written to the
* metadata. We don't have to keep the checkpoint's changes pinned so
- * don't including it in the published snap_min.
+ * don't including it in the published pinned ID.
*/
if ((id = txn_global->checkpoint_txnid) != WT_TXN_NONE)
txn->snapshot[n++] = id;
/* For pure read-only workloads, avoid scanning. */
if (prev_oldest_id == current_id) {
- txn_state->snap_min = current_id;
+ txn_state->pinned_id = current_id;
/* Check that the oldest ID has not moved in the meantime. */
WT_ASSERT(session, prev_oldest_id == txn_global->oldest_id);
goto done;
@@ -172,18 +165,18 @@ __wt_txn_get_snapshot(WT_SESSION_IMPL *session)
(id = s->id) != WT_TXN_NONE &&
WT_TXNID_LE(prev_oldest_id, id)) {
txn->snapshot[n++] = id;
- if (WT_TXNID_LT(id, snap_min))
- snap_min = id;
+ if (WT_TXNID_LT(id, pinned_id))
+ pinned_id = id;
}
}
/*
- * If we got a new snapshot, update the published snap_min for this
+ * If we got a new snapshot, update the published pinned ID for this
* session.
*/
- WT_ASSERT(session, WT_TXNID_LE(prev_oldest_id, snap_min));
+ WT_ASSERT(session, WT_TXNID_LE(prev_oldest_id, pinned_id));
WT_ASSERT(session, prev_oldest_id == txn_global->oldest_id);
- txn_state->snap_min = snap_min;
+ txn_state->pinned_id = pinned_id;
done: __wt_readunlock(session, txn_global->scan_rwlock);
__txn_sort_snapshot(session, n, current_id);
@@ -232,13 +225,13 @@ __txn_oldest_scan(WT_SESSION_IMPL *session,
/*
* !!!
- * Note: Don't ignore snap_min values older than the previous
- * oldest ID. Read-uncommitted operations publish snap_min
+ * Note: Don't ignore pinned ID values older than the previous
+ * oldest ID. Read-uncommitted operations publish pinned ID
* values without acquiring the scan lock to protect the global
- * table. See the comment in __wt_txn_cursor_op for
- * more details.
+ * table. See the comment in __wt_txn_cursor_op for more
+ * details.
*/
- if ((id = s->snap_min) != WT_TXN_NONE &&
+ if ((id = s->pinned_id) != WT_TXN_NONE &&
WT_TXNID_LT(id, oldest_id)) {
oldest_id = id;
oldest_session = &conn->sessions[i];
@@ -292,7 +285,7 @@ __wt_txn_update_oldest(WT_SESSION_IMPL *session, uint32_t flags)
/* First do a read-only scan. */
if (wait)
- __wt_readlock(session, txn_global->scan_rwlock);
+ __wt_readlock_spin(session, txn_global->scan_rwlock);
else if ((ret =
__wt_try_readlock(session, txn_global->scan_rwlock)) != 0)
return (ret == EBUSY ? 0 : ret);
@@ -360,7 +353,7 @@ __wt_txn_update_oldest(WT_SESSION_IMPL *session, uint32_t flags)
__wt_verbose(session, WT_VERB_TRANSACTION,
"old snapshot %" PRIu64
" pinned in session %" PRIu32 " [%s]"
- " with snap_min %" PRIu64 "\n",
+ " with snap_min %" PRIu64,
oldest_id, oldest_session->id,
oldest_session->lastop,
oldest_session->txn.snap_min);
@@ -659,21 +652,21 @@ __wt_txn_rollback(WT_SESSION_IMPL *session, const char *cfg[])
* Initialize a session's transaction data.
*/
int
-__wt_txn_init(WT_SESSION_IMPL *session)
+__wt_txn_init(WT_SESSION_IMPL *session, WT_SESSION_IMPL *session_ret)
{
WT_TXN *txn;
- txn = &session->txn;
+ txn = &session_ret->txn;
txn->id = WT_TXN_NONE;
WT_RET(__wt_calloc_def(session,
- S2C(session)->session_size, &txn->snapshot));
+ S2C(session_ret)->session_size, &txn->snapshot));
#ifdef HAVE_DIAGNOSTIC
- if (S2C(session)->txn_global.states != NULL) {
+ if (S2C(session_ret)->txn_global.states != NULL) {
WT_TXN_STATE *txn_state;
- txn_state = WT_SESSION_TXN_STATE(session);
- WT_ASSERT(session, txn_state->snap_min == WT_TXN_NONE);
+ txn_state = WT_SESSION_TXN_STATE(session_ret);
+ WT_ASSERT(session, txn_state->pinned_id == WT_TXN_NONE);
}
#endif
@@ -683,7 +676,7 @@ __wt_txn_init(WT_SESSION_IMPL *session)
*/
txn->mod = NULL;
- txn->isolation = session->isolation;
+ txn->isolation = session_ret->isolation;
return (0);
}
@@ -773,7 +766,7 @@ __wt_txn_global_init(WT_SESSION_IMPL *session, const char *cfg[])
WT_CACHE_LINE_ALIGNMENT_VERIFY(session, txn_global->states);
for (i = 0, s = txn_global->states; i < conn->session_size; i++, s++)
- s->id = s->snap_min = WT_TXN_NONE;
+ s->id = s->pinned_id = WT_TXN_NONE;
return (0);
}
diff --git a/src/third_party/wiredtiger/src/txn/txn_ckpt.c b/src/third_party/wiredtiger/src/txn/txn_ckpt.c
index 3aad95f5a9f..1efd0200a3d 100644
--- a/src/third_party/wiredtiger/src/txn/txn_ckpt.c
+++ b/src/third_party/wiredtiger/src/txn/txn_ckpt.c
@@ -314,7 +314,7 @@ __checkpoint_update_generation(WT_SESSION_IMPL *session)
* __checkpoint_reduce_dirty_cache --
* Release clean trees from the list cached for checkpoints.
*/
-static int
+static void
__checkpoint_reduce_dirty_cache(WT_SESSION_IMPL *session)
{
WT_CACHE *cache;
@@ -332,9 +332,9 @@ __checkpoint_reduce_dirty_cache(WT_SESSION_IMPL *session)
/* Give up if scrubbing is disabled. */
if (cache->eviction_checkpoint_target == 0 ||
cache->eviction_checkpoint_target >= cache->eviction_dirty_trigger)
- return (0);
+ return;
- WT_RET(__wt_epoch(session, &start));
+ __wt_epoch(session, &start);
last = start;
bytes_written_last = 0;
bytes_written_start = cache->bytes_written;
@@ -345,7 +345,7 @@ __checkpoint_reduce_dirty_cache(WT_SESSION_IMPL *session)
* cache via reconfigure. This avoids potential divide by zero.
*/
if (cache_size < 10 * WT_MEGABYTE)
- return (0);
+ return;
stepdown_us = 10000;
work_us = 0;
progress = false;
@@ -371,7 +371,7 @@ __checkpoint_reduce_dirty_cache(WT_SESSION_IMPL *session)
break;
__wt_sleep(0, stepdown_us / 10);
- WT_RET(__wt_epoch(session, &stop));
+ __wt_epoch(session, &stop);
current_us = WT_TIMEDIFF_US(stop, last);
total_ms = WT_TIMEDIFF_MS(stop, start);
bytes_written_total =
@@ -427,14 +427,12 @@ __checkpoint_reduce_dirty_cache(WT_SESSION_IMPL *session)
WT_MAX(cache->eviction_dirty_target, current_dirty - delta);
WT_STAT_CONN_SET(session, txn_checkpoint_scrub_target,
cache->eviction_scrub_limit);
- WT_RET(__wt_epoch(session, &last));
+ __wt_epoch(session, &last);
}
- WT_RET(__wt_epoch(session, &stop));
+ __wt_epoch(session, &stop);
total_ms = WT_TIMEDIFF_MS(stop, start);
WT_STAT_CONN_SET(session, txn_checkpoint_scrub_time, total_ms);
-
- return (0);
}
/*
@@ -497,7 +495,7 @@ __checkpoint_stats(
* __checkpoint_verbose_track --
* Output a verbose message with timing information
*/
-static int
+static void
__checkpoint_verbose_track(WT_SESSION_IMPL *session,
const char *msg, struct timespec *start)
{
@@ -506,9 +504,9 @@ __checkpoint_verbose_track(WT_SESSION_IMPL *session,
uint64_t msec;
if (!WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT))
- return (0);
+ return;
- WT_RET(__wt_epoch(session, &stop));
+ __wt_epoch(session, &stop);
/*
* Get time diff in microseconds.
@@ -526,7 +524,6 @@ __checkpoint_verbose_track(WT_SESSION_IMPL *session,
WT_UNUSED(msg);
WT_UNUSED(start);
#endif
- return (0);
}
/*
@@ -565,7 +562,7 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
/*
* Do a pass over the configuration arguments and figure out what kind
- * kind of checkpoint this is.
+ * of checkpoint this is.
*/
WT_RET(__checkpoint_apply_all(session, cfg, NULL, &full));
@@ -576,7 +573,7 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
conn->cache->evict_max_page_size = 0;
/* Initialize the verbose tracking timer */
- WT_ERR(__wt_epoch(session, &verb_timer));
+ __wt_epoch(session, &verb_timer);
/*
* Update the global oldest ID so we do all possible cleanup.
@@ -594,18 +591,18 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
* Try to reduce the amount of dirty data in cache so there is less
* work do during the critical section of the checkpoint.
*/
- WT_ERR(__checkpoint_reduce_dirty_cache(session));
+ __checkpoint_reduce_dirty_cache(session);
/* Tell logging that we are about to start a database checkpoint. */
if (full && logging)
WT_ERR(__wt_txn_checkpoint_log(
session, full, WT_TXN_LOG_CKPT_PREPARE, NULL));
- WT_ERR(__checkpoint_verbose_track(session,
- "starting transaction", &verb_timer));
+ __checkpoint_verbose_track(session,
+ "starting transaction", &verb_timer);
if (full)
- WT_ERR(__wt_epoch(session, &start));
+ __wt_epoch(session, &start);
/*
* Start the checkpoint for real.
@@ -666,6 +663,14 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
WT_ERR(__wt_txn_id_check(session));
/*
+ * Mark the connection as clean. If some data gets modified after
+ * generating checkpoint transaction id, connection will be reset to
+ * dirty when reconciliation marks the btree dirty on encountering the
+ * dirty page.
+ */
+ conn->modified = false;
+
+ /*
* Save the checkpoint session ID.
*
* We never do checkpoints in the default session (with id zero).
@@ -689,7 +694,7 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
*/
WT_ASSERT(session,
WT_TXNID_LE(txn_global->oldest_id, txn_state->id) &&
- WT_TXNID_LE(txn_global->oldest_id, txn_state->snap_min));
+ WT_TXNID_LE(txn_global->oldest_id, txn_state->pinned_id));
/*
* Clear our entry from the global transaction session table. Any
@@ -698,7 +703,7 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
* can safely ignore the checkpoint ID (see the visible all check for
* details).
*/
- txn_state->id = txn_state->snap_min = WT_TXN_NONE;
+ txn_state->id = txn_state->pinned_id = WT_TXN_NONE;
__wt_writeunlock(session, txn_global->scan_rwlock);
/*
@@ -739,23 +744,22 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
WT_ERR(__checkpoint_apply(session, cfg, __checkpoint_presync));
__wt_evict_server_wake(session);
- WT_ERR(__checkpoint_verbose_track(session,
- "committing transaction", &verb_timer));
+ __checkpoint_verbose_track(session,
+ "committing transaction", &verb_timer);
/*
* Checkpoints have to hit disk (it would be reasonable to configure for
* lazy checkpoints, but we don't support them yet).
*/
- WT_ERR(__wt_epoch(session, &fsync_start));
+ __wt_epoch(session, &fsync_start);
WT_ERR(__checkpoint_apply(session, cfg, __wt_checkpoint_sync));
- WT_ERR(__wt_epoch(session, &fsync_stop));
+ __wt_epoch(session, &fsync_stop);
fsync_duration_usecs = WT_TIMEDIFF_US(fsync_stop, fsync_start);
WT_STAT_CONN_INCR(session, txn_checkpoint_fsync_post);
WT_STAT_CONN_SET(session,
txn_checkpoint_fsync_post_duration, fsync_duration_usecs);
- WT_ERR(__checkpoint_verbose_track(session,
- "sync completed", &verb_timer));
+ __checkpoint_verbose_track(session, "sync completed", &verb_timer);
/*
* Commit the transaction now that we are sure that all files in the
@@ -793,8 +797,8 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
ret = __wt_checkpoint_sync(session, NULL));
WT_ERR(ret);
- WT_ERR(__checkpoint_verbose_track(session,
- "metadata sync completed", &verb_timer));
+ __checkpoint_verbose_track(session,
+ "metadata sync completed", &verb_timer);
} else
WT_WITH_DHANDLE(session,
WT_SESSION_META_DHANDLE(session),
@@ -808,7 +812,7 @@ __txn_checkpoint(WT_SESSION_IMPL *session, const char *cfg[])
txn_global->checkpoint_pinned = WT_TXN_NONE;
if (full) {
- WT_ERR(__wt_epoch(session, &stop));
+ __wt_epoch(session, &stop);
__checkpoint_stats(session, &start, &stop);
}
@@ -825,6 +829,9 @@ err: /*
* overwritten the checkpoint, so what ends up on disk is not
* consistent.
*/
+ if (ret != 0 && !conn->modified)
+ conn->modified = true;
+
session->isolation = txn->isolation = WT_ISO_READ_UNCOMMITTED;
if (tracking)
WT_TRET(__wt_meta_track_off(session, false, ret != 0));
@@ -1352,9 +1359,13 @@ __checkpoint_tree(
* out of sync with the set of dirty pages (modify is set, but there
* are no dirty pages), we perform a checkpoint without any writes, no
* checkpoint is created, and then things get bad.
+ * While marking the root page as dirty, we do not want to dirty the
+ * btree because we are marking the btree as clean just after this call.
+ * Also, marking the btree dirty at this stage will unnecessarily mark
+ * the connection as dirty causing checkpoint-skip code to fail.
*/
WT_ERR(__wt_page_modify_init(session, btree->root.page));
- __wt_page_modify_set(session, btree->root.page);
+ __wt_page_only_modify_set(session, btree->root.page);
/*
* Clear the tree's modified flag; any changes before we clear the flag
@@ -1366,7 +1377,7 @@ __checkpoint_tree(
* it sets the modified flag itself. Use a full barrier so we get the
* store done quickly, this isn't a performance path.
*/
- btree->modified = 0;
+ btree->modified = false;
WT_FULL_BARRIER();
/* Tell logging that a file checkpoint is starting. */
@@ -1440,8 +1451,11 @@ err: /*
* If the checkpoint didn't complete successfully, make sure the
* tree is marked dirty.
*/
- if (ret != 0 && !btree->modified && was_modified)
- btree->modified = 1;
+ if (ret != 0 && !btree->modified && was_modified) {
+ btree->modified = true;
+ if (!S2C(session)->modified)
+ S2C(session)->modified = true;
+ }
__wt_meta_ckptlist_free(session, ckptbase);
btree->ckpt = NULL;
diff --git a/src/third_party/wiredtiger/src/txn/txn_log.c b/src/third_party/wiredtiger/src/txn/txn_log.c
index f9dd9bee807..f1b78879d76 100644
--- a/src/third_party/wiredtiger/src/txn/txn_log.c
+++ b/src/third_party/wiredtiger/src/txn/txn_log.c
@@ -262,20 +262,20 @@ err: __wt_logrec_free(session, &logrec);
* Read a log record for a checkpoint operation.
*/
int
-__wt_txn_checkpoint_logread(
- WT_SESSION_IMPL *session, const uint8_t **pp, const uint8_t *end,
- WT_LSN *ckpt_lsn)
+__wt_txn_checkpoint_logread(WT_SESSION_IMPL *session,
+ const uint8_t **pp, const uint8_t *end, WT_LSN *ckpt_lsn)
{
- WT_ITEM ckpt_snapshot;
+ WT_DECL_RET;
+ WT_ITEM ckpt_snapshot_unused;
uint32_t ckpt_file, ckpt_offset;
- u_int ckpt_nsnapshot;
+ u_int ckpt_nsnapshot_unused;
const char *fmt = WT_UNCHECKED_STRING(IIIU);
- WT_RET(__wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
+ if ((ret = __wt_struct_unpack(session, *pp, WT_PTRDIFF(end, *pp), fmt,
&ckpt_file, &ckpt_offset,
- &ckpt_nsnapshot, &ckpt_snapshot));
- WT_UNUSED(ckpt_nsnapshot);
- WT_UNUSED(ckpt_snapshot);
+ &ckpt_nsnapshot_unused, &ckpt_snapshot_unused)) != 0)
+ WT_RET_MSG(session,
+ ret, "txn_checkpoint_logread: unpack failure");
WT_SET_LSN(ckpt_lsn, ckpt_file, ckpt_offset);
*pp = end;
return (0);
diff --git a/src/third_party/wiredtiger/src/txn/txn_nsnap.c b/src/third_party/wiredtiger/src/txn/txn_nsnap.c
index 8f7e93238de..7ba0cc8700e 100644
--- a/src/third_party/wiredtiger/src/txn/txn_nsnap.c
+++ b/src/third_party/wiredtiger/src/txn/txn_nsnap.c
@@ -42,9 +42,16 @@ __nsnap_drop_one(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *name)
return (WT_NOTFOUND);
/* Bump the global ID if we are removing the first entry */
- if (found == TAILQ_FIRST(&txn_global->nsnaph))
+ if (found == TAILQ_FIRST(&txn_global->nsnaph)) {
+ WT_ASSERT(session, !__wt_txn_visible_all(
+ session, txn_global->nsnap_oldest_id));
txn_global->nsnap_oldest_id = (TAILQ_NEXT(found, q) != NULL) ?
- TAILQ_NEXT(found, q)->snap_min : WT_TXN_NONE;
+ TAILQ_NEXT(found, q)->pinned_id : WT_TXN_NONE;
+ WT_DIAGNOSTIC_YIELD;
+ WT_ASSERT(session, txn_global->nsnap_oldest_id == WT_TXN_NONE ||
+ !__wt_txn_visible_all(
+ session, txn_global->nsnap_oldest_id));
+ }
TAILQ_REMOVE(&txn_global->nsnaph, found, q);
__nsnap_destroy(session, found);
WT_STAT_CONN_INCR(session, txn_snapshots_dropped);
@@ -104,7 +111,7 @@ __nsnap_drop_to(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *name, bool inclusive)
}
if (TAILQ_NEXT(last, q) != NULL)
- new_nsnap_oldest = TAILQ_NEXT(last, q)->snap_min;
+ new_nsnap_oldest = TAILQ_NEXT(last, q)->pinned_id;
}
do {
@@ -117,7 +124,15 @@ __nsnap_drop_to(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *name, bool inclusive)
} while (nsnap != last && !TAILQ_EMPTY(&txn_global->nsnaph));
/* Now that the queue of named snapshots is updated, update the ID */
+ WT_ASSERT(session, !__wt_txn_visible_all(
+ session, txn_global->nsnap_oldest_id) &&
+ (new_nsnap_oldest == WT_TXN_NONE ||
+ WT_TXNID_LE(txn_global->nsnap_oldest_id, new_nsnap_oldest)));
txn_global->nsnap_oldest_id = new_nsnap_oldest;
+ WT_DIAGNOSTIC_YIELD;
+ WT_ASSERT(session,
+ new_nsnap_oldest == WT_TXN_NONE ||
+ !__wt_txn_visible_all(session, new_nsnap_oldest));
return (ret);
}
@@ -157,6 +172,7 @@ __wt_txn_named_snapshot_begin(WT_SESSION_IMPL *session, const char *cfg[])
WT_ERR(__wt_calloc_one(session, &nsnap_new));
nsnap = nsnap_new;
WT_ERR(__wt_strndup(session, cval.str, cval.len, &nsnap->name));
+ nsnap->pinned_id = WT_SESSION_TXN_STATE(session)->pinned_id;
nsnap->snap_min = txn->snap_min;
nsnap->snap_max = txn->snap_max;
if (txn->snapshot_count > 0) {
@@ -175,15 +191,25 @@ __wt_txn_named_snapshot_begin(WT_SESSION_IMPL *session, const char *cfg[])
*/
WT_ERR_NOTFOUND_OK(__nsnap_drop_one(session, &cval));
- if (TAILQ_EMPTY(&txn_global->nsnaph))
- txn_global->nsnap_oldest_id = nsnap_new->snap_min;
+ if (TAILQ_EMPTY(&txn_global->nsnaph)) {
+ WT_ASSERT(session, txn_global->nsnap_oldest_id == WT_TXN_NONE &&
+ !__wt_txn_visible_all(session, nsnap_new->pinned_id));
+ __wt_readlock(session, txn_global->scan_rwlock);
+ txn_global->nsnap_oldest_id = nsnap_new->pinned_id;
+ __wt_readunlock(session, txn_global->scan_rwlock);
+ }
TAILQ_INSERT_TAIL(&txn_global->nsnaph, nsnap_new, q);
WT_STAT_CONN_INCR(session, txn_snapshots_created);
nsnap_new = NULL;
-err: if (started_txn)
+err: if (started_txn) {
+#ifdef HAVE_DIAGNOSTIC
+ uint64_t pinned_id = WT_SESSION_TXN_STATE(session)->pinned_id;
+#endif
WT_TRET(__wt_txn_rollback(session, NULL));
- else if (ret == 0)
+ WT_DIAGNOSTIC_YIELD;
+ WT_ASSERT(session, !__wt_txn_visible_all(session, pinned_id));
+ } else if (ret == 0)
F_SET(txn, WT_TXN_NAMED_SNAPSHOT);
if (nsnap_new != NULL)
@@ -258,7 +284,20 @@ __wt_txn_named_snapshot_get(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *nameval)
__wt_readlock(session, txn_global->nsnap_rwlock);
TAILQ_FOREACH(nsnap, &txn_global->nsnaph, q)
if (WT_STRING_MATCH(nsnap->name, nameval->str, nameval->len)) {
- txn->snap_min = txn_state->snap_min = nsnap->snap_min;
+ /*
+ * Acquire the scan lock so the oldest ID can't move
+ * forward without seeing our pinned ID.
+ */
+ __wt_readlock(session, txn_global->scan_rwlock);
+ txn_state->pinned_id = nsnap->pinned_id;
+ __wt_readunlock(session, txn_global->scan_rwlock);
+
+ WT_ASSERT(session, !__wt_txn_visible_all(
+ session, txn_state->pinned_id) &&
+ txn_global->nsnap_oldest_id != WT_TXN_NONE &&
+ WT_TXNID_LE(txn_global->nsnap_oldest_id,
+ txn_state->pinned_id));
+ txn->snap_min = nsnap->snap_min;
txn->snap_max = nsnap->snap_max;
if ((txn->snapshot_count = nsnap->snapshot_count) != 0)
memcpy(txn->snapshot, nsnap->snapshot,
diff --git a/src/third_party/wiredtiger/src/txn/txn_recover.c b/src/third_party/wiredtiger/src/txn/txn_recover.c
index ae21e58d9b6..65811aa3bf4 100644
--- a/src/third_party/wiredtiger/src/txn/txn_recover.c
+++ b/src/third_party/wiredtiger/src/txn/txn_recover.c
@@ -231,9 +231,12 @@ __txn_op_apply(
/* Reset the cursor so it doesn't block eviction. */
if (cursor != NULL)
WT_ERR(cursor->reset(cursor));
+ return (0);
-err: if (ret != 0)
- __wt_err(session, ret, "Operation failed during recovery");
+err: __wt_err(session, ret,
+ "operation apply failed during recovery: operation type %d "
+ "at LSN %" PRIu32 "/%" PRIu32,
+ optype, lsnp->l.file, lsnp->l.offset);
return (ret);
}
@@ -263,12 +266,14 @@ __txn_log_recover(WT_SESSION_IMPL *session,
WT_ITEM *logrec, WT_LSN *lsnp, WT_LSN *next_lsnp,
void *cookie, int firstrecord)
{
+ WT_DECL_RET;
WT_RECOVERY *r;
- const uint8_t *end, *p;
- uint64_t txnid;
+ uint64_t txnid_unused;
uint32_t rectype;
+ const uint8_t *end, *p;
WT_UNUSED(next_lsnp);
+
r = cookie;
p = WT_LOG_SKIP_HEADER(logrec->data);
end = (const uint8_t *)logrec->data + logrec->size;
@@ -285,8 +290,10 @@ __txn_log_recover(WT_SESSION_IMPL *session,
break;
case WT_LOGREC_COMMIT:
- WT_RET(__wt_vunpack_uint(&p, WT_PTRDIFF(end, p), &txnid));
- WT_UNUSED(txnid);
+ if ((ret = __wt_vunpack_uint(
+ &p, WT_PTRDIFF(end, p), &txnid_unused)) != 0)
+ WT_RET_MSG(
+ session, ret, "txn_log_recover: unpack failure");
WT_RET(__txn_commit_apply(r, lsnp, &p, end));
break;
}
diff --git a/src/third_party/wiredtiger/test/bloom/test_bloom.c b/src/third_party/wiredtiger/test/bloom/test_bloom.c
index 7a298f000aa..67249ff887e 100644
--- a/src/third_party/wiredtiger/test/bloom/test_bloom.c
+++ b/src/third_party/wiredtiger/test/bloom/test_bloom.c
@@ -56,8 +56,6 @@ void usage(void)
extern char *__wt_optarg;
extern int __wt_optind;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/checkpoint/smoke.sh b/src/third_party/wiredtiger/test/checkpoint/smoke.sh
index 2f1d4345ad7..123d4e00df5 100755
--- a/src/third_party/wiredtiger/test/checkpoint/smoke.sh
+++ b/src/third_party/wiredtiger/test/checkpoint/smoke.sh
@@ -6,6 +6,9 @@ set -e
echo "checkpoint: 3 mixed tables"
$TEST_WRAPPER ./t -T 3 -t m
+# We are done if short tests are requested
+test -z "$TESTUTIL_DISABLE_LONG_TESTS" || exit 0
+
echo "checkpoint: 6 column-store tables"
$TEST_WRAPPER ./t -T 6 -t c
diff --git a/src/third_party/wiredtiger/test/checkpoint/test_checkpoint.c b/src/third_party/wiredtiger/test/checkpoint/test_checkpoint.c
index 6b2f0d4466c..4998019ad8e 100644
--- a/src/third_party/wiredtiger/test/checkpoint/test_checkpoint.c
+++ b/src/third_party/wiredtiger/test/checkpoint/test_checkpoint.c
@@ -42,8 +42,6 @@ static int wt_shutdown(void);
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/csuite/Makefile.am b/src/third_party/wiredtiger/test/csuite/Makefile.am
index 15db2fbcf46..a96492c1e71 100644
--- a/src/third_party/wiredtiger/test/csuite/Makefile.am
+++ b/src/third_party/wiredtiger/test/csuite/Makefile.am
@@ -7,6 +7,9 @@ AM_LDFLAGS = -static
test_wt1965_col_efficiency_SOURCES = wt1965_col_efficiency/main.c
noinst_PROGRAMS = test_wt1965_col_efficiency
+test_wt2403_lsm_workload_SOURCES = wt2403_lsm_workload/main.c
+noinst_PROGRAMS += test_wt2403_lsm_workload
+
test_wt2246_col_append_SOURCES = wt2246_col_append/main.c
noinst_PROGRAMS += test_wt2246_col_append
@@ -34,6 +37,9 @@ noinst_PROGRAMS += test_wt2834_join_bloom_fix
test_wt2853_perf_SOURCES = wt2853_perf/main.c
noinst_PROGRAMS += test_wt2853_perf
+test_wt2999_join_extractor_SOURCES = wt2999_join_extractor/main.c
+noinst_PROGRAMS += test_wt2999_join_extractor
+
# Run this during a "make check" smoke test.
TESTS = $(noinst_PROGRAMS)
LOG_COMPILER = $(TEST_WRAPPER)
diff --git a/src/third_party/wiredtiger/test/csuite/wt1965_col_efficiency/main.c b/src/third_party/wiredtiger/test/csuite/wt1965_col_efficiency/main.c
index 0dc367c0611..a7235d81b31 100644
--- a/src/third_party/wiredtiger/test/csuite/wt1965_col_efficiency/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt1965_col_efficiency/main.c
@@ -35,8 +35,6 @@
* it is demonstrating an inefficiency rather than a correctness bug.
*/
-void (*custom_die)(void) = NULL;
-
/* If changing field count also need to change set_value and get_value calls */
#define NR_FIELDS 8
#define NR_OBJECTS 100
diff --git a/src/third_party/wiredtiger/test/csuite/wt2246_col_append/main.c b/src/third_party/wiredtiger/test/csuite/wt2246_col_append/main.c
index b795816c76f..4b352b26051 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2246_col_append/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2246_col_append/main.c
@@ -42,8 +42,6 @@
#define MILLION 1000000
-void (*custom_die)(void) = NULL;
-
/* Needs to be global for signal handling. */
static TEST_OPTS *opts, _opts;
@@ -104,6 +102,8 @@ main(int argc, char *argv[])
char buf[100];
opts = &_opts;
+ if (testutil_disable_long_tests())
+ return (0);
memset(opts, 0, sizeof(*opts));
opts->table_type = TABLE_ROW;
opts->n_append_threads = N_APPEND_THREADS;
diff --git a/src/third_party/wiredtiger/test/csuite/wt2323_join_visibility/main.c b/src/third_party/wiredtiger/test/csuite/wt2323_join_visibility/main.c
index bbf1626fe82..239a3f300d0 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2323_join_visibility/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2323_join_visibility/main.c
@@ -52,8 +52,6 @@
* of inserts set low as a default.
*/
-void (*custom_die)(void) = NULL;
-
#define N_RECORDS 10000
#define N_INSERT 500000
#define N_INSERT_THREAD 2
@@ -96,6 +94,8 @@ main(int argc, char *argv[])
opts = &_opts;
sharedopts = &_sharedopts;
+ if (testutil_disable_long_tests())
+ return (0);
memset(opts, 0, sizeof(*opts));
memset(sharedopts, 0, sizeof(*sharedopts));
@@ -225,7 +225,8 @@ test_join(TEST_OPTS *opts, SHARED_OPTS *sharedopts, bool bloom,
testutil_check(session->close(session, NULL));
}
-static void *thread_insert(void *arg)
+static void *
+thread_insert(void *arg)
{
SHARED_OPTS *sharedopts;
TEST_OPTS *opts;
@@ -239,7 +240,7 @@ static void *thread_insert(void *arg)
threadargs = (THREAD_ARGS *)arg;
opts = threadargs->testopts;
sharedopts = threadargs->sharedopts;
- testutil_check(__wt_random_init_seed(NULL, &rnd));
+ __wt_random_init_seed(NULL, &rnd);
testutil_check(opts->conn->open_session(
opts->conn, NULL, NULL, &session));
diff --git a/src/third_party/wiredtiger/test/csuite/wt2403_lsm_workload/main.c b/src/third_party/wiredtiger/test/csuite/wt2403_lsm_workload/main.c
new file mode 100644
index 00000000000..0c287484b9e
--- /dev/null
+++ b/src/third_party/wiredtiger/test/csuite/wt2403_lsm_workload/main.c
@@ -0,0 +1,241 @@
+/*-
+ * Public Domain 2014-2016 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.
+ */
+
+#include "test_util.h"
+
+static const char name[] = "lsm:test";
+#define NUM_DOCS 100000
+#define NUM_QUERIES (NUM_DOCS/100)
+
+static void
+rand_str(uint64_t i, char *str)
+{
+ uint64_t x, y;
+
+ y = strlen(str);
+ for (x = y; x > y - 8; x--) {
+ str[x - 1] = (char)(i % 10) + 48;
+ i = i / 10;
+ }
+}
+
+static void
+check_str(uint64_t i, char *str, bool mod)
+{
+ char str2[] = "0000000000000000";
+
+ rand_str(i, str2);
+ if (mod)
+ str2[0] = 'A';
+ testutil_checkfmt(strcmp(str, str2),
+ "strcmp failed, got %s, expected %s", str, str2);
+}
+
+static void
+query_docs(WT_CURSOR *cursor, bool mod)
+{
+ WT_ITEM key, value;
+ int i;
+
+ for (i = 0; i < NUM_QUERIES; i++) {
+ testutil_check(cursor->next(cursor));
+ testutil_check(cursor->get_key(cursor, &key));
+ testutil_check(cursor->get_value(cursor, &value));
+ check_str((uint64_t)key.data, (char *)value.data, mod);
+ }
+ printf("%d documents read\n", NUM_QUERIES);
+}
+
+static void *
+compact_thread(void *args)
+{
+ WT_SESSION *session;
+
+ session = (WT_SESSION *)args;
+ testutil_check(session->compact(session, name, NULL));
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ WT_CURSOR *rcursor, *wcursor;
+ WT_ITEM key, value;
+ WT_SESSION *session, *session2;
+ pthread_t thread;
+ uint64_t i;
+
+ char str[] = "0000000000000000";
+
+ /*
+ * Create a clean test directory for this run of the test program if the
+ * environment variable isn't already set (as is done by make check).
+ */
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+ testutil_check(wiredtiger_open(opts->home,
+ NULL, "create,cache_size=200M", &opts->conn));
+
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session));
+ testutil_check(
+ opts->conn->open_session(opts->conn, NULL, NULL, &session2));
+
+ testutil_check(session->create(session, name,
+ "key_format=Q,value_format=S"));
+
+ /* Populate the table with some data. */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "overwrite", &wcursor));
+ for (i = 0; i < NUM_DOCS; i++) {
+ wcursor->set_key(wcursor, i);
+ rand_str(i, str);
+ wcursor->set_value(wcursor, str);
+ testutil_check(wcursor->insert(wcursor));
+ }
+ testutil_check(wcursor->close(wcursor));
+ printf("%d documents inserted\n", NUM_DOCS);
+
+ /* Perform some random reads */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "next_random=true", &rcursor));
+ query_docs(rcursor, false);
+ testutil_check(rcursor->close(rcursor));
+
+ /* Setup Transaction to pin the current values */
+ testutil_check(
+ session2->begin_transaction(session2, "isolation=snapshot"));
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+
+ /* Perform updates in a txn to confirm that we see only the original. */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "overwrite", &wcursor));
+ for (i = 0; i < NUM_DOCS; i++) {
+ rand_str(i, str);
+ str[0] = 'A';
+ wcursor->set_key(wcursor, i);
+ wcursor->set_value(wcursor, str);
+ testutil_check(wcursor->update(wcursor));
+ }
+ testutil_check(wcursor->close(wcursor));
+ printf("%d documents set to update\n", NUM_DOCS);
+
+ /* Random reads, which should see the original values */
+ query_docs(rcursor, false);
+ testutil_check(rcursor->close(rcursor));
+
+ /* Finish the txn */
+ testutil_check(session2->rollback_transaction(session2, NULL));
+
+ /* Random reads, which should see the updated values */
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+ query_docs(rcursor, true);
+ testutil_check(rcursor->close(rcursor));
+
+ /* Setup a pre-delete txn */
+ testutil_check(
+ session2->begin_transaction(session2, "isolation=snapshot"));
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+
+ /* Delete all but one document */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "overwrite", &wcursor));
+ for (i = 0; i < NUM_DOCS - 1; i++) {
+ wcursor->set_key(wcursor, i);
+ testutil_check(wcursor->remove(wcursor));
+ }
+ testutil_check(wcursor->close(wcursor));
+ printf("%d documents deleted\n", NUM_DOCS - 1);
+
+ /* Random reads, which should not see the deletes */
+ query_docs(rcursor, true);
+ testutil_check(rcursor->close(rcursor));
+
+ /* Rollback the txn so we can see the deletes */
+ testutil_check(session2->rollback_transaction(session2, NULL));
+
+ /* Find the one remaining document 3 times */
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+ for (i = 0; i < 3; i++) {
+ testutil_check(rcursor->next(rcursor));
+ testutil_check(rcursor->get_key(rcursor, &key));
+ testutil_check(rcursor->get_value(rcursor, &value));
+ /* There should only be one value available to us */
+ testutil_assertfmt((uint64_t)key.data == NUM_DOCS - 1,
+ "expected %d and got %" PRIu64,
+ NUM_DOCS - 1, (uint64_t)key.data);
+ check_str((uint64_t)key.data, (char *)value.data, true);
+ }
+ printf("Found the deleted doc 3 times\n");
+ testutil_check(rcursor->close(rcursor));
+
+ /* Repopulate the table for compact. */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "overwrite", &wcursor));
+ for (i = 0; i < NUM_DOCS - 1; i++) {
+ wcursor->set_key(wcursor, i);
+ rand_str(i, str);
+ str[0] = 'A';
+ wcursor->set_value(wcursor, str);
+ testutil_check(wcursor->insert(wcursor));
+ }
+ testutil_check(wcursor->close(wcursor));
+
+ /* Run random cursor queries while compact is running */
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+ testutil_check(pthread_create(&thread, NULL, compact_thread, session));
+ query_docs(rcursor, true);
+ testutil_check(rcursor->close(rcursor));
+ testutil_check(pthread_join(thread, NULL));
+
+ /* Delete everything. Check for infinite loops */
+ testutil_check(session->open_cursor(
+ session, name, NULL, "overwrite", &wcursor));
+ for (i = 0; i < NUM_DOCS; i++) {
+ wcursor->set_key(wcursor, i);
+ testutil_check(wcursor->remove(wcursor));
+ }
+ testutil_check(wcursor->close(wcursor));
+
+ testutil_check(session2->open_cursor(
+ session2, name, NULL, "next_random=true", &rcursor));
+ for (i = 0; i < 3; i++)
+ testutil_assert(rcursor->next(rcursor) == WT_NOTFOUND);
+ printf("Successfully got WT_NOTFOUND\n");
+
+ testutil_cleanup(opts);
+ return (EXIT_SUCCESS);
+}
diff --git a/src/third_party/wiredtiger/test/csuite/wt2447_join_main_table/main.c b/src/third_party/wiredtiger/test/csuite/wt2447_join_main_table/main.c
index bbae61e7ed5..1368e7c8c09 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2447_join_main_table/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2447_join_main_table/main.c
@@ -49,8 +49,6 @@
* table.
*/
-void (*custom_die)(void) = NULL;
-
#define N_RECORDS 10000
static void
diff --git a/src/third_party/wiredtiger/test/csuite/wt2535_insert_race/main.c b/src/third_party/wiredtiger/test/csuite/wt2535_insert_race/main.c
index 5eaca3279b6..ae18760a829 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2535_insert_race/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2535_insert_race/main.c
@@ -36,8 +36,6 @@
* Failure mode: Check that the data is correct at the end of the run.
*/
-void (*custom_die)(void) = NULL;
-
void *thread_insert_race(void *);
int
@@ -52,6 +50,8 @@ main(int argc, char *argv[])
int i;
opts = &_opts;
+ if (testutil_disable_long_tests())
+ return (0);
memset(opts, 0, sizeof(*opts));
opts->nthreads = 10;
opts->nrecords = 1000;
diff --git a/src/third_party/wiredtiger/test/csuite/wt2592_join_schema/main.c b/src/third_party/wiredtiger/test/csuite/wt2592_join_schema/main.c
index 4ffc9194646..0ec1c765d99 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2592_join_schema/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2592_join_schema/main.c
@@ -36,12 +36,6 @@
* Failure mode: The failure seen in WT-2592 was that no items were returned
* by a join.
*/
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <wiredtiger.h>
/* The C struct for the data we are storing in a WiredTiger table. */
typedef struct {
@@ -66,8 +60,6 @@ static POP_RECORD pop_data[] = {
{ "", 0, 0 }
};
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/csuite/wt2695_checksum/main.c b/src/third_party/wiredtiger/test/csuite/wt2695_checksum/main.c
index afb9d0788bd..db4fed5dc53 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2695_checksum/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2695_checksum/main.c
@@ -32,8 +32,6 @@
* Test case description: Smoke-test the CRC.
*/
-void (*custom_die)(void) = NULL;
-
static inline void
check(uint32_t hw, uint32_t sw, size_t len, const char *msg)
{
@@ -61,7 +59,7 @@ main(int argc, char *argv[])
wiredtiger_open(opts->home, NULL, "create", &opts->conn));
/* Initialize the RNG. */
- testutil_check(__wt_random_init_seed(NULL, &rnd));
+ __wt_random_init_seed(NULL, &rnd);
/* Allocate aligned memory for the data. */
data = dcalloc(DATASIZE, sizeof(uint8_t));
diff --git a/src/third_party/wiredtiger/test/csuite/wt2719_reconfig/main.c b/src/third_party/wiredtiger/test/csuite/wt2719_reconfig/main.c
index b67dae6d647..0942cfc73b2 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2719_reconfig/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2719_reconfig/main.c
@@ -34,9 +34,7 @@
* Test case description: Fuzz testing for WiredTiger reconfiguration.
*/
-void (*custom_die)(void) = NULL;
-
-static const char *list[] = {
+static const char * const list[] = {
",async=(enabled=0)",
",async=(enabled=1)",
",async=(ops_max=2048)",
@@ -256,7 +254,7 @@ main(int argc, char *argv[])
session, opts->uri, "type=lsm,key_format=S,value_format=S"));
/* Initialize the RNG. */
- testutil_check(__wt_random_init_seed(NULL, &rnd));
+ __wt_random_init_seed(NULL, &rnd);
/* Allocate memory for the config. */
len = WT_ELEMENTS(list) * 64;
diff --git a/src/third_party/wiredtiger/test/csuite/wt2834_join_bloom_fix/main.c b/src/third_party/wiredtiger/test/csuite/wt2834_join_bloom_fix/main.c
index 1e2d919d3c7..7c80496f1b6 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2834_join_bloom_fix/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2834_join_bloom_fix/main.c
@@ -39,8 +39,6 @@
*
* Failure mode: We get results back from our join.
*/
-void (*custom_die)(void) = NULL;
-
#define N_RECORDS 100000
#define N_INSERT 1000000
@@ -62,6 +60,8 @@ main(int argc, char *argv[])
char joinuri[256];
opts = &_opts;
+ if (testutil_disable_long_tests())
+ return (0);
memset(opts, 0, sizeof(*opts));
testutil_check(testutil_parse_opts(argc, argv, opts));
@@ -101,8 +101,8 @@ main(int argc, char *argv[])
&maincur));
maincur->set_key(maincur, N_RECORDS);
maincur->set_value(maincur, 54321, 0, "", 0, N_RECORDS);
- maincur->insert(maincur);
- maincur->close(maincur);
+ testutil_check(maincur->insert(maincur));
+ testutil_check(maincur->close(maincur));
testutil_check(session->close(session, NULL));
populate(opts);
@@ -151,6 +151,7 @@ main(int argc, char *argv[])
key, key2, post, balance, flag);
count++;
}
+ testutil_assert(ret == WT_NOTFOUND);
testutil_assert(count == 0);
testutil_cleanup(opts);
@@ -159,7 +160,8 @@ main(int argc, char *argv[])
return (0);
}
-void populate(TEST_OPTS *opts)
+void
+populate(TEST_OPTS *opts)
{
WT_CURSOR *maincur;
WT_SESSION *session;
@@ -167,7 +169,7 @@ void populate(TEST_OPTS *opts)
int balance, i, flag, post;
WT_RAND_STATE rnd;
- testutil_check(__wt_random_init_seed(NULL, &rnd));
+ __wt_random_init_seed(NULL, &rnd);
testutil_check(opts->conn->open_session(
opts->conn, NULL, NULL, &session));
@@ -194,6 +196,6 @@ void populate(TEST_OPTS *opts)
testutil_check(maincur->insert(maincur));
testutil_check(session->commit_transaction(session, NULL));
}
- maincur->close(maincur);
- session->close(session, NULL);
+ testutil_check(maincur->close(maincur));
+ testutil_check(session->close(session, NULL));
}
diff --git a/src/third_party/wiredtiger/test/csuite/wt2853_perf/main.c b/src/third_party/wiredtiger/test/csuite/wt2853_perf/main.c
index 67ba4a20ada..6cec9634cd1 100644
--- a/src/third_party/wiredtiger/test/csuite/wt2853_perf/main.c
+++ b/src/third_party/wiredtiger/test/csuite/wt2853_perf/main.c
@@ -42,8 +42,6 @@
* continues until the test ends (~30 seconds).
*/
-void (*custom_die)(void) = NULL;
-
static void *thread_insert(void *);
static void *thread_get(void *);
@@ -201,7 +199,7 @@ thread_insert(void *arg)
threadargs = (THREAD_ARGS *)arg;
opts = threadargs->testopts;
- testutil_check(__wt_random_init_seed(NULL, &rnd));
+ __wt_random_init_seed(NULL, &rnd);
(void)time(&prevtime);
testutil_check(opts->conn->open_session(
diff --git a/src/third_party/wiredtiger/test/csuite/wt2999_join_extractor/main.c b/src/third_party/wiredtiger/test/csuite/wt2999_join_extractor/main.c
new file mode 100644
index 00000000000..646a7077af1
--- /dev/null
+++ b/src/third_party/wiredtiger/test/csuite/wt2999_join_extractor/main.c
@@ -0,0 +1,168 @@
+/*-
+ * Public Domain 2014-2016 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.
+ */
+#include "test_util.h"
+
+/*
+ * JIRA ticket reference: WT-2999
+ *
+ * Test case description: Create a table that stores ~4K size blobs;
+ * two indices are defined using a pair of custom extractors
+ * that pull the first and second 32-bit integers from the blob.
+ * A simple join is created using the two indices, and iterated.
+ *
+ * Failure mode: When a custom extractor is used with cursor
+ * joins, there are memory leaks at the point where the extractor
+ * sets the key.
+ */
+static int
+custom_extract1(WT_EXTRACTOR *extractor, WT_SESSION *session,
+ const WT_ITEM *key, const WT_ITEM *value, WT_CURSOR *result_cursor)
+{
+ WT_ITEM item;
+ int32_t v1;
+
+ (void)extractor;
+ (void)key;
+ testutil_check(wiredtiger_struct_unpack(
+ session, value->data, value->size, "u", &item));
+
+ v1 = ((int*)item.data)[0];
+ item.data = &v1;
+ item.size = sizeof(v1);
+
+ result_cursor->set_key(result_cursor, &item);
+ return (result_cursor->insert(result_cursor));
+}
+
+static int
+custom_extract2(WT_EXTRACTOR *extractor, WT_SESSION *session,
+ const WT_ITEM *key, const WT_ITEM *value, WT_CURSOR *result_cursor)
+{
+ WT_ITEM item;
+ int32_t v2;
+
+ (void)extractor;
+ (void)key;
+ testutil_check(wiredtiger_struct_unpack(
+ session, value->data, value->size, "u", &item));
+
+ v2 = ((int*)item.data)[1];
+ item.data = &v2;
+ item.size = sizeof(v2);
+
+ result_cursor->set_key(result_cursor, &item);
+ return (result_cursor->insert(result_cursor));
+}
+
+static WT_EXTRACTOR custom_extractor1 = { custom_extract1, NULL, NULL };
+static WT_EXTRACTOR custom_extractor2 = { custom_extract2, NULL, NULL };
+
+int
+main(int argc, char *argv[])
+{
+ TEST_OPTS *opts, _opts;
+ WT_CONNECTION *conn;
+ WT_CURSOR *cursor1, *cursor2, *jcursor;
+ WT_ITEM k, v;
+ WT_SESSION *session;
+ int i, ret;
+ int32_t key, val[2];
+
+ opts = &_opts;
+ memset(opts, 0, sizeof(*opts));
+ testutil_check(testutil_parse_opts(argc, argv, opts));
+ testutil_make_work_dir(opts->home);
+
+ testutil_check(wiredtiger_open(opts->home, NULL, "create", &conn));
+ opts->conn = conn;
+ testutil_check(conn->open_session(conn, NULL, NULL, &session));
+
+ testutil_check(conn->add_extractor(conn, "custom_extractor1",
+ &custom_extractor1, NULL));
+ testutil_check(conn->add_extractor(conn, "custom_extractor2",
+ &custom_extractor2, NULL));
+
+ testutil_check(session->create(session,
+ "table:main", "key_format=u,value_format=u,columns=(k,v)"));
+ testutil_check(session->create(session,
+ "index:main:index1", "key_format=u,extractor=custom_extractor1"));
+ testutil_check(session->create(session,
+ "index:main:index2", "key_format=u,extractor=custom_extractor2"));
+
+ testutil_check(session->open_cursor(session, "table:main", NULL, NULL,
+ &cursor1));
+
+ v.data = val;
+ v.size = sizeof(val);
+ k.data = &key;
+ k.size = sizeof(key);
+
+ key = 10;
+ val[0] = 20;
+ val[1] = 30;
+ for (i = 0; i < 100000; ++i) {
+ key += i;
+ val[0] += i; val[1] += i;
+ cursor1->set_key(cursor1, &k);
+ cursor1->set_value(cursor1, &v);
+ testutil_check(cursor1->insert(cursor1));
+ }
+
+ testutil_check(cursor1->close(cursor1));
+
+ testutil_check(session->open_cursor(session, "index:main:index1", NULL,
+ NULL, &cursor1));
+ key = 20;
+ cursor1->set_key(cursor1, &k);
+ testutil_check(cursor1->search(cursor1));
+
+ testutil_check(session->open_cursor(session, "index:main:index2", NULL,
+ NULL, &cursor2));
+ key = 30;
+ cursor2->set_key(cursor2, &k);
+ testutil_check(cursor2->search(cursor2));
+
+ testutil_check(session->open_cursor(session, "join:table:main", NULL,
+ NULL, &jcursor));
+ testutil_check(session->join(session, jcursor, cursor1, "compare=gt"));
+ testutil_check(session->join(session, jcursor, cursor2, "compare=gt"));
+
+ while ((ret = jcursor->next(jcursor)) == 0) //leak
+ ;
+ testutil_assert(ret == WT_NOTFOUND);
+
+ testutil_check(jcursor->close(jcursor));
+ testutil_check(cursor1->close(cursor1));
+ testutil_check(cursor2->close(cursor2));
+
+ testutil_check(opts->conn->close(opts->conn, NULL));
+ opts->conn = NULL;
+ testutil_cleanup(opts);
+
+ return (EXIT_SUCCESS);
+}
diff --git a/src/third_party/wiredtiger/test/cursor_order/cursor_order.c b/src/third_party/wiredtiger/test/cursor_order/cursor_order.c
index aa351e6fea8..85b8c68e545 100644
--- a/src/third_party/wiredtiger/test/cursor_order/cursor_order.c
+++ b/src/third_party/wiredtiger/test/cursor_order/cursor_order.c
@@ -44,8 +44,6 @@ static void wt_shutdown(SHARED_CONFIG *);
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/fops/t.c b/src/third_party/wiredtiger/test/fops/t.c
index bf0588d5a53..7b4a7cf8fca 100644
--- a/src/third_party/wiredtiger/test/fops/t.c
+++ b/src/third_party/wiredtiger/test/fops/t.c
@@ -51,8 +51,6 @@ static void wt_shutdown(void);
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/format/config.c b/src/third_party/wiredtiger/test/format/config.c
index 542adf33da2..839ff5058de 100644
--- a/src/third_party/wiredtiger/test/format/config.c
+++ b/src/third_party/wiredtiger/test/format/config.c
@@ -187,8 +187,17 @@ config_setup(void)
/* Give in-memory configuration a final review. */
config_in_memory_check();
- /* Make the default maximum-run length 20 minutes. */
- if (!config_is_perm("timer"))
+ /*
+ * Run-length configured by a number of operations and a timer. If the
+ * operation count and the timer are both set by a configuration, there
+ * isn't anything to do. If only the operation count was configured,
+ * set a default maximum-run of 20 minutes. If only the timer is set,
+ * clear the operations count (which was set randomly).
+ */
+ if (config_is_perm("timer")) {
+ if (!config_is_perm("ops"))
+ config_single("ops=0", 0);
+ } else
config_single("timer=20", 0);
/*
@@ -270,28 +279,33 @@ config_compression(const char *conf_name)
*/
switch (mmrand(NULL, 1, 20)) {
#ifdef HAVE_BUILTIN_EXTENSION_LZ4
- case 1: case 2: case 3: case 4: /* 20% lz4 */
+ case 1: case 2: /* 10% lz4 */
cstr = "lz4";
break;
- case 5: /* 5% lz4-no-raw */
+ case 3: /* 5% lz4-no-raw */
cstr = "lz4-noraw";
break;
#endif
#ifdef HAVE_BUILTIN_EXTENSION_SNAPPY
- case 6: case 7: case 8: case 9: /* 30% snappy */
- case 10: case 11:
+ case 4: case 5: case 6: case 7: /* 30% snappy */
+ case 8: case 9:
cstr = "snappy";
break;
#endif
#ifdef HAVE_BUILTIN_EXTENSION_ZLIB
- case 12: case 13: case 14: case 15: /* 20% zlib */
+ case 10: case 11: case 12: case 13: /* 20% zlib */
cstr = "zlib";
break;
- case 16: /* 5% zlib-no-raw */
+ case 14: /* 5% zlib-no-raw */
cstr = "zlib-noraw";
break;
#endif
- case 17: case 18: case 19: case 20: /* 20% no compression */
+#ifdef HAVE_BUILTIN_EXTENSION_ZSTD
+ case 15: case 16 case 17: /* 15% zstd */
+ cstr = "zstd";
+ break;
+#endif
+ case 18: case 19: case 20: /* 15% no compression */
default:
break;
}
@@ -748,6 +762,8 @@ config_map_compression(const char *s, u_int *vp)
*vp = COMPRESS_ZLIB;
else if (strcmp(s, "zlib-noraw") == 0)
*vp = COMPRESS_ZLIB_NO_RAW;
+ else if (strcmp(s, "zstd") == 0)
+ *vp = COMPRESS_ZSTD;
else
testutil_die(EINVAL,
"illegal compression configuration: %s", s);
diff --git a/src/third_party/wiredtiger/test/format/config.h b/src/third_party/wiredtiger/test/format/config.h
index 725bc7c5d97..9bfba3cd0df 100644
--- a/src/third_party/wiredtiger/test/format/config.h
+++ b/src/third_party/wiredtiger/test/format/config.h
@@ -58,7 +58,7 @@ typedef struct {
} CONFIG;
#define COMPRESSION_LIST \
- "(none | lz4 | lz4-noraw | snappy | zlib | zlib-noraw)"
+ "(none | lz4 | lz4-noraw | snappy | zlib | zlib-noraw | zstd)"
static CONFIG c[] = {
{ "abort",
diff --git a/src/third_party/wiredtiger/test/format/format.h b/src/third_party/wiredtiger/test/format/format.h
index 363dcf9eea8..820bc020c9b 100644
--- a/src/third_party/wiredtiger/test/format/format.h
+++ b/src/third_party/wiredtiger/test/format/format.h
@@ -48,6 +48,8 @@
EXTPATH "compressors/snappy/.libs/libwiredtiger_snappy.so"
#define ZLIB_PATH \
EXTPATH "compressors/zlib/.libs/libwiredtiger_zlib.so"
+#define ZSTD_PATH \
+ EXTPATH "compressors/zstd/.libs/libwiredtiger_zstd.so"
#define REVERSE_PATH \
EXTPATH "collators/reverse/.libs/libwiredtiger_reverse_collator.so"
@@ -219,6 +221,7 @@ typedef struct {
#define COMPRESS_SNAPPY 5
#define COMPRESS_ZLIB 6
#define COMPRESS_ZLIB_NO_RAW 7
+#define COMPRESS_ZSTD 8
u_int c_compression_flag; /* Compression flag value */
u_int c_logging_compression_flag; /* Log compression flag value */
diff --git a/src/third_party/wiredtiger/test/format/lrt.c b/src/third_party/wiredtiger/test/format/lrt.c
index 937525522fa..69d6b22d71f 100644
--- a/src/third_party/wiredtiger/test/format/lrt.c
+++ b/src/third_party/wiredtiger/test/format/lrt.c
@@ -96,6 +96,22 @@ lrt(void *arg)
pinned = 0;
} else {
/*
+ * Test named snapshots: create a snapshot, wait to
+ * give the transaction state time to move forward,
+ * then start a transaction with the named snapshot,
+ * drop it, then commit the transaction. This exercises
+ * most of the named snapshot logic under load.
+ */
+ testutil_check(session->snapshot(session, "name=test"));
+ sleep(1);
+ testutil_check(session->begin_transaction(
+ session, "snapshot=test"));
+ testutil_check(session->snapshot(
+ session, "drop=(all)"));
+ testutil_check(session->commit_transaction(
+ session, NULL));
+
+ /*
* Begin transaction: without an explicit transaction,
* the snapshot is only kept around while a cursor is
* positioned. As soon as the cursor loses its position
diff --git a/src/third_party/wiredtiger/test/format/t.c b/src/third_party/wiredtiger/test/format/t.c
index 12258af8e51..7701595776c 100644
--- a/src/third_party/wiredtiger/test/format/t.c
+++ b/src/third_party/wiredtiger/test/format/t.c
@@ -38,8 +38,6 @@ static void usage(void)
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = format_die; /* Local death handler. */
-
int
main(int argc, char *argv[])
{
@@ -47,6 +45,8 @@ main(int argc, char *argv[])
int ch, onerun, reps;
const char *config, *home;
+ custom_die = format_die; /* Local death handler. */
+
config = NULL;
#ifdef _WIN32
@@ -115,7 +115,7 @@ main(int argc, char *argv[])
argv += __wt_optind;
/* Initialize the global RNG. */
- testutil_check(__wt_random_init_seed(NULL, &g.rnd));
+ __wt_random_init_seed(NULL, &g.rnd);
/* Set up paths. */
path_setup(home);
diff --git a/src/third_party/wiredtiger/test/format/util.c b/src/third_party/wiredtiger/test/format/util.c
index 667f6d6bcb1..a709aa93a2e 100644
--- a/src/third_party/wiredtiger/test/format/util.c
+++ b/src/third_party/wiredtiger/test/format/util.c
@@ -78,7 +78,7 @@ key_gen_setup(WT_ITEM *key)
}
static void
-key_gen_common(WT_ITEM *key, uint64_t keyno, int suffix)
+key_gen_common(WT_ITEM *key, uint64_t keyno, const char * const suffix)
{
int len;
char *p;
@@ -86,11 +86,15 @@ key_gen_common(WT_ITEM *key, uint64_t keyno, int suffix)
p = key->mem;
/*
- * The key always starts with a 10-digit string (the specified cnt)
+ * The key always starts with a 10-digit string (the specified row)
* followed by two digits, a random number between 1 and 15 if it's
* an insert, otherwise 00.
*/
- len = sprintf(p, "%010" PRIu64 ".%02d", keyno, suffix);
+ u64_to_string_zf(keyno, key->mem, 11);
+ p[10] = '.';
+ p[11] = suffix[0];
+ p[12] = suffix[1];
+ len = 13;
/*
* In a column-store, the key is only used for Berkeley DB inserts,
@@ -118,13 +122,19 @@ key_gen_common(WT_ITEM *key, uint64_t keyno, int suffix)
void
key_gen(WT_ITEM *key, uint64_t keyno)
{
- key_gen_common(key, keyno, 0);
+ key_gen_common(key, keyno, "00");
}
void
key_gen_insert(WT_RAND_STATE *rnd, WT_ITEM *key, uint64_t keyno)
{
- key_gen_common(key, keyno, (int)mmrand(rnd, 1, 15));
+ static const char * const suffix[15] = {
+ "01", "02", "03", "04", "05",
+ "06", "07", "08", "09", "10",
+ "11", "12", "13", "14", "15"
+ };
+
+ key_gen_common(key, keyno, suffix[mmrand(rnd, 1, 15) - 1]);
}
static uint32_t val_dup_data_len; /* Length of duplicate data items */
@@ -221,7 +231,7 @@ val_gen(WT_RAND_STATE *rnd, WT_ITEM *value, uint64_t keyno)
p[10] = '/';
value->size = val_dup_data_len;
} else {
- (void)sprintf(p, "%010" PRIu64, keyno);
+ u64_to_string_zf(keyno, p, 11);
p[10] = '/';
value->size =
value_len(rnd, keyno, g.c_value_min, g.c_value_max);
diff --git a/src/third_party/wiredtiger/test/format/wts.c b/src/third_party/wiredtiger/test/format/wts.c
index 74c4bb902b3..da234ce53c7 100644
--- a/src/third_party/wiredtiger/test/format/wts.c
+++ b/src/third_party/wiredtiger/test/format/wts.c
@@ -50,6 +50,8 @@ compressor(uint32_t compress_flag)
return ("zlib");
case COMPRESS_ZLIB_NO_RAW:
return ("zlib-noraw");
+ case COMPRESS_ZSTD:
+ return ("zstd");
default:
break;
}
@@ -155,8 +157,7 @@ wts_open(const char *home, bool set_api, WT_CONNECTION **connp)
g.c_lsm_worker_threads);
if (DATASOURCE("lsm") || g.c_cache < 20) {
- p += snprintf(p, REMAIN(p, end),
- ",eviction_dirty_target=80,eviction_dirty_trigger=95");
+ p += snprintf(p, REMAIN(p, end), ",eviction_dirty_trigger=95");
}
/* Eviction worker configuration. */
@@ -210,13 +211,14 @@ wts_open(const char *home, bool set_api, WT_CONNECTION **connp)
/* Extensions. */
p += snprintf(p, REMAIN(p, end),
",extensions=["
- "\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"],",
+ "\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"],",
g.c_reverse ? REVERSE_PATH : "",
access(LZ4_PATH, R_OK) == 0 ? LZ4_PATH : "",
access(LZO_PATH, R_OK) == 0 ? LZO_PATH : "",
access(ROTN_PATH, R_OK) == 0 ? ROTN_PATH : "",
access(SNAPPY_PATH, R_OK) == 0 ? SNAPPY_PATH : "",
access(ZLIB_PATH, R_OK) == 0 ? ZLIB_PATH : "",
+ access(ZSTD_PATH, R_OK) == 0 ? ZSTD_PATH : "",
DATASOURCE("kvsbdb") ? KVS_BDB_PATH : "");
/*
@@ -546,6 +548,7 @@ wts_stats(void)
WT_DECL_RET;
WT_SESSION *session;
FILE *fp;
+ size_t len;
char *stat_name;
const char *pval, *desc;
uint64_t v;
@@ -582,8 +585,9 @@ wts_stats(void)
/* Data source statistics. */
fprintf(fp, "\n\n====== Data source statistics:\n");
- stat_name = dmalloc(strlen("statistics:") + strlen(g.uri) + 1);
- sprintf(stat_name, "statistics:%s", g.uri);
+ len = strlen("statistics:") + strlen(g.uri) + 1;
+ stat_name = dmalloc(len);
+ snprintf(stat_name, len, "statistics:%s", g.uri);
testutil_check(session->open_cursor(
session, stat_name, NULL, NULL, &cursor));
free(stat_name);
diff --git a/src/third_party/wiredtiger/test/huge/huge.c b/src/third_party/wiredtiger/test/huge/huge.c
index 3aa61a9048e..17e2db353d5 100644
--- a/src/third_party/wiredtiger/test/huge/huge.c
+++ b/src/third_party/wiredtiger/test/huge/huge.c
@@ -159,8 +159,6 @@ run(CONFIG *cp, int bigkey, size_t bytes)
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/java/com/wiredtiger/test/ConcurrentCloseTest.java b/src/third_party/wiredtiger/test/java/com/wiredtiger/test/ConcurrentCloseTest.java
index fece0353bf0..fead0b0bf38 100644
--- a/src/third_party/wiredtiger/test/java/com/wiredtiger/test/ConcurrentCloseTest.java
+++ b/src/third_party/wiredtiger/test/java/com/wiredtiger/test/ConcurrentCloseTest.java
@@ -34,6 +34,7 @@ import com.wiredtiger.db.WiredTigerException;
import com.wiredtiger.db.wiredtiger;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.File;
@@ -69,7 +70,7 @@ class InsertThread extends Thread {
Session session = conn.open_session(null);
Cursor cursor = session.open_cursor("table:cclose", null,
"overwrite");
- cursor.putKeyString("key"+threadId + "-" + i);
+ cursor.putKeyString("key" + threadId + "-" + i);
cursor.putValueString("value1");
ret = cursor.insert();
cursor.close();
@@ -127,36 +128,36 @@ public class ConcurrentCloseTest {
setup();
try {
List<Thread> threads = new ArrayList<Thread>();
- int i, ret;
+ int i;
- ret = session.create("table:cclose", "key_format=S,value_format=S");
+ assertEquals(0, session.create("table:cclose",
+ "key_format=S,value_format=S"));
Cursor cursor = session.open_cursor("table:cclose", null,
"overwrite");
cursor.putKeyString("key1");
cursor.putValueString("value1");
- ret = cursor.insert();
+ assertEquals(0, cursor.insert());
cursor.close();
- ret = session.close(null);
+ assertEquals(0, session.close(null));
for (i = 0; i < NUM_THREADS; i++) {
Thread insertThread = new InsertThread(conn, i);
- Thread scanThread = new InsertThread(conn, i);
+ Thread scanThread = new ScanThread(conn);
insertThread.start();
scanThread.start();
threads.add(insertThread);
threads.add(scanThread);
}
-
for (Thread thread : threads)
try {
thread.join();
- ret = -1;
}
catch (InterruptedException ie) {
+ fail();
}
- ret = conn.close(null);
- System.exit(ret);
+ assertEquals(0, conn.close(null));
+ System.exit(0);
}
catch (WiredTigerException wte) {
System.err.println("Exception: " + wte);
diff --git a/src/third_party/wiredtiger/test/manydbs/manydbs.c b/src/third_party/wiredtiger/test/manydbs/manydbs.c
index c5c9a9a7ccd..7e986d47af3 100644
--- a/src/third_party/wiredtiger/test/manydbs/manydbs.c
+++ b/src/third_party/wiredtiger/test/manydbs/manydbs.c
@@ -68,8 +68,6 @@ usage(void)
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
static WT_CONNECTION **connections = NULL;
static WT_CURSOR **cursors = NULL;
static WT_RAND_STATE rnd;
diff --git a/src/third_party/wiredtiger/test/packing/intpack-test.c b/src/third_party/wiredtiger/test/packing/intpack-test.c
index 76851b38e35..c84823b741b 100644
--- a/src/third_party/wiredtiger/test/packing/intpack-test.c
+++ b/src/third_party/wiredtiger/test/packing/intpack-test.c
@@ -28,8 +28,6 @@
#include "test_util.h"
-void (*custom_die)(void) = NULL;
-
int
main(void)
{
diff --git a/src/third_party/wiredtiger/test/packing/intpack-test2.c b/src/third_party/wiredtiger/test/packing/intpack-test2.c
index a7d31329069..4e612808a35 100644
--- a/src/third_party/wiredtiger/test/packing/intpack-test2.c
+++ b/src/third_party/wiredtiger/test/packing/intpack-test2.c
@@ -28,8 +28,6 @@
#include "test_util.h"
-void (*custom_die)(void) = NULL;
-
int
main(void)
{
diff --git a/src/third_party/wiredtiger/test/packing/intpack-test3.c b/src/third_party/wiredtiger/test/packing/intpack-test3.c
index aac0178578f..763b0255ecf 100644
--- a/src/third_party/wiredtiger/test/packing/intpack-test3.c
+++ b/src/third_party/wiredtiger/test/packing/intpack-test3.c
@@ -28,8 +28,6 @@
#include "test_util.h"
-void (*custom_die)(void) = NULL;
-
void test_value(int64_t);
void test_spread(int64_t, int64_t, int64_t);
diff --git a/src/third_party/wiredtiger/test/packing/packing-test.c b/src/third_party/wiredtiger/test/packing/packing-test.c
index f251c17eb67..919b0622806 100644
--- a/src/third_party/wiredtiger/test/packing/packing-test.c
+++ b/src/third_party/wiredtiger/test/packing/packing-test.c
@@ -28,8 +28,6 @@
#include "test_util.h"
-void (*custom_die)(void) = NULL;
-
static void
check(const char *fmt, ...)
{
diff --git a/src/third_party/wiredtiger/test/readonly/readonly.c b/src/third_party/wiredtiger/test/readonly/readonly.c
index 7a131912c31..a4b79f5859f 100644
--- a/src/third_party/wiredtiger/test/readonly/readonly.c
+++ b/src/third_party/wiredtiger/test/readonly/readonly.c
@@ -158,8 +158,6 @@ open_dbs(int op, const char *dir,
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/recovery/random-abort.c b/src/third_party/wiredtiger/test/recovery/random-abort.c
index 16065cec29e..21460646017 100644
--- a/src/third_party/wiredtiger/test/recovery/random-abort.c
+++ b/src/third_party/wiredtiger/test/recovery/random-abort.c
@@ -34,6 +34,7 @@
static char home[512]; /* Program working dir */
static const char *progname; /* Program name */
static const char * const uri = "table:main";
+bool inmem;
#define MAX_TH 12
#define MIN_TH 5
@@ -41,7 +42,9 @@ static const char * const uri = "table:main";
#define MIN_TIME 10
#define RECORDS_FILE "records-%" PRIu32
-#define ENV_CONFIG \
+#define ENV_CONFIG_DEF \
+ "create,log=(file_max=10M,archive=false,enabled)"
+#define ENV_CONFIG_TXNSYNC \
"create,log=(file_max=10M,archive=false,enabled)," \
"transaction_sync=(enabled,method=none)"
#define ENV_CONFIG_REC "log=(recover=on)"
@@ -73,11 +76,15 @@ thread_run(void *arg)
WT_THREAD_DATA *td;
uint64_t i;
int ret;
- char buf[MAX_VAL], kname[64];
+ size_t lsize;
+ char buf[MAX_VAL], kname[64], lgbuf[8];
+ char large[128*1024];
__wt_random_init(&rnd);
memset(buf, 0, sizeof(buf));
memset(kname, 0, sizeof(kname));
+ lsize = sizeof(large);
+ memset(large, 0, lsize);
td = (WT_THREAD_DATA *)arg;
/*
@@ -85,6 +92,13 @@ thread_run(void *arg)
*/
snprintf(buf, sizeof(buf), RECORDS_FILE, td->id);
/*
+ * Set up a large value putting our id in it. Write it in there a
+ * bunch of times, but the rest of the buffer can just be zero.
+ */
+ snprintf(lgbuf, sizeof(lgbuf), "th-%" PRIu32, td->id);
+ for (i = 0; i < 128; i += strlen(lgbuf))
+ snprintf(&large[i], lsize - i, "%s", lgbuf);
+ /*
* Keep a separate file with the records we wrote for checking.
*/
(void)unlink(buf);
@@ -107,8 +121,18 @@ thread_run(void *arg)
*/
for (i = td->start; ; ++i) {
snprintf(kname, sizeof(kname), "%" PRIu64, i);
- data.size = __wt_random(&rnd) % MAX_VAL;
cursor->set_key(cursor, kname);
+ /*
+ * Every 30th record write a very large record that exceeds the
+ * log buffer size. This forces us to use the unbuffered path.
+ */
+ if (i % 30 == 0) {
+ data.size = 128 * 1024;
+ data.data = large;
+ } else {
+ data.size = __wt_random(&rnd) % MAX_VAL;
+ data.data = buf;
+ }
cursor->set_value(cursor, &data);
if ((ret = cursor->insert(cursor)) != 0)
testutil_die(ret, "WT_CURSOR.insert");
@@ -136,12 +160,17 @@ fill_db(uint32_t nth)
WT_THREAD_DATA *td;
uint32_t i;
int ret;
+ const char *envconf;
thr = dcalloc(nth, sizeof(pthread_t));
td = dcalloc(nth, sizeof(WT_THREAD_DATA));
if (chdir(home) != 0)
testutil_die(errno, "Child chdir: %s", home);
- if ((ret = wiredtiger_open(NULL, NULL, ENV_CONFIG, &conn)) != 0)
+ if (inmem)
+ envconf = ENV_CONFIG_DEF;
+ else
+ envconf = ENV_CONFIG_TXNSYNC;
+ if ((ret = wiredtiger_open(NULL, NULL, envconf, &conn)) != 0)
testutil_die(ret, "wiredtiger_open");
if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
testutil_die(ret, "WT_CONNECTION:open_session");
@@ -179,8 +208,6 @@ fill_db(uint32_t nth)
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
@@ -189,11 +216,11 @@ main(int argc, char *argv[])
WT_CURSOR *cursor;
WT_SESSION *session;
WT_RAND_STATE rnd;
- uint64_t key, last_key;
- uint32_t absent, count, i, nth, timeout;
+ uint64_t absent, count, key, last_key, middle;
+ uint32_t i, nth, timeout;
int ch, status, ret;
pid_t pid;
- bool rand_th, rand_time, verify_only;
+ bool fatal, rand_th, rand_time, verify_only;
const char *working_dir;
char fname[64], kname[64];
@@ -202,17 +229,21 @@ main(int argc, char *argv[])
else
++progname;
+ inmem = false;
nth = MIN_TH;
rand_th = rand_time = true;
timeout = MIN_TIME;
verify_only = false;
working_dir = "WT_TEST.random-abort";
- while ((ch = __wt_getopt(progname, argc, argv, "h:T:t:v")) != EOF)
+ while ((ch = __wt_getopt(progname, argc, argv, "h:mT:t:v")) != EOF)
switch (ch) {
case 'h':
working_dir = __wt_optarg;
break;
+ case 'm':
+ inmem = true;
+ break;
case 'T':
rand_th = false;
nth = (uint32_t)atoi(__wt_optarg);
@@ -245,7 +276,7 @@ main(int argc, char *argv[])
if (!verify_only) {
testutil_make_work_dir(home);
- testutil_assert(__wt_random_init_seed(NULL, &rnd) == 0);
+ __wt_random_init_seed(NULL, &rnd);
if (rand_time) {
timeout = __wt_random(&rnd) % MAX_TIME;
if (timeout < MIN_TIME)
@@ -305,7 +336,9 @@ main(int argc, char *argv[])
testutil_die(ret, "WT_SESSION.open_cursor: %s", uri);
absent = count = 0;
+ fatal = false;
for (i = 0; i < nth; ++i) {
+ middle = 0;
snprintf(fname, sizeof(fname), RECORDS_FILE, i);
if ((fp = fopen(fname, "r")) == NULL) {
fprintf(stderr,
@@ -315,8 +348,10 @@ main(int argc, char *argv[])
/*
* For every key in the saved file, verify that the key exists
- * in the table after recovery. Since we did write-no-sync, we
- * expect every key to have been recovered.
+ * in the table after recovery. If we're doing in-memory
+ * log buffering we never expect a record missing in the middle,
+ * but records may be missing at the end. If we did
+ * write-no-sync, we expect every key to have been recovered.
*/
for (last_key = UINT64_MAX;; ++count, last_key = key) {
ret = fscanf(fp, "%" SCNu64 "\n", &key);
@@ -340,9 +375,20 @@ main(int argc, char *argv[])
if ((ret = cursor->search(cursor)) != 0) {
if (ret != WT_NOTFOUND)
testutil_die(ret, "search");
- printf("%s: no record with key %" PRIu64 "\n",
- fname, key);
- ++absent;
+ if (!inmem)
+ printf("%s: no record with key %"
+ PRIu64 "\n", fname, key);
+ absent++;
+ middle = key;
+ } else if (middle != 0) {
+ /*
+ * We should never find an existing key after
+ * we have detected one missing.
+ */
+ printf("%s: after absent record at %" PRIu64
+ " key %" PRIu64 " exists\n",
+ fname, middle, key);
+ fatal = true;
}
}
if (fclose(fp) != 0)
@@ -350,11 +396,13 @@ main(int argc, char *argv[])
}
if ((ret = conn->close(conn, NULL)) != 0)
testutil_die(ret, "WT_CONNECTION:close");
- if (absent) {
- printf("%" PRIu32 " record(s) absent from %" PRIu32 "\n",
+ if (fatal)
+ return (EXIT_FAILURE);
+ if (!inmem && absent) {
+ printf("%" PRIu64 " record(s) absent from %" PRIu64 "\n",
absent, count);
return (EXIT_FAILURE);
}
- printf("%" PRIu32 " records verified\n", count);
+ printf("%" PRIu64 " records verified\n", count);
return (EXIT_SUCCESS);
}
diff --git a/src/third_party/wiredtiger/test/recovery/smoke.sh b/src/third_party/wiredtiger/test/recovery/smoke.sh
index c7677b64503..ce0662d3b2b 100755
--- a/src/third_party/wiredtiger/test/recovery/smoke.sh
+++ b/src/third_party/wiredtiger/test/recovery/smoke.sh
@@ -5,4 +5,5 @@ set -e
# Smoke-test recovery as part of running "make check".
$TEST_WRAPPER ./random-abort -t 10 -T 5
+$TEST_WRAPPER ./random-abort -m -t 10 -T 5
$TEST_WRAPPER ./truncated-log
diff --git a/src/third_party/wiredtiger/test/recovery/truncated-log.c b/src/third_party/wiredtiger/test/recovery/truncated-log.c
index c0effa85e95..c265263d44c 100644
--- a/src/third_party/wiredtiger/test/recovery/truncated-log.c
+++ b/src/third_party/wiredtiger/test/recovery/truncated-log.c
@@ -258,8 +258,6 @@ fill_db(void)
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/salvage/salvage.c b/src/third_party/wiredtiger/test/salvage/salvage.c
index bad0167ca8e..b8553bbd72d 100644
--- a/src/third_party/wiredtiger/test/salvage/salvage.c
+++ b/src/third_party/wiredtiger/test/salvage/salvage.c
@@ -64,8 +64,6 @@ static int verbose; /* -v flag */
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/suite/helper.py b/src/third_party/wiredtiger/test/suite/helper.py
index 9f34b566b3c..d1f41f05e8b 100644
--- a/src/third_party/wiredtiger/test/suite/helper.py
+++ b/src/third_party/wiredtiger/test/suite/helper.py
@@ -30,6 +30,8 @@
import glob, os, shutil, string, subprocess
import wiredtiger
+from wtdataset import SimpleDataSet, SimpleIndexDataSet, ComplexDataSet
+
# python has a filecmp.cmp function, but different versions of python approach
# file comparison differently. To make sure we get byte for byte comparison,
# we define it here.
@@ -121,188 +123,3 @@ def copy_wiredtiger_home(olddir, newdir, aligned=True):
cmd_list = ['dd', inpf, outf, 'bs=300']
a = subprocess.Popen(cmd_list)
a.wait()
-
-# create a simple_populate or complex_populate key
-def key_populate(cursor, i):
- key_format = cursor.key_format
- if key_format == 'i' or key_format == 'r' or key_format == 'u':
- return i
- elif key_format == 'S':
- return str('%015d' % i)
- else:
- raise AssertionError(
- 'key_populate: object has unexpected format: ' + key_format)
-
-# create a simple_populate value
-def value_populate(cursor, i):
- value_format = cursor.value_format
- if value_format == 'i' or value_format == 'r' or value_format == 'u':
- return i
- elif value_format == 'S':
- return str(i) + ': abcdefghijklmnopqrstuvwxyz'
- elif value_format == '8t':
- value = (
- 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xaa, 0xab,
- 0xac, 0xad, 0xae, 0xaf, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf)
- return value[i % len(value)]
- else:
- raise AssertionError(
- 'value_populate: object has unexpected format: ' + value_format)
-
-# population of a simple object
-# uri: object
-# config: prefix of the session.create configuration string (defaults
-# to string value formats)
-# rows: entries to insert
-def simple_populate(self, uri, config, rows):
- self.pr('simple_populate: ' + uri + ' with ' + str(rows) + ' rows')
- self.session.create(uri, 'value_format=S,' + config)
- cursor = self.session.open_cursor(uri, None)
- for i in range(1, rows + 1):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
- cursor.close()
-
-def simple_populate_check_cursor(self, cursor, rows):
- i = 0
- for key,val in cursor:
- i += 1
- self.assertEqual(key, key_populate(cursor, i))
- if cursor.value_format == '8t' and val == 0: # deleted
- continue
- self.assertEqual(val, value_populate(cursor, i))
- self.assertEqual(i, rows)
-
-def simple_populate_check(self, uri, rows):
- self.pr('simple_populate_check: ' + uri)
- cursor = self.session.open_cursor(uri, None)
- simple_populate_check_cursor(self, cursor, rows)
- cursor.close()
-
-# population of a simple object, with a single index
-# uri: object
-# config: prefix of the session.create configuration string (defaults
-# to string value formats)
-# rows: entries to insert
-def simple_index_populate(self, uri, config, rows):
- self.pr('simple_index_populate: ' + uri + ' with ' + str(rows) + ' rows')
- self.session.create(uri, 'value_format=S,columns=(key0,value0),' + config)
- indxname = 'index:' + uri.split(":")[1]
- self.session.create(indxname + ':index1', 'columns=(value0,key0)')
- cursor = self.session.open_cursor(uri, None)
- for i in range(1, rows + 1):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
- cursor.close()
-
-def simple_index_populate_check_cursor(self, cursor, rows):
- i = 0
- for key,val in cursor:
- i += 1
- self.assertEqual(key, key_populate(cursor, i))
- if cursor.value_format == '8t' and val == 0: # deleted
- continue
- self.assertEqual(val, value_populate(cursor, i))
- self.assertEqual(i, rows)
-
-def simple_index_populate_check(self, uri, rows):
- self.pr('simple_index_populate_check: ' + uri)
-
- # Check values in the main table.
- cursor = self.session.open_cursor(uri, None)
- simple_index_populate_check_cursor(self, cursor, rows)
-
- # Check values in the index.
- indxname = 'index:' + uri.split(":")[1]
- idxcursor = self.session.open_cursor(indxname + ':index1')
- for i in range(1, rows + 1):
- k = key_populate(cursor, i)
- v = value_populate(cursor, i)
- ik = (v,k) # The index key is columns=(v,k).
- self.assertEqual(v, idxcursor[ik])
- idxcursor.close()
- cursor.close()
-
-# Return the value stored in a complex object.
-def complex_value_populate(cursor, i):
- return [str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%26],
- i,
- str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%23],
- str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%18]]
-
-# Return the number of column groups used
-def complex_populate_colgroup_count():
- return 6
-
-# Return the number of indices used
-def complex_populate_index_count():
- return 6
-
-# population of a complex object
-# uri: object
-# config: prefix of the session.create configuration string
-# rows: entries to insert
-def complex_populate(self, uri, config, rows):
- complex_populate_type(self, uri, config, '', rows, '')
-def complex_populate_cgconfig(self, uri, config, rows):
- complex_populate_type(self, uri, config, config, rows, '')
-def complex_populate_lsm(self, uri, config, rows):
- complex_populate_type(self, uri, config, '', rows, 'type=lsm')
-def complex_populate_cgconfig_lsm(self, uri, config, rows):
- complex_populate_type(self, uri, config, config, rows, 'type=lsm')
-def complex_populate_type(self, uri, config, cgconfig, rows, type):
- self.session.create(uri,
- config + ',value_format=SiSS,' +
- 'columns=(record,column2,column3,column4,column5),' +
- 'colgroups=(cgroup1,cgroup2,cgroup3,cgroup4,cgroup5,cgroup6)')
-
- cgname = 'colgroup:' + uri.split(":")[1]
- cgcfg = ',' + cgconfig + ',' + type
- self.session.create(cgname + ':cgroup1', 'columns=(column2)' + ',' + cgcfg)
- self.session.create(cgname + ':cgroup2', 'columns=(column3)' + ',' + cgcfg)
- self.session.create(cgname + ':cgroup3', 'columns=(column4)' + ',' + cgcfg)
- self.session.create(
- cgname + ':cgroup4', 'columns=(column2,column3)' + ',' + cgcfg)
- self.session.create(
- cgname + ':cgroup5', 'columns=(column3,column4)' + ',' + cgcfg)
- self.session.create(
- cgname + ':cgroup6', 'columns=(column2,column4,column5)' + ',' + cgcfg)
- indxname = 'index:' + uri.split(":")[1]
- self.session.create(indxname + ':indx1', 'columns=(column2)' + ',' + cgcfg)
- self.session.create(indxname + ':indx2', 'columns=(column3)' + ',' + cgcfg)
- self.session.create(indxname + ':indx3', 'columns=(column4)' + ',' + cgcfg)
- self.session.create(
- indxname + ':indx4', 'columns=(column2,column4)' + ',' + cgcfg)
- cursor = self.session.open_cursor(uri, None)
- for i in range(1, rows + 1):
- cursor[key_populate(cursor, i)] = \
- tuple(complex_value_populate(cursor, i))
- cursor.close()
- # add some indices after populating
- self.session.create(
- indxname + ':indx5', 'columns=(column3,column5)' + ',' + cgcfg)
- self.session.create(
- indxname + ':indx6', 'columns=(column3,column5,column4)' + ',' + cgcfg)
-
-def complex_populate_colgroup_name(self, uri, i):
- return 'colgroup:' + uri.split(":")[1] + ':cgroup' + str(i + 1)
-
-def complex_populate_index_name(self, uri, i):
- return 'index:' + uri.split(":")[1] + ':indx' + str(i + 1)
-
-def complex_populate_check_cursor(self, cursor, rows):
- i = 0
- for key, s1, i2, s3, s4 in cursor:
- i += 1
- self.assertEqual(key, key_populate(cursor, i))
- v = complex_value_populate(cursor, i)
- self.assertEqual(s1, v[0])
- self.assertEqual(i2, v[1])
- self.assertEqual(s3, v[2])
- self.assertEqual(s4, v[3])
- self.assertEqual(i, rows)
-
-def complex_populate_check(self, uri, rows):
- self.pr('complex_populate_check: ' + uri)
- cursor = self.session.open_cursor(uri, None)
- complex_populate_check_cursor(self, cursor, rows)
- cursor.close()
diff --git a/src/third_party/wiredtiger/test/suite/test_async03.py b/src/third_party/wiredtiger/test/suite/test_async03.py
index 98851885f70..4859360924a 100644
--- a/src/third_party/wiredtiger/test/suite/test_async03.py
+++ b/src/third_party/wiredtiger/test/suite/test_async03.py
@@ -54,12 +54,11 @@ class test_async03(wttest.WiredTigerTestCase):
tablearg = 'table:' + self.table_name1
self.session.create(tablearg, 'key_format=S,value_format=S')
- # Populate table with async inserts, callback checks
- # to ensure key/value is correct.
callback = Callback()
- self.assertRaises(wiredtiger.WiredTigerError,
- lambda: self.conn.async_new_op(tablearg, None, callback))
+ msg = '/Asynchronous operations not configured/'
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda: self.conn.async_new_op(tablearg, None, callback), msg)
self.conn.async_flush()
diff --git a/src/third_party/wiredtiger/test/suite/test_backup01.py b/src/third_party/wiredtiger/test/suite/test_backup01.py
index 92d39514440..4e98b6d8e77 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup01.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup01.py
@@ -32,8 +32,8 @@ import shutil
import string
from suite_subprocess import suite_subprocess
import wiredtiger, wttest
-from helper import compare_files,\
- complex_populate, complex_populate_lsm, simple_populate
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
+from helper import compare_files
# test_backup.py
# Utilities: wt backup
@@ -43,14 +43,14 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess):
pfx = 'test_backup'
objs = [
- ( 'file:' + pfx + '.1', simple_populate, 0),
- ( 'file:' + pfx + '.2', simple_populate, 0),
- ('table:' + pfx + '.3', simple_populate, 0),
- ('table:' + pfx + '.4', simple_populate, 0),
- ('table:' + pfx + '.5', complex_populate, 0),
- ('table:' + pfx + '.6', complex_populate, 0),
- ('table:' + pfx + '.7', complex_populate_lsm, 1),
- ('table:' + pfx + '.8', complex_populate_lsm, 1),
+ ( 'file:' + pfx + '.1', SimpleDataSet, 0),
+ ( 'file:' + pfx + '.2', SimpleDataSet, 0),
+ ('table:' + pfx + '.3', SimpleDataSet, 0),
+ ('table:' + pfx + '.4', SimpleDataSet, 0),
+ ('table:' + pfx + '.5', ComplexDataSet, 0),
+ ('table:' + pfx + '.6', ComplexDataSet, 0),
+ ('table:' + pfx + '.7', ComplexLSMDataSet, 1),
+ ('table:' + pfx + '.8', ComplexLSMDataSet, 1),
]
# Populate a set of objects.
@@ -59,7 +59,7 @@ class test_backup(wttest.WiredTigerTestCase, suite_subprocess):
if i[2]:
if skiplsm:
continue
- i[1](self, i[0], 'key_format=S', 100)
+ i[1](self, i[0], 100).populate()
# Compare the original and backed-up files using the wt dump command.
def compare(self, uri):
diff --git a/src/third_party/wiredtiger/test/suite/test_backup02.py b/src/third_party/wiredtiger/test/suite/test_backup02.py
index 398d55abd7a..d4089273be0 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup02.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup02.py
@@ -28,7 +28,6 @@
import Queue
import threading, time, wiredtiger, wttest
-from helper import key_populate, simple_populate
from wtthread import backup_thread, checkpoint_thread, op_thread
from wtscenario import make_scenarios
diff --git a/src/third_party/wiredtiger/test/suite/test_backup03.py b/src/third_party/wiredtiger/test/suite/test_backup03.py
index ba7ce60f519..73d05f0b0a1 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup03.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup03.py
@@ -27,11 +27,11 @@
# OTHER DEALINGS IN THE SOFTWARE.
import glob, os, shutil, string
+import wiredtiger, wttest
+from helper import compare_files
from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
-import wiredtiger, wttest
-from helper import compare_files,\
- complex_populate, complex_populate_lsm, simple_populate
# test_backup03.py
# Utilities: wt backup
@@ -50,10 +50,10 @@ class test_backup_target(wttest.WiredTigerTestCase, suite_subprocess):
# to the backup to confirm the backup is correct.
pfx = 'test_backup'
objs = [ # Objects
- ('table:' + pfx + '.1', simple_populate, 0),
- ( 'lsm:' + pfx + '.2', simple_populate, 1),
- ('table:' + pfx + '.3', complex_populate, 2),
- ('table:' + pfx + '.4', complex_populate_lsm, 3),
+ ('table:' + pfx + '.1', SimpleDataSet, 0),
+ ( 'lsm:' + pfx + '.2', SimpleDataSet, 1),
+ ('table:' + pfx + '.3', ComplexDataSet, 2),
+ ('table:' + pfx + '.4', ComplexLSMDataSet, 3),
]
list = [
( 'backup_1', dict(big=0,list=[0])), # Target objects individually
@@ -85,7 +85,7 @@ class test_backup_target(wttest.WiredTigerTestCase, suite_subprocess):
rows = 200000 # Big object
else:
rows = 1000 # Small object
- i[1](self, i[0], 'key_format=S', rows)
+ i[1](self, i[0], rows).populate()
# Backup needs a checkpoint
self.session.checkpoint(None)
diff --git a/src/third_party/wiredtiger/test/suite/test_backup04.py b/src/third_party/wiredtiger/test/suite/test_backup04.py
index dd4dbc1d60f..919649fed57 100644
--- a/src/third_party/wiredtiger/test/suite/test_backup04.py
+++ b/src/third_party/wiredtiger/test/suite/test_backup04.py
@@ -29,10 +29,11 @@
import Queue
import threading, time, wiredtiger, wttest
import glob, os, shutil
+from helper import compare_files
from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet, simple_key
from wtscenario import make_scenarios
from wtthread import op_thread
-from helper import compare_files, key_populate
# test_backup04.py
# Utilities: wt backup
@@ -67,14 +68,14 @@ class test_backup_target(wttest.WiredTigerTestCase, suite_subprocess):
self.pr('populate: ' + uri + ' with ' + str(rows) + ' rows')
cursor = self.session.open_cursor(uri, None)
for i in range(1, rows + 1):
- cursor[key_populate(cursor, i)] = str(i) + ':' + 'a' * dsize
+ cursor[simple_key(cursor, i)] = str(i) + ':' + 'a' * dsize
cursor.close()
def update(self, uri, dsize, upd, rows):
self.pr('update: ' + uri + ' with ' + str(rows) + ' rows')
cursor = self.session.open_cursor(uri, None)
for i in range(1, rows + 1):
- cursor[key_populate(cursor, i)] = str(i) + ':' + upd * dsize
+ cursor[simple_key(cursor, i)] = str(i) + ':' + upd * dsize
cursor.close()
# Compare the original and backed-up files using the wt dump command.
diff --git a/src/third_party/wiredtiger/test/suite/test_backup06.py b/src/third_party/wiredtiger/test/suite/test_backup06.py
new file mode 100644
index 00000000000..9f7a247f2b9
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_backup06.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2016 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 glob
+import os
+import shutil
+import string
+from suite_subprocess import suite_subprocess
+import wiredtiger, wttest
+from wiredtiger import stat
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
+
+# test_backup06.py
+# Test that opening a backup cursor does not open file handles.
+class test_backup06(wttest.WiredTigerTestCase, suite_subprocess):
+ conn_config = 'statistics=(fast)'
+ # This will create several hundred tables.
+ num_table_sets = 10
+ pfx='test_backup'
+
+ # We try to do some schema operations. Have some well
+ # known names.
+ schema_uri = 'file:schema_test'
+ rename_uri = 'file:new_test'
+ trename_uri = 'table:new_test'
+
+ fobjs = [
+ ( 'file:' + pfx + '.1', SimpleDataSet),
+ ( 'file:' + pfx + '.2', SimpleDataSet),
+ ]
+ tobjs = [
+ ('table:' + pfx + '.3', SimpleDataSet),
+ ('table:' + pfx + '.4', SimpleDataSet),
+ ('table:' + pfx + '.5', ComplexDataSet),
+ ('table:' + pfx + '.6', ComplexDataSet),
+ ('table:' + pfx + '.7', ComplexLSMDataSet),
+ ('table:' + pfx + '.8', ComplexLSMDataSet),
+ ]
+
+ # Populate a set of objects.
+ def populate_many(self):
+ for t in range(0, self.num_table_sets):
+ for i in self.fobjs:
+ uri = i[0] + "." + str(t)
+ i[1](self, uri, 10).populate()
+ for i in self.tobjs:
+ uri = i[0] + "." + str(t)
+ i[1](self, uri, 10).populate()
+
+ def populate(self):
+ for i in self.fobjs:
+ i[1](self, i[0], 100).populate()
+ for i in self.tobjs:
+ i[1](self, i[0], 100).populate()
+
+ # Test that the open handle count does not change.
+ def test_cursor_open_handles(self):
+ self.populate_many()
+ # Close and reopen the connection so the populate dhandles are
+ # not in the list.
+ self.reopen_conn()
+
+ # Confirm that opening a backup cursor does not open
+ # file handles.
+ stat_cursor = self.session.open_cursor('statistics:', None, None)
+ dh_before = stat_cursor[stat.conn.dh_conn_handle_count][2]
+ stat_cursor.close()
+ cursor = self.session.open_cursor('backup:', None, None)
+ stat_cursor = self.session.open_cursor('statistics:', None, None)
+ dh_after = stat_cursor[stat.conn.dh_conn_handle_count][2]
+ stat_cursor.close()
+ if (dh_before != dh_after):
+ print "Dhandles open before backup open: " + str(dh_before)
+ print "Dhandles open after backup open: " + str(dh_after)
+ self.assertEqual(dh_after == dh_before, True)
+ cursor.close()
+
+ def test_cursor_schema_protect(self):
+ schema_uri = 'file:schema_test'
+ rename_uri = 'file:new_test'
+ trename_uri = 'table:new_test'
+
+ #
+ # Set up a number of tables. Close and reopen the connection so that
+ # we do not have open dhandles. Then we want to open a backup cursor
+ # testing both with and without the configuration setting.
+ # We want to confirm that we open data handles when using schema
+ # protection and we do not open the data handles when set to false.
+ # We also want to make sure we detect and get an error when set to
+ # false. When set to true the open handles protect against schema
+ # operations.
+ self.populate()
+ cursor = self.session.open_cursor('backup:', None, None)
+ # Check that we can create.
+ self.session.create(schema_uri, None)
+ for i in self.fobjs:
+ self.assertRaises(wiredtiger.WiredTigerError,
+ lambda: self.session.drop(i[0], None))
+ self.assertRaises(wiredtiger.WiredTigerError,
+ lambda: self.session.rename(i[0], rename_uri))
+ for i in self.tobjs:
+ self.assertRaises(wiredtiger.WiredTigerError,
+ lambda: self.session.drop(i[0], None))
+ self.assertRaises(wiredtiger.WiredTigerError,
+ lambda: self.session.rename(i[0], trename_uri))
+ cursor.close()
+
+ # Test cursor reset runs through the list twice.
+ def test_cursor_reset(self):
+ self.populate()
+ cursor = self.session.open_cursor('backup:', None, None)
+ i = 0
+ while True:
+ ret = cursor.next()
+ if ret != 0:
+ break
+ i += 1
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+ total = i * 2
+ cursor.reset()
+ while True:
+ ret = cursor.next()
+ if ret != 0:
+ break
+ i += 1
+ self.assertEqual(ret, wiredtiger.WT_NOTFOUND)
+ self.assertEqual(i, total)
+ cursor.close()
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_bug004.py b/src/third_party/wiredtiger/test/suite/test_bug004.py
index 316d15d8080..464cc57e272 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug004.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug004.py
@@ -30,7 +30,7 @@
# Regression tests.
import wiredtiger, wttest
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet, simple_key, simple_value
# Check to make sure we see the right versions of overflow keys and values
# when they are deleted in reconciliation without having been instantiated
@@ -50,8 +50,8 @@ class test_bug004(wttest.WiredTigerTestCase):
self.session.create(self.uri, self.config)
c1 = self.session.open_cursor(self.uri, None)
for i in range(1, self.nentries):
- c1[key_populate(c1, i) + 'abcdef' * 100] = \
- value_populate(c1, i) + 'abcdef' * 100
+ c1[simple_key(c1, i) + 'abcdef' * 100] = \
+ simple_value(c1, i) + 'abcdef' * 100
c1.close()
# Verify the object, force it to disk, and verify the on-disk version.
@@ -69,9 +69,9 @@ class test_bug004(wttest.WiredTigerTestCase):
# currently do -- that's unlikely to change, but is a problem for the
# test going forward.)
c1 = self.session.open_cursor(self.uri, None)
- c1.set_key(key_populate(c1, self.nentries - 5) + 'abcdef' * 100)
+ c1.set_key(simple_key(c1, self.nentries - 5) + 'abcdef' * 100)
c2 = self.session.open_cursor(self.uri, None)
- c2.set_key(key_populate(c2, self.nentries + 5) + 'abcdef' * 100)
+ c2.set_key(simple_key(c2, self.nentries + 5) + 'abcdef' * 100)
self.session.truncate(None, c1, c2, None)
c1.close()
c2.close()
@@ -81,14 +81,14 @@ class test_bug004(wttest.WiredTigerTestCase):
# Use the snapshot cursor to retrieve the old key/value pairs
c1 = tmp_session.open_cursor(self.uri, None)
- c1.set_key(key_populate(c1, 1) + 'abcdef' * 100)
+ c1.set_key(simple_key(c1, 1) + 'abcdef' * 100)
c1.search()
for i in range(2, self.nentries):
c1.next()
self.assertEquals(
- c1.get_key(), key_populate(c1, i) + 'abcdef' * 100)
+ c1.get_key(), simple_key(c1, i) + 'abcdef' * 100)
self.assertEquals(
- c1.get_value(), value_populate(c1, i) + 'abcdef' * 100)
+ c1.get_value(), simple_value(c1, i) + 'abcdef' * 100)
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_bug005.py b/src/third_party/wiredtiger/test/suite/test_bug005.py
index 6437b329096..69df175ae67 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug005.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug005.py
@@ -30,7 +30,7 @@
# Regression tests.
import wiredtiger, wttest
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet, simple_key, simple_value
# Check that verify works when the file has additional data after the last
# checkpoint.
@@ -43,7 +43,7 @@ class test_bug005(wttest.WiredTigerTestCase):
self.session.create(self.uri, 'value_format=S,key_format=S')
cursor = self.session.open_cursor(self.uri, None)
for i in range(1, 1000):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
cursor.close()
# Verify the object, force it to disk, and verify the on-disk version.
diff --git a/src/third_party/wiredtiger/test/suite/test_bug006.py b/src/third_party/wiredtiger/test/suite/test_bug006.py
index e01333f59de..c0f6055f720 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug006.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug006.py
@@ -30,7 +30,7 @@
# Regression tests.
import wiredtiger, wttest
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet, simple_key, simple_value
from wtscenario import make_scenarios
# Check that verify and salvage both raise exceptions if there is an open
@@ -47,7 +47,7 @@ class test_bug006(wttest.WiredTigerTestCase):
self.session.create(uri, 'value_format=S,key_format=S')
cursor = self.session.open_cursor(uri, None)
for i in range(1, 1000):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Table operations should fail, the cursor is open.
self.assertRaises(
diff --git a/src/third_party/wiredtiger/test/suite/test_bug008.py b/src/third_party/wiredtiger/test/suite/test_bug008.py
index 8e787ae14d1..c54c92fc864 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug008.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug008.py
@@ -30,7 +30,7 @@
# Regression tests.
import wiredtiger, wttest
-from helper import simple_populate, key_populate, value_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# Test search/search-near operations, including invisible values and keys
@@ -38,23 +38,25 @@ from wtscenario import make_scenarios
class test_bug008(wttest.WiredTigerTestCase):
uri = 'file:test_bug008' # This is a btree layer test.
scenarios = make_scenarios([
- ('fix', dict(fmt='key_format=r,value_format=8t', empty=1, colvar=0)),
- ('row', dict(fmt='key_format=S', empty=0, colvar=0)),
- ('var', dict(fmt='key_format=r', empty=0, colvar=1))
+ ('fix', dict(key_format='r', value_format='8t', empty=1, colvar=0)),
+ ('row', dict(key_format='S', value_format='S', empty=0, colvar=0)),
+ ('var', dict(key_format='r', value_format='S', empty=0, colvar=1))
])
# Verify cursor search and search-near operations in an empty table.
def test_search_empty(self):
# Create the object and open a cursor.
- self.session.create(self.uri, self.fmt)
+ ds = SimpleDataSet(self, self.uri, 0, key_format=self.key_format,
+ value_format=self.value_format)
+ ds.create()
cursor = self.session.open_cursor(self.uri, None)
# Search for a record past the end of the table, which should fail.
- cursor.set_key(key_populate(cursor, 100))
+ cursor.set_key(ds.key(100))
self.assertEqual(cursor.search(), wiredtiger.WT_NOTFOUND)
# Search-near for a record past the end of the table, which should fail.
- cursor.set_key(key_populate(cursor, 100))
+ cursor.set_key(ds.key(100))
self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND)
# Verify cursor search and search-near operations at and past the end of
@@ -62,35 +64,37 @@ class test_bug008(wttest.WiredTigerTestCase):
def test_search_eot(self):
# Populate the tree and reopen the connection, forcing it to disk
# and moving the records to an on-page format.
- simple_populate(self, self.uri, self.fmt, 100)
+ ds = SimpleDataSet(self, self.uri, 100, key_format=self.key_format,
+ value_format=self.value_format)
+ ds.populate()
self.reopen_conn()
# Open a cursor.
cursor = self.session.open_cursor(self.uri, None)
# Search for a record at the end of the table, which should succeed.
- cursor.set_key(key_populate(cursor, 100))
+ cursor.set_key(ds.key(100))
self.assertEqual(cursor.search(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 100))
- self.assertEqual(cursor.get_value(), value_populate(cursor, 100))
+ self.assertEqual(cursor.get_key(), ds.key(100))
+ self.assertEqual(cursor.get_value(), ds.value(100))
# Search-near for a record at the end of the table, which should
# succeed, returning the last record.
- cursor.set_key(key_populate(cursor, 100))
+ cursor.set_key(ds.key(100))
self.assertEqual(cursor.search_near(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 100))
- self.assertEqual(cursor.get_value(), value_populate(cursor, 100))
+ self.assertEqual(cursor.get_key(), ds.key(100))
+ self.assertEqual(cursor.get_value(), ds.value(100))
# Search for a record past the end of the table, which should fail.
- cursor.set_key(key_populate(cursor, 200))
+ cursor.set_key(ds.key(200))
self.assertEqual(cursor.search(), wiredtiger.WT_NOTFOUND)
# Search-near for a record past the end of the table, which should
# succeed, returning the last record.
- cursor.set_key(key_populate(cursor, 200))
+ cursor.set_key(ds.key(200))
self.assertEqual(cursor.search_near(), -1)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 100))
- self.assertEqual(cursor.get_value(), value_populate(cursor, 100))
+ self.assertEqual(cursor.get_key(), ds.key(100))
+ self.assertEqual(cursor.get_value(), ds.value(100))
# Verify cursor search-near operations before and after a set of
# column-store duplicates.
@@ -99,18 +103,20 @@ class test_bug008(wttest.WiredTigerTestCase):
return
# Populate the tree.
- simple_populate(self, self.uri, self.fmt, 105)
+ ds = SimpleDataSet(self, self.uri, 105, key_format=self.key_format,
+ value_format=self.value_format)
+ ds.populate()
# Set up deleted records before and after a set of duplicate records,
# and make sure search/search-near returns the correct record.
cursor = self.session.open_cursor(self.uri, None)
for i in range(20, 100):
- cursor[key_populate(cursor, i)] = '=== IDENTICAL VALUE ==='
+ cursor[ds.key(i)] = '=== IDENTICAL VALUE ==='
for i in range(15, 25):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.remove(), 0)
for i in range(95, 106):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.remove(), 0)
cursor.close()
@@ -123,26 +129,28 @@ class test_bug008(wttest.WiredTigerTestCase):
# Search-near for a record in the deleted set before the duplicate set,
# which should succeed, returning the first record in the duplicate set.
- cursor.set_key(key_populate(cursor, 18))
+ cursor.set_key(ds.key(18))
self.assertEqual(cursor.search_near(), 1)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 25))
+ self.assertEqual(cursor.get_key(), ds.key(25))
# Search-near for a record in the deleted set after the duplicate set,
# which should succeed, returning the last record in the duplicate set.
- cursor.set_key(key_populate(cursor, 98))
+ cursor.set_key(ds.key(98))
self.assertEqual(cursor.search_near(), -1)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 94))
+ self.assertEqual(cursor.get_key(), ds.key(94))
# Verify cursor search and search-near operations on a file with a set of
# on-page visible records, and a set of insert-list invisible records.
def test_search_invisible_one(self):
# Populate the tree.
- simple_populate(self, self.uri, self.fmt, 100)
+ ds = SimpleDataSet(self, self.uri, 100, key_format=self.key_format,
+ value_format=self.value_format)
+ ds.populate()
# Delete a range of records.
for i in range(5, 10):
cursor = self.session.open_cursor(self.uri, None)
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.remove(), 0)
# Reopen the connection, forcing it to disk and moving the records to
@@ -155,11 +163,11 @@ class test_bug008(wttest.WiredTigerTestCase):
self.session.begin_transaction()
cursor = self.session.open_cursor(self.uri, None)
for i in range(5, 10):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i + 1000)
+ cursor[ds.key(i)] = ds.value(i + 1000)
for i in range(30, 40):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i + 1000)
+ cursor[ds.key(i)] = ds.value(i + 1000)
for i in range(100, 140):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i + 1000)
+ cursor[ds.key(i)] = ds.value(i + 1000)
# Open a separate session and cursor.
s = self.conn.open_session()
@@ -168,7 +176,7 @@ class test_bug008(wttest.WiredTigerTestCase):
# Search for an existing record in the deleted range, should not find
# it.
for i in range(5, 10):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
if self.empty:
# Fixed-length column-store rows always exist.
self.assertEqual(cursor.search(), 0)
@@ -180,13 +188,13 @@ class test_bug008(wttest.WiredTigerTestCase):
# Search for an existing record in the updated range, should see the
# original value.
for i in range(30, 40):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.search(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, i))
+ self.assertEqual(cursor.get_key(), ds.key(i))
# Search for a added record, should not find it.
for i in range(120, 130):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
if self.empty:
# Invisible updates to fixed-length column-store objects are
# invisible to the reader, but the fact that they exist past
@@ -203,7 +211,7 @@ class test_bug008(wttest.WiredTigerTestCase):
# the next largest record. (This depends on the implementation behavior
# which currently includes a bias to prefix search.)
for i in range(5, 10):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
if self.empty:
# Fixed-length column-store rows always exist.
self.assertEqual(cursor.search_near(), 0)
@@ -211,19 +219,19 @@ class test_bug008(wttest.WiredTigerTestCase):
self.assertEqual(cursor.get_value(), 0)
else:
self.assertEqual(cursor.search_near(), 1)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 10))
+ self.assertEqual(cursor.get_key(), ds.key(10))
# Search-near for an existing record in the updated range, should see
# the original value.
for i in range(30, 40):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.search_near(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, i))
+ self.assertEqual(cursor.get_key(), ds.key(i))
# Search-near for an added record, should find the previous largest
# record.
for i in range(120, 130):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
if self.empty:
# Invisible updates to fixed-length column-store objects are
# invisible to the reader, but the fact that they exist past
@@ -234,7 +242,7 @@ class test_bug008(wttest.WiredTigerTestCase):
self.assertEqual(cursor.get_value(), 0)
else:
self.assertEqual(cursor.search_near(), -1)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 100))
+ self.assertEqual(cursor.get_key(), ds.key(100))
# Verify cursor search and search-near operations on a file with a set of
# on-page visible records, a set of insert-list visible records, and a set
@@ -246,27 +254,29 @@ class test_bug008(wttest.WiredTigerTestCase):
def test_search_invisible_two(self):
# Populate the tree and reopen the connection, forcing it to disk
# and moving the records to an on-page format.
- simple_populate(self, self.uri, self.fmt, 100)
+ ds = SimpleDataSet(self, self.uri, 100, key_format=self.key_format,
+ value_format=self.value_format)
+ ds.populate()
self.reopen_conn()
# Add some additional visible records.
cursor = self.session.open_cursor(self.uri, None)
for i in range(100, 120):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[ds.key(i)] = ds.value(i)
cursor.close()
# Begin a transaction, and add some additional records.
self.session.begin_transaction()
cursor = self.session.open_cursor(self.uri, None)
for i in range(120, 140):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[ds.key(i)] = ds.value(i)
# Open a separate session and cursor.
s = self.conn.open_session()
cursor = s.open_cursor(self.uri, None)
# Search for an invisible record.
- cursor.set_key(key_populate(cursor, 130))
+ cursor.set_key(ds.key(130))
if self.empty:
# Invisible updates to fixed-length column-store objects are
# invisible to the reader, but the fact that they exist past
@@ -281,7 +291,7 @@ class test_bug008(wttest.WiredTigerTestCase):
# Search-near for an invisible record, which should succeed, returning
# the last visible record.
- cursor.set_key(key_populate(cursor, 130))
+ cursor.set_key(ds.key(130))
cursor.search_near()
if self.empty:
# Invisible updates to fixed-length column-store objects are
@@ -294,8 +304,8 @@ class test_bug008(wttest.WiredTigerTestCase):
else:
# Otherwise, we should find the closest record for which we can see
# the value.
- self.assertEqual(cursor.get_key(), key_populate(cursor, 119))
- self.assertEqual(cursor.get_value(), value_populate(cursor, 119))
+ self.assertEqual(cursor.get_key(), ds.key(119))
+ self.assertEqual(cursor.get_value(), ds.value(119))
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_bug011.py b/src/third_party/wiredtiger/test/suite/test_bug011.py
index fceb7a22ddb..29bb08ec2e5 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug011.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug011.py
@@ -26,8 +26,8 @@
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
-import helper, random, wiredtiger, wttest
-from helper import simple_populate
+import random, wiredtiger, wttest
+from wtdataset import SimpleDataSet
# test_bug011.py
# Eviction working on more files than there are hazard pointers.
@@ -46,11 +46,13 @@ class test_bug011(wttest.WiredTigerTestCase):
def test_eviction(self):
cursors = []
+ datasets = []
for i in range(0, self.ntables):
this_uri = 'table:%s-%03d' % (self.table_name, i)
- simple_populate(self, this_uri,
- 'key_format=S,allocation_size=1KB,leaf_page_max=1KB',
- self.nrows)
+ ds = SimpleDataSet(self, this_uri, self.nrows,
+ config='allocation_size=1KB,leaf_page_max=1KB')
+ ds.populate()
+ datasets.append(ds)
# Switch over to on-disk trees with multiple leaf pages
self.reopen_conn()
@@ -63,8 +65,7 @@ class test_bug011(wttest.WiredTigerTestCase):
# Make use of the cache.
for i in range(0, self.nops):
for i in range(0, self.ntables):
- cursors[i].set_key(helper.key_populate(cursors[i],
- random.randint(0, self.nrows - 1)))
+ cursors[i].set_key(ds.key(random.randint(0, self.nrows - 1)))
cursors[i].search()
cursors[i].reset()
diff --git a/src/third_party/wiredtiger/test/suite/test_bug012.py b/src/third_party/wiredtiger/test/suite/test_bug012.py
index a28834f36d2..91f49d14b3f 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug012.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug012.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import complex_populate
+from wtdataset import ComplexDataSet
# test_bug012.py
class test_bug012(wttest.WiredTigerTestCase):
@@ -71,10 +71,11 @@ class test_bug012(wttest.WiredTigerTestCase):
# Test that we detect illegal extractors.
#
- # This test is a little fragile, we're depending on complex_populate to do
+ # This test is a little fragile, we're depending on ComplexDataSet to do
# the heavy-lifting, so if that function changes, this could break.
def test_illegal_extractor(self):
- complex_populate(self, 'table:A', 'key_format=S', 10)
+ ds = ComplexDataSet(self, 'table:A', 10)
+ ds.populate()
msg = '/unknown extractor/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda:
self.session.create('index:A:xyzzy',
diff --git a/src/third_party/wiredtiger/test/suite/test_bug014.py b/src/third_party/wiredtiger/test/suite/test_bug014.py
index c951cb45293..1dee933e839 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug014.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug014.py
@@ -27,7 +27,8 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import copy_wiredtiger_home, key_populate, simple_populate
+from wtdataset import SimpleDataSet
+from helper import copy_wiredtiger_home
# test_bug014.py
# JIRA WT-2115: fast-delete pages can be incorrectly lost due to a crash.
@@ -35,8 +36,9 @@ class test_bug014(wttest.WiredTigerTestCase):
def test_bug014(self):
# Populate a table with 1000 keys on small pages.
uri = 'table:test_bug014'
- simple_populate(self, uri,
- 'allocation_size=512,leaf_page_max=512,key_format=S', 1000)
+ ds = SimpleDataSet(self, uri, 1000,
+ config='allocation_size=512,leaf_page_max=512')
+ ds.populate()
# Reopen it so we can fast-delete pages.
self.reopen_conn()
@@ -44,9 +46,9 @@ class test_bug014(wttest.WiredTigerTestCase):
# Truncate a chunk of the key/value pairs inside a transaction.
self.session.begin_transaction(None)
start = self.session.open_cursor(uri, None)
- start.set_key(key_populate(start, 250))
+ start.set_key(ds.key(250))
end = self.session.open_cursor(uri, None)
- end.set_key(key_populate(end, 500))
+ end.set_key(ds.key(500))
self.session.truncate(None, start, end, None)
start.close()
end.close()
@@ -66,7 +68,7 @@ class test_bug014(wttest.WiredTigerTestCase):
# Confirm all of the records are there.
for i in range(1, 1001):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
self.assertEqual(cursor.search(), 0)
conn.close()
diff --git a/src/third_party/wiredtiger/test/suite/test_bug015.py b/src/third_party/wiredtiger/test/suite/test_bug015.py
index 7892f66759d..68cca49688f 100644
--- a/src/third_party/wiredtiger/test/suite/test_bug015.py
+++ b/src/third_party/wiredtiger/test/suite/test_bug015.py
@@ -27,7 +27,6 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import copy_wiredtiger_home, key_populate, simple_populate
# test_bug015.py
# JIRA WT-2162: index drop in a certain order triggers NULL pointer deref
diff --git a/src/third_party/wiredtiger/test/suite/test_bug017.py b/src/third_party/wiredtiger/test/suite/test_bug017.py
new file mode 100644
index 00000000000..03e7b2ba714
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_bug017.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2016 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
+
+# test_bug017.py
+# WT-2987: opening a cursor on an incomplete table drops core
+class test_bug017(wttest.WiredTigerTestCase):
+
+ def test_bug017_run(self):
+ self.session.create("table:bug17",
+ 'key_format=r,value_format=5sHQ,' +
+ 'columns=(id,country,year,population),colgroups=(main,population)')
+
+ msg = '/column groups/'
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda: self.session.open_cursor("table:bug17(country)", None),
+ msg)
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_bulk01.py b/src/third_party/wiredtiger/test/suite/test_bulk01.py
index addebe80647..8d5b6a04385 100644
--- a/src/third_party/wiredtiger/test/suite/test_bulk01.py
+++ b/src/third_party/wiredtiger/test/suite/test_bulk01.py
@@ -31,7 +31,7 @@
#
import wiredtiger, wttest
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet, simple_key, simple_value
from wtscenario import make_scenarios
# Smoke test bulk-load.
@@ -61,7 +61,7 @@ class test_bulk_load(wttest.WiredTigerTestCase):
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk")
for i in range(1, 1000):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Test a bulk-load triggers variable-length column-store RLE correctly.
def test_bulk_load_var_rle(self):
@@ -76,7 +76,7 @@ class test_bulk_load(wttest.WiredTigerTestCase):
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk")
for i in range(1, 1000):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i/7)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i/7)
# Test a bulk-load variable-length column-store append ignores any key.
def test_bulk_load_var_append(self):
@@ -88,13 +88,13 @@ class test_bulk_load(wttest.WiredTigerTestCase):
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk,append")
for i in range(1, 1000):
- cursor[key_populate(cursor, 37)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, 37)] = simple_value(cursor, i)
cursor.close()
cursor = self.session.open_cursor(uri, None, None)
for i in range(1, 1000):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(simple_key(cursor, i))
cursor.search()
- self.assertEqual(cursor.get_value(), value_populate(cursor, i))
+ self.assertEqual(cursor.get_value(), simple_value(cursor, i))
# Test that column-store bulk-load handles skipped records correctly.
def test_bulk_load_col_delete(self):
@@ -107,21 +107,21 @@ class test_bulk_load(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(uri, None, "bulk")
for i in range(1, 1000):
if i % 7 == 0:
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Ensure we create all the missing records.
i = i + 1
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
cursor.close()
cursor = self.session.open_cursor(uri, None, None)
# Verify all the records are there, in their proper state.
for i in range(1, 1000):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(simple_key(cursor, i))
if i % 7 == 0:
cursor.search()
- self.assertEqual(cursor.get_value(), value_populate(cursor, i))
+ self.assertEqual(cursor.get_value(), simple_value(cursor, i))
elif cursor.value_format == '8t':
cursor.search()
self.assertEqual(cursor.get_value(), 0)
@@ -139,17 +139,17 @@ class test_bulk_load(wttest.WiredTigerTestCase):
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk")
for i in range(1, 10):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# A big record -- if it's not efficient, we'll just hang.
big = 18446744073709551606
- cursor[key_populate(cursor, big)] = value_populate(cursor, big)
+ cursor[simple_key(cursor, big)] = simple_value(cursor, big)
cursor.close()
cursor = self.session.open_cursor(uri, None, None)
- cursor.set_key(key_populate(cursor, big))
+ cursor.set_key(simple_key(cursor, big))
cursor.search()
- self.assertEqual(cursor.get_value(), value_populate(cursor, big))
+ self.assertEqual(cursor.get_value(), simple_value(cursor, big))
# Test that bulk-load out-of-order fails.
def test_bulk_load_order_check(self):
@@ -157,16 +157,16 @@ class test_bulk_load(wttest.WiredTigerTestCase):
self.session.create(uri,
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk")
- cursor[key_populate(cursor, 10)] = value_populate(cursor, 10)
+ cursor[simple_key(cursor, 10)] = simple_value(cursor, 10)
for i in [1, 9, 10]:
- cursor.set_key(key_populate(cursor, 1))
- cursor.set_value(value_populate(cursor, 1))
+ cursor.set_key(simple_key(cursor, 1))
+ cursor.set_value(simple_value(cursor, 1))
msg = '/than previously inserted key/'
self.assertRaisesWithMessage(
wiredtiger.WiredTigerError, lambda: cursor.insert(), msg)
- cursor[key_populate(cursor, 11)] = value_populate(cursor, 11)
+ cursor[simple_key(cursor, 11)] = simple_value(cursor, 11)
# Test that row-store bulk-load out-of-order can succeed.
def test_bulk_load_row_order_nocheck(self):
@@ -181,8 +181,8 @@ class test_bulk_load(wttest.WiredTigerTestCase):
self.session.create(uri,
'key_format=' + self.keyfmt + ',value_format=' + self.valfmt)
cursor = self.session.open_cursor(uri, None, "bulk,skip_sort_check")
- cursor[key_populate(cursor, 10)] = value_populate(cursor, 10)
- cursor[key_populate(cursor, 1)] = value_populate(cursor, 1)
+ cursor[simple_key(cursor, 10)] = simple_value(cursor, 10)
+ cursor[simple_key(cursor, 1)] = simple_value(cursor, 1)
if not wiredtiger.diagnostic_build():
self.skipTest('requires a diagnostic build')
@@ -197,7 +197,7 @@ class test_bulk_load(wttest.WiredTigerTestCase):
uri = self.type + self.name
self.session.create(uri, 'key_format=S,value_format=S')
cursor = self.session.open_cursor(uri, None)
- cursor[key_populate(cursor, 1)] = value_populate(cursor, 1)
+ cursor[simple_key(cursor, 1)] = simple_value(cursor, 1)
# Close the insert cursor, else we'll get EBUSY.
cursor.close()
msg = '/bulk-load is only supported on newly created objects/'
@@ -209,7 +209,7 @@ class test_bulk_load(wttest.WiredTigerTestCase):
uri = self.type + self.name
self.session.create(uri, 'key_format=S,value_format=S')
cursor = self.session.open_cursor(uri, None)
- cursor[key_populate(cursor, 1)] = value_populate(cursor, 1)
+ cursor[simple_key(cursor, 1)] = simple_value(cursor, 1)
# Don't close the insert cursor, we want EBUSY.
self.assertRaises(wiredtiger.WiredTigerError,
lambda: self.session.open_cursor(uri, None, "bulk"))
diff --git a/src/third_party/wiredtiger/test/suite/test_bulk02.py b/src/third_party/wiredtiger/test/suite/test_bulk02.py
index c959098179c..fb9240e91e7 100644
--- a/src/third_party/wiredtiger/test/suite/test_bulk02.py
+++ b/src/third_party/wiredtiger/test/suite/test_bulk02.py
@@ -30,10 +30,11 @@
# Bulk-load testing.
import shutil, os
-from helper import confirm_empty, key_populate, value_populate
+import wiredtiger, wttest
+from helper import confirm_empty
from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet, simple_key, simple_value
from wtscenario import make_scenarios
-import wiredtiger, wttest
# test_bulkload_checkpoint
# Test bulk-load with checkpoints.
@@ -56,7 +57,7 @@ class test_bulkload_checkpoint(wttest.WiredTigerTestCase, suite_subprocess):
self.session.create(self.uri, 'key_format=S,value_format=S')
cursor = self.session.open_cursor(self.uri, None, 'bulk')
for i in range(1, 10):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Checkpoint a few times (to test the drop code).
for i in range(1, 5):
@@ -110,7 +111,7 @@ class test_bulkload_backup(wttest.WiredTigerTestCase, suite_subprocess):
self.session.create(self.uri, 'key_format=S,value_format=S')
cursor = self.session.open_cursor(self.uri, None, 'bulk')
for i in range(1, 10):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Test without a checkpoint, with an unnamed checkpoint, with a named
# checkpoint.
diff --git a/src/third_party/wiredtiger/test/suite/test_checkpoint01.py b/src/third_party/wiredtiger/test/suite/test_checkpoint01.py
index 013cda96e4e..c0d004db78d 100644
--- a/src/third_party/wiredtiger/test/suite/test_checkpoint01.py
+++ b/src/third_party/wiredtiger/test/suite/test_checkpoint01.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import key_populate, complex_populate_lsm, simple_populate
+from wtdataset import SimpleDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
# test_checkpoint01.py
@@ -145,7 +145,7 @@ class test_checkpoint_cursor(wttest.WiredTigerTestCase):
# Check that you cannot open a checkpoint that doesn't exist.
def test_checkpoint_dne(self):
- simple_populate(self, self.uri, 'key_format=' + self.fmt, 100)
+ SimpleDataSet(self, self.uri, 100, key_format=self.fmt).populate()
self.assertRaises(wiredtiger.WiredTigerError,
lambda: self.session.open_cursor(
self.uri, None, "checkpoint=checkpoint-1"))
@@ -155,7 +155,7 @@ class test_checkpoint_cursor(wttest.WiredTigerTestCase):
# Check that you can open checkpoints more than once.
def test_checkpoint_multiple_open(self):
- simple_populate(self, self.uri, 'key_format=' + self.fmt, 100)
+ SimpleDataSet(self, self.uri, 100, key_format=self.fmt).populate()
self.session.checkpoint("name=checkpoint-1")
c1 = self.session.open_cursor(self.uri, None, "checkpoint=checkpoint-1")
c2 = self.session.open_cursor(self.uri, None, "checkpoint=checkpoint-1")
@@ -172,7 +172,7 @@ class test_checkpoint_cursor(wttest.WiredTigerTestCase):
# Check that you cannot drop a checkpoint if it's in use.
def test_checkpoint_inuse(self):
- simple_populate(self, self.uri, 'key_format=' + self.fmt, 100)
+ SimpleDataSet(self, self.uri, 100, key_format=self.fmt).populate()
self.session.checkpoint("name=checkpoint-1")
self.session.checkpoint("name=checkpoint-2")
self.session.checkpoint("name=checkpoint-3")
@@ -208,44 +208,49 @@ class test_checkpoint_target(wttest.WiredTigerTestCase):
('table', dict(uri='table:checkpoint',fmt='S'))
])
- def update(self, uri, value):
+ def update(self, uri, ds, value):
cursor = self.session.open_cursor(uri, None, "overwrite")
- cursor[key_populate(cursor, 10)] = value
+ cursor[ds.key(10)] = value
cursor.close()
- def check(self, uri, value):
+ def check(self, uri, ds, value):
cursor = self.session.open_cursor(uri, None, "checkpoint=checkpoint-1")
- self.assertEquals(cursor[key_populate(cursor, 10)], value)
+ self.assertEquals(cursor[ds.key(10)], value)
cursor.close()
def test_checkpoint_target(self):
# Create 3 objects, change one record to an easily recognizable string.
uri = self.uri + '1'
- simple_populate(self, uri, 'key_format=' + self.fmt, 100)
- self.update(uri, 'ORIGINAL')
+ ds1 = SimpleDataSet(self, uri, 100, key_format=self.fmt)
+ ds1.populate()
+ self.update(uri, ds1, 'ORIGINAL')
+
uri = self.uri + '2'
- simple_populate(self, uri, 'key_format=' + self.fmt, 100)
- self.update(uri, 'ORIGINAL')
+ ds2 = SimpleDataSet(self, uri, 100, key_format=self.fmt)
+ ds2.populate()
+ self.update(uri, ds2, 'ORIGINAL')
+
uri = self.uri + '3'
- simple_populate(self, uri, 'key_format=' + self.fmt, 100)
- self.update(uri, 'ORIGINAL')
+ ds3 = SimpleDataSet(self, uri, 100, key_format=self.fmt)
+ ds3.populate()
+ self.update(uri, ds3, 'ORIGINAL')
# Checkpoint all three objects.
self.session.checkpoint("name=checkpoint-1")
# Update all 3 objects, then checkpoint two of the objects with the
# same checkpoint name.
- self.update(self.uri + '1', 'UPDATE')
- self.update(self.uri + '2', 'UPDATE')
- self.update(self.uri + '3', 'UPDATE')
+ self.update(self.uri + '1', ds1, 'UPDATE')
+ self.update(self.uri + '2', ds2, 'UPDATE')
+ self.update(self.uri + '3', ds3, 'UPDATE')
target = 'target=("' + self.uri + '1"' + ',"' + self.uri + '2")'
self.session.checkpoint("name=checkpoint-1," + target)
# Confirm the checkpoint has the old value in objects that weren't
# checkpointed, and the new value in objects that were checkpointed.
- self.check(self.uri + '1', 'UPDATE')
- self.check(self.uri + '2', 'UPDATE')
- self.check(self.uri + '3', 'ORIGINAL')
+ self.check(self.uri + '1', ds1, 'UPDATE')
+ self.check(self.uri + '2', ds2, 'UPDATE')
+ self.check(self.uri + '3', ds3, 'ORIGINAL')
# Check that you can't write checkpoint cursors.
class test_checkpoint_cursor_update(wttest.WiredTigerTestCase):
@@ -257,10 +262,11 @@ class test_checkpoint_cursor_update(wttest.WiredTigerTestCase):
])
def test_checkpoint_cursor_update(self):
- simple_populate(self, self.uri, 'key_format=' + self.fmt, 100)
+ ds = SimpleDataSet(self, self.uri, 100, key_format=self.fmt)
+ ds.populate()
self.session.checkpoint("name=ckpt")
cursor = self.session.open_cursor(self.uri, None, "checkpoint=ckpt")
- cursor.set_key(key_populate(cursor, 10))
+ cursor.set_key(ds.key(10))
cursor.set_value("XXX")
msg = "/Unsupported cursor/"
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
@@ -284,12 +290,13 @@ class test_checkpoint_last(wttest.WiredTigerTestCase):
# value. Repeat this action, we want to be sure the engine gets the
# latest checkpoint information each time.
uri = self.uri
- simple_populate(self, uri, 'key_format=' + self.fmt, 100)
+ ds = SimpleDataSet(self, uri, 100, key_format=self.fmt)
+ ds.populate()
for value in ('FIRST', 'SECOND', 'THIRD', 'FOURTH', 'FIFTH'):
# Update the object.
cursor = self.session.open_cursor(uri, None, "overwrite")
- cursor[key_populate(cursor, 10)] = value
+ cursor[ds.key(10)] = value
cursor.close()
# Checkpoint the object.
@@ -298,14 +305,15 @@ class test_checkpoint_last(wttest.WiredTigerTestCase):
# Verify the "last" checkpoint sees the correct value.
cursor = self.session.open_cursor(
uri, None, "checkpoint=WiredTigerCheckpoint")
- self.assertEquals(cursor[key_populate(cursor, 10)], value)
+ self.assertEquals(cursor[ds.key(10)], value)
# Don't close the checkpoint cursor, we want it to remain open until
# the test completes.
# Check we can't use the reserved name as an application checkpoint name.
class test_checkpoint_illegal_name(wttest.WiredTigerTestCase):
def test_checkpoint_illegal_name(self):
- simple_populate(self, "file:checkpoint", 'key_format=S', 100)
+ ds = SimpleDataSet(self, "file:checkpoint", 100, key_format='S')
+ ds.populate()
msg = '/the checkpoint name.*is reserved/'
for conf in (
'name=WiredTigerCheckpoint',
@@ -329,8 +337,8 @@ class test_checkpoint_illegal_name(wttest.WiredTigerTestCase):
# Check we can't name checkpoints that include LSM tables.
class test_checkpoint_lsm_name(wttest.WiredTigerTestCase):
def test_checkpoint_lsm_name(self):
- complex_populate_lsm(self,
- "table:checkpoint", 'type=lsm,key_format=S', 1000)
+ ds = ComplexLSMDataSet(self, "table:checkpoint", 1000)
+ ds.populate()
msg = '/object does not support named checkpoints/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.checkpoint("name=ckpt"), msg)
diff --git a/src/third_party/wiredtiger/test/suite/test_checkpoint02.py b/src/third_party/wiredtiger/test/suite/test_checkpoint02.py
index 0d166544472..b5d20fb73b1 100644
--- a/src/third_party/wiredtiger/test/suite/test_checkpoint02.py
+++ b/src/third_party/wiredtiger/test/suite/test_checkpoint02.py
@@ -28,7 +28,6 @@
import Queue
import threading, time, wiredtiger, wttest
-from helper import key_populate, simple_populate
from wtthread import checkpoint_thread, op_thread
from wtscenario import make_scenarios
diff --git a/src/third_party/wiredtiger/test/suite/test_colgap.py b/src/third_party/wiredtiger/test/suite/test_colgap.py
index dbe748ae58f..91df0fd6c1c 100644
--- a/src/third_party/wiredtiger/test/suite/test_colgap.py
+++ b/src/third_party/wiredtiger/test/suite/test_colgap.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import simple_populate, key_populate, value_populate
+from wtdataset import SimpleDataSet, simple_key, simple_value
from wtscenario import make_scenarios
# test_colgap.py
@@ -61,14 +61,16 @@ class test_column_store_gap(wttest.WiredTigerTestCase):
# namespace. If this runs in less-than-glacial time, it's working.
def test_column_store_gap(self):
uri = 'table:gap'
- simple_populate(self, uri, 'key_format=r,value_format=S', 0)
+ # Initially just create tables.
+ ds = SimpleDataSet(self, uri, 0, key_format='r')
+ ds.populate()
cursor = self.session.open_cursor(uri, None, None)
self.nentries = 0
# Create a column-store table with large gaps in the name-space.
v = [ 1000, 2000000000000, 30000000000000 ]
for i in v:
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[ds.key(i)] = ds.value(i)
self.nentries += 1
# In-memory cursor forward, backward.
@@ -84,7 +86,9 @@ class test_column_store_gap(wttest.WiredTigerTestCase):
def test_column_store_gap_traverse(self):
uri = 'table:gap'
- simple_populate(self, uri, 'key_format=r,value_format=S', 0)
+ # Initially just create tables.
+ ds = SimpleDataSet(self, uri, 0, key_format='r')
+ ds.populate()
cursor = self.session.open_cursor(uri, None, None)
self.nentries = 0
@@ -92,7 +96,7 @@ class test_column_store_gap(wttest.WiredTigerTestCase):
# important, we just want some gaps.
v = [ 1000, 1001, 2000, 2001]
for i in v:
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[ds.key(i)] = ds.value(i)
self.nentries += 1
# In-memory cursor forward, backward.
@@ -111,7 +115,7 @@ class test_column_store_gap(wttest.WiredTigerTestCase):
# so the traversal walks to them.
v2 = [ 1500, 1501 ]
for i in v2:
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[ds.key(i)] = ds.value(i)
self.nentries += 1
# Tell the validation what to expect.
@@ -168,7 +172,7 @@ class test_colmax(wttest.WiredTigerTestCase):
# Optionaly make the big record the only record in the table.
if not self.single:
for i in range(1, 723):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Confirm searching past the end of the table works.
if not self.bulk:
@@ -176,7 +180,7 @@ class test_colmax(wttest.WiredTigerTestCase):
self.assertEqual(cursor.search(), wiredtiger.WT_NOTFOUND)
# Insert the big record.
- cursor[key_populate(cursor, recno)] = value_populate(cursor, recno)
+ cursor[simple_key(cursor, recno)] = simple_value(cursor, recno)
# Optionally flush to disk; re-open the cursor as necessary.
if self.bulk or self.reopen:
@@ -189,18 +193,18 @@ class test_colmax(wttest.WiredTigerTestCase):
# Search for the large record.
cursor.set_key(recno)
self.assertEqual(cursor.search(), 0)
- self.assertEqual(cursor.get_value(), value_populate(cursor, recno))
+ self.assertEqual(cursor.get_value(), simple_value(cursor, recno))
# Update it.
- cursor[key_populate(cursor, recno)] = value_populate(cursor, 37)
+ cursor[simple_key(cursor, recno)] = simple_value(cursor, 37)
cursor.set_key(recno)
self.assertEqual(cursor.search(), 0)
- self.assertEqual(cursor.get_value(), value_populate(cursor, 37))
+ self.assertEqual(cursor.get_value(), simple_value(cursor, 37))
# Remove it.
cursor.set_key(recno)
self.assertEqual(cursor.remove(), 0)
- cursor.set_key(key_populate(cursor, recno))
+ cursor.set_key(simple_key(cursor, recno))
self.assertEqual(cursor.search(), wiredtiger.WT_NOTFOUND)
if __name__ == '__main__':
diff --git a/src/third_party/wiredtiger/test/suite/test_compact01.py b/src/third_party/wiredtiger/test/suite/test_compact01.py
index 8da1a0df4da..56ab6d39076 100644
--- a/src/third_party/wiredtiger/test/suite/test_compact01.py
+++ b/src/third_party/wiredtiger/test/suite/test_compact01.py
@@ -27,8 +27,8 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import complex_populate, simple_populate, key_populate
from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet, ComplexDataSet
from wiredtiger import stat
from wtscenario import make_scenarios
@@ -39,14 +39,14 @@ class test_compact(wttest.WiredTigerTestCase, suite_subprocess):
# Use a small page size because we want to create lots of pages.
config = 'allocation_size=512,' +\
- 'leaf_page_max=512,value_format=S,key_format=S'
+ 'leaf_page_max=512,key_format=S'
nentries = 50000
# The table is a complex object, give it roughly 5 pages per underlying
# file.
types = [
- ('file', dict(type='file:', pop=simple_populate, maxpages=5)),
- ('table', dict(type='table:', pop=complex_populate, maxpages=50))
+ ('file', dict(type='file:', dataset=SimpleDataSet, maxpages=5)),
+ ('table', dict(type='table:', dataset=ComplexDataSet, maxpages=50))
]
compact = [
('method', dict(utility=0,reopen=0)),
@@ -64,7 +64,8 @@ class test_compact(wttest.WiredTigerTestCase, suite_subprocess):
def test_compact(self):
# Populate an object
uri = self.type + self.name
- self.pop(self, uri, self.config, self.nentries - 1)
+ ds = self.dataset(self, uri, self.nentries - 1, config=self.config)
+ ds.populate()
# Reopen the connection to force the object to disk.
self.reopen_conn()
@@ -76,9 +77,9 @@ class test_compact(wttest.WiredTigerTestCase, suite_subprocess):
# Remove most of the object.
c1 = self.session.open_cursor(uri, None)
- c1.set_key(key_populate(c1, 5))
+ c1.set_key(ds.key(5))
c2 = self.session.open_cursor(uri, None)
- c2.set_key(key_populate(c2, self.nentries - 5))
+ c2.set_key(ds.key(self.nentries - 5))
self.session.truncate(None, c1, c2, None)
c1.close()
c2.close()
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor06.py b/src/third_party/wiredtiger/test/suite/test_cursor06.py
index 3a6240bc6c7..117e29b0605 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor06.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor06.py
@@ -27,8 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import key_populate, value_populate, simple_populate
-from helper import complex_value_populate, complex_populate
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
# test_cursor06.py
@@ -36,33 +35,26 @@ from wtscenario import make_scenarios
class test_cursor06(wttest.WiredTigerTestCase):
name = 'reconfigure'
scenarios = make_scenarios([
- ('file-r', dict(type='file:', config='key_format=r', complex=0)),
- ('file-S', dict(type='file:', config='key_format=S', complex=0)),
- ('lsm-S', dict(type='lsm:', config='key_format=S', complex=0)),
- ('table-r',
- dict(type='table:', config='key_format=r', complex=0)),
- ('table-S',
- dict(type='table:', config='key_format=S', complex=0)),
- ('table-r-complex',
- dict(type='table:', config='key_format=r', complex=1)),
- ('table-S-complex',
- dict(type='table:', config='key_format=S', complex=1)),
- ('table-S-complex-lsm',
- dict(type='table:', config='key_format=S,type=lsm', complex=1)),
+ ('file-r', dict(type='file:', keyfmt='r', dataset=SimpleDataSet)),
+ ('file-S', dict(type='file:', keyfmt='S', dataset=SimpleDataSet)),
+ ('lsm-S', dict(type='lsm:', keyfmt='S', dataset=SimpleDataSet)),
+ ('table-r', dict(type='table:', keyfmt='r', dataset=SimpleDataSet)),
+ ('table-S', dict(type='table:', keyfmt='S', dataset=SimpleDataSet)),
+ ('table-r-complex', dict(type='table:', keyfmt='r',
+ dataset=ComplexDataSet)),
+ ('table-S-complex', dict(type='table:', keyfmt='S',
+ dataset=ComplexDataSet)),
+ ('table-S-complex-lsm', dict(type='table:', keyfmt='S',
+ dataset=ComplexLSMDataSet)),
])
def populate(self, uri):
- if self.complex:
- complex_populate(self, uri, self.config, 100)
- else:
- simple_populate(self, uri, self.config, 100)
+ self.ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ self.ds.populate()
def set_kv(self, cursor):
- cursor.set_key(key_populate(cursor, 10))
- if self.complex:
- cursor.set_value(tuple(complex_value_populate(cursor, 10)))
- else:
- cursor.set_value(value_populate(cursor, 10))
+ cursor.set_key(self.ds.key(10))
+ cursor.set_value(self.ds.value(10))
def test_reconfigure_overwrite(self):
uri = self.type + self.name
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor09.py b/src/third_party/wiredtiger/test/suite/test_cursor09.py
index a05caea4f1f..9a1fc06b617 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor09.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor09.py
@@ -27,27 +27,24 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import key_populate, value_populate, simple_populate
-from helper import complex_populate, complex_value_populate
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
# test_cursor09.py
# JIRA WT-2217: insert resets key/value "set".
class test_cursor09(wttest.WiredTigerTestCase):
scenarios = make_scenarios([
- ('file-r', dict(type='file:', config='key_format=r', complex=0)),
- ('file-S', dict(type='file:', config='key_format=S', complex=0)),
- ('lsm-S', dict(type='lsm:', config='key_format=S', complex=0)),
- ('table-r',
- dict(type='table:', config='key_format=r', complex=0)),
- ('table-S',
- dict(type='table:', config='key_format=S', complex=0)),
- ('table-r-complex',
- dict(type='table:', config='key_format=r', complex=1)),
- ('table-S-complex',
- dict(type='table:', config='key_format=S', complex=1)),
- ('table-S-complex-lsm',
- dict(type='table:', config='key_format=S,type=lsm', complex=1)),
+ ('file-r', dict(type='file:', keyfmt='r', dataset=SimpleDataSet)),
+ ('file-S', dict(type='file:', keyfmt='S', dataset=SimpleDataSet)),
+ ('lsm-S', dict(type='lsm:', keyfmt='S', dataset=SimpleDataSet)),
+ ('table-r', dict(type='table:', keyfmt='r', dataset=SimpleDataSet)),
+ ('table-S', dict(type='table:', keyfmt='S', dataset=SimpleDataSet)),
+ ('table-r-complex', dict(type='table:', keyfmt='r',
+ dataset=ComplexDataSet)),
+ ('table-S-complex', dict(type='table:', keyfmt='S',
+ dataset=ComplexDataSet)),
+ ('table-S-complex-lsm', dict(type='table:', keyfmt='S',
+ dataset=ComplexLSMDataSet)),
])
# WT_CURSOR.insert doesn't leave the cursor positioned, verify any
@@ -55,15 +52,11 @@ class test_cursor09(wttest.WiredTigerTestCase):
def test_cursor09(self):
uri = self.type + 'cursor09'
- if self.complex:
- complex_populate(self, uri, self.config, 100)
- else:
- simple_populate(self, uri, self.config, 100)
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
cursor = self.session.open_cursor(uri, None, None)
- cursor[key_populate(cursor, 10)] = \
- tuple(complex_value_populate(cursor, 10)) if self.complex \
- else value_populate(cursor, 10)
+ cursor[ds.key(10)] = ds.value(10)
msg = '/requires key be set/'
self.assertRaisesWithMessage(
wiredtiger.WiredTigerError, cursor.search, msg)
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_compare.py b/src/third_party/wiredtiger/test/suite/test_cursor_compare.py
index 4610a2f30a9..c0feb1d4867 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor_compare.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_compare.py
@@ -27,8 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest, exceptions
-from helper import complex_populate, simple_populate, key_populate
-from helper import complex_populate_index_name
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
# Test cursor comparisons.
@@ -36,9 +35,9 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
name = 'test_compare'
types = [
- ('file', dict(type='file:', config='')),
- ('lsm', dict(type='table:', config=',type=lsm')),
- ('table', dict(type='table:', config=''))
+ ('file', dict(type='file:', dataset=SimpleDataSet)),
+ ('lsm', dict(type='table:', dataset=ComplexLSMDataSet)),
+ ('table', dict(type='table:', dataset=ComplexDataSet))
]
keyfmt = [
('integer', dict(keyfmt='i')),
@@ -52,28 +51,20 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
uriX = self.type + 'compareX'
# Build the object.
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ dsX = self.dataset(self, uriX, 100, key_format=self.keyfmt)
+ ds.populate()
+ dsX.populate()
if self.type == 'file:':
- simple_populate(
- self, uri, 'key_format=' + self.keyfmt + self.config, 100)
- simple_populate(
- self, uriX, 'key_format=' + self.keyfmt + self.config, 100)
ix0_0 = None
ix0_1 = None
ix1_0 = None
ixX_0 = None
else:
- complex_populate(
- self, uri, 'key_format=' + self.keyfmt + self.config, 100)
- complex_populate(
- self, uriX, 'key_format=' + self.keyfmt + self.config, 100)
- ix0_0 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 0), None)
- ix0_1 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 0), None)
- ix1_0 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 1), None)
- ixX_0 = self.session.open_cursor(
- complex_populate_index_name(self, uriX, 0), None)
+ ix0_0 = self.session.open_cursor(ds.index_name(0), None)
+ ix0_1 = self.session.open_cursor(ds.index_name(0), None)
+ ix1_0 = self.session.open_cursor(ds.index_name(1), None)
+ ixX_0 = self.session.open_cursor(dsX.index_name(0), None)
ix0_0.next()
ix0_1.next()
ix1_0.next()
@@ -90,17 +81,17 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
wiredtiger.WiredTigerError, lambda: c2.compare(c1), msg)
# Test cursors before they're positioned.
- c1.set_key(key_populate(c1, 10))
- c2.set_key(key_populate(c2, 20))
+ c1.set_key(ds.key(10))
+ c2.set_key(ds.key(20))
self.assertGreater(c2.compare(c1), 0)
self.assertLess(c1.compare(c2), 0)
- c2.set_key(key_populate(c2, 10))
+ c2.set_key(ds.key(10))
self.assertEqual(c1.compare(c2), 0)
self.assertEqual(c2.compare(c1), 0)
# Confirm failure for different objects.
cX = self.session.open_cursor(uriX, None)
- cX.set_key(key_populate(cX, 10))
+ cX.set_key(dsX.key(10))
msg = '/must reference the same object/'
self.assertRaisesWithMessage(
wiredtiger.WiredTigerError, lambda: cX.compare(c1), msg)
@@ -125,20 +116,20 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
wiredtiger.WiredTigerError, lambda: ix0_0.compare(ix1_0), msg)
# Test cursors after they're positioned (shouldn't matter for compare).
- c1.set_key(key_populate(c1, 10))
+ c1.set_key(ds.key(10))
self.assertEqual(c1.search(), 0)
- c2.set_key(key_populate(c2, 20))
+ c2.set_key(ds.key(20))
self.assertEqual(c2.search(), 0)
self.assertGreater(c2.compare(c1), 0)
self.assertLess(c1.compare(c2), 0)
- c2.set_key(key_populate(c2, 10))
+ c2.set_key(ds.key(10))
self.assertEqual(c2.search(), 0)
self.assertEqual(c1.compare(c2), 0)
self.assertEqual(c2.compare(c1), 0)
# Confirm failure for different objects.
cX = self.session.open_cursor(uriX, None)
- cX.set_key(key_populate(cX, 10))
+ cX.set_key(dsX.key(10))
self.assertEqual(cX.search(), 0)
msg = '/must reference the same object/'
self.assertRaisesWithMessage(
@@ -149,28 +140,20 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
uriX = self.type + 'compareX'
# Build the object.
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ dsX = self.dataset(self, uriX, 100, key_format=self.keyfmt)
+ ds.populate()
+ dsX.populate()
if self.type == 'file:':
- simple_populate(
- self, uri, 'key_format=' + self.keyfmt + self.config, 100)
- simple_populate(
- self, uriX, 'key_format=' + self.keyfmt + self.config, 100)
ix0_0 = None
ix0_1 = None
ix1_0 = None
ixX_0 = None
else:
- complex_populate(
- self, uri, 'key_format=' + self.keyfmt + self.config, 100)
- complex_populate(
- self, uriX, 'key_format=' + self.keyfmt + self.config, 100)
- ix0_0 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 0), None)
- ix0_1 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 0), None)
- ix1_0 = self.session.open_cursor(
- complex_populate_index_name(self, uri, 1), None)
- ixX_0 = self.session.open_cursor(
- complex_populate_index_name(self, uriX, 0), None)
+ ix0_0 = self.session.open_cursor(ds.index_name(0), None)
+ ix0_1 = self.session.open_cursor(ds.index_name(0), None)
+ ix1_0 = self.session.open_cursor(ds.index_name(1), None)
+ ixX_0 = self.session.open_cursor(dsX.index_name(0), None)
ix0_0.next()
ix0_1.next()
ix1_0.next()
@@ -187,17 +170,17 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
wiredtiger.WiredTigerError, lambda: c2.equals(c1), msg)
# Test cursors before they're positioned.
- c1.set_key(key_populate(c1, 10))
- c2.set_key(key_populate(c2, 20))
+ c1.set_key(ds.key(10))
+ c2.set_key(ds.key(20))
self.assertFalse(c1.equals(c2))
self.assertFalse(c2.equals(c1))
- c2.set_key(key_populate(c2, 10))
+ c2.set_key(ds.key(10))
self.assertTrue(c1.equals(c2))
self.assertTrue(c2.equals(c1))
# Confirm failure for different objects.
cX = self.session.open_cursor(uriX, None)
- cX.set_key(key_populate(cX, 10))
+ cX.set_key(dsX.key(10))
msg = '/must reference the same object/'
self.assertRaisesWithMessage(
wiredtiger.WiredTigerError, lambda: cX.equals(c1), msg)
@@ -222,20 +205,20 @@ class test_cursor_comparison(wttest.WiredTigerTestCase):
# Test cursors after they're positioned (internally, it's a different
# search path if keys are positioned in the tree).
- c1.set_key(key_populate(c1, 10))
+ c1.set_key(ds.key(10))
self.assertEqual(c1.search(), 0)
- c2.set_key(key_populate(c2, 20))
+ c2.set_key(ds.key(20))
self.assertEqual(c2.search(), 0)
self.assertFalse(c1.equals(c2))
self.assertFalse(c2.equals(c1))
- c2.set_key(key_populate(c2, 10))
+ c2.set_key(ds.key(10))
self.assertEqual(c2.search(), 0)
self.assertTrue(c1.equals(c2))
self.assertTrue(c2.equals(c1))
# Confirm failure for different objects.
cX = self.session.open_cursor(uriX, None)
- cX.set_key(key_populate(cX, 10))
+ cX.set_key(dsX.key(10))
self.assertEqual(cX.search(), 0)
msg = '/must reference the same object/'
self.assertRaisesWithMessage(
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_pin.py b/src/third_party/wiredtiger/test/suite/test_cursor_pin.py
index b6119a93897..cb7045c7e41 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor_pin.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_pin.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import simple_populate, key_populate, value_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# test_cursor_pin.py
@@ -36,7 +36,7 @@ from wtscenario import make_scenarios
class test_cursor_pin(wttest.WiredTigerTestCase):
uri = 'file:cursor_pin'
nentries = 10000
- config = 'allocation_size=512,leaf_page_max=512,value_format=S,key_format='
+ config = 'allocation_size=512,leaf_page_max=512'
scenarios = make_scenarios([
('recno', dict(keyfmt='r')),
('string', dict(keyfmt='S')),
@@ -45,72 +45,75 @@ class test_cursor_pin(wttest.WiredTigerTestCase):
# Create a multi-page file, confirm that a simple search to the local
# page works, followed by a search to a different page.
def test_smoke(self):
- simple_populate(self,
- self.uri, self.config + self.keyfmt, self.nentries)
+ ds = SimpleDataSet(self, self.uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
+ ds.populate()
self.reopen_conn()
c = self.session.open_cursor(self.uri, None)
- c.set_key(key_populate(c, 100))
+ c.set_key(ds.key(100))
self.assertEqual(c.search(), 0)
- self.assertEqual(c.get_value(), value_populate(c, 100))
- c.set_key(key_populate(c, 101))
+ self.assertEqual(c.get_value(), ds.value(100))
+ c.set_key(ds.key(101))
self.assertEqual(c.search(), 0)
- self.assertEqual(c.get_value(), value_populate(c, 101))
- c.set_key(key_populate(c, 9999))
+ self.assertEqual(c.get_value(), ds.value(101))
+ c.set_key(ds.key(9999))
self.assertEqual(c.search(), 0)
- self.assertEqual(c.get_value(), value_populate(c, 9999))
+ self.assertEqual(c.get_value(), ds.value(9999))
# Forward check.
- def forward(self, c, max, notfound):
+ def forward(self, c, ds, max, notfound):
for i in range(1, max + 1):
- c.set_key(key_populate(c, i))
+ c.set_key(ds.key(i))
if i in notfound:
self.assertEqual(c.search(), wiredtiger.WT_NOTFOUND)
else:
self.assertEqual(c.search(), 0)
- self.assertEqual(c.get_value(), value_populate(c, i))
+ self.assertEqual(c.get_value(), ds.value(i))
# Backward check.
- def backward(self, c, max, notfound):
+ def backward(self, c, ds, max, notfound):
for i in range(max, 0, -1):
- c.set_key(key_populate(c, i))
+ c.set_key(ds.key(i))
if i in notfound:
self.assertEqual(c.search(), wiredtiger.WT_NOTFOUND)
else:
self.assertEqual(c.search(), 0)
- self.assertEqual(c.get_value(), value_populate(c, i))
+ self.assertEqual(c.get_value(), ds.value(i))
# Create a multi-page file, search backward, forward to check page
# boundaries.
def test_basic(self):
- simple_populate(self,
- self.uri, self.config + self.keyfmt, self.nentries)
+ ds = SimpleDataSet(self, self.uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
+ ds.populate()
self.reopen_conn()
c = self.session.open_cursor(self.uri, None)
- self.forward(c, self.nentries, [])
- self.backward(c, self.nentries, [])
+ self.forward(c, ds, self.nentries, [])
+ self.backward(c, ds, self.nentries, [])
# Create a multi-page file with a big chunk of missing space in the
# middle (to exercise column-store searches).
def test_missing(self):
- simple_populate(self,
- self.uri, self.config + self.keyfmt, self.nentries)
+ ds = SimpleDataSet(self, self.uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
+ ds.populate()
c = self.session.open_cursor(self.uri, None)
for i in range(self.nentries + 3000, self.nentries + 5001):
- c[key_populate(c, i)] = value_populate(c, i)
+ c[ds.key(i)] = ds.value(i)
self.reopen_conn()
c = self.session.open_cursor(self.uri, None)
- self.forward(c, self.nentries + 5000,
+ self.forward(c, ds, self.nentries + 5000,
list(range(self.nentries + 1, self.nentries + 3000)))
- self.backward(c, self.nentries + 5000,
+ self.backward(c, ds, self.nentries + 5000,
list(range(self.nentries + 1, self.nentries + 3000)))
# Insert into the empty space so we test searching inserted items.
for i in range(self.nentries + 1000, self.nentries + 2001):
- c[key_populate(c, i)] = value_populate(c, i)
- self.forward(c, self.nentries + 5000,
+ c[ds.key(i)] = ds.value(i)
+ self.forward(c, ds, self.nentries + 5000,
list(range(self.nentries + 1, self.nentries + 1000) +\
range(self.nentries + 2001, self.nentries + 3000)))
- self.backward(c, self.nentries + 5000,
+ self.backward(c, ds, self.nentries + 5000,
list(range(self.nentries + 1, self.nentries + 1000) +\
range(self.nentries + 2001, self.nentries + 3000)))
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_random.py b/src/third_party/wiredtiger/test/suite/test_cursor_random.py
index 205b9c3c9c5..3bda6dc9946 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor_random.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_random.py
@@ -27,16 +27,15 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import complex_populate, simple_populate
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet, ComplexDataSet, simple_key, simple_value
from wtscenario import make_scenarios
# test_cursor_random.py
# Cursor next_random operations
class test_cursor_random(wttest.WiredTigerTestCase):
types = [
- ('file', dict(type='file:random')),
- ('table', dict(type='table:random'))
+ ('file', dict(type='file:random', dataset=SimpleDataSet)),
+ ('table', dict(type='table:random', dataset=ComplexDataSet))
]
config = [
('sample', dict(config='next_random=true,next_random_sample_size=35')),
@@ -89,12 +88,9 @@ class test_cursor_random(wttest.WiredTigerTestCase):
# where the values are in an insert list.
def test_cursor_random_multiple_insert_records(self):
uri = self.type
- if uri.startswith('file:'):
- simple_populate(self, uri,
- 'allocation_size=512,leaf_page_max=512,key_format=S', 100)
- else:
- complex_populate(self, uri,
- 'allocation_size=512,leaf_page_max=512,key_format=S', 100)
+ ds = self.dataset(self, uri, 100,
+ config='allocation_size=512,leaf_page_max=512')
+ ds.populate()
# In a insert list, next_random always selects the middle key/value
# pair, all we can do is confirm cursor.next works.
@@ -105,12 +101,9 @@ class test_cursor_random(wttest.WiredTigerTestCase):
# where the values are in a disk format page.
def cursor_random_multiple_page_records(self, reopen):
uri = self.type
- if uri.startswith('file:'):
- simple_populate(self, uri,
- 'allocation_size=512,leaf_page_max=512,key_format=S', 10000)
- else:
- complex_populate(self, uri,
- 'allocation_size=512,leaf_page_max=512,key_format=S', 10000)
+ ds = self.dataset(self, uri, 10000,
+ config='allocation_size=512,leaf_page_max=512')
+ ds.populate()
# Optionally close the connection so everything is forced to disk,
# insert lists are an entirely different path in the code.
@@ -168,7 +161,7 @@ class test_cursor_random_invisible(wttest.WiredTigerTestCase):
# Start a transaction.
self.session.begin_transaction()
for i in range(1, 100):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Open another session, the updates won't yet be visible, we shouldn't
# find anything at all.
@@ -182,19 +175,19 @@ class test_cursor_random_invisible(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(uri, None)
# Insert a single leading record.
- cursor[key_populate(cursor, 1)] = value_populate(cursor, 1)
+ cursor[simple_key(cursor, 1)] = simple_value(cursor, 1)
# Start a transaction.
self.session.begin_transaction()
for i in range(2, 100):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Open another session, the updates won't yet be visible, we should
# return the only possible record.
s = self.conn.open_session()
cursor = s.open_cursor(uri, None, self.config)
self.assertEquals(cursor.next(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 1))
+ self.assertEqual(cursor.get_key(), simple_key(cursor, 1))
def test_cursor_random_invisible_before(self):
uri = self.type
@@ -202,19 +195,19 @@ class test_cursor_random_invisible(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(uri, None)
# Insert a single leading record.
- cursor[key_populate(cursor, 99)] = value_populate(cursor, 99)
+ cursor[simple_key(cursor, 99)] = simple_value(cursor, 99)
# Start a transaction.
self.session.begin_transaction()
for i in range(2, 100):
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
+ cursor[simple_key(cursor, i)] = simple_value(cursor, i)
# Open another session, the updates won't yet be visible, we should
# return the only possible record.
s = self.conn.open_session()
cursor = s.open_cursor(uri, None, self.config)
self.assertEquals(cursor.next(), 0)
- self.assertEqual(cursor.get_key(), key_populate(cursor, 99))
+ self.assertEqual(cursor.get_key(), simple_key(cursor, 99))
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_random02.py b/src/third_party/wiredtiger/test/suite/test_cursor_random02.py
index 93aa97f2282..d18d8efd94d 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor_random02.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_random02.py
@@ -27,14 +27,16 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import complex_populate, simple_populate
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# test_cursor_random02.py
# Cursor next_random operations
class test_cursor_random02(wttest.WiredTigerTestCase):
- type = 'table:random'
+ types = [
+ ('lsm', dict(type='lsm:random')),
+ ('table', dict(type='table:random'))
+ ]
config = [
('not-sample', dict(config='next_random=true'))
]
@@ -46,26 +48,37 @@ class test_cursor_random02(wttest.WiredTigerTestCase):
('10000', dict(records=10000)),
('50000', dict(records=50000)),
]
- scenarios = make_scenarios(config, records)
+ scenarios = make_scenarios(config, records, types)
# Check that next_random works in the presence of a larger set of values,
# where the values are in an insert list.
def test_cursor_random_reasonable_distribution(self):
uri = self.type
num_entries = self.records
+ if uri == 'table:random':
+ config = 'leaf_page_max=100MB'
+ else:
+ config = ''
# Set the leaf-page-max value, otherwise the page might split.
- simple_populate(self, uri,
- 'leaf_page_max=100MB,key_format=S', num_entries)
+ ds = SimpleDataSet(self, uri, num_entries, config=config)
+ ds.populate()
# Setup an array to track which keys are seen
visitedKeys = [0] * (num_entries + 1)
+ # Setup a counter to see when we find a sequential key
+ sequentialKeys = 0
cursor = self.session.open_cursor(uri, None, 'next_random=true')
+ lastKey = None
for i in range(0, num_entries):
self.assertEqual(cursor.next(), 0)
current = cursor.get_key()
current = int(current)
visitedKeys[current] = visitedKeys[current] + 1
+ if lastKey != None:
+ if current == (lastKey + 1):
+ sequentialKeys += 1
+ lastKey = current
differentKeys = sum(x > 0 for x in visitedKeys)
@@ -76,7 +89,10 @@ class test_cursor_random02(wttest.WiredTigerTestCase):
str(num_entries) + ', ' + \
str((int)((differentKeys * 100) / num_entries)) + '%')
'''
-
+ # Can't test for non-sequential data when there is 1 item in the table
+ if num_entries > 1:
+ self.assertGreater(num_entries - 1, sequentialKeys,
+ 'cursor is returning sequential data')
self.assertGreater(differentKeys, num_entries / 4,
'next_random random distribution not adequate')
diff --git a/src/third_party/wiredtiger/test/suite/test_drop.py b/src/third_party/wiredtiger/test/suite/test_drop.py
index a3e80214295..e241c05aa68 100644
--- a/src/third_party/wiredtiger/test/suite/test_drop.py
+++ b/src/third_party/wiredtiger/test/suite/test_drop.py
@@ -28,8 +28,8 @@
import os, time
import wiredtiger, wttest
-from helper import confirm_does_not_exist, complex_populate, \
- complex_populate_index_name, simple_populate
+from helper import confirm_does_not_exist
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# test_drop.py
@@ -45,9 +45,10 @@ class test_drop(wttest.WiredTigerTestCase):
])
# Populate an object, remove it and confirm it no longer exists.
- def drop(self, populate, with_cursor, reopen, drop_index):
+ def drop(self, dataset, with_cursor, reopen, drop_index):
uri = self.uri + self.name
- populate(self, uri, 'key_format=S' + self.extra_config, 10)
+ ds = dataset(self, uri, 10, config=self.extra_config)
+ ds.populate()
# Open cursors should cause failure.
if with_cursor:
@@ -60,7 +61,7 @@ class test_drop(wttest.WiredTigerTestCase):
self.reopen_conn()
if drop_index:
- drop_uri = complex_populate_index_name(self, uri, 0)
+ drop_uri = ds.index_name(0)
else:
drop_uri = uri
self.session.drop(drop_uri, None)
@@ -73,7 +74,7 @@ class test_drop(wttest.WiredTigerTestCase):
# case has no indices.
for with_cursor in [False, True]:
for reopen in [False, True]:
- self.drop(simple_populate, with_cursor, reopen, False)
+ self.drop(SimpleDataSet, with_cursor, reopen, False)
# A complex, multi-file table object.
# Try all test combinations.
@@ -81,7 +82,7 @@ class test_drop(wttest.WiredTigerTestCase):
for with_cursor in [False, True]:
for reopen in [False, True]:
for drop_index in [False, True]:
- self.drop(complex_populate, with_cursor,
+ self.drop(ComplexDataSet, with_cursor,
reopen, drop_index)
# Test drop of a non-existent object: force succeeds, without force fails.
diff --git a/src/third_party/wiredtiger/test/suite/test_drop02.py b/src/third_party/wiredtiger/test/suite/test_drop02.py
index 677ba3866b2..017aa64e312 100644
--- a/src/third_party/wiredtiger/test/suite/test_drop02.py
+++ b/src/third_party/wiredtiger/test/suite/test_drop02.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import simple_populate
+from wtdataset import SimpleDataSet
# test_drop02.py
# Test dropping an LSM tree on first open. There was a bug where this
@@ -38,7 +38,8 @@ class test_drop02(wttest.WiredTigerTestCase):
# Populate an object, remove it and confirm it no longer exists.
def test_drop(self):
uri = 'lsm:' + self.name
- simple_populate(self, uri, 'key_format=S', 100000)
+ ds = SimpleDataSet(self, uri, 100000)
+ ds.populate()
self.reopen_conn()
self.session.drop(uri, None)
diff --git a/src/third_party/wiredtiger/test/suite/test_dump.py b/src/third_party/wiredtiger/test/suite/test_dump.py
index 280d5870359..f6a83c32489 100644
--- a/src/third_party/wiredtiger/test/suite/test_dump.py
+++ b/src/third_party/wiredtiger/test/suite/test_dump.py
@@ -28,19 +28,18 @@
import os, shutil
import wiredtiger, wttest
-from helper import \
- complex_populate, complex_populate_check, \
- simple_populate, simple_populate_check, \
- simple_index_populate, simple_index_populate_check
+
from suite_subprocess import suite_subprocess
from wtscenario import make_scenarios
+from wtdataset import SimpleDataSet, SimpleIndexDataSet, SimpleLSMDataSet, \
+ ComplexDataSet, ComplexLSMDataSet
# test_dump.py
# Utilities: wt dump
# Test the dump utility (I'm not testing the dump cursors, that's what the
# utility uses underneath).
class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
- dir='dump.dir' # Backup directory name
+ dir = 'dump.dir' # Backup directory name
name = 'test_dump'
name2 = 'test_dumpb'
@@ -56,30 +55,20 @@ class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
('string', dict(keyfmt='S'))
]
types = [
- ('file', dict(uri='file:', config='', lsm=False,
- populate=simple_populate,
- populate_check=simple_populate_check)),
- ('lsm', dict(uri='lsm:', config='', lsm=True,
- populate=simple_populate,
- populate_check=simple_populate_check)),
- ('table-simple', dict(uri='table:', config='', lsm=False,
- populate=simple_populate,
- populate_check=simple_populate_check)),
- ('table-index', dict(uri='table:', config='', lsm=False,
- populate=simple_index_populate,
- populate_check=simple_index_populate_check)),
- ('table-simple-lsm', dict(uri='table:', config='type=lsm', lsm=True,
- populate=simple_populate,
- populate_check=simple_populate_check)),
- ('table-complex', dict(uri='table:', config='', lsm=False,
- populate=complex_populate,
- populate_check=complex_populate_check)),
- ('table-complex-lsm', dict(uri='table:', config='type=lsm', lsm=True,
- populate=complex_populate,
- populate_check=complex_populate_check))
+ ('file', dict(uri='file:', dataset=SimpleDataSet)),
+ ('lsm', dict(uri='lsm:', dataset=SimpleDataSet)),
+ ('table-simple', dict(uri='table:', dataset=SimpleDataSet)),
+ ('table-index', dict(uri='table:', dataset=SimpleIndexDataSet)),
+ ('table-simple-lsm', dict(uri='table:', dataset=SimpleLSMDataSet)),
+ ('table-complex', dict(uri='table:', dataset=ComplexDataSet)),
+ ('table-complex-lsm', dict(uri='table:', dataset=ComplexLSMDataSet)),
]
scenarios = make_scenarios(types, keyfmt, dumpfmt)
+ def skip(self):
+ return (self.dataset.is_lsm() or self.uri == 'lsm:') and \
+ self.keyfmt == 'r'
+
# Extract the values lines from the dump output.
def value_lines(self, fname):
# mode:
@@ -108,14 +97,14 @@ class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
# Dump, re-load and do a content comparison.
def test_dump(self):
# LSM and column-store isn't a valid combination.
- if self.lsm and self.keyfmt == 'r':
+ if self.skip():
return
# Create the object.
uri = self.uri + self.name
uri2 = self.uri + self.name2
- self.populate(self, uri,
- self.config + ',key_format=' + self.keyfmt, self.nentries)
+ pop = self.dataset(self, uri, self.nentries, key_format=self.keyfmt)
+ pop.populate()
# Dump the object.
os.mkdir(self.dir)
@@ -136,14 +125,14 @@ class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
# Check the object's contents
self.reopen_conn(self.dir)
- self.populate_check(self, uri, self.nentries)
+ pop.check()
# Re-load the object again in the original directory.
self.reopen_conn('.')
self.runWt(['-h', self.dir, 'load', '-f', 'dump.out'])
# Check the contents, they shouldn't have changed.
- self.populate_check(self, uri, self.nentries)
+ pop.check()
# Re-load the object again, but confirm -n (no overwrite) fails.
self.runWt(['-h', self.dir, 'load', '-n', '-f', 'dump.out'],
@@ -151,7 +140,7 @@ class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
self.check_non_empty_file('errfile.out')
# If there are indices, dump one of them and check the output.
- if self.populate == complex_populate:
+ if self.dataset == ComplexDataSet:
indexuri = 'index:' + self.name + ':indx1'
hexopt = ['-x'] if self.hex == 1 else []
self.runWt(['-h', self.dir, 'dump'] + hexopt + [indexuri],
@@ -166,7 +155,8 @@ class test_dump(wttest.WiredTigerTestCase, suite_subprocess):
# Check the contents in the new table.
self.reopen_conn(self.dir)
- self.populate_check(self, uri2, self.nentries)
+ pop = self.dataset(self, uri2, self.nentries, key_format=self.keyfmt)
+ pop.check()
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_dupc.py b/src/third_party/wiredtiger/test/suite/test_dupc.py
index 12b18f1ba79..c0cf6acc75e 100644
--- a/src/third_party/wiredtiger/test/suite/test_dupc.py
+++ b/src/third_party/wiredtiger/test/suite/test_dupc.py
@@ -32,7 +32,7 @@
import os, time
import wiredtiger, wttest
-from helper import complex_populate, key_populate, simple_populate
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# Test session.open_cursor with cursor duplication.
@@ -40,8 +40,6 @@ class test_duplicate_cursor(wttest.WiredTigerTestCase):
name = 'test_dupc'
nentries = 1000
- config = 'key_format='
-
scenarios = make_scenarios([
('file-r', dict(uri='file:', fmt='r')),
('file-S', dict(uri='file:', fmt='S')),
@@ -51,7 +49,7 @@ class test_duplicate_cursor(wttest.WiredTigerTestCase):
# Iterate through an object, duplicate the cursor and checking that it
# matches the original and is set to the same record.
- def iterate(self, uri):
+ def iterate(self, uri, ds):
cursor = self.session.open_cursor(uri, None, None)
next = 0
while True:
@@ -59,10 +57,10 @@ class test_duplicate_cursor(wttest.WiredTigerTestCase):
if nextret != 0:
break
next += 1
- self.assertEqual(cursor.get_key(), key_populate(cursor, next))
+ self.assertEqual(cursor.get_key(), ds.key(next))
dupc = self.session.open_cursor(None, cursor, None)
self.assertEqual(cursor.compare(dupc), 0)
- self.assertEqual(dupc.get_key(), key_populate(dupc, next))
+ self.assertEqual(dupc.get_key(), ds.key(next))
cursor.close()
cursor = dupc
self.assertEqual(next, self.nentries)
@@ -73,14 +71,16 @@ class test_duplicate_cursor(wttest.WiredTigerTestCase):
uri = self.uri + self.name
# A simple, one-file file or table object.
- simple_populate(self, uri, self.config + self.fmt, self.nentries)
- self.iterate(uri)
+ ds = SimpleDataSet(self, uri, self.nentries, key_format=self.fmt)
+ ds.populate()
+ self.iterate(uri, ds)
self.session.drop(uri, None)
# A complex, multi-file table object.
if self.uri == "table:":
- complex_populate(self, uri, self.config + self.fmt, self.nentries)
- self.iterate(uri)
+ ds = ComplexDataSet(self, uri, self.nentries, key_format=self.fmt)
+ ds.populate()
+ self.iterate(uri, ds)
self.session.drop(uri, None)
if __name__ == '__main__':
diff --git a/src/third_party/wiredtiger/test/suite/test_empty.py b/src/third_party/wiredtiger/test/suite/test_empty.py
index 9fe88107412..578bec618c9 100644
--- a/src/third_party/wiredtiger/test/suite/test_empty.py
+++ b/src/third_party/wiredtiger/test/suite/test_empty.py
@@ -28,7 +28,7 @@
import os
import wiredtiger, wttest
-from helper import key_populate
+from wtdataset import simple_key
from wtscenario import make_scenarios
# test_empty.py
@@ -64,7 +64,7 @@ class test_empty(wttest.WiredTigerTestCase):
# Add a few records to the object and remove them.
cursor = self.session.open_cursor(uri, None, None)
for i in range(1,5):
- key = key_populate(cursor, i)
+ key = simple_key(cursor, i)
cursor[key] = "XXX"
del cursor[key]
diff --git a/src/third_party/wiredtiger/test/suite/test_encrypt01.py b/src/third_party/wiredtiger/test/suite/test_encrypt01.py
index d314cbeadfd..746c9d13e96 100644
--- a/src/third_party/wiredtiger/test/suite/test_encrypt01.py
+++ b/src/third_party/wiredtiger/test/suite/test_encrypt01.py
@@ -57,6 +57,7 @@ class test_encrypt01(wttest.WiredTigerTestCase):
('lz4', dict(log_compress='lz4', block_compress='lz4')),
('snappy', dict(log_compress='snappy', block_compress='snappy')),
('zlib', dict(log_compress='zlib', block_compress='zlib')),
+ ('zstd', dict(log_compress='zstd', block_compress='zstd')),
('none-snappy', dict(log_compress=None, block_compress='snappy')),
('snappy-lz4', dict(log_compress='snappy', block_compress='lz4')),
]
diff --git a/src/third_party/wiredtiger/test/suite/test_inmem01.py b/src/third_party/wiredtiger/test/suite/test_inmem01.py
index c6ae7ff6c4b..1af43bbd9d9 100644
--- a/src/third_party/wiredtiger/test/suite/test_inmem01.py
+++ b/src/third_party/wiredtiger/test/suite/test_inmem01.py
@@ -28,8 +28,7 @@
import wiredtiger, wttest
from time import sleep
-from helper import simple_populate, simple_populate_check
-from helper import key_populate, value_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# test_inmem01.py
@@ -41,54 +40,60 @@ class test_inmem01(wttest.WiredTigerTestCase):
table_config = ',memory_page_max=32k,leaf_page_max=4k'
scenarios = make_scenarios([
- ('col', dict(fmt='key_format=r,value_format=S')),
- ('fix', dict(fmt='key_format=r,value_format=8t')),
- ('row', dict(fmt='key_format=S,value_format=S'))
+ ('col', dict(keyfmt='r', valuefmt='S')),
+ ('fix', dict(keyfmt='r', valuefmt='8t')),
+ ('row', dict(keyfmt='S', valuefmt='S')),
])
# Smoke-test in-memory configurations, add a small amount of data and
# ensure it's visible.
def test_insert(self):
- config = self.fmt + self.table_config
- simple_populate(self, self.uri, config, 1000)
- simple_populate_check(self, self.uri, 1000)
+ ds = SimpleDataSet(self, self.uri, 1000, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.table_config)
+ ds.populate()
+ ds.check()
# Add more data than fits into the configured cache and verify it fails.
def test_insert_over_capacity(self):
- config = self.fmt + self.table_config
msg = '/WT_CACHE_FULL.*/'
+ ds = SimpleDataSet(self, self.uri, 10000000, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.table_config)
self.assertRaisesHavingMessage(wiredtiger.WiredTigerError,
- lambda:simple_populate(self, self.uri, config, 10000000), msg)
+ lambda:ds.populate(), msg)
# Figure out the last key we successfully inserted, and check all
# previous inserts are still there.
cursor = self.session.open_cursor(self.uri, None)
cursor.prev()
last_key = int(cursor.get_key())
- simple_populate_check(self, self.uri, last_key)
+ ds = SimpleDataSet(self, self.uri, last_key, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.table_config)
+ ds.check()
# Fill the cache with data, remove some data, ensure more data can be
# inserted (after a reasonable amount of time for space to be reclaimed).
def test_insert_over_delete(self):
- config = self.fmt + self.table_config
msg = '/WT_CACHE_FULL.*/'
+ ds = SimpleDataSet(self, self.uri, 10000000, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.table_config)
self.assertRaisesHavingMessage(wiredtiger.WiredTigerError,
- lambda:simple_populate(self, self.uri, config, 10000000), msg)
+ lambda:ds.populate(), msg)
# Now that the database contains as much data as will fit into
# the configured cache, verify removes succeed.
cursor = self.session.open_cursor(self.uri, None)
for i in range(1, 100):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
cursor.remove()
# Run queries after adding, removing and re-inserting data.
# Try out keeping a cursor open while adding new data.
def test_insert_over_delete_replace(self):
- config = self.fmt + self.table_config
msg = '/WT_CACHE_FULL.*/'
+ ds = SimpleDataSet(self, self.uri, 10000000, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.table_config)
self.assertRaisesHavingMessage(wiredtiger.WiredTigerError,
- lambda:simple_populate(self, self.uri, config, 10000000), msg)
+ lambda:ds.populate(), msg)
cursor = self.session.open_cursor(self.uri, None)
cursor.prev()
@@ -98,7 +103,7 @@ class test_inmem01(wttest.WiredTigerTestCase):
# the configured cache, verify removes succeed.
cursor = self.session.open_cursor(self.uri, None)
for i in range(1, last_key / 4, 1):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
cursor.remove()
cursor.reset()
@@ -106,7 +111,7 @@ class test_inmem01(wttest.WiredTigerTestCase):
inserted = False
for i in range(1, 1000):
try:
- cursor[key_populate(cursor, 1)] = value_populate(cursor, 1)
+ cursor[ds.key(1)] = ds.value(1)
except wiredtiger.WiredTigerError:
cursor.reset()
sleep(1)
diff --git a/src/third_party/wiredtiger/test/suite/test_inmem02.py b/src/third_party/wiredtiger/test/suite/test_inmem02.py
new file mode 100644
index 00000000000..b5e07fea967
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_inmem02.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2016 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 time import sleep
+from wtdataset import SimpleDataSet
+
+# test_inmem02.py
+# Test in-memory with ignore-cache-size setting.
+class test_inmem02(wttest.WiredTigerTestCase):
+ uri = 'table:inmem02'
+ conn_config = \
+ 'cache_size=3MB,file_manager=(close_idle_time=0),in_memory=true'
+ table_config = 'memory_page_max=32k,leaf_page_max=4k'
+
+ # Add more data than fits into the configured cache and verify it fails.
+ def test_insert_over_allowed(self):
+
+ # Create a new table that is allowed to exceed the cache size, do this
+ # before filling the cache so that the create succeeds
+ self.session.create(
+ self.uri + '_over', 'ignore_in_memory_cache_size=true')
+
+ # Populate a table with enough data to fill the cache.
+ msg = '/WT_CACHE_FULL.*/'
+ ds = SimpleDataSet(self, self.uri, 10000000, config=self.table_config)
+ self.assertRaisesHavingMessage(wiredtiger.WiredTigerError,
+ lambda:ds.populate(), msg)
+
+ # Add some content to the new table
+ cursor = self.session.open_cursor(self.uri + '_over', None)
+ for i in range(1, 1000):
+ cursor[str('%015d' % i)] = str(i) + ': abcdefghijklmnopqrstuvwxyz'
+ cursor.close()
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_join09.py b/src/third_party/wiredtiger/test/suite/test_join09.py
new file mode 100644
index 00000000000..d48353b1580
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_join09.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2016 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 os
+import wiredtiger, wttest, run
+from wtscenario import make_scenarios
+
+# test_join09.py
+# Join bloom filters with false positives
+class test_join09(wttest.WiredTigerTestCase):
+ nentries = 1000
+
+ bloomscen = [
+ ('nobloom', dict(false_positives=False, config='')),
+ ('bloom1000', dict(false_positives=False,
+ config='strategy=bloom,count=1000')),
+ ('bloom10fp', dict(false_positives=True,
+ config='strategy=bloom,count=10,bloom_false_positives=true'))
+ ]
+
+ scenarios = make_scenarios(bloomscen)
+
+ def gen_values(self, i):
+ s = str(i) # 345 => "345"
+ f = s[0:1] + s[0:1] + s[0:1] # 345 => "333"
+ return [s, f]
+
+ def populate(self, s, gen_values):
+ c = s.open_cursor('table:join09', None, None)
+ for i in range(0, self.nentries):
+ c.set_key(i)
+ c.set_value(*gen_values(i))
+ c.insert()
+ c.close()
+
+ # Common function for testing the most basic functionality
+ # of joins
+ def test_join(self):
+ self.session.create('table:join09',
+ 'columns=(k,v0,v1),key_format=i,value_format=SS')
+ self.session.create('index:join09:index0','columns=(v0)')
+ self.session.create('index:join09:index1','columns=(v1)')
+
+ self.populate(self.session, self.gen_values)
+
+ jc = self.session.open_cursor('join:table:join09', None, None)
+ c0 = self.session.open_cursor('index:join09:index0', None, None)
+ c0.set_key('520')
+ self.assertEquals(0, c0.search())
+ self.session.join(jc, c0, 'compare=ge')
+
+ joinconfig = 'compare=eq,' + self.config
+ c1 = self.session.open_cursor('index:join09:index1', None, None)
+ c1.set_key('555')
+ self.assertEquals(0, c1.search())
+ self.session.join(jc, c1, joinconfig)
+
+ mbr = set(range(520,600)) | set(range(53,60))
+
+ fp_count = 0
+ while jc.next() == 0:
+ [k] = jc.get_keys()
+ [v0,v1] = jc.get_values()
+ self.assertEquals(self.gen_values(k), [v0, v1])
+ if not k in mbr:
+ # With false positives, we can see extra values
+ if self.false_positives:
+ fp_count += 1
+ continue
+ self.tty('**** ERROR: result ' + str(k) + ' is not in: ' +
+ str(mbr))
+ self.assertTrue(k in mbr)
+ mbr.remove(k)
+
+ if len(mbr) != 0:
+ self.tty('**** ERROR: did not see these: ' + str(mbr))
+ self.assertEquals(0, len(mbr))
+
+ # Turning on false positives does not guarantee we'll see extra
+ # values, but we've configured our test with a low count to
+ # make sure it happens.
+ if self.false_positives:
+ self.assertTrue(fp_count > 0)
+ jc.close()
+ c1.close()
+ c0.close()
+ self.session.drop('table:join09')
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_jsondump01.py b/src/third_party/wiredtiger/test/suite/test_jsondump01.py
index dc8027c2115..c7fa9cdf397 100644
--- a/src/third_party/wiredtiger/test/suite/test_jsondump01.py
+++ b/src/third_party/wiredtiger/test/suite/test_jsondump01.py
@@ -28,11 +28,9 @@
import os, json
import wiredtiger, wttest
-from helper import \
- complex_populate, complex_populate_check, complex_populate_check_cursor,\
- simple_populate, simple_populate_check, simple_populate_check_cursor, \
- simple_index_populate, simple_index_populate_check, \
- simple_index_populate_check_cursor, compare_files
+from wtdataset import SimpleDataSet, SimpleLSMDataSet, SimpleIndexDataSet, \
+ ComplexDataSet, ComplexLSMDataSet
+from helper import compare_files
from suite_subprocess import suite_subprocess
from wtscenario import make_scenarios
@@ -40,7 +38,8 @@ from wtscenario import make_scenarios
# It emulates a WT cursor well enough for the *_check_cursor methods.
# They just need an iterable object.
class FakeCursor:
- def __init__(self, keyfmt, valuefmt, rows):
+ def __init__(self, uri, keyfmt, valuefmt, rows):
+ self.uri = uri
self.key_format = keyfmt
self.value_format = valuefmt
self.rows = rows
@@ -79,47 +78,30 @@ class test_jsondump01(wttest.WiredTigerTestCase, suite_subprocess):
('string', dict(keyfmt='S'))
]
types = [
- ('file', dict(uri='file:', config='', lsm=False,
- populate=simple_populate,
- populate_check=simple_populate_check,
- populate_check_cursor=simple_populate_check_cursor)),
- ('lsm', dict(uri='lsm:', config='', lsm=True,
- populate=simple_populate,
- populate_check=simple_populate_check,
- populate_check_cursor=simple_populate_check_cursor)),
- ('table-simple', dict(uri='table:', config='', lsm=False,
- populate=simple_populate,
- populate_check=simple_populate_check,
- populate_check_cursor=simple_populate_check_cursor)),
- ('table-index', dict(uri='table:', config='', lsm=False,
- populate=simple_index_populate,
- populate_check=simple_index_populate_check,
- populate_check_cursor=simple_index_populate_check_cursor)),
- ('table-simple-lsm', dict(uri='table:', config='type=lsm', lsm=True,
- populate=simple_populate,
- populate_check=simple_populate_check,
- populate_check_cursor=simple_populate_check_cursor)),
- ('table-complex', dict(uri='table:', config='', lsm=False,
- populate=complex_populate,
- populate_check=complex_populate_check,
- populate_check_cursor=complex_populate_check_cursor)),
- ('table-complex-lsm', dict(uri='table:', config='type=lsm', lsm=True,
- populate=complex_populate,
- populate_check=complex_populate_check,
- populate_check_cursor=complex_populate_check_cursor))
+ ('file', dict(uri='file:', dataset=SimpleDataSet)),
+ ('lsm', dict(uri='lsm:', dataset=SimpleDataSet)),
+ ('table-simple', dict(uri='table:', dataset=SimpleDataSet)),
+ ('table-index', dict(uri='table:', dataset=SimpleIndexDataSet)),
+ ('table-simple-lsm', dict(uri='table:', dataset=SimpleLSMDataSet)),
+ ('table-complex', dict(uri='table:', dataset=ComplexDataSet)),
+ ('table-complex-lsm', dict(uri='table:', dataset=ComplexLSMDataSet))
]
scenarios = make_scenarios(types, keyfmt)
+ def skip(self):
+ return (self.dataset.is_lsm() or self.uri == 'lsm:') and \
+ self.keyfmt == 'r'
+
# Dump using util, re-load using python's JSON, and do a content comparison.
def test_jsondump_util(self):
# LSM and column-store isn't a valid combination.
- if self.lsm and self.keyfmt == 'r':
+ if self.skip():
return
# Create the object.
uri = self.uri + self.name
- self.populate(self, uri, self.config + ',key_format=' + self.keyfmt,
- self.nentries)
+ ds = self.dataset(self, uri, self.nentries, key_format=self.keyfmt)
+ ds.populate()
# Dump the object.
self.runWt(['dump', '-j', uri], outfilename='jsondump.out')
@@ -141,20 +123,21 @@ class test_jsondump01(wttest.WiredTigerTestCase, suite_subprocess):
# check the contents of the data we read.
# we only use a wt cursor to get the key_format/value_format.
cursor = self.session.open_cursor(uri, None)
- fake = FakeCursor(cursor.key_format, cursor.value_format, data)
+ fake = FakeCursor(uri, cursor.key_format, cursor.value_format, data)
cursor.close()
- self.populate_check_cursor(self, fake, self.nentries)
+ ds.check_cursor(fake)
# Dump using util, re-load using python's JSON, and do a content comparison.
def test_jsonload_util(self):
# LSM and column-store isn't a valid combination.
- if self.lsm and self.keyfmt == 'r':
+ if self.skip():
return
# Create the object.
uri = self.uri + self.name
uri2 = self.uri + self.name2
- self.populate(self, uri, 'key_format=' + self.keyfmt, self.nentries)
+ ds = self.dataset(self, uri, self.nentries, key_format=self.keyfmt)
+ ds.populate()
# Dump the object.
self.runWt(['dump', '-j', uri], outfilename='jsondump.out')
@@ -165,7 +148,9 @@ class test_jsondump01(wttest.WiredTigerTestCase, suite_subprocess):
self.runWt(loadcmd)
# Check the contents of the data we read.
- self.populate_check(self, uri2, self.nentries)
+ # We use the dataset only for checking.
+ ds2 = self.dataset(self, uri2, self.nentries, key_format=self.keyfmt)
+ ds2.check()
# Reload into the original uri, and dump into another file.
self.session.drop(uri, None)
@@ -175,7 +160,7 @@ class test_jsondump01(wttest.WiredTigerTestCase, suite_subprocess):
# Compare the two outputs, and check the content again.
compare_files(self, 'jsondump.out', 'jsondump2.out')
- self.populate_check(self, uri, self.nentries)
+ ds.check()
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_lsm01.py b/src/third_party/wiredtiger/test/suite/test_lsm01.py
index f6cee20e896..b44df4bae14 100644
--- a/src/third_party/wiredtiger/test/suite/test_lsm01.py
+++ b/src/third_party/wiredtiger/test/suite/test_lsm01.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wtscenario, wttest
-from helper import simple_populate
+from wtdataset import SimpleDataSet
# test_lsm01.py
# Test LSM tree configuration options.
@@ -77,7 +77,7 @@ class test_lsm01(wttest.WiredTigerTestCase):
args += ')' # Close the LSM configuration option group
self.verbose(3,
'Test LSM with config: ' + args + ' count: ' + str(self.nrecs))
- simple_populate(self, self.uri, args, self.nrecs)
+ SimpleDataSet(self, self.uri, self.nrecs).populate()
# TODO: Adding an explicit drop here can cause deadlocks, if a merge
# is still happening. See issue #349.
diff --git a/src/third_party/wiredtiger/test/suite/test_lsm02.py b/src/third_party/wiredtiger/test/suite/test_lsm02.py
index 85067665017..e9628139a97 100644
--- a/src/third_party/wiredtiger/test/suite/test_lsm02.py
+++ b/src/third_party/wiredtiger/test/suite/test_lsm02.py
@@ -27,7 +27,6 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wtscenario, wttest
-from helper import simple_populate
# test_lsm02.py
# Test LSM schema level operations
diff --git a/src/third_party/wiredtiger/test/suite/test_lsm03.py b/src/third_party/wiredtiger/test/suite/test_lsm03.py
index 448d864c646..d916db415da 100644
--- a/src/third_party/wiredtiger/test/suite/test_lsm03.py
+++ b/src/third_party/wiredtiger/test/suite/test_lsm03.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wtscenario, wttest
-from helper import simple_populate
+from wtdataset import SimpleDataSet
# test_lsm03.py
# Check to make sure that LSM schema operations don't get EBUSY when
@@ -37,13 +37,14 @@ class test_lsm03(wttest.WiredTigerTestCase):
# Use small pages so we generate some internal layout
# Setup LSM so multiple chunks are present
- config = 'key_format=S,allocation_size=512,internal_page_max=512' + \
+ config = 'allocation_size=512,internal_page_max=512' + \
',leaf_page_max=1k,lsm=(chunk_size=512k,merge_min=10)'
# Populate an object then drop it.
def test_lsm_drop_active(self):
uri = 'lsm:' + self.name
- simple_populate(self, uri, self.config, 10000)
+ ds = SimpleDataSet(self, uri, 10000, config=self.config)
+ ds.populate()
# Force to disk
self.reopen_conn()
@@ -55,6 +56,7 @@ class test_lsm03(wttest.WiredTigerTestCase):
cursor.close()
# Add enough records that a merge should be running
- simple_populate(self, uri, self.config, 50000)
+ ds = SimpleDataSet(self, uri, 50000, config=self.config)
+ ds.populate()
# The drop should succeed even when LSM work units are active
self.session.drop(uri)
diff --git a/src/third_party/wiredtiger/test/suite/test_nsnap01.py b/src/third_party/wiredtiger/test/suite/test_nsnap01.py
index 7e8951750f8..4d5555277fe 100644
--- a/src/third_party/wiredtiger/test/suite/test_nsnap01.py
+++ b/src/third_party/wiredtiger/test/suite/test_nsnap01.py
@@ -30,7 +30,7 @@
# Named snapshots: basic API
from suite_subprocess import suite_subprocess
-from helper import simple_populate
+from wtdataset import SimpleDataSet
import wiredtiger, wttest
class test_nsnap01(wttest.WiredTigerTestCase, suite_subprocess):
@@ -53,7 +53,7 @@ class test_nsnap01(wttest.WiredTigerTestCase, suite_subprocess):
def test_named_snapshots(self):
# Populate a table
end = start = 0
- simple_populate(self, self.uri, 'key_format=i', 0)
+ SimpleDataSet(self, self.uri, 0, key_format='i').populate()
# Now run a workload:
# every iteration:
diff --git a/src/third_party/wiredtiger/test/suite/test_nsnap02.py b/src/third_party/wiredtiger/test/suite/test_nsnap02.py
index 510c9d421ef..ed1c96ebe50 100644
--- a/src/third_party/wiredtiger/test/suite/test_nsnap02.py
+++ b/src/third_party/wiredtiger/test/suite/test_nsnap02.py
@@ -30,7 +30,7 @@
# Named snapshots: Combinations of dropping snapshots
from suite_subprocess import suite_subprocess
-from helper import simple_populate
+from wtdataset import SimpleDataSet
import wiredtiger, wttest
class test_nsnap02(wttest.WiredTigerTestCase, suite_subprocess):
@@ -63,7 +63,7 @@ class test_nsnap02(wttest.WiredTigerTestCase, suite_subprocess):
def create_snapshots(self):
# Populate a table
end = start = 0
- simple_populate(self, self.uri, 'key_format=i', 0)
+ SimpleDataSet(self, self.uri, 0, key_format='i').populate()
# Create a set of snapshots
# Each snapshot has a bunch of new data
diff --git a/src/third_party/wiredtiger/test/suite/test_nsnap03.py b/src/third_party/wiredtiger/test/suite/test_nsnap03.py
index 3986c0c1a0a..6964fb914c3 100644
--- a/src/third_party/wiredtiger/test/suite/test_nsnap03.py
+++ b/src/third_party/wiredtiger/test/suite/test_nsnap03.py
@@ -30,7 +30,7 @@
# Named snapshots: Access and create from multiple sessions
from suite_subprocess import suite_subprocess
-from helper import simple_populate
+from wtdataset import SimpleDataSet
import wiredtiger, wttest
class test_nsnap03(wttest.WiredTigerTestCase, suite_subprocess):
@@ -55,7 +55,7 @@ class test_nsnap03(wttest.WiredTigerTestCase, suite_subprocess):
def test_named_snapshots(self):
# Populate a table
end = start = 0
- simple_populate(self, self.uri, 'key_format=i', 0)
+ SimpleDataSet(self, self.uri, 0, key_format='i').populate()
# Now run a workload:
# every iteration:
diff --git a/src/third_party/wiredtiger/test/suite/test_nsnap04.py b/src/third_party/wiredtiger/test/suite/test_nsnap04.py
index f9ef26b5600..60901dd2ee3 100644
--- a/src/third_party/wiredtiger/test/suite/test_nsnap04.py
+++ b/src/third_party/wiredtiger/test/suite/test_nsnap04.py
@@ -29,9 +29,9 @@
# test_nsnap04.py
# Named snapshots: Create snapshot from running transaction
-from suite_subprocess import suite_subprocess
-from helper import simple_populate
import wiredtiger, wttest
+from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet
class test_nsnap04(wttest.WiredTigerTestCase, suite_subprocess):
tablename = 'test_nsnap04'
@@ -52,7 +52,7 @@ class test_nsnap04(wttest.WiredTigerTestCase, suite_subprocess):
def test_named_snapshots(self):
# Populate a table
end = start = 0
- simple_populate(self, self.uri, 'key_format=i', 0)
+ SimpleDataSet(self, self.uri, 0, key_format='i').populate()
snapshots = []
c = self.session.open_cursor(self.uri)
diff --git a/src/third_party/wiredtiger/test/suite/test_overwrite.py b/src/third_party/wiredtiger/test/suite/test_overwrite.py
index b7af1612243..4739abaa578 100644
--- a/src/third_party/wiredtiger/test/suite/test_overwrite.py
+++ b/src/third_party/wiredtiger/test/suite/test_overwrite.py
@@ -27,7 +27,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
import wiredtiger, wttest
-from helper import key_populate, simple_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# test_overwrite.py
@@ -35,22 +35,23 @@ from wtscenario import make_scenarios
class test_overwrite(wttest.WiredTigerTestCase):
name = 'overwrite'
scenarios = make_scenarios([
- ('file-r', dict(type='file:',keyfmt='r')),
- ('file-S', dict(type='file:',keyfmt='S')),
- ('lsm-S', dict(type='lsm:',keyfmt='S')),
- ('table-r', dict(type='table:',keyfmt='r')),
- ('table-S', dict(type='table:',keyfmt='S')),
+ ('file-r', dict(type='file:', keyfmt='r', dataset=SimpleDataSet)),
+ ('file-S', dict(type='file:', keyfmt='S', dataset=SimpleDataSet)),
+ ('lsm-S', dict(type='lsm:', keyfmt='S', dataset=SimpleDataSet)),
+ ('table-r', dict(type='table:', keyfmt='r', dataset=SimpleDataSet)),
+ ('table-S', dict(type='table:', keyfmt='S', dataset=SimpleDataSet)),
])
# Confirm a cursor configured with/without overwrite correctly handles
# non-existent records during insert, remove and update operations.
def test_overwrite_insert(self):
uri = self.type + self.name
- simple_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
# Insert of an existing record with overwrite off fails.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 5))
+ cursor.set_key(ds.key(5))
cursor.set_value('XXXXXXXXXX')
self.assertRaises(wiredtiger.WiredTigerError, lambda: cursor.insert())
@@ -60,78 +61,80 @@ class test_overwrite(wttest.WiredTigerTestCase):
# failure modes are for non-existent records, and you cannot duplicate
# cursor pointing to non-existent records.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 5))
+ cursor.set_key(ds.key(5))
dupc = self.session.open_cursor(None, cursor, "overwrite=true")
dupc.set_value('XXXXXXXXXX')
self.assertEquals(dupc.insert(), 0)
# Insert of an existing record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 6))
+ cursor.set_key(ds.key(6))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.insert(), 0)
# Insert of a non-existent record with overwrite off succeeds.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 200))
+ cursor.set_key(ds.key(200))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.insert(), 0)
# Insert of a non-existent record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 201))
+ cursor.set_key(ds.key(201))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.insert(), 0)
def test_overwrite_remove(self):
uri = self.type + self.name
- simple_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
# Remove of an existing record with overwrite off succeeds.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 5))
+ cursor.set_key(ds.key(5))
self.assertEquals(cursor.remove(), 0)
# Remove of an existing record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 6))
+ cursor.set_key(ds.key(6))
self.assertEquals(cursor.remove(), 0)
# Remove of a non-existent record with overwrite off fails.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 200))
+ cursor.set_key(ds.key(200))
self.assertEquals(cursor.remove(), wiredtiger.WT_NOTFOUND)
# Remove of a non-existent record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 201))
+ cursor.set_key(ds.key(201))
self.assertEquals(cursor.remove(), 0)
def test_overwrite_update(self):
uri = self.type + self.name
- simple_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = self.dataset(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
# Update of an existing record with overwrite off succeeds.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 5))
+ cursor.set_key(ds.key(5))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.update(), 0)
# Update of an existing record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 6))
+ cursor.set_key(ds.key(6))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.update(), 0)
# Update of a non-existent record with overwrite off fails.
cursor = self.session.open_cursor(uri, None, "overwrite=false")
- cursor.set_key(key_populate(cursor, 200))
+ cursor.set_key(ds.key(200))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.update(), wiredtiger.WT_NOTFOUND)
# Update of a non-existent record with overwrite on succeeds.
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, 201))
+ cursor.set_key(ds.key(201))
cursor.set_value('XXXXXXXXXX')
self.assertEquals(cursor.update(), 0)
diff --git a/src/third_party/wiredtiger/test/suite/test_readonly03.py b/src/third_party/wiredtiger/test/suite/test_readonly03.py
index a5607fcfe80..f30c591ca59 100644
--- a/src/third_party/wiredtiger/test/suite/test_readonly03.py
+++ b/src/third_party/wiredtiger/test/suite/test_readonly03.py
@@ -31,9 +31,9 @@
# all return ENOTSUP.
#
-from helper import simple_populate
-from suite_subprocess import suite_subprocess
import os, sys, wiredtiger, wttest
+from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet
class test_readonly03(wttest.WiredTigerTestCase, suite_subprocess):
uri = 'table:test_readonly03'
@@ -61,7 +61,8 @@ class test_readonly03(wttest.WiredTigerTestCase, suite_subprocess):
create_params = 'key_format=i,value_format=i'
entries = 10
# Create a database and a table.
- simple_populate(self, self.uri, create_params, entries)
+ SimpleDataSet(self, self.uri, entries, key_format='i',
+ value_format='i').populate()
#
# Now close and reopen. Note that the connection function
diff --git a/src/third_party/wiredtiger/test/suite/test_rebalance.py b/src/third_party/wiredtiger/test/suite/test_rebalance.py
index acb73d82a13..2d160bafec0 100644
--- a/src/third_party/wiredtiger/test/suite/test_rebalance.py
+++ b/src/third_party/wiredtiger/test/suite/test_rebalance.py
@@ -28,7 +28,7 @@
import os, time
import wiredtiger, wttest
-from helper import complex_populate, simple_populate
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# test_rebalance.py
@@ -38,7 +38,7 @@ class test_rebalance(wttest.WiredTigerTestCase):
# Use small pages so we generate some internal layout
# Setup LSM so multiple chunks are present
- config = 'key_format=S,allocation_size=512,internal_page_max=512' + \
+ config = 'allocation_size=512,internal_page_max=512' + \
',leaf_page_max=1k,lsm=(chunk_size=512k,merge_min=10)'
scenarios = make_scenarios([
@@ -48,9 +48,10 @@ class test_rebalance(wttest.WiredTigerTestCase):
])
# Populate an object, then rebalance it.
- def rebalance(self, populate, with_cursor):
+ def rebalance(self, dataset, with_cursor):
uri = self.uri + self.name
- populate(self, uri, self.config, 10000)
+ ds = dataset(self, uri, 10000, config=self.config)
+ ds.populate()
# Force to disk, we don't rebalance in-memory objects.
self.reopen_conn()
@@ -68,13 +69,13 @@ class test_rebalance(wttest.WiredTigerTestCase):
# Test rebalance of an object.
def test_rebalance(self):
# Simple file or table object.
- self.rebalance(simple_populate, False)
- self.rebalance(simple_populate, True)
+ self.rebalance(SimpleDataSet, False)
+ self.rebalance(SimpleDataSet, True)
# A complex, multi-file table object.
if self.uri == "table:":
- self.rebalance(complex_populate, False)
- self.rebalance(complex_populate, True)
+ self.rebalance(ComplexDataSet, False)
+ self.rebalance(ComplexDataSet, True)
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_reconfig01.py b/src/third_party/wiredtiger/test/suite/test_reconfig01.py
index fb3fb7edac6..e76becac76a 100644
--- a/src/third_party/wiredtiger/test/suite/test_reconfig01.py
+++ b/src/third_party/wiredtiger/test/suite/test_reconfig01.py
@@ -28,7 +28,7 @@
import time
import wiredtiger, wttest
-from helper import simple_populate
+from wtdataset import SimpleDataSet
# test_reconfig01.py
# Smoke-test the connection reconfiguration operations.
@@ -70,7 +70,7 @@ class test_reconfig01(wttest.WiredTigerTestCase):
# Take all the defaults.
uri = "lsm:test_reconfig"
nrecs = 10
- simple_populate(self, uri, 'key_format=S', nrecs)
+ SimpleDataSet(self, uri, nrecs).populate()
# Sleep to make sure all threads are started.
time.sleep(2)
# Now that an LSM tree exists, reconfigure LSM manager threads.
@@ -78,7 +78,7 @@ class test_reconfig01(wttest.WiredTigerTestCase):
self.conn.reconfigure("lsm_manager=(worker_thread_max=10)")
# Generate some work
nrecs = 20
- simple_populate(self, uri, 'key_format=S', nrecs)
+ SimpleDataSet(self, uri, nrecs).populate()
# Now reconfigure fewer threads.
self.conn.reconfigure("lsm_manager=(worker_thread_max=3)")
diff --git a/src/third_party/wiredtiger/test/suite/test_reconfig02.py b/src/third_party/wiredtiger/test/suite/test_reconfig02.py
index 9d9ac220aa7..36a78a1805f 100644
--- a/src/third_party/wiredtiger/test/suite/test_reconfig02.py
+++ b/src/third_party/wiredtiger/test/suite/test_reconfig02.py
@@ -28,7 +28,6 @@
import fnmatch, os, time
import wiredtiger, wttest
-from helper import simple_populate
# test_reconfig02.py
# Smoke-test the connection reconfiguration operations.
diff --git a/src/third_party/wiredtiger/test/suite/test_reconfig03.py b/src/third_party/wiredtiger/test/suite/test_reconfig03.py
index e9d39ea5a76..0019bf4814e 100644
--- a/src/third_party/wiredtiger/test/suite/test_reconfig03.py
+++ b/src/third_party/wiredtiger/test/suite/test_reconfig03.py
@@ -28,7 +28,7 @@
import fnmatch, os, time
import wiredtiger, wttest
-from helper import simple_populate
+from wtdataset import SimpleDataSet
# test_reconfig03.py
# Test the connection reconfiguration operations used in the MongoDB
@@ -41,16 +41,16 @@ class test_reconfig03(wttest.WiredTigerTestCase):
# can run after we've made modifications.
def test_reconfig03_mdb(self):
entries = 10000
- simple_populate(self, self.uri, 'key_format=S', entries)
+ SimpleDataSet(self, self.uri, entries).populate()
time.sleep(1)
self.conn.reconfigure("eviction_target=81")
- simple_populate(self, self.uri, 'key_format=S', entries * 2)
+ SimpleDataSet(self, self.uri, entries * 2).populate()
time.sleep(1)
self.conn.reconfigure("cache_size=81M")
- simple_populate(self, self.uri, 'key_format=S', entries * 3)
+ SimpleDataSet(self, self.uri, entries * 3).populate()
time.sleep(1)
self.conn.reconfigure("eviction_dirty_target=82")
- simple_populate(self, self.uri, 'key_format=S', entries * 4)
+ SimpleDataSet(self, self.uri, entries * 4).populate()
time.sleep(1)
self.conn.reconfigure("shared_cache=(chunk=11MB, name=bar, reserve=12MB, size=1G)")
diff --git a/src/third_party/wiredtiger/test/suite/test_rename.py b/src/third_party/wiredtiger/test/suite/test_rename.py
index 1979bbb802a..4e3af8e13e0 100644
--- a/src/third_party/wiredtiger/test/suite/test_rename.py
+++ b/src/third_party/wiredtiger/test/suite/test_rename.py
@@ -28,9 +28,8 @@
import os, time
import wiredtiger, wttest
-from helper import confirm_does_not_exist,\
- complex_populate, complex_populate_check,\
- simple_populate, simple_populate_check
+from helper import confirm_does_not_exist
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# test_rename.py
@@ -46,10 +45,13 @@ class test_rename(wttest.WiredTigerTestCase):
# Populate and object, and rename it a couple of times, confirming the
# old name doesn't exist and the new name has the right contents.
- def rename(self, populate, check, with_cursor):
+ def rename(self, dataset, with_cursor):
uri1 = self.uri + self.name1
uri2 = self.uri + self.name2
- populate(self, uri1, 'key_format=S', 10)
+ # Only populate uri1, we keep ds2 for checking.
+ ds1 = dataset(self, uri1, 10)
+ ds2 = dataset(self, uri2, 10)
+ ds1.populate()
# Open cursors should cause failure.
if with_cursor:
@@ -60,24 +62,24 @@ class test_rename(wttest.WiredTigerTestCase):
self.session.rename(uri1, uri2, None)
confirm_does_not_exist(self, uri1)
- check(self, uri2, 10)
+ ds2.check()
self.session.rename(uri2, uri1, None)
confirm_does_not_exist(self, uri2)
- check(self, uri1, 10)
+ ds1.check()
self.session.drop(uri1)
# Test rename of an object.
def test_rename(self):
# Simple, one-file file or table object.
- self.rename(simple_populate, simple_populate_check, False)
- self.rename(simple_populate, simple_populate_check, True)
+ self.rename(SimpleDataSet, False)
+ self.rename(SimpleDataSet, True)
# A complex, multi-file table object.
if self.uri == "table:":
- self.rename(complex_populate, complex_populate_check, False)
- self.rename(complex_populate, complex_populate_check, True)
+ self.rename(ComplexDataSet, False)
+ self.rename(ComplexDataSet, True)
def test_rename_dne(self):
uri1 = self.uri + self.name1
diff --git a/src/third_party/wiredtiger/test/suite/test_shared_cache01.py b/src/third_party/wiredtiger/test/suite/test_shared_cache01.py
index 896a1e569ef..70560a625ee 100644
--- a/src/third_party/wiredtiger/test/suite/test_shared_cache01.py
+++ b/src/third_party/wiredtiger/test/suite/test_shared_cache01.py
@@ -31,7 +31,6 @@ import os
import shutil
import wiredtiger, wttest
from wttest import unittest
-from helper import key_populate, simple_populate
# test_shared_cache01.py
# Checkpoint tests
diff --git a/src/third_party/wiredtiger/test/suite/test_shared_cache02.py b/src/third_party/wiredtiger/test/suite/test_shared_cache02.py
index 4afccbe59f7..7cde6c86695 100644
--- a/src/third_party/wiredtiger/test/suite/test_shared_cache02.py
+++ b/src/third_party/wiredtiger/test/suite/test_shared_cache02.py
@@ -31,7 +31,6 @@ import os
import shutil
import wiredtiger, wttest
from wttest import unittest
-from helper import key_populate, simple_populate
# test_shared_cache02.py
# Shared cache tests
diff --git a/src/third_party/wiredtiger/test/suite/test_split.py b/src/third_party/wiredtiger/test/suite/test_split.py
index 28bf6bc59b0..411778f21ae 100644
--- a/src/third_party/wiredtiger/test/suite/test_split.py
+++ b/src/third_party/wiredtiger/test/suite/test_split.py
@@ -32,9 +32,6 @@
import wiredtiger, wttest
from wiredtiger import stat
-from helper import confirm_empty,\
- key_populate, value_populate, simple_populate,\
- complex_populate, complex_value_populate
# Test splits
class test_split(wttest.WiredTigerTestCase):
diff --git a/src/third_party/wiredtiger/test/suite/test_stat01.py b/src/third_party/wiredtiger/test/suite/test_stat01.py
index 1ad51ee9882..2b04a3cbcd5 100644
--- a/src/third_party/wiredtiger/test/suite/test_stat01.py
+++ b/src/third_party/wiredtiger/test/suite/test_stat01.py
@@ -28,7 +28,7 @@
import helper, wiredtiger, wttest
from wiredtiger import stat
-from helper import key_populate, simple_populate
+from wtdataset import SimpleDataSet, simple_key
from wtscenario import make_scenarios
# test_stat01.py
@@ -89,8 +89,8 @@ class test_stat01(wttest.WiredTigerTestCase):
# Test simple connection statistics.
def test_basic_conn_stats(self):
# Build an object and force some writes.
- config = self.config + ',key_format=' + self.keyfmt
- simple_populate(self, self.uri, config, 1000)
+ SimpleDataSet(self, self.uri, 1000,
+ config=self.config, key_format=self.keyfmt).populate()
self.session.checkpoint(None)
# See that we can get a specific stat value by its key and verify its
@@ -113,7 +113,7 @@ class test_stat01(wttest.WiredTigerTestCase):
value = ""
for i in range(1, self.nentries):
value = value + 1000 * "a"
- cursor[key_populate(cursor, i)] = value
+ cursor[simple_key(cursor, i)] = value
cursor.close()
# Force the object to disk, otherwise we can't check the overflow count.
@@ -140,9 +140,10 @@ class test_stat01(wttest.WiredTigerTestCase):
# Test simple per-checkpoint statistics.
def test_checkpoint_stats(self):
+ ds = SimpleDataSet(self, self.uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
for name in ('first', 'second', 'third'):
- config = self.config + ',key_format=' + self.keyfmt
- helper.simple_populate(self, self.uri, config, self.nentries)
+ ds.populate()
self.session.checkpoint('name=' + name)
cursor = self.session.open_cursor(
'statistics:' + self.uri, None, 'checkpoint=' + name)
diff --git a/src/third_party/wiredtiger/test/suite/test_stat02.py b/src/third_party/wiredtiger/test/suite/test_stat02.py
index 3d2a83d1c3c..cecda7f1ddc 100644
--- a/src/third_party/wiredtiger/test/suite/test_stat02.py
+++ b/src/third_party/wiredtiger/test/suite/test_stat02.py
@@ -28,22 +28,21 @@
import itertools, wiredtiger, wttest
from suite_subprocess import suite_subprocess
+from wtdataset import SimpleDataSet, SimpleLSMDataSet, ComplexDataSet, \
+ ComplexLSMDataSet
from wtscenario import make_scenarios
from wiredtiger import stat
-from helper import complex_populate, complex_populate_lsm, simple_populate
# test_stat02.py
# Statistics cursor configurations.
class test_stat_cursor_config(wttest.WiredTigerTestCase):
pfx = 'test_stat_cursor_config'
uri = [
- ('file', dict(uri='file:' + pfx, pop=simple_populate, cfg='')),
- ('table', dict(uri='table:' + pfx, pop=simple_populate, cfg='')),
- ('table-lsm',
- dict(uri='table:' + pfx, pop=simple_populate, cfg=',type=lsm')),
- ('complex', dict(uri='table:' + pfx, pop=complex_populate, cfg='')),
- ('complex-lsm',
- dict(uri='table:' + pfx, pop=complex_populate_lsm, cfg=''))
+ ('file', dict(uri='file:' + pfx, dataset=SimpleDataSet)),
+ ('table', dict(uri='table:' + pfx, dataset=SimpleDataSet)),
+ ('table-lsm', dict(uri='table:' + pfx, dataset=SimpleLSMDataSet)),
+ ('complex', dict(uri='table:' + pfx, dataset=ComplexDataSet)),
+ ('complex-lsm', dict(uri='table:' + pfx, dataset=ComplexLSMDataSet))
]
data_config = [
('none', dict(data_config='none', ok=[])),
@@ -66,7 +65,7 @@ class test_stat_cursor_config(wttest.WiredTigerTestCase):
# For each database/cursor configuration, confirm the right combinations
# succeed or fail.
def test_stat_cursor_config(self):
- self.pop(self, self.uri, 'key_format=S' + self.cfg, 100)
+ self.dataset(self, self.uri, 100).populate()
config = 'statistics=('
if self.cursor_config != 'empty':
config = config + self.cursor_config
@@ -85,7 +84,7 @@ class test_stat_cursor_conn_clear(wttest.WiredTigerTestCase):
def test_stat_cursor_conn_clear(self):
uri = 'table:' + self.pfx
- complex_populate(self, uri, 'key_format=S', 100)
+ ComplexDataSet(self, uri, 100).populate()
# cursor_insert should clear
# cache_bytes_dirty should not clear
@@ -103,17 +102,17 @@ class test_stat_cursor_dsrc_clear(wttest.WiredTigerTestCase):
pfx = 'test_stat_cursor_dsrc_clear'
uri = [
- ('dsrc_clear_1', dict(uri='file:' + pfx, pop=simple_populate)),
- ('dsrc_clear_2', dict(uri='table:' + pfx, pop=simple_populate)),
- ('dsrc_clear_3', dict(uri='table:' + pfx, pop=complex_populate)),
- ('dsrc_clear_4', dict(uri='table:' + pfx, pop=complex_populate_lsm))
+ ('dsrc_clear_1', dict(uri='file:' + pfx, dataset=SimpleDataSet)),
+ ('dsrc_clear_2', dict(uri='table:' + pfx, dataset=SimpleDataSet)),
+ ('dsrc_clear_3', dict(uri='table:' + pfx, dataset=ComplexDataSet)),
+ ('dsrc_clear_4', dict(uri='table:' + pfx, dataset=ComplexLSMDataSet))
]
scenarios = make_scenarios(uri)
conn_config = 'statistics=(all)'
def test_stat_cursor_dsrc_clear(self):
- self.pop(self, self.uri, 'key_format=S', 100)
+ self.dataset(self, self.uri, 100).populate()
# cursor_insert should clear
#
@@ -132,17 +131,17 @@ class test_stat_cursor_fast(wttest.WiredTigerTestCase):
pfx = 'test_stat_cursor_fast'
uri = [
- ('fast_1', dict(uri='file:' + pfx, pop=simple_populate)),
- ('fast_2', dict(uri='table:' + pfx, pop=simple_populate)),
- ('fast_3', dict(uri='table:' + pfx, pop=complex_populate)),
- ('fast_4', dict(uri='table:' + pfx, pop=complex_populate_lsm))
+ ('fast_1', dict(uri='file:' + pfx, dataset=SimpleDataSet)),
+ ('fast_2', dict(uri='table:' + pfx, dataset=SimpleDataSet)),
+ ('fast_3', dict(uri='table:' + pfx, dataset=ComplexDataSet)),
+ ('fast_4', dict(uri='table:' + pfx, dataset=ComplexLSMDataSet))
]
scenarios = make_scenarios(uri)
conn_config = 'statistics=(all)'
def test_stat_cursor_fast(self):
- self.pop(self, self.uri, 'key_format=S', 100)
+ self.dataset(self, self.uri, 100).populate()
# A "fast" cursor shouldn't see the underlying btree statistics.
# Check "fast" first, otherwise we get a copy of the statistics
@@ -165,7 +164,7 @@ class test_stat_cursor_conn_error(wttest.WiredTigerTestCase):
args = ['none', 'all', 'fast']
for i in list(itertools.permutations(args, 2)):
config = 'create,statistics=(' + i[0] + ',' + i[1] + ')'
- msg = '/only one statistics configuration value/'
+ msg = '/Only one of/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.wiredtiger_open('.', config), msg)
@@ -174,24 +173,90 @@ class test_stat_cursor_dsrc_error(wttest.WiredTigerTestCase):
pfx = 'test_stat_cursor_dsrc_error'
uri = [
- ('dsrc_error_1', dict(uri='file:' + pfx, pop=simple_populate)),
- ('dsrc_error_2', dict(uri='table:' + pfx, pop=simple_populate)),
- ('dsrc_error_3', dict(uri='table:' + pfx, pop=complex_populate)),
- ('dsrc_error_4', dict(uri='table:' + pfx, pop=complex_populate_lsm))
+ ('dsrc_error_1', dict(uri='file:' + pfx, dataset=SimpleDataSet)),
+ ('dsrc_error_2', dict(uri='table:' + pfx, dataset=SimpleDataSet)),
+ ('dsrc_error_3', dict(uri='table:' + pfx, dataset=ComplexDataSet)),
+ ('dsrc_error_4', dict(uri='table:' + pfx, dataset=ComplexLSMDataSet))
]
scenarios = make_scenarios(uri)
conn_config = 'statistics=(all)'
def test_stat_cursor_dsrc_error(self):
- self.pop(self, self.uri, 'key_format=S', 100)
+ self.dataset(self, self.uri, 100).populate()
args = ['all', 'fast']
for i in list(itertools.permutations(args, 2)):
config = 'statistics=(' + i[0] + ',' + i[1] + ')'
- msg = '/only one statistics configuration value/'
+ msg = '/Only one of/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.open_cursor(
'statistics:' + self.uri, None, config), msg)
+# Test data-source cache walk statistics
+class test_stat_cursor_dsrc_cache_walk(wttest.WiredTigerTestCase):
+ uri = 'file:test_stat_cursor_dsrc_cache_walk'
+
+ conn_config = 'statistics=(none)'
+
+ def test_stat_cursor_dsrc_cache_walk(self):
+ SimpleDataSet(self, self.uri, 100).populate()
+ # Ensure that it's an error to get cache_walk stats if none is set
+ msg = '/doesn\'t match the database statistics/'
+ self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
+ lambda: self.session.open_cursor(
+ 'statistics:' + self.uri, None, None), msg)
+
+ # Test configurations that are valid but should not collect
+ # cache walk information. Do these first since the cache walk
+ # statistics are mostly marked as not cleared - so once they are
+ # populated the values will always be returned
+ self.conn.reconfigure('statistics=(cache_walk,fast,clear)')
+ c = self.session.open_cursor(
+ 'statistics:' + self.uri, None, 'statistics=(fast)')
+ self.assertEqual(c[stat.dsrc.cache_state_root_size][2], 0)
+ c.close()
+
+ self.conn.reconfigure('statistics=(all,clear)')
+ c = self.session.open_cursor(
+ 'statistics:' + self.uri, None, 'statistics=(fast)')
+ self.assertEqual(c[stat.dsrc.cache_state_root_size][2], 0)
+ c.close()
+
+ self.conn.reconfigure('statistics=(cache_walk,fast,clear)')
+ c = self.session.open_cursor('statistics:' + self.uri, None, None)
+ self.assertGreater(c[stat.dsrc.cache_state_root_size][2], 0)
+ # Verify that cache_walk didn't imply tree_walk
+ self.assertEqual(c[stat.dsrc.btree_entries][2], 0)
+ c.close()
+
+ self.conn.reconfigure('statistics=(cache_walk,tree_walk,fast,clear)')
+ c = self.session.open_cursor('statistics:' + self.uri, None, None)
+ self.assertGreater(c[stat.dsrc.cache_state_root_size][2], 0)
+ # Verify that cache_walk didn't exclude tree_walk
+ self.assertGreater(c[stat.dsrc.btree_entries][2], 0)
+ c.close()
+
+ self.conn.reconfigure('statistics=(all,clear)')
+ c = self.session.open_cursor(
+ 'statistics:' + self.uri, None, 'statistics=(all)')
+ self.assertGreater(c[stat.dsrc.cache_state_root_size][2], 0)
+ self.assertGreater(c[stat.dsrc.btree_entries][2], 0)
+ c.close()
+
+ # Verify that cache and tree walk can operate independantly
+ self.conn.reconfigure('statistics=(all,clear)')
+ c = self.session.open_cursor(
+ 'statistics:' + self.uri, None, 'statistics=(cache_walk,fast)')
+ self.assertGreater(c[stat.dsrc.cache_state_root_size][2], 0)
+ self.assertEqual(c[stat.dsrc.btree_entries][2], 0)
+ c.close()
+
+ self.conn.reconfigure('statistics=(all,clear)')
+ c = self.session.open_cursor(
+ 'statistics:' + self.uri, None, 'statistics=(tree_walk,fast)')
+ # Don't check the cache walk stats for empty - they won't be cleared
+ self.assertGreater(c[stat.dsrc.btree_entries][2], 0)
+ c.close()
+
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_stat03.py b/src/third_party/wiredtiger/test/suite/test_stat03.py
index 1570ac4c9d8..7e5cf46ef13 100644
--- a/src/third_party/wiredtiger/test/suite/test_stat03.py
+++ b/src/third_party/wiredtiger/test/suite/test_stat03.py
@@ -30,10 +30,7 @@ import itertools, wiredtiger, wttest
from suite_subprocess import suite_subprocess
from wiredtiger import stat
-from helper import complex_populate, complex_populate_lsm, simple_populate
-from helper import key_populate, complex_value_populate, value_populate
-from helper import complex_populate_colgroup_count, complex_populate_index_count
-from helper import complex_populate_colgroup_name, complex_populate_index_name
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
from wtscenario import make_scenarios
# test_stat03.py
@@ -41,14 +38,11 @@ from wtscenario import make_scenarios
class test_stat_cursor_reset(wttest.WiredTigerTestCase):
pfx = 'test_stat_cursor_reset'
uri = [
- ('file-simple',
- dict(uri='file:' + pfx, pop=simple_populate)),
- ('table-simple',
- dict(uri='table:' + pfx, pop=simple_populate)),
- ('table-complex',
- dict(uri='table:' + pfx, pop=complex_populate)),
- ('table-complex-lsm',
- dict(uri='table:' + pfx, pop=complex_populate_lsm)),
+ ('file-simple', dict(uri='file:' + pfx, dataset=SimpleDataSet)),
+ ('table-simple', dict(uri='table:' + pfx, dataset=SimpleDataSet)),
+ ('table-complex', dict(uri='table:' + pfx, dataset=ComplexDataSet)),
+ ('table-complex-lsm', dict(uri='table:' + pfx,
+ dataset=ComplexLSMDataSet))
]
scenarios = make_scenarios(uri)
@@ -59,25 +53,23 @@ class test_stat_cursor_reset(wttest.WiredTigerTestCase):
'statistics:' + uri, None, 'statistics=(all)')
def test_stat_cursor_reset(self):
+ n = 100
+ ds = self.dataset(self, self.uri, n)
+ ds.populate()
+
# The number of btree_entries reported is influenced by the
# number of column groups and indices. Each insert will have
# a multiplied effect.
- if self.pop == simple_populate:
+ if self.dataset == SimpleDataSet:
multiplier = 1 # no declared colgroup is like one big colgroup
else:
- multiplier = complex_populate_colgroup_count() + \
- complex_populate_index_count()
- n = 100
- self.pop(self, self.uri, 'key_format=S', n)
+ multiplier = ds.colgroup_count() + ds.index_count()
statc = self.stat_cursor(self.uri)
self.assertEqual(statc[stat.dsrc.btree_entries][2], n * multiplier)
c = self.session.open_cursor(self.uri)
- c.set_key(key_populate(c, 200))
- if self.pop == simple_populate:
- c.set_value(value_populate(c, 200))
- else:
- c.set_value(tuple(complex_value_populate(c, 200)))
+ c.set_key(ds.key(200))
+ c.set_value(ds.value(200))
c.insert()
# Test that cursor reset re-loads the values.
@@ -88,14 +80,12 @@ class test_stat_cursor_reset(wttest.WiredTigerTestCase):
# For applications with indices and/or column groups, verify
# that there is a way to count the base number of entries.
- if self.pop != simple_populate:
+ if self.dataset != SimpleDataSet:
statc.close()
- statc = self.stat_cursor(
- complex_populate_index_name(self, self.uri, 0))
+ statc = self.stat_cursor(ds.index_name(0))
self.assertEqual(statc[stat.dsrc.btree_entries][2], n)
statc.close()
- statc = self.stat_cursor(
- complex_populate_colgroup_name(self, self.uri, 0))
+ statc = self.stat_cursor(ds.colgroup_name(0))
self.assertEqual(statc[stat.dsrc.btree_entries][2], n)
statc.close()
diff --git a/src/third_party/wiredtiger/test/suite/test_stat05.py b/src/third_party/wiredtiger/test/suite/test_stat05.py
index 62562f78ed6..ef4d65e85e4 100644
--- a/src/third_party/wiredtiger/test/suite/test_stat05.py
+++ b/src/third_party/wiredtiger/test/suite/test_stat05.py
@@ -30,8 +30,7 @@ import itertools, wiredtiger, wttest
from suite_subprocess import suite_subprocess
from wtscenario import make_scenarios
from wiredtiger import stat
-from helper import complex_populate, complex_populate_lsm, simple_populate
-from helper import complex_value_populate, key_populate, value_populate
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
# test_stat05.py
# Statistics cursor using size only
@@ -40,17 +39,17 @@ class test_stat_cursor_config(wttest.WiredTigerTestCase):
conn_config = 'statistics=(fast)'
uri = [
- ('file', dict(uri='file:' + pfx, pop=simple_populate, cfg='')),
- ('table', dict(uri='table:' + pfx, pop=simple_populate, cfg='')),
- ('inmem', dict(uri='table:' + pfx, pop=simple_populate, cfg='',
+ ('file', dict(uri='file:' + pfx, dataset=SimpleDataSet, cfg='')),
+ ('table', dict(uri='table:' + pfx, dataset=SimpleDataSet, cfg='')),
+ ('inmem', dict(uri='table:' + pfx, dataset=SimpleDataSet, cfg='',
conn_config = 'in_memory,statistics=(fast)')),
- ('table-lsm', dict(uri='table:' + pfx, pop=simple_populate,
- cfg=',type=lsm,lsm=(chunk_size=1MB,merge_min=2)',
+ ('table-lsm', dict(uri='table:' + pfx, dataset=SimpleDataSet,
+ cfg='lsm=(chunk_size=1MB,merge_min=2)',
conn_config = 'statistics=(fast),eviction_dirty_target=99,eviction_dirty_trigger=99')),
- ('complex', dict(uri='table:' + pfx, pop=complex_populate, cfg='')),
+ ('complex', dict(uri='table:' + pfx, dataset=ComplexDataSet, cfg='')),
('complex-lsm',
- dict(uri='table:' + pfx, pop=complex_populate_lsm,
- cfg=',lsm=(chunk_size=1MB,merge_min=2)',
+ dict(uri='table:' + pfx, dataset=ComplexLSMDataSet,
+ cfg='lsm=(chunk_size=1MB,merge_min=2)',
conn_config = 'statistics=(fast),eviction_dirty_target=99,eviction_dirty_trigger=99')),
]
@@ -68,17 +67,14 @@ class test_stat_cursor_config(wttest.WiredTigerTestCase):
# the cursor open succeeds. Insert enough data that LSM tables to need to
# switch and merge.
def test_stat_cursor_size(self):
- self.pop(self, self.uri, 'key_format=S' + self.cfg, 100)
+ ds = self.dataset(self, self.uri, 100, config=self.cfg)
+ ds.populate()
self.openAndWalkStatCursor()
cursor = self.session.open_cursor(self.uri, None)
for i in range(100, 40000 + 1):
if i % 100 == 0:
self.openAndWalkStatCursor()
- if self.pop == simple_populate:
- cursor[key_populate(cursor, i)] = value_populate(cursor, i)
- else:
- cursor[key_populate(cursor, i)] = \
- tuple(complex_value_populate(cursor, i))
+ cursor[ds.key(i)] = ds.value(i)
cursor.close()
self.openAndWalkStatCursor()
diff --git a/src/third_party/wiredtiger/test/suite/test_truncate01.py b/src/third_party/wiredtiger/test/suite/test_truncate01.py
index 37cd1f12c79..2319eeddbef 100644
--- a/src/third_party/wiredtiger/test/suite/test_truncate01.py
+++ b/src/third_party/wiredtiger/test/suite/test_truncate01.py
@@ -31,9 +31,8 @@
#
import wiredtiger, wttest
-from helper import confirm_empty,\
- key_populate, value_populate, simple_populate,\
- complex_populate, complex_value_populate
+from helper import confirm_empty
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# Test truncation arguments.
@@ -49,7 +48,7 @@ class test_truncate_arguments(wttest.WiredTigerTestCase):
# either cursor specified, expect errors.
def test_truncate_bad_args(self):
uri = self.type + self.name
- simple_populate(self, uri, 'key_format=S', 100)
+ SimpleDataSet(self, uri, 100).populate()
msg = '/either a URI or start/stop cursors/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.truncate(None, None, None, None), msg)
@@ -64,11 +63,12 @@ class test_truncate_arguments(wttest.WiredTigerTestCase):
uri = self.type + self.name
msg = '/requires key be set/'
- simple_populate(self, uri, 'key_format=S', 100)
+ ds = SimpleDataSet(self, uri, 100)
+ ds.populate()
c1 = self.session.open_cursor(uri, None)
c2 = self.session.open_cursor(uri, None)
- c2.set_key(key_populate(c2, 10))
+ c2.set_key(ds.key(10))
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.truncate(None, c1, c2, None), msg)
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
@@ -89,13 +89,13 @@ class test_truncate_uri(wttest.WiredTigerTestCase):
uri = self.type + self.name
# A simple, one-file file or table object.
- simple_populate(self, uri, 'key_format=S', 100)
+ SimpleDataSet(self, uri, 100).populate()
self.session.truncate(uri, None, None, None)
confirm_empty(self, uri)
self.session.drop(uri, None)
if self.type == "table:":
- complex_populate(self, uri, 'key_format=S', 100)
+ ComplexDataSet(self, uri, 100).populate()
self.session.truncate(uri, None, None, None)
confirm_empty(self, uri)
self.session.drop(uri, None)
@@ -118,16 +118,17 @@ class test_truncate_cursor_order(wttest.WiredTigerTestCase):
# Test an illegal order, then confirm that equal cursors works.
def test_truncate_cursor_order(self):
uri = self.type + self.name
- simple_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = SimpleDataSet(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
c1 = self.session.open_cursor(uri, None)
c2 = self.session.open_cursor(uri, None)
- c1.set_key(key_populate(c1, 20))
- c2.set_key(key_populate(c2, 10))
+ c1.set_key(ds.key(20))
+ c2.set_key(ds.key(10))
msg = '/the start cursor position is after the stop cursor position/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.truncate(None, c1, c2, None), msg)
- c2.set_key(key_populate(c2, 20))
+ c2.set_key(ds.key(20))
self.session.truncate(None, c1, c2, None)
# Test truncation of cursors past the end of the object.
@@ -150,22 +151,24 @@ class test_truncate_cursor_end(wttest.WiredTigerTestCase):
uri = self.type + self.name
# A simple, one-file file or table object.
- simple_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = SimpleDataSet(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
c1 = self.session.open_cursor(uri, None)
- c1.set_key(key_populate(c1, 1000))
+ c1.set_key(ds.key(1000))
c2 = self.session.open_cursor(uri, None)
- c2.set_key(key_populate(c2, 2000))
+ c2.set_key(ds.key(2000))
self.session.truncate(None, c1, c2, None)
self.assertEquals(c1.close(), 0)
self.assertEquals(c2.close(), 0)
self.session.drop(uri)
if self.type == "table:":
- complex_populate(self, uri, 'key_format=' + self.keyfmt, 100)
+ ds = ComplexDataSet(self, uri, 100, key_format=self.keyfmt)
+ ds.populate()
c1 = self.session.open_cursor(uri, None)
- c1.set_key(key_populate(c1, 1000))
+ c1.set_key(ds.key(1000))
c2 = self.session.open_cursor(uri, None)
- c2.set_key(key_populate(c2, 2000))
+ c2.set_key(ds.key(2000))
self.session.truncate(None, c1, c2, None)
self.assertEquals(c1.close(), 0)
self.assertEquals(c2.close(), 0)
@@ -179,13 +182,12 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
# The underlying table routines don't easily support 8t value types, limit
# those tests to file objects.
types = [
- ('file', dict(type='file:',\
- config='allocation_size=512,leaf_page_max=512,key_format=')),
- ('file8t', dict(type='file:',\
- config='allocation_size=512,\
- leaf_page_max=512,value_format=8t,key_format=')),
- ('table', dict(type='table:',\
- config='allocation_size=512,leaf_page_max=512,key_format=')),
+ ('file', dict(type='file:', valuefmt='S',
+ config='allocation_size=512,leaf_page_max=512')),
+ ('file8t', dict(type='file:', valuefmt='8t',
+ config='allocation_size=512,leaf_page_max=512')),
+ ('table', dict(type='table:', valuefmt='S',
+ config='allocation_size=512,leaf_page_max=512')),
]
keyfmt = [
('integer', dict(keyfmt='i')),
@@ -204,18 +206,18 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
scenarios = make_scenarios(types, keyfmt, size, reopen)
# Set a cursor key.
- def cursorKey(self, uri, key):
+ def cursorKey(self, ds, uri, key):
if key == -1:
return None
cursor = self.session.open_cursor(uri, None)
- cursor.set_key(key_populate(cursor, key))
+ cursor.set_key(ds.key(key))
return cursor
# Truncate a range using cursors, and check the results.
- def truncateRangeAndCheck(self, uri, begin, end, expected):
+ def truncateRangeAndCheck(self, ds, uri, begin, end, expected):
self.pr('truncateRangeAndCheck: ' + str(begin) + ',' + str(end))
- cur1 = self.cursorKey(uri, begin)
- cur2 = self.cursorKey(uri, end)
+ cur1 = self.cursorKey(ds, uri, begin)
+ cur2 = self.cursorKey(ds, uri, end)
self.session.truncate(None, cur1, cur2, None)
if not cur1:
begin = 1
@@ -234,7 +236,7 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
# Check the expected values against the object.
cursor = self.session.open_cursor(uri, None)
for i in range(begin, end + 1):
- expected[key_populate(cursor, i)] = [0]
+ expected[ds.key(i)] = [0]
for k, v in expected.iteritems():
cursor.set_key(k)
if v == [0] and \
@@ -306,6 +308,10 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
(self.skip, self.skip) # middle to same middle
]
+ # Using this data set to compare only, it doesn't create or populate.
+ ds = SimpleDataSet(self, uri, 0, key_format=self.keyfmt,
+ value_format=self.valuefmt, config=self.config)
+
# Build the layout we're going to test
total = self.nentries
for begin_skipped,begin_insert,end_skipped,end_insert in layout:
@@ -332,15 +338,16 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
# Create the object.
self.session.create(
- uri, 'value_format=S,' + self.config + self.keyfmt)
+ uri, self.config + ',key_format=' + self.keyfmt +
+ ',value_format=' + self.valuefmt)
# Insert the records that aren't skipped or inserted.
start = begin_skipped + begin_insert
stop = self.nentries - (end_skipped + end_insert)
cursor = self.session.open_cursor(uri, None)
for i in range(start + 1, stop + 1):
- k = key_populate(cursor, i)
- v = value_populate(cursor, i)
+ k = ds.key(i)
+ v = ds.value(i)
cursor[k] = v
expected[k] = [v]
cursor.close()
@@ -355,33 +362,33 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
start = 0
for i in range(0, begin_skipped):
start += 1
- k = key_populate(cursor, start)
+ k = ds.key(start)
expected[k] = [0]
# Optionally insert leading records.
for i in range(0, begin_insert):
start += 1
- k = key_populate(cursor, start)
- v = value_populate(cursor, start)
+ k = ds.key(start)
+ v = ds.value(start)
cursor[k] = v
expected[k] = [v]
# Optionally insert trailing skipped records.
for i in range(0, end_skipped):
stop += 1
- k = key_populate(cursor, stop)
+ k = ds.key(stop)
expected[k] = [0]
# Optionally insert trailing records.
for i in range(0, end_insert):
stop += 1
- k = key_populate(cursor, stop)
- v = value_populate(cursor, stop)
+ k = ds.key(stop)
+ v = ds.value(stop)
cursor[k] = v
expected[k] = [v]
cursor.close()
- self.truncateRangeAndCheck(uri, begin, end, expected)
+ self.truncateRangeAndCheck(ds, uri, begin, end, expected)
self.session.drop(uri, None)
# Test truncation of complex tables using cursors. We can't do the kind of
@@ -423,16 +430,16 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
'''
# Create the object.
- complex_populate(
- self, uri, self.config + self.keyfmt, self.nentries)
+ ds = ComplexDataSet(self, uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
+ ds.populate()
# Build a dictionary of what the object should look like for
# later comparison
cursor = self.session.open_cursor(uri, None)
expected = {}
for i in range(1, self.nentries + 1):
- expected[key_populate(cursor, i)] = \
- complex_value_populate(cursor, i)
+ expected[ds.key(i)] = ds.comparable_value(i)
cursor.close()
# Optionally close and re-open the object to get a disk image
@@ -440,7 +447,7 @@ class test_truncate_cursor(wttest.WiredTigerTestCase):
if self.reopen:
self.reopen_conn()
- self.truncateRangeAndCheck(uri, begin, end, expected)
+ self.truncateRangeAndCheck(ds, uri, begin, end, expected)
self.session.drop(uri, None)
if __name__ == '__main__':
diff --git a/src/third_party/wiredtiger/test/suite/test_truncate02.py b/src/third_party/wiredtiger/test/suite/test_truncate02.py
index 7184cd67242..73fed362354 100644
--- a/src/third_party/wiredtiger/test/suite/test_truncate02.py
+++ b/src/third_party/wiredtiger/test/suite/test_truncate02.py
@@ -31,7 +31,7 @@
#
import wiredtiger, wttest
-from helper import key_populate, value_populate, simple_populate
+from wtdataset import SimpleDataSet
from wtscenario import make_scenarios
# test_truncate_fast_delete
@@ -46,8 +46,7 @@ class test_truncate_fast_delete(wttest.WiredTigerTestCase):
# of individual pages in the file.
types = [
('file', dict(type='file:', config=\
- 'allocation_size=512,leaf_page_max=512,' +\
- 'value_format=S,key_format=')),
+ 'allocation_size=512,leaf_page_max=512')),
]
# This is all about testing the btree layer, not the schema layer, test
@@ -123,15 +122,17 @@ class test_truncate_fast_delete(wttest.WiredTigerTestCase):
'''
# Create the object.
- simple_populate(self, uri, self.config + self.keyfmt, self.nentries)
+ ds = SimpleDataSet(self, uri, self.nentries,
+ config=self.config, key_format=self.keyfmt)
+ ds.populate()
# Optionally add a few overflow records so we block fast delete on
# those pages.
if self.overflow:
cursor = self.session.open_cursor(uri, None, 'overwrite=false')
for i in range(1, self.nentries, 3123):
- cursor.set_key(key_populate(cursor, i))
- cursor.set_value(value_populate(cursor, i))
+ cursor.set_key(ds.key(i))
+ cursor.set_value(ds.value(i))
cursor.update()
cursor.close()
@@ -143,21 +144,21 @@ class test_truncate_fast_delete(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(uri, None, 'overwrite=false')
if self.readbefore:
for i in range(1, self.nentries, 737):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
cursor.search()
if self.writebefore:
for i in range(1, self.nentries, 988):
- cursor.set_key(key_populate(cursor, i))
- cursor.set_value(value_populate(cursor, i + 100))
+ cursor.set_key(ds.key(i))
+ cursor.set_value(ds.value(i + 100))
cursor.update()
cursor.close()
# Begin a transaction, and truncate a big range of rows.
self.session.begin_transaction(None)
start = self.session.open_cursor(uri, None)
- start.set_key(key_populate(start, 10))
+ start.set_key(ds.key(10))
end = self.session.open_cursor(uri, None)
- end.set_key(key_populate(end, self.nentries - 10))
+ end.set_key(ds.key(self.nentries - 10))
self.session.truncate(None, start, end, None)
start.close()
end.close()
@@ -167,12 +168,12 @@ class test_truncate_fast_delete(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(uri, None, 'overwrite=false')
if self.readafter:
for i in range(1, self.nentries, 1123):
- cursor.set_key(key_populate(cursor, i))
+ cursor.set_key(ds.key(i))
cursor.search()
if self.writeafter:
for i in range(1, self.nentries, 621):
- cursor.set_key(key_populate(cursor, i))
- cursor.set_value(value_populate(cursor, i + 100))
+ cursor.set_key(ds.key(i))
+ cursor.set_value(ds.value(i + 100))
cursor.update()
cursor.close()
diff --git a/src/third_party/wiredtiger/test/suite/test_truncate03.py b/src/third_party/wiredtiger/test/suite/test_truncate03.py
index 49044ac5690..2b4628950b3 100644
--- a/src/third_party/wiredtiger/test/suite/test_truncate03.py
+++ b/src/third_party/wiredtiger/test/suite/test_truncate03.py
@@ -30,7 +30,7 @@
# session level operations on tables
import wiredtiger, wttest
-from helper import key_populate, simple_populate, value_populate
+from wtdataset import SimpleDataSet
# A standalone test case that exercises address-deleted cells.
class test_truncate_address_deleted(wttest.WiredTigerTestCase):
@@ -39,15 +39,15 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# Use a small page size and lots of keys because we want to create lots
# of individual pages in the file.
nentries = 10000
- config = 'allocation_size=512,' +\
- 'leaf_page_max=512,value_format=S,key_format=S'
+ config = 'allocation_size=512,leaf_page_max=512'
# address_deleted routine:
# Create an object that has a bunch of address-deleted cells on disk.
# Recover the object, and turn the address-deleted cells into free pages.
def address_deleted(self):
# Create the object, force it to disk, and verify the object.
- simple_populate(self, self.uri, self.config, self.nentries)
+ ds = SimpleDataSet(self, self.uri, self.nentries, config=self.config)
+ ds.populate()
self.reopen_conn()
self.session.verify(self.uri)
@@ -59,9 +59,9 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# Truncate a big range of rows; the leaf pages aren't in memory, so
# leaf page references will be deleted without being read.
start = self.session.open_cursor(self.uri, None)
- start.set_key(key_populate(start, 10))
+ start.set_key(ds.key(10))
end = self.session.open_cursor(self.uri, None)
- end.set_key(key_populate(end, self.nentries - 10))
+ end.set_key(ds.key(self.nentries - 10))
self.session.truncate(None, start, end, None)
self.assertEqual(start.close(), 0)
self.assertEqual(end.close(), 0)
@@ -78,7 +78,7 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# we get a good look at all the internal pages and the address-deleted
# cells.
cursor = self.session.open_cursor(self.uri, None)
- cursor.set_key(key_populate(cursor, 5))
+ cursor.set_key(ds.key(5))
cursor.set_value("changed value")
self.assertEqual(cursor.update(), 0)
cursor.reset()
@@ -88,12 +88,13 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# Checkpoint, freeing the pages.
self.session.checkpoint()
+ return ds
# Test object creation, recovery, and conversion of address-deleted cells
# into free pages.
def test_truncate_address_deleted_free(self):
# Create the object on disk.
- self.address_deleted()
+ ds = self.address_deleted()
# That's all just verify that worked.
self.session.verify(self.uri)
@@ -103,7 +104,7 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# empty pages by a reader after the underlying leaf pages are removed.
def test_truncate_address_deleted_empty_page(self):
# Create the object on disk.
- self.address_deleted()
+ ds = self.address_deleted()
# Open a cursor and update pages in the middle of the range, forcing
# creation of empty pages (once the underlying leaf page is freed, we
@@ -111,11 +112,11 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
# the value as well as write the page and get it back.
cursor = self.session.open_cursor(self.uri, None)
for i in range(3000, 7000, 137):
- k = key_populate(cursor, i)
+ k = ds.key(i)
v = 'changed value: ' + str(i)
cursor[k] = v
for i in range(3000, 7000, 137):
- k = key_populate(cursor, i)
+ k = ds.key(i)
v = 'changed value: ' + str(i)
cursor.set_key(k)
self.assertEqual(cursor.search(), 0)
@@ -128,7 +129,7 @@ class test_truncate_address_deleted(wttest.WiredTigerTestCase):
cursor = self.session.open_cursor(self.uri, None)
for i in range(3000, 7000, 137):
- k = key_populate(cursor, i)
+ k = ds.key(i)
v = 'changed value: ' + str(i)
cursor.set_key(k)
self.assertEqual(cursor.search(), 0)
diff --git a/src/third_party/wiredtiger/test/suite/test_txn06.py b/src/third_party/wiredtiger/test/suite/test_txn06.py
index e4636e40e2e..2bff97f6aac 100644
--- a/src/third_party/wiredtiger/test/suite/test_txn06.py
+++ b/src/third_party/wiredtiger/test/suite/test_txn06.py
@@ -30,7 +30,7 @@
# Transactions: test long-running snapshots
from suite_subprocess import suite_subprocess
-from helper import simple_populate
+from wtdataset import SimpleDataSet
import wiredtiger, wttest
class test_txn06(wttest.WiredTigerTestCase, suite_subprocess):
@@ -47,7 +47,7 @@ class test_txn06(wttest.WiredTigerTestCase, suite_subprocess):
def test_long_running(self):
# Populate a table
- simple_populate(self, self.source_uri, 'key_format=S', self.nrows)
+ SimpleDataSet(self, self.source_uri, self.nrows).populate()
# Now scan the table and copy the rows into a new table
c_src = self.session.create(self.uri, "key_format=S")
diff --git a/src/third_party/wiredtiger/test/suite/test_txn11.py b/src/third_party/wiredtiger/test/suite/test_txn11.py
index dfb02799434..147bf3a76c0 100644
--- a/src/third_party/wiredtiger/test/suite/test_txn11.py
+++ b/src/third_party/wiredtiger/test/suite/test_txn11.py
@@ -31,7 +31,7 @@
import fnmatch, os, time
from suite_subprocess import suite_subprocess
-from helper import simple_populate
+from wtdataset import SimpleDataSet
import wttest
class test_txn11(wttest.WiredTigerTestCase, suite_subprocess):
@@ -64,7 +64,7 @@ class test_txn11(wttest.WiredTigerTestCase, suite_subprocess):
def test_ops(self):
# Populate a table
- simple_populate(self, self.source_uri, 'key_format=S', self.nrows)
+ SimpleDataSet(self, self.source_uri, self.nrows).populate()
# Run forced checkpoints
self.run_checkpoints()
diff --git a/src/third_party/wiredtiger/test/suite/test_upgrade.py b/src/third_party/wiredtiger/test/suite/test_upgrade.py
index 1c2e3f6cda8..4eb6a9e6817 100644
--- a/src/third_party/wiredtiger/test/suite/test_upgrade.py
+++ b/src/third_party/wiredtiger/test/suite/test_upgrade.py
@@ -28,7 +28,7 @@
import os, time
import wiredtiger, wttest
-from helper import complex_populate, simple_populate
+from wtdataset import SimpleDataSet, ComplexDataSet
from wtscenario import make_scenarios
# test_upgrade.py
@@ -42,9 +42,9 @@ class test_upgrade(wttest.WiredTigerTestCase):
])
# Populate an object, then upgrade it.
- def upgrade(self, populate, with_cursor):
+ def upgrade(self, dataset, with_cursor):
uri = self.uri + self.name
- populate(self, uri, 'key_format=S', 10)
+ dataset(self, uri, 10).populate()
# Open cursors should cause failure.
if with_cursor:
@@ -59,13 +59,13 @@ class test_upgrade(wttest.WiredTigerTestCase):
# Test upgrade of an object.
def test_upgrade(self):
# Simple file or table object.
- self.upgrade(simple_populate, False)
- self.upgrade(simple_populate, True)
+ self.upgrade(SimpleDataSet, False)
+ self.upgrade(SimpleDataSet, True)
# A complex, multi-file table object.
if self.uri == "table:":
- self.upgrade(complex_populate, False)
- self.upgrade(complex_populate, True)
+ self.upgrade(ComplexDataSet, False)
+ self.upgrade(ComplexDataSet, True)
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/test_util02.py b/src/third_party/wiredtiger/test/suite/test_util02.py
index 13f52d6aeea..7aa24605ed1 100644
--- a/src/third_party/wiredtiger/test/suite/test_util02.py
+++ b/src/third_party/wiredtiger/test/suite/test_util02.py
@@ -29,8 +29,8 @@
import string, os
import wiredtiger, wttest
from suite_subprocess import suite_subprocess
+from wtdataset import ComplexDataSet
from wtscenario import make_scenarios
-from helper import complex_populate
# test_util02.py
# Utilities: wt load
@@ -169,7 +169,7 @@ class test_load_commandline(wttest.WiredTigerTestCase, suite_subprocess):
def load_commandline(self, args, fail):
errfile= "errfile"
- complex_populate(self, self.uri, "key_format=S,value_format=S", 20)
+ ComplexDataSet(self, self.uri, 20).populate()
self.runWt(["dump", self.uri], outfilename="dump.out")
loadargs = ["load", "-f", "dump.out"] + args
self.runWt(loadargs, errfilename=errfile, failure=fail)
diff --git a/src/third_party/wiredtiger/test/suite/test_util13.py b/src/third_party/wiredtiger/test/suite/test_util13.py
index 9804dc700ba..7890d4fdb1b 100644
--- a/src/third_party/wiredtiger/test/suite/test_util13.py
+++ b/src/third_party/wiredtiger/test/suite/test_util13.py
@@ -28,11 +28,9 @@
import os, re, string
from suite_subprocess import suite_subprocess
-import itertools, wiredtiger, wttest
+from wtdataset import SimpleDataSet, ComplexDataSet, ComplexLSMDataSet
+import wiredtiger, wttest
-from helper import complex_populate_cgconfig, complex_populate_cgconfig_lsm
-from helper import simple_populate
-from helper import complex_populate_check, simple_populate_check
from wtscenario import make_scenarios
# test_util13.py
@@ -52,25 +50,23 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
# Select table configuration settings that are not the default.
#
types = [
- ('file-simple', dict(uri='file:' + pfx, pop=simple_populate,
- populate_check=simple_populate_check,
- table_config='prefix_compression_min=3', cfg='')),
- ('lsm-simple', dict(uri='lsm:' + pfx, pop=simple_populate,
- populate_check=simple_populate_check,
- table_config='lsm=(bloom_bit_count=29)',
- cfg='bloom_bit_count=29')),
- ('table-simple', dict(uri='table:' + pfx, pop=simple_populate,
- populate_check=simple_populate_check,
- table_config='split_pct=50', cfg='')),
+ ('file-simple', dict(uri='file:' + pfx, dataset=SimpleDataSet,
+ table_config='prefix_compression_min=3', cfg='',
+ cg_config='')),
+ ('lsm-simple', dict(uri='lsm:' + pfx, dataset=SimpleDataSet,
+ table_config='lsm=(bloom_bit_count=29)', cfg='bloom_bit_count=29',
+ cg_config='')),
+ ('table-simple', dict(uri='table:' + pfx, dataset=SimpleDataSet,
+ table_config='split_pct=50', cfg='',
+ cg_config='')),
('table-complex',
- dict(uri='table:' + pfx, pop=complex_populate_cgconfig,
- populate_check=complex_populate_check,
- table_config='allocation_size=512B', cfg='')),
+ dict(uri='table:' + pfx, dataset=ComplexDataSet,
+ table_config='allocation_size=512B', cfg='',
+ cg_config='allocation_size=512B')),
('table-complex-lsm',
- dict(uri='table:' + pfx, pop=complex_populate_cgconfig_lsm,
- populate_check=complex_populate_check,
- table_config='lsm=(merge_max=5)',
- cfg='merge_max=5')),
+ dict(uri='table:' + pfx, dataset=ComplexLSMDataSet,
+ table_config='lsm=(merge_max=5)', cfg='merge_max=5',
+ cg_config='lsm=(merge_max=5)'))
]
scenarios = make_scenarios(types)
@@ -83,7 +79,7 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
#print "compare_config Expected config "
#print expected_cfg
cfg_orig = actual_cfg
- if self.pop != simple_populate:
+ if self.dataset != SimpleDataSet:
#
# If we have a complex config, strip out the colgroups and
# columns from the config. Doing so allows us to keep the
@@ -127,7 +123,7 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
break
return True
- def load_recheck(self, expect_subset, dump_out):
+ def load_recheck(self, ds, expect_subset, dump_out):
newdump = "newdump.out"
os.mkdir(self.dir)
self.runWt(['-h', self.dir, 'load', '-f', dump_out])
@@ -135,7 +131,7 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
conn = self.wiredtiger_open(self.dir)
session = conn.open_session()
cursor = session.open_cursor(self.uri, None, None)
- self.populate_check
+ ds.check()
conn.close()
dumpargs = ["-h"]
dumpargs.append(self.dir)
@@ -150,8 +146,9 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
# The number of btree_entries reported is influenced by the
# number of column groups and indices. Each insert will have
# a multiplied effect.
- self.pop(self, self.uri,
- 'key_format=S,value_format=S,' + self.table_config, self.nentries)
+ ds = self.dataset(self, self.uri, self.nentries,
+ config=self.table_config, cgconfig=self.cg_config)
+ ds.populate()
ver = wiredtiger.wiredtiger_version()
verstring = str(ver[1]) + '.' + str(ver[2]) + '.' + str(ver[3])
@@ -166,7 +163,7 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
expectout.write('Header\n')
expectout.write(self.uri + '\n')
# Check the config on the colgroup itself for complex tables.
- if self.pop != simple_populate:
+ if self.dataset != SimpleDataSet:
expectout.write('key_format=S\n')
expectout.write('colgroup:' + self.pfx + ':cgroup1\n')
if self.cfg == '':
@@ -182,7 +179,7 @@ class test_util13(wttest.WiredTigerTestCase, suite_subprocess):
self.runWt(dumpargs, outfilename=outfile)
self.assertTrue(self.compare_files(expectfile, outfile))
- self.assertTrue(self.load_recheck(expectfile, outfile))
+ self.assertTrue(self.load_recheck(ds, expectfile, outfile))
if __name__ == '__main__':
wttest.run()
diff --git a/src/third_party/wiredtiger/test/suite/wtdataset.py b/src/third_party/wiredtiger/test/suite/wtdataset.py
new file mode 100644
index 00000000000..cb77735510c
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/wtdataset.py
@@ -0,0 +1,302 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-2016 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.
+#
+
+class BaseDataSet(object):
+ """
+ BaseDataSet is an abstract base class for other *DataSet classes.
+ An object of this type should not be created directly. These classes
+ represent test data sets that can be used to populate tables and
+ to check the contents of existing tables.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ self.testcase = testcase
+ self.uri = uri
+ self.rows = rows
+ self.key_format = kwargs.get('key_format', 'S')
+ self.value_format = kwargs.get('value_format', 'S')
+ self.config = kwargs.get('config', '')
+
+ def create(self):
+ self.testcase.session.create(self.uri, 'key_format=' + self.key_format
+ + ',value_format=' + self.value_format
+ + ',' + self.config)
+
+ def fill(self):
+ c = self.testcase.session.open_cursor(self.uri, None)
+ for i in range(1, self.rows + 1):
+ c[self.key(i)] = self.value(i)
+ c.close()
+
+ def postfill(self):
+ pass
+
+ @classmethod
+ def is_lsm(cls):
+ return False
+
+ def populate(self):
+ self.testcase.pr('populate: ' + self.uri + ' with '
+ + str(self.rows) + ' rows')
+ self.create()
+ self.fill()
+ self.postfill()
+
+ # Create a key for a Simple or Complex data set.
+ @staticmethod
+ def key_by_format(i, key_format):
+ if key_format == 'i' or key_format == 'r' or key_format == 'u':
+ return i
+ elif key_format == 'S':
+ return str('%015d' % i)
+ else:
+ raise AssertionError(
+ 'key: object has unexpected format: ' + key_format)
+
+ # Create a value for a Simple data set.
+ @staticmethod
+ def value_by_format(i, value_format):
+ if value_format == 'i' or value_format == 'r' or value_format == 'u':
+ return i
+ elif value_format == 'S':
+ return str(i) + ': abcdefghijklmnopqrstuvwxyz'
+ elif value_format == '8t':
+ value = (
+ 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xaa, 0xab,
+ 0xac, 0xad, 0xae, 0xaf, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf)
+ return value[i % len(value)]
+ else:
+ raise AssertionError(
+ 'value: object has unexpected format: '
+ + value_format)
+
+ # Create a key for this data set. Simple and Complex data sets have
+ # the same key space.
+ def key(self, i):
+ return BaseDataSet.key_by_format(i, self.key_format)
+
+ def check(self):
+ self.testcase.pr('check: ' + self.uri)
+ cursor = self.testcase.session.open_cursor(self.uri, None)
+ self.check_cursor(cursor)
+ cursor.close()
+
+class SimpleDataSet(BaseDataSet):
+ """
+ SimpleDataSet creates a table with a single key and value that is
+ populated with predefined data, up to the requested number of rows.
+ key_format and value_format may be set in the constructor to
+ override the simple string defaults.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ super(SimpleDataSet, self).__init__(testcase, uri, rows, **kwargs)
+
+ def value(self, i):
+ return BaseDataSet.value_by_format(i, self.value_format)
+
+ def check_cursor(self, cursor):
+ i = 0
+ for key, val in cursor:
+ i += 1
+ self.testcase.assertEqual(key, self.key(i))
+ if cursor.value_format == '8t' and val == 0: # deleted
+ continue
+ self.testcase.assertEqual(val, self.value(i))
+ self.testcase.assertEqual(i, self.rows)
+
+class SimpleLSMDataSet(SimpleDataSet):
+ """
+ SimpleLSMDataSet is identical to SimpleDataSet, but using LSM files
+ via the type=lsm configuration.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ kwargs['config'] = kwargs.get('config', '') + ',type=lsm'
+ super(SimpleLSMDataSet, self).__init__(
+ testcase, uri, rows, **kwargs)
+
+ @classmethod
+ def is_lsm(cls):
+ return True
+
+class SimpleIndexDataSet(SimpleDataSet):
+ """
+ SimpleIndexDataSet is identical to SimpleDataSet, adding one index
+ that maps the value to the key.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ self.indexname = 'index:' + uri.split(":")[1] + ':index1'
+ self.origconfig = kwargs.get('config', '')
+ kwargs['config'] = self.origconfig + ',columns=(key0,value0)'
+ super(SimpleIndexDataSet, self).__init__(
+ testcase, uri, rows, **kwargs)
+
+ def create(self):
+ super(SimpleIndexDataSet, self).create()
+ self.testcase.session.create(self.indexname, 'columns=(value0,key0),' +
+ self.origconfig)
+
+ def check(self):
+ BaseDataSet.check(self)
+
+ # Check values in the index.
+ idxcursor = self.testcase.session.open_cursor(self.indexname)
+ for i in range(1, self.rows + 1):
+ k = self.key(i)
+ v = self.value(i)
+ ik = (v, k) # The index key is columns=(v,k).
+ self.testcase.assertEqual(v, idxcursor[ik])
+ idxcursor.close()
+
+class SimpleIndexLSMDataSet(SimpleIndexDataSet):
+ """
+ SimpleIndexLSMDataSet is identical to SimpleIndexDataSet, but
+ using LSM files.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ kwargs['config'] = kwargs.get('config', '') + ',type=lsm'
+ super(SimpleIndexLSMDataSet, self).__init__(
+ testcase, uri, rows, **kwargs)
+
+ @classmethod
+ def is_lsm(cls):
+ return True
+
+class ComplexDataSet(BaseDataSet):
+ """
+ ComplexDataSet populates a table with a mixed set of indices
+ and column groups. Some indices are created before the
+ table is populated, some after.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ self.indexlist = [
+ ['indx1', 'column2'],
+ ['indx2', 'column3'],
+ ['indx3', 'column4'],
+ ['indx4', 'column2,column4'],
+ ['indx5', 'column3,column5'],
+ ['indx6', 'column3,column5,column4']]
+ self.cglist = [
+ ['cgroup1', 'column2'],
+ ['cgroup2', 'column3'],
+ ['cgroup3', 'column4'],
+ ['cgroup4', 'column2,column3'],
+ ['cgroup5', 'column3,column4'],
+ ['cgroup6', 'column2,column4,column5']]
+ self.cgconfig = kwargs.pop('cgconfig', '')
+ config = kwargs.get('config', '')
+ config += ',columns=(record,column2,column3,column4,column5),' + \
+ 'colgroups=(cgroup1,cgroup2,cgroup3,cgroup4,cgroup5,cgroup6)'
+ kwargs['config'] = config
+ kwargs['value_format'] = 'SiSS'
+ super(ComplexDataSet, self).__init__(testcase, uri, rows, **kwargs)
+
+ def create(self):
+ config = 'key_format=' + self.key_format + \
+ ',value_format=' + self.value_format + ',' + self.config
+ session = self.testcase.session
+ ##self.testcase.tty('URI=' + self.uri + 'CONFIG=' + config)
+ session.create(self.uri, config)
+ tablepart = self.uri.split(":")[1] + ':'
+ for cg in self.cglist:
+ session.create('colgroup:' + tablepart + cg[0],
+ ',columns=(' + cg[1] + '),' + self.cgconfig)
+ for index in self.indexlist[0:4]:
+ session.create('index:' + tablepart + index[0],
+ ',columns=(' + index[1] + '),' + self.config)
+
+ def postfill(self):
+ # add some indices after filling the table
+ tablepart = self.uri.split(":")[1] + ':'
+ session = self.testcase.session
+ for index in self.indexlist[4:]:
+ session.create('index:' + tablepart + index[0],
+ ',columns=(' + index[1] + ')')
+
+ def colgroup_count(self):
+ return len(self.cglist)
+
+ def colgroup_name(self, i):
+ return 'colgroup:' + self.uri.split(":")[1] + ':' + self.cglist[i][0]
+
+ def index_count(self):
+ return len(self.indexlist)
+
+ def index_name(self, i):
+ return 'index:' + self.uri.split(":")[1] + ':' + self.indexlist[i][0]
+
+ # A value suitable for checking the value returned by a cursor, as
+ # cursor.get_value() returns a list.
+ def comparable_value(self, i):
+ return [str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%26],
+ i,
+ str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%23],
+ str(i) + ': abcdefghijklmnopqrstuvwxyz'[0:i%18]]
+
+ # A value suitable for assigning to a cursor, as
+ # cursor.set_value() expects a tuple when there it is used with
+ # a single argument and the value is composite.
+ def value(self, i):
+ return tuple(self.comparable_value(i))
+
+ def check_cursor(self, cursor):
+ i = 0
+ for key, s1, i2, s3, s4 in cursor:
+ i += 1
+ self.testcase.assertEqual(key, self.key(i))
+ v = self.value(i)
+ self.testcase.assertEqual(s1, v[0])
+ self.testcase.assertEqual(i2, v[1])
+ self.testcase.assertEqual(s3, v[2])
+ self.testcase.assertEqual(s4, v[3])
+ self.testcase.assertEqual(i, self.rows)
+
+class ComplexLSMDataSet(ComplexDataSet):
+ """
+ ComplexLSMDataSet is identical to ComplexDataSet, but using LSM files.
+ """
+ def __init__(self, testcase, uri, rows, **kwargs):
+ kwargs['cgconfig'] = kwargs.get('cgconfig', '') + ',type=lsm'
+ super(ComplexLSMDataSet, self).__init__(
+ testcase, uri, rows, **kwargs)
+
+ @classmethod
+ def is_lsm(cls):
+ return True
+
+# create a key based on a cursor as a shortcut to creating a SimpleDataSet
+def simple_key(cursor, i):
+ return BaseDataSet.key_by_format(i, cursor.key_format)
+
+# create a value based on a cursor as a shortcut to creating a SimpleDataSet
+def simple_value(cursor, i):
+ return BaseDataSet.value_by_format(i, cursor.value_format)
+
+# create a key based on a cursor as a shortcut to creating a ComplexDataSet
+def complex_key(cursor, i):
+ return BaseDataSet.key_by_format(i, cursor.key_format)
diff --git a/src/third_party/wiredtiger/test/thread/t.c b/src/third_party/wiredtiger/test/thread/t.c
index 5b53532e8a6..baadbf2adb9 100644
--- a/src/third_party/wiredtiger/test/thread/t.c
+++ b/src/third_party/wiredtiger/test/thread/t.c
@@ -52,8 +52,6 @@ static void wt_shutdown(void);
extern int __wt_optind;
extern char *__wt_optarg;
-void (*custom_die)(void) = NULL;
-
int
main(int argc, char *argv[])
{
diff --git a/src/third_party/wiredtiger/test/utility/misc.c b/src/third_party/wiredtiger/test/utility/misc.c
index 096bc752726..1491c9a6938 100644
--- a/src/third_party/wiredtiger/test/utility/misc.c
+++ b/src/third_party/wiredtiger/test/utility/misc.c
@@ -27,6 +27,8 @@
*/
#include "test_util.h"
+void (*custom_die)(void) = NULL;
+
/*
* die --
* Report an error and quit.
@@ -142,8 +144,6 @@ testutil_cleanup(TEST_OPTS *opts)
if (!opts->preserve)
testutil_clean_work_dir(opts->home);
- free(opts->conn_config);
- free(opts->table_config);
free(opts->uri);
free(opts->home);
}
diff --git a/src/third_party/wiredtiger/test/utility/parse_opts.c b/src/third_party/wiredtiger/test/utility/parse_opts.c
index 08aeafa9617..74a1c021d5d 100644
--- a/src/third_party/wiredtiger/test/utility/parse_opts.c
+++ b/src/third_party/wiredtiger/test/utility/parse_opts.c
@@ -27,10 +27,6 @@
*/
#include "test_util.h"
-extern int __wt_opterr; /* if error message should be printed */
-extern int __wt_optind; /* index into parent argv vector */
-extern int __wt_optopt; /* character checked for validity */
-extern int __wt_optreset; /* reset getopt */
extern char *__wt_optarg; /* argument associated with option */
/*
@@ -59,7 +55,7 @@ testutil_parse_opts(int argc, char * const *argv, TEST_OPTS *opts)
opts->n_append_threads = (uint64_t)atoll(__wt_optarg);
break;
case 'h': /* Home directory */
- opts->home = __wt_optarg;
+ opts->home = dstrdup(__wt_optarg);
break;
case 'n': /* Number of records */
opts->nrecords = (uint64_t)atoll(__wt_optarg);
@@ -116,12 +112,14 @@ testutil_parse_opts(int argc, char * const *argv, TEST_OPTS *opts)
}
/*
- * Setup the home directory. It needs to be unique for every test
- * or the auto make parallel tester gets upset.
+ * Setup the home directory if not explicitly specified. It needs to be
+ * unique for every test or the auto make parallel tester gets upset.
*/
- len = strlen("WT_TEST.") + strlen(opts->progname) + 10;
- opts->home = dmalloc(len);
- snprintf(opts->home, len, "WT_TEST.%s", opts->progname);
+ if (opts->home == NULL) {
+ len = strlen("WT_TEST.") + strlen(opts->progname) + 10;
+ opts->home = dmalloc(len);
+ snprintf(opts->home, len, "WT_TEST.%s", opts->progname);
+ }
/* Setup the default URI string */
len = strlen("table:") + strlen(opts->progname) + 10;
diff --git a/src/third_party/wiredtiger/test/utility/test_util.h b/src/third_party/wiredtiger/test/utility/test_util.h
index 3c1d0e2630a..f6a9cd68e02 100644
--- a/src/third_party/wiredtiger/test/utility/test_util.h
+++ b/src/third_party/wiredtiger/test/utility/test_util.h
@@ -68,10 +68,8 @@ typedef struct {
* resources.
*/
WT_CONNECTION *conn;
- char *conn_config;
WT_SESSION *session;
bool running;
- char *table_config;
char *uri;
volatile uint64_t next_threadid;
uint64_t max_inserted_id;
@@ -87,6 +85,16 @@ typedef struct {
} while (0)
/*
+ * testutil_assertfmt --
+ * Complain and quit if something isn't true.
+ */
+#define testutil_assertfmt(a, fmt, ...) do { \
+ if (!(a)) \
+ testutil_die(0, "%s/%d: %s: " fmt, \
+ __func__, __LINE__, #a, __VA_ARGS__); \
+} while (0)
+
+/*
* testutil_check --
* Complain and quit if a function call fails.
*/
@@ -108,6 +116,62 @@ typedef struct {
__func__, __LINE__, #call, __VA_ARGS__); \
} while (0)
+/*
+ * u64_to_string --
+ * Convert a uint64_t to a text string.
+ *
+ * Algorithm from Andrei Alexandrescu's talk: "Three Optimization Tips for C++"
+ */
+static inline void
+u64_to_string(uint64_t n, char **pp)
+{
+ static const char hundred_lookup[201] =
+ "0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+ u_int i;
+ char *p;
+
+ /*
+ * The argument pointer references the last element of a buffer (which
+ * must be large enough to hold any possible value).
+ *
+ * Nul-terminate the buffer.
+ */
+ for (p = *pp, *p-- = '\0'; n >= 100; n /= 100) {
+ i = (n % 100) * 2;
+ *p-- = hundred_lookup[i + 1];
+ *p-- = hundred_lookup[i];
+ }
+
+ /* Handle the last two digits. */
+ i = (u_int)n * 2;
+ *p = hundred_lookup[i + 1];
+ if (n >= 10)
+ *--p = hundred_lookup[i];
+
+ /* Return a pointer to the first byte of the text string. */
+ *pp = p;
+}
+
+/*
+ * u64_to_string_zf --
+ * Convert a uint64_t to a text string, zero-filling the buffer.
+ */
+static inline void
+u64_to_string_zf(uint64_t n, char *buf, size_t len)
+{
+ char *p;
+
+ p = buf + (len - 1);
+ u64_to_string(n, &p);
+
+ while (p > buf)
+ *--p = '0';
+}
+
/* Allow tests to add their own death handling. */
extern void (*custom_die)(void);
diff --git a/src/third_party/wiredtiger/tools/wtstats/stat_data.py b/src/third_party/wiredtiger/tools/wtstats/stat_data.py
index 8f47b86a23e..635e710c469 100644
--- a/src/third_party/wiredtiger/tools/wtstats/stat_data.py
+++ b/src/third_party/wiredtiger/tools/wtstats/stat_data.py
@@ -91,6 +91,24 @@ no_scale_per_second_list = [
'btree: row-store leaf pages',
'cache: bytes currently in the cache',
'cache: overflow values cached in memory',
+ 'cache_walk: Average difference between current eviction generation when the page was last considered',
+ 'cache_walk: Average on-disk page image size seen',
+ 'cache_walk: Clean pages currently in cache',
+ 'cache_walk: Current eviction generation',
+ 'cache_walk: Dirty pages currently in cache',
+ 'cache_walk: Entries in the root page',
+ 'cache_walk: Internal pages currently in cache',
+ 'cache_walk: Leaf pages currently in cache',
+ 'cache_walk: Maximum difference between current eviction generation when the page was last considered',
+ 'cache_walk: Maximum page size seen',
+ 'cache_walk: Minimum on-disk page image size seen',
+ 'cache_walk: On-disk page image sizes smaller than a single allocation unit',
+ 'cache_walk: Pages created in memory and never written',
+ 'cache_walk: Pages currently queued for eviction',
+ 'cache_walk: Pages that could not be queued for eviction',
+ 'cache_walk: Refs skipped during cache traversal',
+ 'cache_walk: Size of the root page',
+ 'cache_walk: Total number of pages currently in cache',
'LSM: bloom filters in the LSM tree',
'LSM: chunks in the LSM tree',
'LSM: highest merge generation in the LSM tree',
@@ -162,6 +180,24 @@ no_clear_list = [
'transaction: transaction range of IDs currently pinned by named snapshots',
'btree: btree checkpoint generation',
'cache: bytes currently in the cache',
+ 'cache_walk: Average difference between current eviction generation when the page was last considered',
+ 'cache_walk: Average on-disk page image size seen',
+ 'cache_walk: Clean pages currently in cache',
+ 'cache_walk: Current eviction generation',
+ 'cache_walk: Dirty pages currently in cache',
+ 'cache_walk: Entries in the root page',
+ 'cache_walk: Internal pages currently in cache',
+ 'cache_walk: Leaf pages currently in cache',
+ 'cache_walk: Maximum difference between current eviction generation when the page was last considered',
+ 'cache_walk: Maximum page size seen',
+ 'cache_walk: Minimum on-disk page image size seen',
+ 'cache_walk: On-disk page image sizes smaller than a single allocation unit',
+ 'cache_walk: Pages created in memory and never written',
+ 'cache_walk: Pages currently queued for eviction',
+ 'cache_walk: Pages that could not be queued for eviction',
+ 'cache_walk: Refs skipped during cache traversal',
+ 'cache_walk: Size of the root page',
+ 'cache_walk: Total number of pages currently in cache',
'session: open cursor count',
]
prefix_list = [
@@ -169,6 +205,7 @@ prefix_list = [
'reconciliation',
'LSM',
'log',
+ 'lock',
'cache',
'transaction',
'cursor',
@@ -176,9 +213,10 @@ prefix_list = [
'session',
'block-manager',
'thread-yield',
+ 'cache_walk',
'async',
'btree',
'thread-state',
'compression',
]
-groups = {'cursor': ['cursor', 'session'], 'lsm': ['LSM', 'transaction'], 'system': ['connection', 'data-handle', 'session', 'thread-state'], 'evict': ['block-manager', 'cache', 'connection', 'thread-state'], 'memory': ['cache', 'connection', 'reconciliation']} \ No newline at end of file
+groups = {'cursor': ['cursor', 'session'], 'lsm': ['LSM', 'transaction'], 'system': ['connection', 'data-handle', 'session', 'thread-state'], 'evict': ['block-manager', 'cache', 'cache_walk', 'connection', 'thread-state'], 'memory': ['cache', 'cache_walk', 'connection', 'reconciliation']} \ No newline at end of file
diff --git a/src/third_party/wiredtiger/tools/wtstats/wtstats.py b/src/third_party/wiredtiger/tools/wtstats/wtstats.py
index 3549031c30f..bf5557d12f4 100755
--- a/src/third_party/wiredtiger/tools/wtstats/wtstats.py
+++ b/src/third_party/wiredtiger/tools/wtstats/wtstats.py
@@ -115,6 +115,9 @@ def parse_wtstats_file(file, result):
# Parse file
for line in open(file, 'rU'):
month, day, time, v, title = line.strip('\n').split(" ", 4)
+ # The colon in the URI confuses parsing, strip it out.
+ if "cache_walk" in title:
+ title = title.replace("file:", "", 1)
result[title].append((month + " " + day + " " + time, v))
diff --git a/version.json b/version.json
index f898ea3a3d5..a0b32133df4 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "githash": "79d9b3ab5ce20f51c272b4411202710a082d0317",
- "version": "3.2.10"
+ "githash": "009580ad490190ba33d1c6253ebd8d91808923e4",
+ "version": "3.2.11"
} \ No newline at end of file