All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.contrastsecurity.cassandra.migration.action.Migrate Maven / Gradle / Ivy

package com.contrastsecurity.cassandra.migration.action;

import com.contrastsecurity.cassandra.migration.CassandraMigrationException;
import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO;
import com.contrastsecurity.cassandra.migration.info.*;
import com.contrastsecurity.cassandra.migration.logging.Log;
import com.contrastsecurity.cassandra.migration.logging.LogFactory;
import com.contrastsecurity.cassandra.migration.resolver.MigrationExecutor;
import com.contrastsecurity.cassandra.migration.resolver.MigrationResolver;
import com.contrastsecurity.cassandra.migration.utils.StopWatch;
import com.contrastsecurity.cassandra.migration.utils.TimeFormat;
import com.datastax.driver.core.Session;

public class Migrate {
    private static final Log LOG = LogFactory.getLog(Migrate.class);

    private final MigrationVersion target;
    private final SchemaVersionDAO schemaVersionDAO;
    private final MigrationResolver migrationResolver;
    private final Session session;
    private final String user;
    private final boolean allowOutOfOrder;

    public Migrate(MigrationResolver migrationResolver, MigrationVersion target, SchemaVersionDAO schemaVersionDAO,
                   Session session, String user, boolean allowOutOfOrder) {
        this.migrationResolver = migrationResolver;
        this.schemaVersionDAO = schemaVersionDAO;
        this.session = session;
        this.target = target;
        this.user = user;
        this.allowOutOfOrder = allowOutOfOrder;
    }

    public int run() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        int migrationSuccessCount = 0;
        while (true) {
            final boolean firstRun = migrationSuccessCount == 0;

            MigrationInfoService infoService = new MigrationInfoService(migrationResolver, schemaVersionDAO, target, allowOutOfOrder, true);
            infoService.refresh();

            MigrationVersion currentSchemaVersion = MigrationVersion.EMPTY;
            if (infoService.current() != null) {
                currentSchemaVersion = infoService.current().getVersion();
            }
            if (firstRun) {
                LOG.info("Current version of keyspace " + schemaVersionDAO.getKeyspace().getName() + ": " + currentSchemaVersion);
            }

            MigrationInfo[] future = infoService.future();
            if (future.length > 0) {
                MigrationInfo[] resolved = infoService.resolved();
                if (resolved.length == 0) {
                    LOG.warn("Keyspace " + schemaVersionDAO.getKeyspace().getName() + " has version " + currentSchemaVersion
                            + ", but no migration could be resolved in the configured locations !");
                } else {
                    LOG.warn("Keyspace " + schemaVersionDAO.getKeyspace().getName() + " has a version (" + currentSchemaVersion
                            + ") that is newer than the latest available migration ("
                            + resolved[resolved.length - 1].getVersion() + ") !");
                }
            }

            MigrationInfo[] failed = infoService.failed();
            if (failed.length > 0) {
                if ((failed.length == 1)
                        && (failed[0].getState() == MigrationState.FUTURE_FAILED)) {
                    LOG.warn("Keyspace " + schemaVersionDAO.getKeyspace().getName() + " contains a failed future migration to version " + failed[0].getVersion() + " !");
                } else {
                    throw new CassandraMigrationException("Keyspace " + schemaVersionDAO.getKeyspace().getName() + " contains a failed migration to version " + failed[0].getVersion() + " !");
                }
            }

            MigrationInfo[] pendingMigrations = infoService.pending();

            if (pendingMigrations.length == 0) {
                break;
            }

            boolean isOutOfOrder = pendingMigrations[0].getVersion().compareTo(currentSchemaVersion) < 0;
            MigrationVersion mv = applyMigration(pendingMigrations[0], isOutOfOrder);
            if(mv == null) {
                //no more migrations
                break;
            }

            migrationSuccessCount++;
        }

        stopWatch.stop();

        logSummary(migrationSuccessCount, stopWatch.getTotalTimeMillis());

        return migrationSuccessCount;
    }

    private MigrationVersion applyMigration(final MigrationInfo migration, boolean isOutOfOrder) {
        MigrationVersion version = migration.getVersion();
        LOG.info("Migrating keyspace " + schemaVersionDAO.getKeyspace().getName() + " to version " + version + " - " + migration.getDescription() +
                (isOutOfOrder ? " (out of order)" : ""));

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try {
            final MigrationExecutor migrationExecutor = migration.getResolvedMigration().getExecutor();
            try {
                migrationExecutor.execute(session);
            } catch (Exception e) {
                throw new CassandraMigrationException("Unable to apply migration", e);
            }
            LOG.debug("Successfully completed and committed migration of keyspace " +
                    schemaVersionDAO.getKeyspace().getName() + " to version " + version);
        } catch (CassandraMigrationException e) {
            String failedMsg = "Migration of keyspace " + schemaVersionDAO.getKeyspace().getName() +
                    " to version " + version + " failed!";

            LOG.error(failedMsg + " Please restore backups and roll back database and code!");

            stopWatch.stop();
            int executionTime = (int) stopWatch.getTotalTimeMillis();
            AppliedMigration appliedMigration = new AppliedMigration(version, migration.getDescription(),
                    migration.getType(), migration.getScript(), migration.getChecksum(), user, executionTime, false);
            schemaVersionDAO.addAppliedMigration(appliedMigration);
            throw e;
        }

        stopWatch.stop();
        int executionTime = (int) stopWatch.getTotalTimeMillis();

        AppliedMigration appliedMigration = new AppliedMigration(version, migration.getDescription(),
                migration.getType(), migration.getScript(), migration.getChecksum(), user, executionTime, true);
        schemaVersionDAO.addAppliedMigration(appliedMigration);

        return version;
    }

    /**
     * Logs the summary of this migration run.
     *
     * @param migrationSuccessCount The number of successfully applied migrations.
     * @param executionTime         The total time taken to perform this migration run (in ms).
     */
    private void logSummary(int migrationSuccessCount, long executionTime) {
        if (migrationSuccessCount == 0) {
            LOG.info("Keyspace " + schemaVersionDAO.getKeyspace().getName() + " is up to date. No migration necessary.");
            return;
        }

        if (migrationSuccessCount == 1) {
            LOG.info("Successfully applied 1 migration to keyspace " + schemaVersionDAO.getKeyspace().getName() + " (execution time " + TimeFormat.format(executionTime) + ").");
        } else {
            LOG.info("Successfully applied " + migrationSuccessCount + " migrations to keyspace " + schemaVersionDAO.getKeyspace().getName() + " (execution time " + TimeFormat.format(executionTime) + ").");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy