File: Derby5937SlaveShutdownTest.java

package info (click to toggle)
derby 10.14.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 78,896 kB
  • sloc: java: 691,930; sql: 42,686; xml: 20,511; sh: 3,373; sed: 96; makefile: 60
file content (215 lines) | stat: -rw-r--r-- 8,314 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*

Derby - Class org.apache.derbyTesting.functionTests.tests.replicationTests.Derby5937SlaveShutdownTest

Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

package org.apache.derbyTesting.functionTests.tests.replicationTests;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import junit.framework.Test;
import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.JDBCDataSource;
import org.apache.derbyTesting.junit.NetworkServerTestSetup;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.TestConfiguration;

/**
 * <p>
 * Regression test case for DERBY-5937. After fail-over, the slave database
 * will leak a file handle for the active log file.
 * </p>
 *
 * <p>
 * The test case will set up replication between a master database and a
 * slave database, perform fail-over to the slave, and then shut down the
 * slave database. Finally, it attempts to delete the slave database. On
 * Windows, this fails if DERBY-5937 is not fixed, because one of the log
 * files is still held open, and Windows doesn't allow deletion of open
 * files.
 * </p>
 */
public class Derby5937SlaveShutdownTest extends BaseJDBCTestCase {

    private static final String MASTER_DB = "d5937-master-db";
    private static final String SLAVE_DB = "d5937-slave-db";

    private static final String FAILOVER_SUCCESS = "XRE20";
    private static final String DB_SHUTDOWN_SUCCESS = "08006";

    private static final long RETRY_INTERVAL = 50L;

    public Derby5937SlaveShutdownTest(String name) {
        super(name);
    }

    public static Test suite() {
        //DERBY-5975 test fails intermittently with weme causing a hang.
        // Likely a jvm issue, so don't run on that OS...
        if (BaseTestCase.isJ9Platform())
        {
            Test test = new BaseTestSuite("Derby5937SlaveShutdownTest");
            return test;
        }
        Class klass = Derby5937SlaveShutdownTest.class;
        // The default security policy doesn't allow derby.jar to do
        // networking, which is needed for replication, so install a custom
        // policy for this test.
        return new SecurityManagerSetup(
            TestConfiguration.singleUseDatabaseDecorator(
                TestConfiguration.embeddedSuite(klass), MASTER_DB),
            klass.getName().replace('.', '/') + ".policy", true);
    }

    public void testSlaveFailoverLeak() throws Exception {
        // First establish a connection so that the database is created.
        getConnection().close();

        // Then shut down the database cleanly so that it can be used
        // to seed the replication slave.
        final TestConfiguration config = TestConfiguration.getCurrent();
        config.shutdownDatabase();

        // Copy the database to the slave.
        final String masterDb = config.getDatabasePath(MASTER_DB);
        final String slaveDb = config.getDatabasePath(SLAVE_DB);
        PrivilegedFileOpsForTests.copy(new File(masterDb), new File(slaveDb));

        // And start the slave.
        DataSource startSlaveDS = JDBCDataSource.getDataSource(SLAVE_DB);
        JDBCDataSource.setBeanProperty(startSlaveDS, "connectionAttributes",
                "startSlave=true;slaveHost=" + config.getHostName() +
                ";slavePort=" + config.getPort());
        SlaveThread slave = new SlaveThread(startSlaveDS);
        slave.start();

        // Start the master. This will fail until the slave is up, so do
        // it in a loop until successful or time runs out.
        DataSource startMasterDS = JDBCDataSource.getDataSource();
        JDBCDataSource.setBeanProperty(startMasterDS, "connectionAttributes",
                "startMaster=true;slaveHost=" + config.getHostName() +
                ";slavePort=" + config.getPort());
        long giveUp =
            System.currentTimeMillis() + NetworkServerTestSetup.getWaitTime();
        Connection c = null;
        while (c == null) {
            try {
                c = startMasterDS.getConnection();
            } catch (SQLException sqle) {
                slave.checkError(); // Exit early if the slave has failed
                if (System.currentTimeMillis() > giveUp) {
                    fail("Master won't start", sqle);
                } else {
                    println("Retrying after startMaster failed with: " + sqle);
                    Thread.sleep(RETRY_INTERVAL);
                }
            }
        }
        c.close();

        // Wait for the slave thread to complete, which it will do once
        // it's connected to the master.
        slave.join();
        slave.checkError();

        // Perform fail-over.
        DataSource failoverDS = JDBCDataSource.getDataSource();
        JDBCDataSource.setBeanProperty(
                failoverDS, "connectionAttributes", "failover=true");
        try {
            failoverDS.getConnection();
            fail("failover should receive exception");
        } catch (SQLException sqle) {
            assertSQLState(FAILOVER_SUCCESS, sqle);
        }

        // Shut down the slave database. This will fail until failover is
        // complete, so do it in a loop until successful or time runs out.
        giveUp =
            System.currentTimeMillis() + NetworkServerTestSetup.getWaitTime();
        DataSource slaveShutdownDS = JDBCDataSource.getDataSource(SLAVE_DB);
        JDBCDataSource.setBeanProperty(
                slaveShutdownDS, "shutdownDatabase", "shutdown");
        while (true) {
            try {
                slaveShutdownDS.getConnection();
                fail("Shutdown of slave database didn't throw an exception");
            } catch (SQLException sqle) {
                if (DB_SHUTDOWN_SUCCESS.equals(sqle.getSQLState())) {
                    // The expected shutdown exception was thrown. Break out
                    // of the loop.
                    break;
                } else if (System.currentTimeMillis() > giveUp) {
                    fail("Could not shut down slave database", sqle);
                } else {
                    println("Retrying after failover failed with: " + sqle);
                    Thread.sleep(RETRY_INTERVAL);
                }
            }
        }

        // This call used to fail on Windows because one of the log files
        // was still open.
        assertDirectoryDeleted(new File(slaveDb));
    }

    /**
     * Helper thread which starts a replication slave and blocks until the
     * slave is connected to a master database.
     */
    private class SlaveThread extends Thread {

        private final DataSource ds;
        private volatile Throwable error;

        SlaveThread(DataSource ds) {
            this.ds = ds;
        }

        public void run() {
            try {
                run_();
            } catch (Throwable t) {
                error = t;
            }
        }

        private void run_() throws Exception {
            println("Slave thread started.");

            try {
                ds.getConnection();
                fail("startSlave should throw exception");
            } catch (SQLException sqle) {
                assertSQLState("XRE08", sqle);
            }
        }

        void checkError() {
            if (error != null) {
                fail("Slave thread failed", error);
            }
        }
    }
}