More Related Content
Similar to サンプルで学ぶCassandraアプリケーションの作り方 (20)
More from Yuki Morishita (12)
サンプルで学ぶCassandraアプリケーションの作り方
- 5. KillrVideo
Apache Cassandra™ / DataStax Enterprise リファレンスアプリケーション
– https://killrvideo.github.io
– オープンソース (APLv2)
– スケーラブルなマイクロサービスアーキテクチャ
• サービスは Node.js/C#/Java の各言語で実装
– データベース層はApache Cassandra / DSE
• データモデリングのサンプル
• ドライバーの利用サンプル
5 © DataStax, All Rights Reserved.
- 9. 利用している技術
- Docker
- 各構成要素(Web/サービス/DB)をコンテナ化
- node.js
- フロントエンドWebアプリケーション
- React/Redux/Falcor
- マイクロサービスの一つの実装言語
- gRPC
- サービスのインターフェース定義
- Java
- Spring Frameworkを利用してサービスを実装
- DataStax Enterprise
- Apache Cassandraをコアとしたデータベース
© DataStax, All Rights Reserved.9
- 11. タスク: コメント機能を作る
- コメント機能の概要
- ユーザーが動画にコメントを書く
- 各動画のページにその動画に書かれたコメントを新着順に表示する
- ユーザーは自分が投降したコメントをユーザーのページで見ることができる
© DataStax, All Rights Reserved.11
- 13. gRPCサービスの定義
// Manages comments
service CommentsService {
// Add a new comment to a video
rpc CommentOnVideo(CommentOnVideoRequest) returns (CommentOnVideoResponse);
// Get comments made by a user
rpc GetUserComments(GetUserCommentsRequest) returns (GetUserCommentsResponse);
// Get comments made on a video
rpc GetVideoComments(GetVideoCommentsRequest) returns (GetVideoCommentsResponse);
}
© DataStax, All Rights Reserved.13
- 19. クエリを知る
© DataStax, All Rights Reserved.19
動画に紐づくコメントを
取得する
Q2
ユーザーに紐づくコメントを
取得する
Q1
コメントサービスのアプリケーションワークフロー
Q1: ユーザーID をもとに動画のコメントを 投稿順(新しいものを先) に取得
Q2: ビデオID をもとにユーザーからのコメントを 投稿順(新しいものを先) に取得
- 30. コメント機能のChebotkoダイアグラム
© DataStax, All Rights Reserved.30
Q2Q1
Q1: ユーザーID をもとに動画のコメントを 投稿順(新しいものを先) に取得
Q2: ビデオID をもとにユーザーからのコメントを 投稿順(新しいものを先) に取得
comments_by_user comments_by_video
userId
commentedAt
commentId
videoId
comment
videoId
commentedAt
commentId
userId
comment
K
C↑
C
K
C↑
C
- 32. コメント機能のChebotkoダイアグラム
© DataStax, All Rights Reserved.32
Q2Q1
Q1: ユーザーID をもとに動画のコメントを 投稿順(新しいものを先) に取得
Q2: ビデオID をもとにユーザーからのコメントを 投稿順(新しいものを先) に取得
comments_by_user comments_by_video
userId
commentId
videoId
comment
videoId
commentId
userId
comment
K
C↑
K
C↑
- 33. コメント機能のデータモデリング
CQL DDL
© DataStax, All Rights Reserved.33
CREATE TABLE comments_by_video (
videoid uuid,
commentid timeuuid,
userid uuid,
comment text,
PRIMARY KEY (videoid, commentid)
) WITH CLUSTERING ORDER BY (commentid DESC);
CREATE TABLE comments_by_user (
userid uuid,
commentid timeuuid,
videoid uuid,
comment text,
PRIMARY KEY (userid, commentid)
) WITH CLUSTERING ORDER BY (commentid DESC);
Q1: SELECT commentid, videoid, comment FROM comments_by_user WHERE userid = ?
Q2: SELECT commentid, userid, comment FROM comments_by_video WHERE videoid = ?
- 35. アプリケーションの実装
- DataStaxドライバーのセットアップ
- Apache Cassandra用
- DataStax Java Driver
- https://github.com/datastax/java-driver
- APLv2
- API: Cluster / Session
- DataStax Enterprise用
- DataStax Enterprise Java Driver
- https://github.com/datastax/java-driver-dse
- ライセンスは独自
- API: DseCluster / DseSession
© DataStax, All Rights Reserved.35
- 36. アプリケーションの実装
- Apache Cassandra / DataStax Enterprise への接続
© DataStax, All Rights Reserved.36
Builder clusterConfig = new Builder();
clusterConfig.addContactPoints(cassandraHosts)
.withPort(cassandraPort)
.withClusterName(CLUSTER_NAME);
…
DseCluster dseCluster = clusterConfig.build();
return dseCluster.connect(); // DseSessionオブジェクトを返す
- 37. アプリケーションの実装
- コメントを登録する
- コメントが登録されるとふたつのテーブルに登録しなければならない
- バッチ機能を利用してINSERT
© DataStax, All Rights Reserved.37
PreparedStatement commentsByUserPrepared = dseSession.prepare(
"INSERT INTO killrvideo.comments_by_user (userid, commentid, comment, videoid) VALUES (?, ?, ?, ?)"
).setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
…
BoundStatement bs1 = commentsByUserPrepared.bind(userId, commentId, comment, videoId);
BoundStatement bs2 = commentsByVideoPrepared.bind(videoId, commentId, comment, userId);
…
final BatchStatement batchStatement = new BatchStatement(BatchStatement.Type.LOGGED);
batchStatement.add(bs1);
batchStatement.add(bs2);
batchStatement.setDefaultTimestamp(now.getTime());
FutureUtils.buildCompletableFuture(dseSession.executeAsync(batchStatement))
…
- 38. アプリケーションの実装
- コメントを取得する
- ステートメントの組み立て
© DataStax, All Rights Reserved.38
PreparedStatement getVideoComments_startingPointPrepared = dseSession.prepare(
QueryBuilder.select()
.column("video_id")
.column("comment_id")
.column("user_id")
.column("comment")
.fcall("toTimestamp", QueryBuilder.column("comment_id")).as("comment_timestamp")
.from("killrvideo", "comments_by_video")
.where(QueryBuilder.eq("video_id", QueryBuilder.bindMarker()))
.and(QueryBuilder.lte("comment_id", QueryBuilder.bindMarker()))
).setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
- 39. アプリケーションの実装
- コメントを取得する
- 取得件数の指定とページング
© DataStax, All Rights Reserved.39
final Optional<String> pagingState = Optional.ofNullable(request.getPagingState())
.filter(StringUtils::isNotBlank);
…
statement = getUserComments_startingPointPrepared.bind()
.setUUID("userid", fromString(userId.getValue()))
.setUUID("commentid", fromString(startingCommentId.getValue()));
…
statement.setFetchSize(request.getPageSize());
pagingState.ifPresent( x -> statement.setPagingState(PagingState.fromString(x)));
…
Optional.ofNullable(commentResult.getExecutionInfo().getPagingState())
.map(PagingState::toString)
.ifPresent(builder::setPagingState);
- 40. アプリケーションの実装
- コメントを取得する
- クエリの実行結果の取得
© DataStax, All Rights Reserved.40
FutureUtils.buildCompletableFuture(dseSession.executeAsync(statement))
.handle((commentResult, ex) -> {
…
int remaining = commentResult.getAvailableWithoutFetching();
for (Row row : commentResult) {
CommentsByUser commentByUser = new CommentsByUser(
row.getUUID("userid"), row.getUUID("commentid"),
row.getUUID("videoid"), row.getString("comment")
);
commentByUser.setDateOfComment(
row.getTimestamp("comment_timestamp"));
builder.addComments(commentByUser.toUserComment());
if (--remaining == 0)
break;
}
…
Editor's Notes
- KillrVideoの説明
- デモ
- ログイン
- 動画ページ
- デモ環境
マイクロサービス部分はnode.jsアプリケーション
- コメント機能デモ
- 重複して持つため、Cassandraは"書き込み時にJOIN"するような形になる。複数のテーブルへ書き込みが発生する場合もある。
RDBでは正規化して"読み込み時にJOIN"。
クエリー: ある特定の役者(actor)が登場している映画を探したい
左: クライアント側でJOIN。 もし役者(actor)に紐づく動画(video)が10ある場合、その10パーティション分をクエリーしなければならない。それらは10の違うノードにあるかもしれない。
右: JOINの必要がない。1つのパーティションにアクセスするだけ。