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