Skip to content

Instantly share code, notes, and snippets.

@brianwawok
Created May 10, 2015 20:23
Show Gist options
  • Save brianwawok/01db9d8a1efcff5d2691 to your computer and use it in GitHub Desktop.
Save brianwawok/01db9d8a1efcff5d2691 to your computer and use it in GitHub Desktop.
//EXAMPLE evolution.scala
import java.io.{File, FilenameFilter}
import com.datastax.driver.core.Session
import org.apache.commons.io.filefilter.SuffixFileFilter
import org.joda.time.{DateTime, DateTimeZone}
import play.api.Play.current
import play.api.{Logger, Play}
import scala.collection.JavaConversions._
import scala.io.Source
trait Evolution{
def evolve(session: Session)
}
object Evolutions {
def migrate(session: Session): Unit = {
Logger.info("Starting evolution check...")
val initialTable =
"""
|CREATE TABLE IF NOT EXISTS evolutions(
|name text,
|applied_date timestamp,
|content text,
|PRIMARY KEY (name))
""".stripMargin
session.execute(initialTable)
val appliedSql = "select name from evolutions"
val appliedEvolutions = session.execute(appliedSql).all().map(row => {
row.getString(0)
}).toSet
Logger.debug(s"We found a total of ${appliedEvolutions.size} applied evolutions")
//first apply cql evolutions
val baseDirectory = Play.getExistingFile("conf/evolutions").getOrElse(sys.error("Unable to find file conf/evolutions"))
val evolutions = baseDirectory.listFiles(new SuffixFileFilter("cql").asInstanceOf[FilenameFilter]).sorted
evolutions.foreach(evolutionFile => {
val evolutionName = parseEvolutionName(evolutionFile)
if (appliedEvolutions.contains(evolutionName)) {
Logger.debug(s"Skipping previously applied evolution $evolutionName")
} else {
Logger.info(s"Applying new evolution $evolutionName")
val content = Source.fromFile(evolutionFile).mkString
Source.fromFile(evolutionFile).getLines().filterNot(isComment).mkString("\n").split(";").map(_.trim).foreach(statement => {
if (isInvalidStatement(statement)) {
Logger.debug(s"Skipping invalid statement $statement")
} else if (isExec(statement)){
val className = statement.replaceAll("EXEC", "").trim
Logger.debug(s"Executing class $className")
val clazz = Class.forName(className)
val instance = clazz.newInstance().asInstanceOf[Evolution]
instance.evolve(session)
} else {
Logger.debug(s"Executing valid statement '$statement'")
session.execute(statement)
Thread.sleep(1000)
}
})
Logger.debug("Saving applied evolution to database")
val appliedDate = DateTime.now(DateTimeZone.UTC).toDate
val insertStatement =
"""
|INSERT INTO evolutions (name, applied_date, content) values (?, ?, ?)
""".stripMargin
session.execute(insertStatement, evolutionName, appliedDate, content)
}
})
Logger.info(s"Completed evolution. We are now at version ${parseEvolutionName(evolutions.last)}")
}
private def isComment(statement: String): Boolean = {
statement.startsWith("#")
}
private def isExec(statement : String) : Boolean = {
statement.startsWith("EXEC")
}
private def isInvalidStatement(line: String): Boolean = {
line.length == 0 || isComment(line)
}
private def parseEvolutionName(file : File) : String = {
file.getName.split("\\.").head.toUpperCase
}
}
//example file 2015-05-10.cql to be placed in /conf/evolutions
CREATE TABLE foobar (
businessId uuid,
createdDate timestamp,
PRIMARY KEY(businessId)
)
AND gc_grace_seconds = 3600;
ALTER TABLE zooRaw ADD newColumn boolean;
EXEC com.test.Evolution201505051625;
//sample file com.test.Evolution201505051625 to be placed in with the normal app and do code type migrations
package com.foo.evolutions
import com.datastax.driver.core.Session
import com.foo.Evolution
import scala.collection.JavaConversions._
class Evolution201505051625 extends Evolution {
override def evolve(session: Session): Unit = {
val emails = session.execute("select distinct email from users").all().map(row => {
row.getString(0).trim
})
emails.map(email => {
session.execute(s"update otherTableBar set last_name = 'Fred' where email = ?", email)
})
}
}
@xzeexcz
Copy link

xzeexcz commented Apr 21, 2024

ty

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment