-
Notifications
You must be signed in to change notification settings - Fork 360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updates to fix coercion errors. DSDEEPB-727 #84
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
package cromwell.binding.values | ||
|
||
import scala.util.Try | ||
|
||
trait WdlPrimitive extends WdlValue { | ||
def asString: String | ||
override def toRawString = Try(asString) | ||
override def toRawString = asString | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,9 @@ import java.util.{Date, UUID} | |
import javax.sql.rowset.serial.SerialClob | ||
|
||
import _root_.slick.util.ConfigExtensionMethods._ | ||
import com.typesafe.config.Config | ||
import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory} | ||
import cromwell.binding._ | ||
import cromwell.binding.types.WdlType | ||
import cromwell.binding.values.WdlValue | ||
import cromwell.engine._ | ||
import cromwell.engine.backend.Backend | ||
import cromwell.engine.backend.local.LocalBackend | ||
|
@@ -36,6 +35,32 @@ object SlickDataAccess { | |
implicit class StringToClob(val str: String) extends AnyVal { | ||
def toClob: Clob = new SerialClob(str.toCharArray) | ||
} | ||
|
||
implicit class ConfigWithUniqueSchema(val config: Config) extends AnyVal { | ||
/** | ||
* Modifies config.getString("url") to return a unique schema, if the original url contains the text | ||
* "${slick.uniqueSchema}". | ||
* | ||
* This allows each instance of a SlickDataAccess object to use a clean, and different, in memory database. | ||
* | ||
* @return Config with ${slick.uniqueSchema} in url replaced with a unique string. | ||
*/ | ||
def withUniqueSchema: Config = { | ||
val url = config.getString("url") | ||
if (url.contains("${slick.uniqueSchema}")) { | ||
// Config wasn't updating with a simple withValue/withFallback. | ||
// So instead, do a bit of extra work to insert the generated schema name in the url. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wish I knew Typesafe Config well enough to suggest a workaround... 😢 |
||
val schema = UUID.randomUUID().toString | ||
val newUrl = url.replaceAll("""\$\{slick\.uniqueSchema\}""", schema) | ||
val origin = "url with slick.uniqueSchema=" + schema | ||
val urlConfigValue = ConfigValueFactory.fromAnyRef(newUrl, origin) | ||
val urlConfig = ConfigFactory.empty(origin).withValue("url", urlConfigValue) | ||
urlConfig.withFallback(config) | ||
} else { | ||
config | ||
} | ||
} | ||
} | ||
} | ||
|
||
class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponent) extends DataAccess { | ||
|
@@ -56,7 +81,7 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
import dataAccess.driver.api._ | ||
|
||
// NOTE: if you want to refactor database is inner-class type: this.dataAccess.driver.backend.DatabaseFactory | ||
val database = Database.forConfig("", databaseConfig) | ||
val database = Database.forConfig("", databaseConfig.withUniqueSchema) | ||
|
||
// Possibly create the database | ||
{ | ||
|
@@ -70,11 +95,18 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
// used in key specification without a key length | ||
// | ||
// Perhaps we'll use a more optimized data type for UUID's bytes in the future, as a FK, instead auto-inc cols | ||
// | ||
// The value `${slick.uniqueSchema}` may be used in the url, in combination with `slick.createSchema = true`, to | ||
// generate unique schema instances that don't conflict. | ||
// | ||
// Otherwise, create one DataAccess and hold on to the reference. | ||
if (databaseConfig.getBooleanOr("slick.createSchema")) { | ||
Await.result(database.run(dataAccess.schema.create), Duration.Inf) | ||
} | ||
} | ||
|
||
override def shutdown() = database.shutdown | ||
|
||
// Run action with an outer transaction | ||
private def runTransaction[R](action: DBIOAction[R, _ <: NoStream, _ <: Effect]): Future[R] = { | ||
database.run(action.transactionally) | ||
|
@@ -127,7 +159,7 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
symbol.key.iteration.getOrElse(IterationNone), | ||
if (symbol.isInput) IoInput else IoOutput, | ||
symbol.wdlType.toWdlString, | ||
symbol.wdlValue.map(_.toRawString.get)) | ||
symbol.wdlValue.map(_.toRawString)) | ||
} | ||
} | ||
|
||
|
@@ -341,8 +373,7 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
new SymbolStoreEntry( | ||
symbolStoreKey, | ||
wdlType, | ||
symbolResult.wdlValue.map(wdlType.coerceRawValue(_).get)) | ||
|
||
symbolResult.wdlValue.map(wdlType.fromRawString)) | ||
} | ||
|
||
} yield symbolStoreEntries | ||
|
@@ -351,7 +382,7 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
} | ||
|
||
/** Should fail if a value is already set. The keys in the Map are locally qualified names. */ | ||
override def setOutputs(workflowId: WorkflowId, call: Call, callOutputs: Map[String, WdlValue]): Future[Unit] = { | ||
override def setOutputs(workflowId: WorkflowId, call: Call, callOutputs: WorkflowOutputs): Future[Unit] = { | ||
|
||
val action = for { | ||
workflowExecution <- dataAccess.workflowExecutionsByWorkflowExecutionUuid(workflowId.toString).result.head | ||
|
@@ -360,11 +391,11 @@ class SlickDataAccess(databaseConfig: Config, val dataAccess: DataAccessComponen | |
new Symbol( | ||
workflowExecution.workflowExecutionId.get, | ||
call.fullyQualifiedName, | ||
call.fullyQualifiedName + "." + symbolLocallyQualifiedName, | ||
symbolLocallyQualifiedName, | ||
IterationNone, | ||
IoOutput, | ||
wdlValue.wdlType.toWdlString, | ||
Option(wdlValue.toRawString.get)) | ||
Option(wdlValue.toRawString)) | ||
} | ||
} yield () | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ import akka.pattern.ask | |
import akka.util.Timeout | ||
import com.typesafe.config.ConfigFactory | ||
import com.wordnik.swagger.model.ApiInfo | ||
import cromwell.engine.db.DataAccess | ||
import cromwell.webservice.{CromwellApiService, CromwellApiServiceActor, SwaggerService} | ||
import spray.can.Http | ||
|
||
|
@@ -18,7 +17,12 @@ import scala.util.{Failure, Success} | |
// Note that as per the language specification, this is instantiated lazily and only used when necessary (i.e. server mode) | ||
object CromwellServer extends DefaultWorkflowManagerSystem { | ||
val conf = ConfigFactory.parseFile(new File("/etc/cromwell.conf")) | ||
private lazy val realDataAccess = DataAccess() | ||
|
||
// NOTE: Currently the this.dataAccess is passed in to this.workflowManagerActor. | ||
// The actor could technically restart with the same instance of the dataAccess, | ||
// So, we're not shutting down dataAccess during this.workflowManagerActor.postStop() nor this.service.postStop(). | ||
// Not sure otherwise when this server is really shutting down, so this.dataAccess currently never explicitly closed. | ||
// Shouldn't be an issue unless perhaps test code tries to launch multiple servers and leaves dangling connections. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we have some lifecycle stuff to figure out. Thanks for documenting what's happening here. 😄 |
||
|
||
val swaggerConfig = conf.getConfig("swagger") | ||
val swaggerService = new SwaggerService( | ||
|
@@ -51,7 +55,4 @@ object CromwellServer extends DefaultWorkflowManagerSystem { | |
case _ => | ||
actorSystem.log.info("Cromwell service started...") | ||
} | ||
|
||
override def dataAccess: DataAccess = realDataAccess | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,4 @@ | ||
package cromwell.server | ||
|
||
import cromwell.engine.db.DataAccess | ||
|
||
case class DefaultWorkflowManagerSystem() extends WorkflowManagerSystem { | ||
def dataAccess: DataAccess = DataAccess() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm behind the times, can you explain what this is doing (and why)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function below (optionally) inserts a unique string into the config value
url
, that is seconds later used internally by slick to connect to the database.When this code snippet activates for the default in memory database (see also reference.conf) this allows two things:
First, quick google searches weren't clear on a one-liner way to make slick behave like liquibase, only updating a schema. If special checks weren't added, each time the schema creation ran, after the first time, it would bomb out on "table already exists". There were a couple of search hits on how to peek in the database to see if the tables existed, but I ultimately backed off on this because...
Secondly, providing a clean, unique database each call to
DataAccess()
/new SlickDataAccess()
leaves the behaviorSlickDataAccess
similar to the oldDummyDataAccess
. The coercion bug wasn't discovered earlier asDummyDataAccess
was being used for tests. Now they can just useDataAccess()
and a clean database will be created for the test.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Can you leave some commentary here describing the situation a bit more?