最近在研究如何把 log 塞到 Cassandra 中,使用的 Datastax 的 php-driver 輕鬆很多,但還是有一些需要留意的地方,免得莫名其妙鬼打牆。
這邊先假設我要記錄一個使用者上傳檔案的記錄,包含 ID、檔案名稱、檔案大小以及上傳日期,建立了一個 table:
CREATE TABLE hermes_log.file (
id uuid,
name text,
size decimal,
create_time timestamp,
PRIMARY KEY (id, name, size, create_time)
)
這個時候使用 PHP 塞資料進入,直接建立 CQL 且不使用 prepare/binding,可以成功執行 insertion:
<?php
$cluster = Cassandra::cluster()->build();
$session = $cluster->connect();
$cql = "
insert into hermes_log.file
(
id,
name,
size,
create_time
) values (
uuid(),
'gavatar.jpg',
123.4,
'2017-01-01 10:30:45.678'
)
";
$session->execute($cql);
cqlsh 看一下資料格式:
cqlsh:zero_test> select name, size, create_time from file;
name | size | create_time
-------------+-------+---------------------------------
gavatar.jpg | 123.4 | 2017-01-01 02:30:45.678000+0000
(1 rows)
除了日期會自動轉為 UTC+00:00 以外其他都沒什麼太大的問題。
接下來把 PHP 改為 prepare/bind 格式:
$cql = "
insert into hermes_log.file
(
id,
name,
size,
create_time
) values (
uuid(),
?,
?,
?
)
";
$session->execute($cql, [
'gavatar.jpg',
123.4,
'2017-01-01 10:30:45.678'
]);
執行的時候你會看到錯誤訊:「Uncaught CassandraExceptionInvalidQueryException: Invalid amount of bind variables in your.php」,意思是找不到可以 binding 的資料。仔細去翻文件才會注意到 binding 的資料,不是直接放進 array 就好,而是要放在 key「arguments」底下,所以至卻的寫法應該是:
$session->execute($cql, [
'arguments' => [
'gavatar.jpg',
123.4,
'2017-01-01 10:30:45.678'
],
]);
再次執行後,錯誤訊息不一樣了「Uncaught CassandraExceptionInvalidQueryException: Expected 8 or 0 byte long for date (23) ….」,看起來 create_time 這邊沒辦法使用字串來代表新增日期。
如果你有注意到 Cassandra 是使用 Java 實作的,應該不難理解 PHP 和 Java 的資料型態差異很大,因此 DataStax 的 php-driver library 為了讓資料傳遞時不會發生問題,寫了不少 adapter 來解決資料型態的相容問題,像是 CassandraTimestamp、CassandraDecimal 等,這些類別可以在文件中的 Cassandra namespace 中找到。
所以按照規格改好以後,應該會是下面這樣:
$createTime = microtime(true);
list($sec, $ms) = explode('.', $createTime);
$sec = intval($sec);
$ms = intval(($createTime - $sec) * 1000000); // 毫秒取六位數
$session->execute($cql, [
'arguments' => [
'gavatar.jpg',
new CassandraDecimal(strval(123.4)),
new CassandraTimestamp($sec, $ms),
],
]);