/* * RHQ Management Platform * Copyright 2011, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.server.metrics.migrator; import javax.persistence.EntityManager; import com.datastax.driver.core.Session; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.server.metrics.migrator.workers.AggregateDataMigrator; import org.rhq.server.metrics.migrator.workers.CallableMigrationWorker; import org.rhq.server.metrics.migrator.workers.DeleteAllData; import org.rhq.server.metrics.migrator.workers.MigrationTable; import org.rhq.server.metrics.migrator.workers.RawDataMigrator; /** * @author Stefan Negrea * */ public class DataMigrator { public enum DatabaseType { Postgres, Oracle } public enum Task { Migrate, Estimate } private interface RunnableWithException extends Runnable { Exception getException(); } private final Log log = LogFactory.getLog(DataMigrator.class); public static final double UNDER_ESTIMATION_FACTOR = .15; public static final int SQL_TIMEOUT = 6000000; public static final int MAX_NUMBER_OF_FAILURES = 5; private final DataMigratorConfiguration config; private long estimation; public DataMigrator(EntityManager entityManager, Session session, DatabaseType databaseType) { this(entityManager, session, databaseType, false); } public DataMigrator(EntityManager entityManager, Session session, DatabaseType databaseType, boolean experimentalDataSource) { config = new DataMigratorConfiguration(entityManager, session, databaseType, experimentalDataSource); config.setDeleteDataImmediatelyAfterMigration(false); config.setDeleteAllDataAtEndOfMigration(false); config.setRunRawDataMigration(true); config.setRun1HAggregateDataMigration(true); config.setRun6HAggregateDataMigration(true); config.setRun1DAggregateDataMigration(true); } public void runRawDataMigration(boolean value) { config.setRunRawDataMigration(value); } public void run1HAggregateDataMigration(boolean value) { config.setRun1HAggregateDataMigration(value); } public void run6HAggregateDataMigration(boolean value) { config.setRun6HAggregateDataMigration(value); } public void run1DAggregateDataMigration(boolean value) { config.setRun1DAggregateDataMigration(value); } public void deleteDataImmediatelyAfterMigration() { config.setDeleteDataImmediatelyAfterMigration(true); config.setDeleteAllDataAtEndOfMigration(false); } public void deleteAllDataAtEndOfMigration() { config.setDeleteAllDataAtEndOfMigration(true); config.setDeleteDataImmediatelyAfterMigration(false); } public void preserveData() { config.setDeleteAllDataAtEndOfMigration(false); config.setDeleteDataImmediatelyAfterMigration(false); } public long estimate() throws Exception { this.estimation = 0; if (config.isRunRawDataMigration()) { retryOnFailure(new RawDataMigrator(config), Task.Estimate); } if (config.isRun1HAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.ONE_HOUR, config), Task.Estimate); } if (config.isRun6HAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.SIX_HOUR, config), Task.Estimate); } if (config.isRun1DAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.TWENTY_FOUR_HOUR, config), Task.Estimate); } if (config.isDeleteAllDataAtEndOfMigration()) { retryOnFailure(new DeleteAllData(config), Task.Estimate); } estimation = (long) (estimation + estimation * UNDER_ESTIMATION_FACTOR); return estimation; } public void migrateData() throws Exception { if (config.isRunRawDataMigration()) { retryOnFailure(new RawDataMigrator(config), Task.Migrate); } if (config.isRun1HAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.ONE_HOUR, config), Task.Migrate); } if (config.isRun6HAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.SIX_HOUR, config), Task.Migrate); } if (config.isRun1DAggregateDataMigration()) { retryOnFailure(new AggregateDataMigrator(MigrationTable.TWENTY_FOUR_HOUR, config), Task.Migrate); } if (config.isDeleteAllDataAtEndOfMigration()) { retryOnFailure(new DeleteAllData(config), Task.Migrate); } } public void deleteOldData() throws Exception { if (config.isDeleteAllDataAtEndOfMigration()) { retryOnFailure(new DeleteAllData(config), Task.Migrate); } } /** * Retries the migration {@link #MAX_NUMBER_OF_FAILURES} times before * failing the migration operation. * * @param migrator * @throws Exception */ private Thread retryOnFailure(final CallableMigrationWorker migrator, final Task task) throws Exception { RunnableWithException runnable = new RunnableWithException() { private Exception exception; @Override public void run() { int numberOfFailures = 0; Exception caughtException = null; log.info(migrator.getClass()); while (numberOfFailures < MAX_NUMBER_OF_FAILURES) { try { if (task == Task.Estimate) { estimation += migrator.estimate(); } else { migrator.migrate(); } return; } catch (Exception e) { log.error("Migrator " + migrator.getClass() + " failed. Retrying!", e); caughtException = e; numberOfFailures++; } } this.exception = caughtException; } @Override public Exception getException() { return this.exception; } }; Thread localThread = new Thread(runnable); localThread.start(); localThread.join(); if (runnable.getException() != null) { throw runnable.getException(); } return localThread; } public class DataMigratorConfiguration { private final EntityManager entityManager; private final Session session; private final DatabaseType databaseType; private final boolean experimentalDataSource; private boolean deleteDataImmediatelyAfterMigration; private boolean deleteAllDataAtEndOfMigration; private boolean runRawDataMigration; private boolean run1HAggregateDataMigration; private boolean run6HAggregateDataMigration; private boolean run1DAggregateDataMigration; public DataMigratorConfiguration(EntityManager entityManager, Session session, DatabaseType databaseType, boolean experimentalDataSource) { this.entityManager = entityManager; this.session = session; this.databaseType = databaseType; this.experimentalDataSource = experimentalDataSource; } public boolean isDeleteDataImmediatelyAfterMigration() { return deleteDataImmediatelyAfterMigration; } private void setDeleteDataImmediatelyAfterMigration(boolean deleteDataImmediatelyAfterMigration) { this.deleteDataImmediatelyAfterMigration = deleteDataImmediatelyAfterMigration; } public boolean isDeleteAllDataAtEndOfMigration() { return deleteAllDataAtEndOfMigration; } private void setDeleteAllDataAtEndOfMigration(boolean deleteAllDataAtEndOfMigration) { this.deleteAllDataAtEndOfMigration = deleteAllDataAtEndOfMigration; } public boolean isRunRawDataMigration() { return runRawDataMigration; } private void setRunRawDataMigration(boolean runRawDataMigration) { this.runRawDataMigration = runRawDataMigration; } public boolean isRun1HAggregateDataMigration() { return run1HAggregateDataMigration; } private void setRun1HAggregateDataMigration(boolean run1hAggregateDataMigration) { run1HAggregateDataMigration = run1hAggregateDataMigration; } public boolean isRun6HAggregateDataMigration() { return run6HAggregateDataMigration; } private void setRun6HAggregateDataMigration(boolean run6hAggregateDataMigration) { run6HAggregateDataMigration = run6hAggregateDataMigration; } public boolean isRun1DAggregateDataMigration() { return run1DAggregateDataMigration; } private void setRun1DAggregateDataMigration(boolean run1dAggregateDataMigration) { run1DAggregateDataMigration = run1dAggregateDataMigration; } public EntityManager getEntityManager() { return entityManager; } public Session getSession() { return session; } public DatabaseType getDatabaseType() { return databaseType; } public boolean isExperimentalDataSource() { return experimentalDataSource; } } }