package io.smartcat.migration; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Session; import com.datastax.driver.core.Statement; import io.smartcat.migration.exceptions.MigrationException; import io.smartcat.migration.exceptions.SchemaAgreementException; /** * Abstract migration class that implements session DI and exposes required methods for execution. */ public abstract class Migration { private int version = -1; private MigrationType type = MigrationType.SCHEMA; /** * Active Cassandra session. */ protected Session session; /** * Create new migration with provided type and version. * @param type Migration type (SCHEMA or DATA) * @param version Migration version */ public Migration(final MigrationType type, final int version) { this.type = type; this.version = version; } /** * Enables session injection into migration class. * @param session Session object */ public void setSession(final Session session) { this.session = session; } /** * Returns migration type (schema or data). * @return Migration type */ public MigrationType getType() { return this.type; } /** * Returns resulting database schema version of this migration. * @return Resulting db schema version */ public int getVersion() { return this.version; } /** * Returns migration description (for history purposes). * @return migration description. */ public abstract String getDescription(); /** * Executes migration implementation. * @throws MigrationException exception */ public abstract void execute() throws MigrationException; /** * Execute provided statement and checks if the schema migration has been propagated * to all nodes in the cluster. Use this method when executing schema migrations. * @param statement Statement to be executed * @throws SchemaAgreementException exception */ protected void executeWithSchemaAgreement(Statement statement) throws SchemaAgreementException { ResultSet result = this.session.execute(statement); if (checkSchemaAgreement(result)) { return; } if (checkClusterSchemaAgreement()) { return; } throw new SchemaAgreementException( "Failed to propagate schema update to all nodes (schema agreement error)"); } /** * Whether the cluster had reached schema agreement after the execution of this query. * * After a successful schema-altering query (ex: creating a table), the driver will check if the cluster's nodes * agree on the new schema version. If not, it will keep retrying for a given delay (configurable via * {@link com.datastax.driver.core.Cluster.Builder#withMaxSchemaAgreementWaitSeconds(int)}). * * If this method returns {@code false}, clients can call * {@link com.datastax.driver.core.Metadata#checkSchemaAgreement()} later to perform the check manually. * * Note that the schema agreement check is only performed for schema-altering queries For other query types, this * method will always return {@code true}. * * @param resultSet Statement execution ResultSet * @return whether the cluster reached schema agreement, or {@code true} for a non schema-altering statement. */ protected boolean checkSchemaAgreement(ResultSet resultSet) { return resultSet.getExecutionInfo().isSchemaInAgreement(); } /** * Checks whether hosts that are currently up agree on the schema definition. * * This method performs a one-time check only, without any form of retry; therefore * {@link com.datastax.driver.core.Cluster.Builder#withMaxSchemaAgreementWaitSeconds(int)} * does not apply in this case. * * @return {@code true} if all hosts agree on the schema; {@code false} if * they don't agree, or if the check could not be performed * (for example, if the control connection is down). */ protected boolean checkClusterSchemaAgreement() { return this.session.getCluster().getMetadata().checkSchemaAgreement(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((type == null) ? 0 : type.hashCode()); result = prime * result + version; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Migration other = (Migration) obj; if (type != other.type) return false; if (version != other.version) return false; return true; } }