How to handle a 'Configuration error[Cannot connect to database [...]]' - scala

I am implementing a web service with the Play Framework, that uses multiple databases. All databases are configured in the conf/application.conf by specifying the db.database1..., db.database2... properties.
At startup, play will try to establish connections to all databases configured in the database and if one connection fails, the service will not start.
In my case, not all databases are necessary to start the web service, but the web service can still run with limited functionality, if some databases are not available. Since not all databases are under my control, it is crucial for my web service to handle a connection error.
Therefore my question:
Is there a way to either
handle the connection error by overriding some 'onError' method or insert a try-catch at the right place or
manually create the Datasources at runtime to handle the error when they are created
I would prefer solution 2.
I am using play version 2.4.2 with scala version 2.11.7.
Since the whole exceptions fills multiple pages, I only insert the first lines here:
CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, Configuration error: Configuration error[Cannot connect to database [foo]]
while locating play.api.db.DBApiProvider
while locating play.api.db.DBApi
for field at play.api.db.NamedDatabaseProvider.dbApi(DBModule.scala:80)
while locating play.api.db.NamedDatabaseProvider
at com.google.inject.util.Providers$GuicifiedProviderWithDependencies.initialize(Providers.java:149)
at play.api.db.DBModule$$anonfun$namedDatabaseBindings$1.apply(DBModule.scala:34):
Binding(interface play.api.db.Database qualified with QualifierInstance(#play.db.NamedDatabase(value=appstate)) to ProviderTarget(play.api.db.NamedDatabaseProvider#1a7884c6)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
Caused by: Configuration error: Configuration error[Cannot connect to database [foo]]
at play.api.Configuration$.configError(Configuration.scala:178)
at play.api.Configuration.reportError(Configuration.scala:829)
at play.api.db.DefaultDBApi$$anonfun$connect$1.apply(DefaultDBApi.scala:48)
at play.api.db.DefaultDBApi$$anonfun$connect$1.apply(DefaultDBApi.scala:42)
at scala.collection.immutable.List.foreach(List.scala:381)
at play.api.db.DefaultDBApi.connect(DefaultDBApi.scala:42)
at play.api.db.DBApiProvider.get$lzycompute(DBModule.scala:72)

I remember that exists a Global Settings configuration file to catch errors when the application starts.
Take a look here: https://www.playframework.com/documentation/2.0/ScalaGlobal I know you are using a more recent play version but you will get a more general idea how it works.
In play 2.4.x this file was removed and uses DI now (https://www.playframework.com/documentation/2.4.x/GlobalSettings).

To solve my problem, I ended up writing my own wrapper for BoneCP. This way the initialization of the ConnectionPools were in my hands and I could handle connection errors.
Instead of using the db prefix, I use the prefix database (so that Play will not automatically parse its content) in a new config file database.conf.
object ConnectionPool {
private var connectionPools = Map.empty[String, BoneCP]
val config = ConfigFactory.parseFile(new File("conf/database.conf"))
private def dbConfig(dbId: String): ConfigObject = {
config.getObject("database." + dbId).asInstanceOf[ConfigObject]
}
def createConnectionPool(dbId: String): BoneCP = {
val dbConf = dbConfig(dbId)
val cpConfig: BoneCPConfig = new BoneCPConfig()
cpConfig.setJdbcUrl(dbConf.get("url").unwrapped().toString)
cpConfig.setUsername(dbConf.get("user").unwrapped().toString)
cpConfig.setPassword(dbConf.get("password").unwrapped().toString)
new BoneCP(cpConfig)
}
def getConnectionPool(dbId: String): BoneCP = {
if(!connectionPools.contains(dbId)) {
val cp = createConnectionPool(dbId)
connectionPools = (connectionPools + (dbId -> cp))
}
connectionPools(dbId)
}
def getConnection(dbId: String): Connection = {
getConnectionPool(dbId).getConnection()
}
def withConnection[T](dbId: String)(fun: Connection => T): T = {
val conn = getConnection(dbId)
val t = fun(conn)
conn.close()
t
}
}

Related

How to query flink's queryable state

I am using flink 1.8.0 and I am trying to query my job state.
val descriptor = new ValueStateDescriptor("myState", Types.CASE_CLASS[Foo])
descriptor.setQueryable("my-queryable-State")
I used port 9067 which is the default port according to this, my client:
val client = new QueryableStateClient("127.0.0.1", 9067)
val jobId = JobID.fromHexString("d48a6c980d1a147e0622565700158d9e")
val execConfig = new ExecutionConfig
val descriptor = new ValueStateDescriptor("my-queryable-State", Types.CASE_CLASS[Foo])
val res: Future[ValueState[Foo]] = client.getKvState(jobId, "my-queryable-State","a", BasicTypeInfo.STRING_TYPE_INFO, descriptor)
res.map(_.toString).pipeTo(sender)
but I am getting :
[ERROR] [06/25/2019 20:37:05.499] [bvAkkaHttpServer-akka.actor.default-dispatcher-5] [akka.actor.ActorSystemImpl(bvAkkaHttpServer)] Error during processing of request: 'org.apache.flink.shaded.netty4.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: /127.0.0.1:9067'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
java.util.concurrent.CompletionException: org.apache.flink.shaded.netty4.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: /127.0.0.1:9067
what am I doing wrong ?
how and where should I define QueryableStateOptions
So If You want to use the QueryableState You need to add the proper Jar to Your flink. The jar is flink-queryable-state-runtime, it can be found in the opt folder in Your flink distribution and You should move it to the lib folder.
As for the second question the QueryableStateOption is just a class that is used to create static ConfigOption definitions. The definitions are then used to read the configurations from flink-conf.yaml file. So currently the only option to configure the QueryableState is to use the flink-conf file in the flink distribution.
EDIT: Also, try reading this]1 it provides more info on how does Queryable State works. You shouldn't really connect directly to the server port but rather You should use the proxy port which by default is 9069.

Kerberos Exception launching Spark locally

I am trying to set up a Spark Testng unit test:
#Test
def testStuff(): Unit = {
val sc = new SparkContext(new SparkConf().setAppName("test").setMaster("local"))
...
}
The code fails with: IllegalArgumentException: Can't get Kerberos realm
What am I missing?
The error suggests that your JVM is unable to locate the kerberos config (krb5.conf file).
Depending on your company's environment/infrastruture you have a few options:
Check if your company has standard library to set kerberos authentication.
Alternatively try:
set JVM property: -Djava.security.krb5.conf=/file-path/for/krb5.conf
Put the krb5.conf file into the <jdk-home>/jre/lib/security folder

Phantom Scala Cassandra connector: how to specify port, username, password of nodes

I'm trying to use websudos phantom
Does anyone know how to specify connection arguments to Cassandra such as username and password?
Thanks
Phantom doesn't yet support Kerberos authentication in the phantom-connectors framework but it is on the roadmap for the coming weeks.
However, when using connectors it is possible to override the createCluster method and connect to the cluster in any way you want.
object CustomCassandraManger extends DefaultCasandraManager {
override protected[this] def createCluster: Cluster = {
val inets = hosts.toSeq.map(_.getAddress)
Cluster.builder()
.addContactPoints(inets: _*)
...
.withUsernameAndPassword(..)
}
}
I may not have used the correct methods, but that's how you control the way a cluster is created. Then all you need is to inject this manager in a connector:
trait MyConnector extends SimpleCassandraConnector {
override val manager = CustomCassandraMananger
}
And then you mix in this connector into all your tables, just as you would normally when using phantom.
Recent versions of phantom
In more recent versions of phantom, the API is based on ContactPoints, where you can specify whatever ClusterBuilder options you want, using the following DSL.
import com.datastax.driver.core.PlainTextAuthProvider
lazy val local = ContactPoint.local.withClusterBuilder(
_.withAuthProvider(new PlainTextAuthProvider("user", "pass"))
).keySpace("phantom")

How to do error logging when Play 2.1 crashes on startup

I have a Play 2.1 application which does not start when I have the wrong database url. The problem is, the error message isn't that great.
[error] c.j.b.h.AbstractConnectionHook - Failed to obtain initial connection Sleeping for 0ms and trying again. A ttempts left: 0. Exception: null
Oops, cannot start the server.
Configuration error: Configuration error[Cannot connect to database [default]]
at play.api.Configuration$.play$api$Configuration$$configError(Configuration.scala:74)
at play.api.Configuration.reportError(Configuration.scala:552)
at play.api.db.BoneCPPlugin$$anonfun$onStart$1.apply(DB.scala:248)
at play.api.db.BoneCPPlugin$$anonfun$onStart$1.apply(DB.scala:239)
....
I'd like the server to dump the database url it's trying to use in this case. Does Play 2.1 provide any hooks to execute code when there is an exception during startup?
In Play Framework 2, you can override certain phases of the lifecycle by extending GlobalSettings. Specifically, onLoadConfig get called before the config is parsed and the DB connection is established.
Here's a (hacky) example of intercepting errors. You can create a fake instance of Application, then pass it the Configuration object. Then you can use it to create an instance of BoneCPPlugin and try creating a connection. In case when the DB is not reachable, you'll be able to intercept that in the catch block.
import java.io.File
import play.api._
import play.api.db.BoneCPPlugin
import scala.util.control.NonFatal
object Global extends GlobalSettings {
override def onLoadConfig(config: Configuration, path: File, classloader: ClassLoader, mode: Mode.Mode) = {
val app = new DefaultApplication(path, classloader, None, mode){
override lazy val configuration = config
}
try {
new BoneCPPlugin(app).onStart()
} catch {
case e: PlayException =>
// handle
case _ => // other types of errors that we don't care about here
}
super.onLoadConfig(config, path, classloader, mode)
}
}

How do I run multiple functional specs with TestServer in Play 2.0.1?

I am having problems running multiple functional specs (using specs2), specifically tests that start a TestServer, open an HTMLUNIT browser, and navigate to a page to check an element. The page in question loads the elements that we test on an ajax request. The wait for the element to be present times out with the error message below.
Code snippet:
trait CommonSteps extends BaseSpecfication {
val testServer: TestServer = TestServer(3333)
val testServerBaseURL: String = "http://localhost:3333/"
override def map(fs: => Fragments) =
Step(testServer.start()) ^ super.map(fs) ^ Step(testServer.stop())
}
class FunctionalTest1 extends Specification with CommonSteps { def is =
...
... extends When[...] {
val browser: TestBrowser = TestBrowser.of(HTMLUNIT)
browser.goTo(testServerBaseURL + "/some_path")
browser
}
... extends Then[...] {
browser.await.until("element that is loaded on ajax request").isPresent()
...
}
}
We get the error:
Caused by: java.sql.SQLException: Attempting to obtain a connection from a pool that has already been shutdown.
Stack trace of location where pool was shutdown follows:
java.lang.Thread.getStackTrace(Thread.java:1479)
com.jolbox.bonecp.BoneCP.captureStackTrace(BoneCP.java:543)
com.jolbox.bonecp.BoneCP.shutdown(BoneCP.java:159)
com.jolbox.bonecp.BoneCPDataSource.close(BoneCPDataSource.java:123)
play.api.db.BoneCPApi.shutdownPool(DB.scala:387)
play.api.db.BoneCPPlugin$$anonfun$onStop$1.apply(DB.scala:252)
play.api.db.BoneCPPlugin$$anonfun$onStop$1.apply(DB.scala:250)
scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
scala.collection.immutable.List.foreach(List.scala:45)
play.api.db.BoneCPPlugin.onStop(DB.scala:250)
play.api.Play$$anonfun$stop$1$$anonfun$apply$1.apply(Play.scala:75)
play.api.Play$$anonfun$stop$1$$anonfun$apply$1.apply(Play.scala:74)
scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
scala.collection.immutable.List.foreach(List.scala:45)
play.api.Play$$anonfun$stop$1.apply(Play.scala:74)
play.api.Play$$anonfun$stop$1.apply(Play.scala:74)
scala.Option.map(Option.scala:133)
play.api.Play$.stop(Play.scala:73)
play.core.server.NettyServer.stop(NettyServer.scala:73)
While the test works when run in isolation, we get the error when running two or more of them together.
It seems to be related to this issue, though my example is a Scala Play app. Can anyone confirm that this issue is fixed in a newer version of Play? Or, is there a workaround to avoid this error in Play 2.0.1.
This issue will be fixed in Play 2.0.2, which is currently in RC state. It is safe to upgrade from 2.0.1 to 2.0.2, as everything is backwards compatible.
Thanks to #guillaume-bort for providing this information.