code.onehippo.org is currently readonly. We are migrating to code.bloomreach.com, please continue working there on Monday 14/12. See: https://docs.bloomreach.com/display/engineering/GitLab

Commit 90938409 authored by Ard Schrijvers's avatar Ard Schrijvers

REPO-1939 Fix hippo lock table creation for MySQL

Make sure that *if* the Hippo lock table exists, that it really contains
an index on lockKey (regardless whether it is a primary key or a unique
index). If it doesn't contain one, the lock table was not created
correctly and contains locks that do not work. Hence, truncating the
lock table is ok. After that, correct the table scheme. Right after
the table scheme has been fixed, other running cluster nodes can log
some errors wrt locks. This is not a problem and expected
parent e84fff42
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -142,27 +142,7 @@ public class DbLockManager extends AbstractLockManager {
final String ... uniqueIndexes) throws RuntimeException {
try {
if (schemaCheckEnabled && !connectionHelper.tableExists(tableName)) {
try (Connection connection = dataSource.getConnection()) {
log.info("Creating table {} ", tableName);
try (Statement statement = connection.createStatement()) {
statement.addBatch(String.format(createTableStatement, tableName));
int index = 1;
for (String uniqueIndex : uniqueIndexes) {
statement.addBatch("CREATE UNIQUE INDEX " + tableName + "_idx_"+index+" on " + tableName + "("+uniqueIndex+")");
index++;
}
statement.setQueryTimeout(10);
statement.executeBatch();
} catch (SQLException e) {
if (connectionHelper.tableExists(tableName)) {
log.debug("Table {} already created by another cluster node", tableName);
} else {
final String errm = "Failed to create table "+ tableName;
log.error(errm + ": {}", e.getMessage());
throw new RuntimeException(errm, e);
}
}
}
createTable(dataSource, connectionHelper, createTableStatement, tableName, uniqueIndexes);
}
} catch (SQLException e) {
final RuntimeException re = new RuntimeException("Could not get a connection or could not (check to) create table", e);
......@@ -171,6 +151,30 @@ public class DbLockManager extends AbstractLockManager {
}
}
protected void createTable(final DataSource dataSource, final ConnectionHelper connectionHelper, final String createTableStatement, final String tableName, final String[] uniqueIndexes) throws SQLException {
try (Connection connection = dataSource.getConnection()) {
log.info("Creating table {} ", tableName);
try (Statement statement = connection.createStatement()) {
statement.addBatch(String.format(createTableStatement, tableName));
int index = 1;
for (String uniqueIndex : uniqueIndexes) {
statement.addBatch("CREATE UNIQUE INDEX " + tableName + "_idx_"+index+" on " + tableName + "("+uniqueIndex+")");
index++;
}
statement.setQueryTimeout(10);
statement.executeBatch();
} catch (SQLException e) {
if (connectionHelper.tableExists(tableName)) {
log.debug("Table {} already created by another cluster node", tableName);
} else {
final String errm = "Failed to create table "+ tableName;
log.error(errm + ": {}", e.getMessage());
throw new RuntimeException(errm, e);
}
}
}
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
......
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -66,6 +66,7 @@ public class DbLockManagerFactory {
switch (dbProductName) {
case "MySQL":
return new MySqlDbLockManager(connectionHelper, dataSource, schemaObjectPrefix, schemaCheckEnabled, clusterNodeId);
case "Microsoft SQL Server":
case "PostgreSQL":
case "H2":
......
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed 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.onehippo.repository.lock.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.apache.jackrabbit.core.util.db.ConnectionHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MySqlDbLockManager extends DbLockManager {
private static final Logger log = LoggerFactory.getLogger(DbLockManager.class);
protected MySqlDbLockManager(final ConnectionHelper connectionHelper, final DataSource dataSource,
final String schemaObjectPrefix, final boolean schemaCheckEnabled, final String clusterNodeId) {
super(connectionHelper, dataSource, schemaObjectPrefix, schemaCheckEnabled, clusterNodeId);
}
public void createTableIfNeeded(final DataSource dataSource,
final ConnectionHelper connectionHelper,
final String createTableStatement,
final String tableName,
final boolean schemaCheckEnabled,
final String ... uniqueIndexes) throws RuntimeException {
try {
if (schemaCheckEnabled && !connectionHelper.tableExists(tableName)) {
createTable(dataSource, connectionHelper, createTableStatement, tableName, uniqueIndexes);
} else {
try (Connection connection = dataSource.getConnection()) {
final Statement indicesStatement = connection.createStatement();
final ResultSet resultSet = indicesStatement.executeQuery(String.format("SHOW INDEX FROM %s", tableName));
boolean lockKeyIndexExists = false;
while (resultSet.next()) {
final String columnName = resultSet.getString("Column_name");
if ("lockKey".equals(columnName)) {
lockKeyIndexExists = true;
log.debug("Found correct database table scheme for '{}'", tableName);
break;
}
}
resultSet.close();
if (!lockKeyIndexExists) {
// Incorrectly the lockKey does not have an index! This is a bug typically manifesting itself with
// MySQL 5.6 because we used varchar(255) instead of varchar(190) where 255 is by default too big to be an index
log.info("Found incorrect database table scheme for '{}'. Correcting the table now.", tableName);
correctTableScheme(tableName, connection, 5);
}
} catch (SQLException e) {
final RuntimeException re = new RuntimeException(String.format("Could not validate the %s table or " +
"could not correct it (perhaps alter table is not allowed by the application). " +
"Cannot start up the repository, correct the table %s manually.", tableName, tableName), e);
log.error(re.getMessage());
throw re;
}
}
} catch (SQLException e) {
final RuntimeException re = new RuntimeException("Could not get a connection or could not (check to) create table", e);
log.error(re.getMessage());
throw re;
}
}
private void correctTableScheme(final String tableName, final Connection connection, final int retries) throws SQLException {
Statement correctLockTable = null;
try {
correctLockTable = connection.createStatement();
correctLockTable.addBatch(String.format("TRUNCATE %s", tableName));
correctLockTable.addBatch(String.format("ALTER TABLE %s CHANGE lockKey lockKey varchar(190)", tableName));
correctLockTable.addBatch(String.format("ALTER TABLE %s ADD UNIQUE INDEX %s_idx_1 (lockKey)", tableName, tableName));
correctLockTable.executeBatch();
correctLockTable.close();
} catch (SQLException e) {
if (correctLockTable != null) {
correctLockTable.close();
if (retries == 0) {
log.error("Tried 5 times to alter table '{}' but does not succeed. Stop the entire cluster and retry to start" +
" this cluster node", tableName);
throw e;
}
final String lowerCaseMsg = e.getCause().getMessage().toLowerCase();
if (lowerCaseMsg.contains("duplicate entry") || lowerCaseMsg.contains("data truncated")) {
log.info("After truncate but before table alteration entries have been inserted");
correctTableScheme(tableName, connection, retries - 1);
} else {
throw e;
}
}
}
}
}
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -22,7 +22,7 @@ import org.apache.jackrabbit.core.util.db.ConnectionHelper;
public class OracleDbLockManager extends DbLockManager {
private final static String ORACLE_CREATE_LOCK_TABLE_STATEMENT = "CREATE TABLE %s (" +
"lockKey VARCHAR(256) NOT NULL, " +
"lockKey VARCHAR(190) NOT NULL, " +
"lockOwner VARCHAR(256), " +
"lockThread VARCHAR(256)," +
"status VARCHAR(256) NOT NULL," +
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment