# データモデリング
## ER図
```mermaid
erDiagram
users {
string username "preferredUsername"
string name "表示名"
string bio
string icon
datetime createdAt
datetime updatedAt
}
posts {
string id
string inReplyTo
string body
number likes
number shares
SS to
SS cc
datetime createdAt
datetime updatedAt
}
revisions {
string postId
string body
datetime updatedAt
}
liked {
string objectId
datetime createdAt
}
shares {
string objectId
datetime createdAt
}
replies {
string postId
string objectId
datetime createdAt
}
likes {
string postId
string actorId
datetime createdAt
}
followings {
string actorId
datetime createdAt
}
subscribers {
string actorId
datetime createdAt
}
timeline {
string objectId
datetime when
}
tombstones {
string objectId
string formerType
datetime deletedAt
}
users 1 -- 0+ posts : has
users 1 -- 0+ liked : has
users 1 -- 0+ shares : has
users 1 -- 0+ followings : has
users 1 -- 0+ subscribers : has
users 1 -- 0+ timeline : has
posts 1 -- 0+ likes : has
posts 1 -- 0+ replies : has
posts 1 -- 0+ revisions : has
```
- 便宜上 user**s** として関係も描いているが、存在するuserは1人だけであり、関係は暗黙に存在する
- usernameは便宜上 `me` で固定する: `@me@{domain}`
- tombstonesは、削除した「独り言」をtombstoneオブジェクトに置き換えるために利用する
- postsのidを引いて、なかったときにtombstonesを引いて、あればtombstone、なければnot found、とする
- posts のid(またはpostId)には、ローカルなID部分のみを格納する(対外的には `https://{domain}/.../{postId}` のように変換してグローバルなURI(IRI)にする)
- actorId, objectIdには、グローバルなID(URI(IRI))をそのまま格納する
- 「独り言」の編集
- revisionをもたせる
- revisionなし → 最新版
- revisionあり → そのバージョン
- 編集するときは、現在の「最新版」をrevisionを付けてコピーして、revisionなしの方を更新する
## (Dynamo)DB設計
### メインデータ
| user(s) | `ME` | | 作成日時 | 更新日時 | | name | bio | icon |
| posts | `P#HHHHH` | UUID v7 | 投稿日時 | (更新日時) | | summary | body | inReplyTo | #likes | #shares | #replies | to | cc |
| revisions | `P#{postId}#V` | revision | 更新日時 | | | summary | body | |
| replies | `P#{postId}#R` | object URI | された日時 | | | | | | | | | | |
| likes | `P#{postId}#L` | actor URI | された日時 |
| shares | `P#{postId}#S` | actor URI | された日時 | |
| liked | `L#HHHHH` | object URI | した日時 | |
| shared | `S#HHHHH` | object URI | した日時 | |
| followings | `F` | actor URI | した日時 |
| subscribers | `SUB` | actor URI | された日時 |
| timeline | `TL#HHHHH` | 日時 | | | object URI | |
| tombstones | `D` | object URI | 削除した日時 | | | formerType |
* `HHHHH`には、UNIX時間を16進数表示した(UUID v7の)上位5文字を含める。[メモ「postsのPK」を参照](#postsのpk)
* timelineは基本的に挿入日時でソートすれば良いが、Deleteアクティビティに備えてオブジェクトのURIで検索できるようにLSIを作成する。
### 連合用データ
| objects | `O#{object URI}` | | +_max-age_ | | | object |
| servers | `S#{hostname}` | | +_max-age_ | lrdd template |
| actors | `A#{acct}` | | +_max-age_ | | | actor |
| actors | `A#{acct}` | `pubkey` | +_max-age_ | publicKeyPem | | |
| actors | `A#{acct}` | `webfinger` | +_max-age_ | type | href |
## メモ
* foreignObjectsには、actorIdやobjectIdに対応する、他サーバのActorやNoteオブジェクトをキャッシュしておく
* 参照したときになければ、URI(IRI)にアクセスしてオリジナルを取りに行く
* Amazon S3やCloudflare R2にJSONをファイルとして置いておくようにしても良さそう
### postsのPK
LSIを入れる場合、パーティションキーの1つの値毎に10 GBまでのデータしか入らない。
そうでなくてもパーティションは分かれるようにするのが良さそう。
非常に大雑把に1投稿あたり1 MBになるとすると、パーティションキーあたり10,000件。
UUID v7なら時系列順にソートできるので、UNIX時間の一部となる先頭何文字かをprefixとしてPKに含めれば、使い勝手をあまり損ねずに分割できそう。
```console
$ printf '%d\n' 0x065400000 | xargs -r -I{} date -d @{}
Tue Oct 31 04:12:00 JST 2023
$ printf '%d\n' 0x065410000 | xargs -r -I{} date -d @{}
Tue Oct 31 22:24:16 JST 2023
$ printf '%d\n' 0x065411b07 | xargs -r -I{} date -d @{}
Wed Nov 1 00:19:35 JST 2023
$ printf '%d\n' 0x065420000 | xargs -r -I{} date -d @{}
Wed Nov 1 16:36:32 JST 2023
$ printf '%d\n' 0x065500000 | xargs -r -I{} date -d @{}
Sun Nov 12 07:28:16 JST 2023
$ printf '%d\n' 0x065600000 | xargs -r -I{} date -d @{}
Fri Nov 24 10:44:32 JST 2023
$ printf '%d\n' 0x066000000 | xargs -r -I{} date -d @{}
Sun Mar 24 19:27:12 JST 2024
```
こんな感じなので、5桁あれば約18時間確保できる。
18時間に10,000件投稿できるので、約6秒あたり1件は投稿できる計算になる。そんなにやらないだろう。
### timelineのPK
timelineはオブジェクトIDだけで良いので1件1 KBとして、パーティションキーあたり10,000,000件。
件数的にはあまり分けなくて良さそうだが、postsと合わせておく。
取得できる時間の範囲を揃えておくと都合が良さそう。