summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morton <andrew.morton@mongodb.com>2023-05-14 21:44:42 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-05-14 22:25:25 +0000
commit8faef7bcc431815d964ee35a45a6a1d21003a063 (patch)
tree25fec940a3e535c41f10eff2721f258a3187d552
parent5f8e991bbe20359c7d824a6fed385fde1dbd289f (diff)
downloadmongo-8faef7bcc431815d964ee35a45a6a1d21003a063.tar.gz
Import wiredtiger: 686cea52e636f395e8582919fc1e153c5ba1af28 from branch mongodb-master
ref: 745a933499..686cea52e6 for: 7.1.0-rc0 WT-10987 Always log a truncate even if no work to do.
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/src/session/session_api.c72
-rw-r--r--src/third_party/wiredtiger/test/suite/test_truncate21.py149
3 files changed, 208 insertions, 15 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data
index aab01d4d508..a04df1b55fc 100644
--- a/src/third_party/wiredtiger/import.data
+++ b/src/third_party/wiredtiger/import.data
@@ -2,5 +2,5 @@
"vendor": "wiredtiger",
"github": "wiredtiger/wiredtiger.git",
"branch": "mongodb-master",
- "commit": "745a9334996a60ee8d4ecf7f238325e489ae1e00"
+ "commit": "686cea52e636f395e8582919fc1e153c5ba1af28"
}
diff --git a/src/third_party/wiredtiger/src/session/session_api.c b/src/third_party/wiredtiger/src/session/session_api.c
index 2c5cc0f2cc3..da182348426 100644
--- a/src/third_party/wiredtiger/src/session/session_api.c
+++ b/src/third_party/wiredtiger/src/session/session_api.c
@@ -1483,16 +1483,18 @@ int
__wt_session_range_truncate(
WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *start, WT_CURSOR *stop)
{
+ WT_CURSOR_BTREE *cbt;
+ WT_DATA_HANDLE *dhandle;
WT_DECL_ITEM(orig_start_key);
WT_DECL_ITEM(orig_stop_key);
WT_DECL_RET;
WT_ITEM start_key, stop_key;
WT_TRUNCATE_INFO *trunc_info, _trunc_info;
int cmp;
- bool local_start;
+ bool local_start, log_op, log_trunc;
orig_start_key = orig_stop_key = NULL;
- local_start = false;
+ local_start = log_trunc = false;
/* Setup the truncate information structure */
trunc_info = &_trunc_info;
@@ -1561,6 +1563,24 @@ __wt_session_range_truncate(
}
/*
+ * Now that the truncate is setup and ready regardless of how the API was called, populate our
+ * truncate information cookie.
+ */
+ trunc_info->session = session;
+ trunc_info->start = start;
+ trunc_info->stop = stop;
+ trunc_info->orig_start_key = orig_start_key;
+ trunc_info->orig_stop_key = orig_stop_key;
+ if (uri != NULL)
+ trunc_info->uri = uri;
+ else if (start != NULL)
+ trunc_info->uri = start->internal_uri;
+ else {
+ WT_ASSERT(session, stop != NULL);
+ trunc_info->uri = stop->internal_uri;
+ }
+
+ /*
* Truncate does not require keys actually exist so that applications can discard parts of the
* object's name space without knowing exactly what records currently appear in the object. For
* this reason, do a search-near, rather than a search. Additionally, we have to correct after
@@ -1575,12 +1595,14 @@ __wt_session_range_truncate(
if ((ret = start->search_near(start, &cmp)) != 0 ||
(cmp < 0 && (ret = start->next(start)) != 0)) {
WT_ERR_NOTFOUND_OK(ret, false);
+ log_trunc = true;
goto done;
}
if (stop != NULL && !F_ISSET(stop, WT_CURSTD_KEY_INT))
if ((ret = stop->search_near(stop, &cmp)) != 0 ||
(cmp > 0 && (ret = stop->prev(stop)) != 0)) {
WT_ERR_NOTFOUND_OK(ret, false);
+ log_trunc = true;
goto done;
}
@@ -1596,6 +1618,8 @@ __wt_session_range_truncate(
WT_ERR(__session_open_cursor((WT_SESSION *)session, stop->uri, NULL, NULL, &start));
local_start = true;
WT_ERR(start->next(start));
+ /* Record new start cursor. */
+ trunc_info->start = start;
}
/*
@@ -1603,24 +1627,44 @@ __wt_session_range_truncate(
*/
if (stop != NULL) {
WT_ERR(start->compare(start, stop, &cmp));
- if (cmp > 0)
+ if (cmp > 0) {
+ log_trunc = true;
goto done;
+ }
}
- /*
- * Now that the truncate is setup and ready regardless of how the API was called, populate our
- * truncate information cookie.
- */
- trunc_info->session = session;
- trunc_info->uri = uri != NULL ? uri : start->internal_uri;
- trunc_info->start = start;
- trunc_info->stop = stop;
- trunc_info->orig_start_key = orig_start_key;
- trunc_info->orig_stop_key = orig_stop_key;
-
WT_ERR(__wt_schema_range_truncate(trunc_info));
done:
+ /*
+ * In the cases where truncate doesn't have work to do, we still need to generate a log record
+ * for the operation. That way we can be consistent with other competing inserts or truncates on
+ * other tables in this transaction.
+ */
+ if (log_trunc) {
+ /*
+ * If we have cursors and know there is no work to do, there may not be a dhandle in the
+ * session. Grab it from the start or stop cursor as needed.
+ */
+ dhandle = session->dhandle;
+ if (dhandle == NULL && start != NULL) {
+ cbt = (WT_CURSOR_BTREE *)start;
+ dhandle = cbt->dhandle;
+ } else if (dhandle == NULL && stop != NULL) {
+ cbt = (WT_CURSOR_BTREE *)stop;
+ dhandle = cbt->dhandle;
+ }
+ /* We have to have a dhandle from somewhere. */
+ WT_ASSERT(session, dhandle != NULL);
+ if (WT_DHANDLE_BTREE(dhandle)) {
+ WT_WITH_DHANDLE(session, dhandle, log_op = __wt_log_op(session));
+ if (log_op) {
+ WT_WITH_DHANDLE(session, dhandle, ret = __wt_txn_truncate_log(trunc_info));
+ WT_ERR(ret);
+ __wt_txn_truncate_end(session);
+ }
+ }
+ }
err:
/*
* Close any locally-opened start cursor.
diff --git a/src/third_party/wiredtiger/test/suite/test_truncate21.py b/src/third_party/wiredtiger/test/suite/test_truncate21.py
new file mode 100644
index 00000000000..b9016b3788f
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_truncate21.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+#
+# Public Domain 2014-present MongoDB, Inc.
+# Public Domain 2008-2014 WiredTiger, Inc.
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+import wiredtiger, wttest
+import os
+from helper import copy_wiredtiger_home
+from wtscenario import make_scenarios
+from wiredtiger import WT_NOTFOUND
+
+# test_truncate21.py
+# Test truncate, logging and recovery when truncate has no work to do.
+class test_truncate21(wttest.WiredTigerTestCase):
+ conn_config = 'cache_size=2MB,log=(enabled)'
+ dir = "newdir"
+ nentries = 1000
+
+ uri_fix = 'table:trunc_fix'
+ uri_row = 'table:trunc_row'
+ create_fix = 'key_format=r,value_format=8t'
+ create_row = 'key_format=i,value_format=S'
+
+ start_key = nentries // 4
+ end_key = nentries // 2
+ # Pick a key in the range we will truncate to re-insert.
+ insert_key = (start_key + end_key) // 2
+
+ def trunc_range(self):
+ # In a transaction, truncate the same range from all three tables.
+ # Then truncate the same range again, also inserting one key into the range for
+ # the row-store table. Delete a range from the middle of the table.
+ cfix_start = self.session.open_cursor(self.uri_fix)
+ crow_start = self.session.open_cursor(self.uri_row)
+ cfix_end = self.session.open_cursor(self.uri_fix)
+ crow_end = self.session.open_cursor(self.uri_row)
+ cfix_start.set_key(self.start_key)
+ crow_start.set_key(self.start_key)
+ cfix_end.set_key(self.end_key)
+ crow_end.set_key(self.end_key)
+ # Do the truncate on teach table.
+ self.session.truncate(None, cfix_start, cfix_end, None)
+ self.session.truncate(None, crow_start, crow_end, None)
+ cfix_start.close()
+ crow_start.close()
+ cfix_end.close()
+ crow_end.close()
+
+ def test_truncate21(self):
+
+ # Create one table of each type: FLCS, row-store and VLCS.
+ # Put the same data into each (per allowed by type).
+ self.session.create(self.uri_fix, self.create_fix)
+ self.session.create(self.uri_row, self.create_row)
+
+ cfix = self.session.open_cursor(self.uri_fix)
+ crow = self.session.open_cursor(self.uri_row)
+ for i in range(1, self.nentries):
+ self.session.begin_transaction()
+ cfix[i] = 97
+ crow[i] = 'rowval'
+ self.session.commit_transaction()
+ cfix.close()
+ crow.close()
+ self.session.checkpoint()
+
+ # In a transaction, truncate the same range from all three tables.
+ # Then truncate the same range again, also inserting one key into the range for
+ # the row-store table. Delete a range from the middle of the table.
+ self.session.begin_transaction()
+ self.trunc_range()
+ self.session.commit_transaction()
+
+ # Open a second session and transaction. In one we truncate the same range again.
+ # In the other we insert into the FLCS and row-store tables. (The VLCS table will be
+ # used in a later test.)
+ session2 = self.conn.open_session()
+
+ self.session.begin_transaction()
+ session2.begin_transaction()
+ # In the other session, truncate the same range again.
+ self.trunc_range()
+ # Commit the insert.
+ # With overlapping transactions, insert into the key range for FLCS and row.
+ cfix = session2.open_cursor(self.uri_fix)
+ crow = session2.open_cursor(self.uri_row)
+ cfix[self.insert_key] = 98
+ crow[self.insert_key] = 'newval'
+
+ session2.commit_transaction()
+ # Commit the truncate.
+ self.session.commit_transaction()
+
+ cfix.close()
+ crow.close()
+ session2.close()
+
+ # Flush all log records.
+ self.session.log_flush('sync=on')
+
+ # Make a copy of the database and open it. That forces recovery to be run, which should
+ # replay both truncate calls and then find the keys.
+ os.mkdir(self.dir)
+ copy_wiredtiger_home(self, '.', self.dir)
+
+ new_conn = self.wiredtiger_open(self.dir, self.conn_config)
+ new_sess = self.setUpSessionOpen(new_conn)
+ cfix = new_sess.open_cursor(self.uri_fix)
+ crow = new_sess.open_cursor(self.uri_row)
+ cfix.set_key(self.insert_key)
+ crow.set_key(self.insert_key)
+ ret_fix = cfix.search()
+ ret_row = crow.search()
+ # The key should not exist in both. In FLCS the record number always exists but the value
+ # should be zero.
+ val_fix = cfix.get_value()
+ self.pr('ret_row ' + str(ret_row))
+ self.pr('val_fix ' + str(val_fix))
+ self.assertEqual(ret_row, wiredtiger.WT_NOTFOUND)
+ self.assertEqual(val_fix, 0)
+ cfix.close()
+ crow.close()
+ new_conn.close()
+
+if __name__ == '__main__':
+ wttest.run()