osunix / opengrok-trunk

mirror of opengrok

Clone this repository (size: 5.0 MB): HTTPS / SSH
$ hg clone http://hg.pathscale.com/opengrok-trunk
commit 747: 3079a9923d75
parent 746: df23217d5e87
branch: default
Bug #3624: History cache should be stored in Java DB Performance optimization: Make sure that the index cardinality statistics in the database are up to date after inserting lots of data. Without this change, we have seen that get() could be very slow because the optimizer picks a bad plan based on stale statistics. This adds a call to a system procedure introduced in Derby 10.5 (which is still not released). On older versions of Derby we don't call the procedure and we may therefore still see the performance problems.
Knut...@Sun.COM
19 months ago

Changed (Δ3.2 KB):

Up to file-list src/org/opensolaris/opengrok/history/JDBCHistoryCache.java:

@@ -44,6 +44,11 @@ class JDBCHistoryCache implements Histor
44
44
45
45
    private static final String SCHEMA = "APP";
46
46
47
    /** The names of all the tables created by this class. */
48
    private static final String[] TABLES = {
49
        "REPOSITORIES", "FILES", "AUTHORS", "CHANGESETS", "FILECHANGES"
50
    };
51
47
52
    private ConnectionManager connectionManager;
48
53
49
54
    // Many of the tables contain columns with identical names and types,
@@ -263,6 +268,13 @@ class JDBCHistoryCache implements Histor
263
268
                    connectionManager.getConnectionResource();
264
269
            try {
265
270
                storeHistory(conn, history, repository);
271
                // The tables may have grown significantly and made the
272
                // index cardinality statistics outdated. Call this method
273
                // as a workaround to keep the statistics up to date.
274
                // Without this, we have observed very bad performance when
275
                // calling get(), because the outdated statistics can make
276
                // the optimizer choose a sub-optimal join strategy.
277
                updateIndexCardinalityStatistics(conn);
266
278
            } finally {
267
279
                connectionManager.releaseConnection(conn);
268
280
            }
@@ -315,6 +327,48 @@ class JDBCHistoryCache implements Histor
315
327
    }
316
328
317
329
    /**
330
     * <p>
331
     * Make sure Derby's index cardinality statistics are up to date.
332
     * Otherwise, the optimizer may choose a bad execution strategy for
333
     * some queries. This method should be called if the size of the tables
334
     * has changed significantly.
335
     * </p>
336
     *
337
     * <p>
338
     * This is a workaround for the problems described in
339
     * <a href="https://issues.apache.org/jira/browse/DERBY-269">DERBY-269</a> and
340
     * <a href="https://issues.apache.org/jira/browse/DERBY-3788">DERBY-3788</a>.
341
     * When automatic update of index cardinality statistics has been
342
     * implemented in Derby, the workaround may be removed.
343
     * </p>
344
     *
345
     * <p>
346
     * Note that this method uses a system procedure introduced in Derby 10.5.
347
     * If the Derby version used is less than 10.5, this method is a no-op.
348
     * </p>
349
     */
350
    private void updateIndexCardinalityStatistics(ConnectionResource conn)
351
            throws SQLException {
352
        DatabaseMetaData dmd = conn.getMetaData();
353
        int major = dmd.getDatabaseMajorVersion();
354
        int minor = dmd.getDatabaseMinorVersion();
355
        if (major > 10 || (major == 10 && minor >= 5)) {
356
            PreparedStatement ps = conn.prepareStatement(
357
                    "CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS(?, ?, NULL)");
358
            try {
359
                ps.setString(1, SCHEMA);
360
                for (String table : TABLES) {
361
                    ps.setString(2, table);
362
                    ps.execute();
363
                }
364
                conn.commit();
365
            } finally {
366
                ps.close();
367
            }
368
        }
369
    }
370
371
    /**
318
372
     * Get the id of a repository in the database. If the repository is not
319
373
     * stored in the database, add it and return its id.
320
374
     *

Up to file-list src/org/opensolaris/opengrok/jdbc/ConnectionResource.java:

25
25
package org.opensolaris.opengrok.jdbc;
26
26
27
27
import java.sql.Connection;
28
import java.sql.DatabaseMetaData;
28
29
import java.sql.PreparedStatement;
29
30
import java.sql.SQLException;
30
31
import java.sql.Statement;
@@ -104,4 +105,29 @@ public class ConnectionResource {
104
105
    public Statement createStatement() throws SQLException {
105
106
        return conn.createStatement();
106
107
    }
108
109
    /**
110
     * Prepare a statement. This method should only be used to prepare
111
     * statements that are used infrequently. If a statement is likely to
112
     * be used frequently, a {@code StatementCreator} object should be
113
     * created for it, and the method {@link #getStatement(StatementCreator)}
114
     * should be used instead.
115
     *
116
     * @param sql the SQL text to compile
117
     * @return a prepared statement
118
     * @throws SQLException if a database error occurs
119
     */
120
    public PreparedStatement prepareStatement(String sql) throws SQLException {
121
        return conn.prepareStatement(sql);
122
    }
123
124
    /**
125
     * Get a meta-data object for the underlying connection.
126
     *
127
     * @return a {@code DatabaseMetaData} object
128
     * @throws SQLException if a database error occurs
129
     */
130
    public DatabaseMetaData getMetaData() throws SQLException {
131
        return conn.getMetaData();
132
    }
107
133
}