尝试使用TABLE_NAME.insert将数据插入分区表时,在单分区插入语句中获取“分区元组”。

时间:2019-01-24 10:38:08

标签: scala apache-spark databricks voltdb azure-databricks

我正在使用给定的插入语句创建VoltDB表

CREATE TABLE EMPLOYEE (
    ID VARCHAR(4) NOT NULL,
    CODE VARCHAR(4) NOT NULL,
    FIRST_NAME VARCHAR(30) NOT NULL,
    LAST_NAME VARCHAR(30) NOT NULL,
    PRIMARY KEY (ID, CODE)
);

然后使用

对表进行分区
PARTITION TABLE EMPLOYEE ON COLUMN ID;

我写了一个spark作业将数据插入到VoltDB中,我正在使用下面的Scala代码将记录插入到VoltDB中,如果不对表进行分区,代码会很好地工作

import org.voltdb._;
import org.voltdb.client._;
import scala.collection.JavaConverters._

val voltClient:Client = ClientFactory.createClient();
voltClient.createConnection("IP:PORT");

val empDf = spark.read.format("csv")
          .option("inferSchema", "true")
          .option("header", "true")
          .option("sep", ",")
          .load("/FileStore/tables/employee.csv")

// Code to convert scala seq to java varargs
def callProcedure(procName: String, parameters: Any*): ClientResponse =
    voltClient.callProcedure(procName, paramsToJavaObjects(parameters: _*): _*)

def paramsToJavaObjects(params: Any*) = params.map { param ⇒
    val value = param match {
      case None    ⇒ null
      case Some(v) ⇒ v
      case _       ⇒ param
    }
    value.asInstanceOf[AnyRef]
}

empDf.collect().foreach { row =>
  callProcedure("EMPLOYEE.insert", row.toSeq:_*);
}

但是如果我对表进行分区,我会遇到错误

Mispartitioned tuple in single-partition insert statement.
Constraint Type PARTITIONING, Table CatalogId EMPLOYEE
Relevant Tuples:
ID  CODE  FIRST_NAME  LAST_NAME 
--- ----- ----------- ----------
1   CD01  Naresh       "Joshi"
    at org.voltdb.client.ClientImpl.internalSyncCallProcedure(ClientImpl.java:485)
    at org.voltdb.client.ClientImpl.callProcedureWithClientTimeout(ClientImpl.java:324)
    at org.voltdb.client.ClientImpl.callProcedure(ClientImpl.java:260)
    at line4c569b049a9d4e51a3e8fda7cbb043de32.$read$$iw$$iw$$iw$$iw$$iw$$iw.callProcedure(command-3986740264398828:9)
    at line4c569b049a9d4e51a3e8fda7cbb043de40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1.apply(command-3986740264399793:8)
    at line4c569b049a9d4e51a3e8fda7cbb043de40.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1.apply(command-3986740264399793:7)
    at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
    at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)

我发现找到有关该问题的链接(https://forum.voltdb.com/forum/voltdb-discussions/building-voltdb-applications/1182-mispartitioned-tuple-in-single-partition-insert-statement),并尝试使用以下查询对过程进行分区

PARTITION PROCEDURE EMPLOYEE.insert ON TABLE EMPLOYEE COLUMN ID;

AND

PARTITION PROCEDURE EMPLOYEE.insert ON TABLE EMPLOYEE COLUMN ID [PARAMETER 0];

但是执行这些语句时出现[Ad Hoc DDL Input]: VoltDB DDL Error: "Partition references an undefined procedure "EMPLOYEE.insert""错误。

但是,我可以通过使用@AdHoc存储过程来插入数据,但是对于使用EMPLOYEE.insert存储过程进行插入的上述情况,我无法解决问题或解决方案数据放入分区表中。

1 个答案:

答案 0 :(得分:1)

过程“ EMPLOYEE.insert”是所谓的“默认”过程,该过程在创建表EMPLOYEE时由VoltDB自动生成。已经根据表的分区对其进行了自动分区,因此无法调用“ PARTITION PROCEDURE EMPLOYEE.insert ...”来覆盖它。

我认为正在发生的事情是该过程由ID列分区,该列在EMPLOYEE表中是VARCHAR。因此,输入参数应为字符串。但是,我认为您的代码以某种方式读取了CSV文件并将第一列作为int值传递。

java客户端的callProcedure(String procedureName,Object ... params)方法接受varargs作为参数。这可以是任何Object []。在服务器上的某处进行检查,其中参数的#必须与过程期望的#相匹配,否则过程调用将被拒绝,否则将永远不会执行。但是,我认为在您的情况下,#号参数是可以的,因此它将尝试执行该过程。它对与ID对应的第一个参数值进行哈希处理,然后确定该参数应转到哪个分区。调用被路由到该分区以执行。当它执行时,它尝试插入值,但是还要检查该分区的分区键值是否正确,并且这是失败的。

我认为如果该值作为int传递,则将其散列到错误的分区。然后,在该分区中,它尝试将值插入到VARCHAR列中,因此它可能会将int隐式转换为String,但它不在正确的分区中,因此插入失败,并出现以下错误:“分区插入语句。”如果您编写了一个Java存储过程并将错误的列配置为分区键,则会遇到相同的错误。

披露:我在VoltDB工作。