diff options
author | Andrew Morton <andrew.morton@mongodb.com> | 2023-05-14 21:44:42 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-05-14 22:25:25 +0000 |
commit | 8faef7bcc431815d964ee35a45a6a1d21003a063 (patch) | |
tree | 25fec940a3e535c41f10eff2721f258a3187d552 | |
parent | 5f8e991bbe20359c7d824a6fed385fde1dbd289f (diff) | |
download | mongo-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.data | 2 | ||||
-rw-r--r-- | src/third_party/wiredtiger/src/session/session_api.c | 72 | ||||
-rw-r--r-- | src/third_party/wiredtiger/test/suite/test_truncate21.py | 149 |
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() |