1#!/usr/bin/env python 2# 3# Public Domain 2014-2018 MongoDB, Inc. 4# Public Domain 2008-2014 WiredTiger, Inc. 5# 6# This is free and unencumbered software released into the public domain. 7# 8# Anyone is free to copy, modify, publish, use, compile, sell, or 9# distribute this software, either in source code form or as a compiled 10# binary, for any purpose, commercial or non-commercial, and by any 11# means. 12# 13# In jurisdictions that recognize copyright laws, the author or authors 14# of this software dedicate any and all copyright interest in the 15# software to the public domain. We make this dedication for the benefit 16# of the public at large and to the detriment of our heirs and 17# successors. We intend this dedication to be an overt act of 18# relinquishment in perpetuity of all present and future rights to this 19# software under copyright law. 20# 21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27# OTHER DEALINGS IN THE SOFTWARE. 28# 29# test_txn16.py 30# Recovery: Test that toggling between logging and not logging does not 31# continue to generate more log files. 32# 33 34import fnmatch, os, shutil, time 35from suite_subprocess import suite_subprocess 36import wttest 37 38class test_txn16(wttest.WiredTigerTestCase, suite_subprocess): 39 t1 = 'table:test_txn16_1' 40 t2 = 'table:test_txn16_2' 41 t3 = 'table:test_txn16_3' 42 nentries = 1000 43 create_params = 'key_format=i,value_format=i' 44 # Set the log file size small so we generate checkpoints 45 # with LSNs in different files. 46 conn_config = 'config_base=false,' + \ 47 'log=(archive=false,enabled,file_max=100K),' + \ 48 'transaction_sync=(method=dsync,enabled)' 49 conn_on = 'config_base=false,' + \ 50 'log=(archive=false,enabled,file_max=100K),' + \ 51 'transaction_sync=(method=dsync,enabled)' 52 conn_off = 'config_base=false,log=(enabled=false)' 53 54 def populate_table(self, uri): 55 self.session.create(uri, self.create_params) 56 c = self.session.open_cursor(uri, None, None) 57 # Populate with an occasional checkpoint to generate 58 # some varying LSNs. 59 for i in range(self.nentries): 60 c[i] = i + 1 61 if i % 900 == 0: 62 self.session.checkpoint() 63 c.close() 64 65 def copy_dir(self, olddir, newdir): 66 ''' Simulate a crash from olddir and restart in newdir. ''' 67 # with the connection still open, copy files to new directory 68 shutil.rmtree(newdir, ignore_errors=True) 69 os.mkdir(newdir) 70 for fname in os.listdir(olddir): 71 fullname = os.path.join(olddir, fname) 72 # Skip lock file on Windows since it is locked 73 if os.path.isfile(fullname) and \ 74 "WiredTiger.lock" not in fullname and \ 75 "Tmplog" not in fullname and \ 76 "Preplog" not in fullname: 77 shutil.copy(fullname, newdir) 78 # close the original connection. 79 self.close_conn() 80 81 def run_toggle(self, homedir): 82 loop = 0 83 # Record original log files. There should never be overlap 84 # with these even after they're removed. 85 orig_logs = fnmatch.filter(os.listdir(homedir), "*gerLog*") 86 while loop < 3: 87 # Reopen with logging on to run recovery first time 88 on_conn = self.wiredtiger_open(homedir, self.conn_on) 89 on_conn.close() 90 if loop > 0: 91 # Get current log files. 92 cur_logs = fnmatch.filter(os.listdir(homedir), "*gerLog*") 93 scur = set(cur_logs) 94 sorig = set(orig_logs) 95 # There should never be overlap with the log files that 96 # were there originally. Mostly this checks that after 97 # opening with logging disabled and then re-enabled, we 98 # don't see log file 1. 99 self.assertEqual(scur.isdisjoint(sorig), True) 100 if loop > 1: 101 # We should be creating the same log files each time. 102 for l in cur_logs: 103 self.assertEqual(l in last_logs, True) 104 for l in last_logs: 105 self.assertEqual(l in cur_logs, True) 106 last_logs = cur_logs 107 loop += 1 108 # Remove all log files before opening without logging. 109 cur_logs = fnmatch.filter(os.listdir(homedir), "*gerLog*") 110 for l in cur_logs: 111 path=homedir + "/" + l 112 os.remove(path) 113 off_conn = self.wiredtiger_open(homedir, self.conn_off) 114 off_conn.close() 115 116 def test_recovery(self): 117 ''' Check log file creation when toggling. ''' 118 119 # Here's the strategy: 120 # - With logging populate 4 tables. Checkpoint 121 # them at different times. 122 # - Copy to a new directory to simulate a crash. 123 # - Close the original connection. 124 # On both a "copy" to simulate a crash and the original (3x): 125 # - Record log files existing. 126 # - Reopen with logging to run recovery. Close connection. 127 # - Record log files existing. 128 # - Remove all log files. 129 # - Open connection with logging disabled. 130 # - Record log files existing. Verify we don't keep adding. 131 # 132 self.populate_table(self.t1) 133 self.populate_table(self.t2) 134 self.populate_table(self.t3) 135 self.copy_dir(".", "RESTART") 136 self.run_toggle(".") 137 self.run_toggle("RESTART") 138 139if __name__ == '__main__': 140 wttest.run() 141