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_txn18.py
30#   Transactions: test recovery settings
31#
32
33import fnmatch, os, shutil, time
34from suite_subprocess import suite_subprocess
35import wiredtiger, wttest
36
37class test_txn18(wttest.WiredTigerTestCase, suite_subprocess):
38    t1 = 'table:test_txn18'
39    create_params = 'key_format=i,value_format=i'
40    conn_config = 'log=(archive=false,enabled,file_max=100K),' + \
41                'transaction_sync=(method=dsync,enabled)'
42    conn_recerror = conn_config + ',log=(recover=error)'
43    conn_recon = conn_config + ',log=(recover=on)'
44
45    def simulate_crash(self, olddir, newdir):
46        ''' Simulate a crash from olddir and restart in newdir. '''
47        # with the connection still open, copy files to new directory
48        shutil.rmtree(newdir, ignore_errors=True)
49        os.mkdir(newdir)
50        for fname in os.listdir(olddir):
51            fullname = os.path.join(olddir, fname)
52            # Skip lock file on Windows since it is locked
53            if os.path.isfile(fullname) and \
54                "WiredTiger.lock" not in fullname and \
55                "Tmplog" not in fullname and \
56                "Preplog" not in fullname:
57                shutil.copy(fullname, newdir)
58
59    def test_recovery(self):
60        ''' Run the recovery settings '''
61
62        # Here's the strategy:
63        #    - Create a table (t1).
64        #    - Insert data into t1.
65        #    - Simulate a crash.
66        #    - Make recovery run with recovery=error
67        # and make sure it detects an error since recovery is needed
68        #    - Make recovery run with recovery=on.
69        #    - Do a clean shutdown and restart with recovery=error
70        # and make sure is successful.
71        #
72        # If we aren't tracking file IDs properly, it's possible that
73        # we'd end up apply the log records for t2 to table t1.
74        self.session.create(self.t1, self.create_params)
75        #
76        # Since we're logging, we need to flush out the meta-data file
77        # from the create.
78        self.session.checkpoint()
79        c = self.session.open_cursor(self.t1, None, None)
80        for i in range(10000):
81            c[i] = i + 1
82        c.close()
83        olddir = "."
84        newdir = "RESTART"
85        errdir = "ERROR"
86        self.simulate_crash(olddir, errdir)
87        self.simulate_crash(olddir, newdir)
88        # close the original connection
89        self.close_conn()
90        # Trying to open the error directory with recover=error should return an error.
91        msg = '/recovery must be run/'
92        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
93            lambda:self.wiredtiger_open(errdir, self.conn_recerror), msg)
94
95        # If recover=error is run on the directory and returns an error,
96        # make sure when we subsequently open with recover=on it properly
97        # recovers all the data.
98        self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
99            lambda:self.wiredtiger_open(newdir, self.conn_recerror), msg)
100
101        # Opening with recover=on should succeed.
102        self.conn = self.wiredtiger_open(newdir, self.conn_recon)
103        # Make sure the data we added originally is there
104        self.session = self.setUpSessionOpen(self.conn)
105        c = self.session.open_cursor(self.t1, None, None)
106        i = 0
107        for key, value in c:
108            self.assertEqual(i, key)
109            self.assertEqual(i+1, value)
110            i += 1
111        self.assertEqual(i, 10000)
112        c.close()
113        self.close_conn()
114        # Reopening with recover=error after a clean shutdown should succeed.
115        self.conn = self.wiredtiger_open(newdir, self.conn_recerror)
116
117if __name__ == '__main__':
118    wttest.run()
119