こんにちは、ここあです。
宣伝がメインの記事ですが、技術要素も交えていこうかと。
冒頭でいきなりですが、「ぺったん」というアプリを開発しました。
実はリリースしたのは3月なのですが、当時は機能がまだ充実していなく、大々的に宣伝もしていなかったのでユーザはほとんどいないと言ってしまっても良いくらいです。
ところで最近、Twitter改変だとか、Mastdonが注目を得ていたり、MetaがThreadsを出したりといろいろとSNS界に激震が走ってきていますね。
こんなタイミングで告知をしても「またぽっと出のSNSか。。。」みたいになりそうといえばなりそう。
閑話休題
このアプリの想定ユーザは、
で、今のところ
できます。
ちなみに「フォロワー」という概念を持たないので、「フォロー」というより「サブスクライブ」ですね。FF比みたいな概念から抜け出したかったので
言ってしまえばニャンスタグラムとかそのあたりが近いです。猫を飼い始めて、作りたくなったので作りました。
以下の図に示すようなものを使っています。
殴り描きで、関係を示す線を何も引いていません。雰囲気で感じ取ってください。
サーバサイドでは以下の技術を使っています。
とりあえず列挙するとこんな感じです。うまいこと説明がかけなかったので「それはそう」みたいな用途しか書いていません。
名称 | 用途 |
---|---|
Ktor + Exposed | JetBrains社製のWebアプリケーションフレームワークとO/Rマッパー |
MariaDB | データの格納。それはそう |
Cloud Storage for Firebase | 主に画像置き場 |
Firebase Authentication | 認証基盤 |
Firebase Crashlytics | クラッシュログの収集 |
Firebase Cloud Messaging | ユーザアクションのPUSH通知 |
Redis | キャッシュ |
アカウント削除のときに、削除を保証する方法が少し困りました。
認証基盤と画像データはFirebase(それぞれ別のサービス)にあり、それ以外の各種データはオンプレにあります。
このとき正常にことが運べばよいのですが、例えばFirebase Authからデータを消し、ユーザデータの削除にコケたとき、どうしましょう?
これをすべて一連のトランザクションで行い、コケたらロールバックする、みたいにできたら良いかもしれません。が、いろいろ横断しているのでそれもできません(知らないだけかも)
Twitterのように「30日後に消える」みたいなものはまた別の話ですね。即座にアカウントにアクセスできなくなるかどうかの違いなので、解決策足りえません。
ではどうしたかというと、
としています。
つまりアカウント削除までは保証し、画像など、Cloud Storageに置かれたデータに関しては「即時の削除」を保証していません。(削除画面にも、「通常1週間以内に削除されます」のように書いています。)
ここで登場するのがキースペース通知です。 リンク先を読んでもらえばわかるのですが、一言で言ってしまうと、Pub/Sub機能です。
概ね下記のような使い方をしています。
このようにしておくと、1週間であれば理論上7回実行されることになるので、よほどでない限り削除されているでしょう。
time to live が関連づけられたキーは、Redis により 2 つの方法で expire されます。
あるコマンドでキーがアクセスされ、すでに expire されていることに気づいたとき一切アクセスされなくなったキーを集めるため、expire されたキーをひとつずつ探すバックグラウンドシステムを介して。
‘expired’ イベントは、上記いずれかのシステムによりキーがアクセスされた時に生成されます。したがって、キーの time to live の値がゼロに達したタイミングで Redis サーバーが ‘expired’ イベントを生成するという保証はありません。
対象のキーに対してコンスタントにコマンドが実行されず、また TTL が関連づけられたキーが多く存在する場合、time to live がゼロに達したタイミングと ‘expired’ イベントが生成されるタイミングの間には、大きな遅延が発生する可能性があります。
基本的に、’expired’ イベントは Redis サーバーがキーを削除したときに生成され 、理論上において time to live の値がゼロに達したときに生成されるものではありません。
Djangoとか、そのあたりを使っているとDBのバージョンらへんが簡単に管理できて嬉しいですよね。
この機能、Exposedには存在していません。
flywayを導入すれば良かったと気がついた頃にはときすでに遅しで、実はまだ未解決の問題なのですが。
......自分でマイグレーション用のスクリプトを書けばいいですよね、如何にしてサボろうかなの気持ちでいるので。
厳密には困りポイントではないんですが、重要なとこですね。
アプリの特性上、アップロードされる画像に位置情報がついている場合、ほとんどの場合はユーザの自宅になるわけです。
画像をユーザが保存できないのであればまだ良かったのですが、画像保存機能を実装したということもあり、また将来的にWebを展開することを考えると必須の機能ですね。
Apache-Commons-Imagingと、こんな感じのコードを用意して、アップロードされてきた画像から位置情報のメタデータを削除しています。コードに無駄があったとしても、本番で動いてしまっているのでお構いなしで。
fun ByteArray.removeLocationMetadata(): ByteArray {
val metaData = Imaging.getMetadata(this) ?: return this
return (metaData as? JpegImageMetadata)?.let { jpegImageMetadata ->
jpegImageMetadata.exif?.outputSet?.apply {
gpsDirectory?.fields?.forEach { gpsTag ->
removeField(gpsTag.tag)
}
findField(ExifTagConstants.EXIF_TAG_GPSINFO)?.let {
removeField(ExifTagConstants.EXIF_TAG_GPSINFO)
}
}.run {
ByteArrayOutputStream().use { baos ->
BufferedOutputStream(baos).use { bos ->
ExifRewriter().updateExifMetadataLossless(this@removeLocationMetadata, bos, this)
}
baos.toByteArray()
}
}
} ?: kotlin.run {
this
}
}
ペットを飼っている人もそうでない人も、絶賛ユーザ募集中なのでよかったら試してみてください。