神谷翔一
東大情報科学科出身のエンジニア
主にbackendを書く事が多い
- MySQLの少し変わった使い方
- Finatra便利
一言で言うとMySQLをKV storeとして使う
Tableは基本的にはidとbytesの2 columnのみ
bytesにprotobufのmessageをserializeして保存する
Index/constraintが必要なfieldのみ専用columnとして切り出す
-- eamilは一意性を保証したいので専用columnを用意してindexをはる
CREATE TABLE users (
id BIGINT,
bytes blob,
email varchar(255)
);
CREATE UNIQUE INDEX users_email ON users(email);
KVとして使うが必要に応じてindex/constraint/transactionが使えるのでただのKVより便利
- 豊かな表現力(array, nested message)
- 多くの場合はProtobufの定義を変更するだけなのでmigrationをする事が少ない
JOIN
は手動- binaryが保存されてるので
SELECT * FROM foo
してもデータが読めない
Twitter製のNetty wrapperでhttp/dbへのアクセスが同じinterfaceで抽象化されている
Request/responseのjson mappingは全自動でvalidationもしてくれる
class Tweets @Inject()(
idService: IdService,
tweetsService: TweetsService) extends Controller {
get("/tweet/:id") { request: GetTweetRequest =>
tweetService.get(request.id)
}
case class PostTweetRequest(@Size(min=1 max=144) text: String)
post("/tweet") { request: PostTweetRequest =>
for {
id <- idService.create()
tweet = buildTweet(id, request)
_ <- tweetService.create(tweet)
} yield {
response
.created(tweet)
.location(tweet.id)
}
}
}
class TweetsService @Inject()(mysql: MySQL) {
private[this] val query = "INSERT INTO tweets (id, bytes) SET (?, ?)"
def create(proto: Proto.Tweet): Future[Unit] = {
mysql.prepare(query)(proto.id, proto.toByteArray)
}
}
TweetsServiceはProto.Tweet
を受け取りFuture[Unit]
を返すserviceとして抽象化されている
TweetsServiceを別のserviceとして切り出してもinterfaceは不変
class TweetsService @Inject()(http: httpClient) {
def create(proto: Proto.Tweet): Future[Unit] = {
http.post(...)
}
}
流行りのmicro serviceにも比較的楽に対応出来る
その他service discovery, metricsなどtwitterが実際に使用してる便利機能満載
以上と似た仕組みで社内で動いてるもの
- 広告
- Recommendation engine
- Game server
- Push notification server
- Android/iOSのbackend server
この中で特に広告は普通ではされていない工夫がしてあって面白いので興味がある人は弊社にwelcome