我在Cassandra的DataStax Spark连接器上遇到问题。我的应用程序包含一个Spark操作,该操作在Cassandra数据库上执行许多单记录查询。这些查询中的许多查询将成功,但是在某些时候,其中一个查询将失败,并显示一条NoHostAvailableException
,并显示消息All host(s) tried for query failed (no host was tried)
。
2018-06-26 12:32:09 ERROR Executor:91 - Exception in task 0.3 in stage 0.0 (TID 6)
com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (no host was tried)
at com.datastax.driver.core.exceptions.NoHostAvailableException.copy(NoHostAvailableException.java:84)
at com.datastax.driver.core.exceptions.NoHostAvailableException.copy(NoHostAvailableException.java:37)
at com.datastax.driver.core.DriverThrowables.propagateCause(DriverThrowables.java:37)
at com.datastax.driver.core.DefaultResultSetFuture.getUninterruptibly(DefaultResultSetFuture.java:245)
at com.datastax.driver.core.AbstractSession.execute(AbstractSession.java:68)
at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.datastax.spark.connector.cql.SessionProxy.invoke(SessionProxy.scala:40)
at com.sun.proxy.$Proxy15.execute(Unknown Source)
at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.datastax.spark.connector.cql.SessionProxy.invoke(SessionProxy.scala:40)
at com.sun.proxy.$Proxy16.execute(Unknown Source)
at [line that contains the session.execute() call]
[...]
Caused by: com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (no host was tried)
at com.datastax.driver.core.RequestHandler.reportNoMoreHosts(RequestHandler.java:211)
at com.datastax.driver.core.RequestHandler.access$1000(RequestHandler.java:46)
at com.datastax.driver.core.RequestHandler$SpeculativeExecution.findNextHostAndQuery(RequestHandler.java:275)
at com.datastax.driver.core.RequestHandler.startNewExecution(RequestHandler.java:115)
at com.datastax.driver.core.RequestHandler.sendRequest(RequestHandler.java:95)
at com.datastax.driver.core.SessionManager.executeAsync(SessionManager.java:132)
... 32 more
为了分析这个问题,我成功地在一个简单的环境中重现了它:
下面是我可以用来重现该问题的最少代码。
val pkColumn1Value = 1L
val pkColumn2Values: Dataset[Long] = sparkSession.createDataset(1L to 19 by 2)
val connector: CassandraConnector = [...]
val results: Dataset[SimpleValue] = pkColumn2Values.mapPartitions { iterator =>
connector.withSessionDo { session =>
val clusteringKeyValues = Seq(...)
val preparedStatement = session.prepare("select * from simple_values where pk_column_1_value = ? and pk_column_2_value = ? and clustering_key_value = ?")
iterator.flatMap { pkColumn2Value =>
val boundStatements = clusteringKeyValues.iterator.map(clusteringKeyValue =>
preparedStatement.bind(
pkColumn1Value.asInstanceOf[AnyRef]
, pkColumn2Value.asInstanceOf[AnyRef]
, clusteringKeyValue.asInstanceOf[AnyRef]
)
)
boundStatements.map { boundStatement =>
val record = try {
session.execute(boundStatement).one()
} catch {
case noHostAvailableException: NoHostAvailableException =>
log.error(s"Encountered NHAE, getErrors: ${noHostAvailableException.getErrors}")
throw noHostAvailableException
case exception =>
throw exception
}
log.error(s"Retrieved record $record")
// Sleep to simulate an operation being performed on the value.
Thread.sleep(100)
record
}
}
}
}
log.error(s"Perfunctory log statement that triggers an action: ${results.collect().last}")
Dataset#mapPartitions()
来为每个分区仅准备一次select语句。当我吞下自豪感而改用Dataset#map()
或Dataset#flatMap()
时,问题消失了,但是我想使用Dataset#mapPartitions()
来获得(表面上的)性能优势(每个数据集分区仅准备一次查询) 。NoHostAvailableException
似乎发生了固定的时间。一些调查确认,该时间量等于连接器属性spark.cassandra.connection.keep_alive_ms
的值。将此属性设置为一个高得离谱的值显然可以解决问题,但这似乎是一种肮脏的解决方法,而不是明智的解决方案。在this GitHub issue中的连接器中,评论者pkolaczk提到了一个潜在的问题,该问题可能导致连接器在与Cassandra的初始连接中成功,并在以后尝试建立其他连接时失败。这听起来很有希望,因为它与上述几点相符(这表明问题只会在原始连接关闭后才会发生,如果为数据集中的每个元素分别重新建立连接就永远不会发生);但是,我找不到任何迹象表明我配置了IP地址或任何其他可能的原因导致此现象(甚至没有确认该现象实际上是造成此问题的原因)。
NoHostAvailableException
之前总是有其他错误。我已经多次检查日志,但是找不到其他错误消息或堆栈跟踪。NoHostAvailableException#getErrors
以获得对该问题的更详细说明,但是此方法始终为我返回一个空映射。mapPartitions
时才会发生的事实,而在使用map
时不会发生)。spark.cassandra.connection.local_dc
最初未设置。将此属性设置为适当的数据中心名称不会对该问题产生明显影响。spark.cassandra.connection.timeout_ms
和spark.cassandra.read.timeout_ms
设置为高得离谱的值;这对该问题没有明显影响。任何表示导致这些错误的原因或解决此问题的方法的表示,将不胜感激。
答案 0 :(得分:0)
我将此问题交叉发布到了连接器的Google用户组(https://groups.google.com/a/lists.datastax.com/d/msg/spark-connector-user/oWrP7qeHJ7k/pmgnF_kbBwAJ),其贡献者之一证实了没有理由不为spark.cassandra.connection.keep_alive_ms
赋予很高的价值。我已经将该值提高到可以确定没有任何操作可以通过它的地步,并且此后也没有任何问题。