Misskey: PostgreSQLにする

Created on 19 Mar 2019  ·  52Comments  ·  Source: syuilo/misskey

Summary

  • MongoDBでしか出来ないようなことはほとんどしていない
  • Misskeyで扱うほとんどのデータはきっちりとスキーマが決まっていてリレーショナルなので、リレーショナルデータベースの方が都合が良い
  • 親レコードが削除されたら子レコードも自動的に削除、とかできるので削除系の処理の実装が簡単になりそう
  • プログラム上でObjectIDをいちいち変換するのがめんどい
  • MongoDBはPostgreSQLと比較するとメジャーでない

Related to #3510, #4203

Working on https://github.com/syuilo/misskey/tree/pg

メモ

  • インスタンスをブロックしているかどうかはインスタンステーブルには保存しないでinstanceBlockingなどのテーブルに別途保存する
  • オブジェクトストレージを使用しない場合、ファイルをデータベースに保存するのはやめてサーバーのファイルシステム上に保存するようにする
  • Metaに投稿数などを保存するのをやめる(必要ならその都度カウントする)
  • MongoDBでは重複レコードエラーは e.code === 11000 で判別できたけどPostgreSQLの場合どうなるのか
  • 壁紙設定はクライアントの設定に移動
  • getDriveFileUrl --> getThumbnailUrl に
  • pinnedNoteIds は廃止して userNotePinings といったテーブルを作る
  • 正規表現検索をする方法
  • 配列に値を追加する方法
  • simple-array ではなく array: true を使用
  • 投稿に _files として添付ファイルを非正規化するのをやめる

    • というか isAttachingSensitiveFiles と attachedFileTypes みたいな非正規化プロパティに分解する

  • _reply _renote _user といった非正規化プロパティも replyUserId replyUserHost renoteUserId renoteUserHost userHost userInbox みたいに分割
  • ホームのカスタマイズ情報をサーバーに保存するのをやめる
☢️Breaking ⚙️Server ✨Feature 🏳️Needs help 💬Discussion

Most helpful comment

10%くらい終わった

All 52 comments

大変そう。手伝えることがあったら言ってください。

ありがとうございます!
一部で行っている複雑なクエリをPostgreSQLで実現できるかなど分からない部分もありますが頑張ります

ちなみに何を使うとかいう具体的な方針は決まってたりしますか?

PostgreSQLの具体的なバージョンはまだ未定ですがORMはtypeormの予定です

pgブランチ作りました。
まずモデル定義(src/models)をtypeormを使用したものに書き換えていく必要があります。例はここら辺に
時間のある方はこの作業に参加していただけると助かります

外部キー制約って配列には適用できない...?
例えば投稿の添付された複数のファイルを表すfileIdsとか

noteFileAttachment といった別のテーブル作るしかないか

renoteCount とか repliesCount とか reactionCounts とか保存すべき?
これらのカラムはすべて非正規化されてるようなもので、これらが無くても理論的にはその投稿のRenote数や返信数やリアクション数は取得できる

とりあえず無くしといてパフォーマンス上の問題があるようなら非正規化でも良いかもしれないけど後から設計を変えるのもそれはそれで面倒

visibleUserIds も外部キーの配列だけど、さすがにこれのためだけにテーブル作るのはなんかアレ
不通に数値(ID)の配列として保存するか...

んーでも、自分宛てのダイレクト投稿を一覧取得するケースもあるか...

renoteCount とか repliesCount とか reactionCounts とか保存すべき?

renoteCount (APIではnumber) 保存する
repliesCount (APIではnumber) 保存する
reactionCounts (APIではObject) 必ずNoteReactionsみたいなテーブルを見るとおもうので保存しない
とか?

ありがとうございます

必ずNoteReactionsみたいなテーブルを見るとおもう

んー、そんなことないかも

んー、そんなことないかも

JSON? 今みたいにNoteReactionを使うのかと…
あれ、今NoteReactioninsertしかしてない

typeormはやめてsequelize-typescriptにします

TypeORM 評判悪いと聞いていたので正直ほっとしました。

個々のIDが変わっても問題ないならばマイグレ用意することはできる
ただ大変という問題はあるけど

ユーザーに関しては、IDが比較可能である必要はない(ランダムな文字列で問題ない)ため、マイグレの際は以前のObjectIDをそのまま引き継ぐことが可能そう

現在のIDについては
https://docs.mongodb.com/manual/reference/method/ObjectId/

日付が含まれているものの、生成時期を考えると (上位4ビットが0になった時期がないので)
事実上固定長 (24文字hex文字列) なので比較可能だと思います。

また、現在のIDは上位の方のビットが0101までのIDが採番されているので
新たに1xxxなど最上位ビットが埋まっているIDなどを採番すれば重複することもないのではと思います。

そういえば: PostgreSQLになったらElasticSearchではなくなる?

sequelize-typescript は ActiveRecord しかできなくて微妙

PeerTube の開発者の方々にお伺いをたてるのも手かと。

Misskeyはデータベースから取得したレコードはすぐレスポンスとして返す場合がほとんど(いじくったりしない)ので、レコード自体にメソッドがあっても意味ないし、いちいちインスタンス化するコストもかかる
ORMによっては取得時にインスタンス化しないオプションを提供するものもあるけど、ほぼすべての find 時にそのオプションを true にするのは面倒すぎる

とりあえず sequelize の線は消えそう

PostgreSQLになったらElasticSearchではなくなる?

MastodonはPostgreSQLだけどElasticSearchを必要としていることからMisskeyでも引き続き必要になりそう

TypeORMに戻る

10%くらい終わった

v10時点でのデータベースの設計や命名などで気になっている点がある方は今のうちに言っておいてください

20%くらい終わった

ついでに #4018 もやる

型定義は作らないかもしれないけど、「不要な値を削除」方式ではなくこんな感じに「必要な値をコピー」する感じ
https://github.com/syuilo/misskey/blob/578143096f1accdc7e23a72b2cbe76768969d1f4/src/models/repositories/user.ts#L48-L84

型定義作るとしたら、せっかく作っても型定義とドキュメントが一致していないということが起こり得るので、ドキュメントから型定義を自動生成するようにする
それなら型定義とドキュメントが必ず一致することが保証される

今はAPIのパラメータの型情報はドキュメント(cafy定義)から自動生成している状態なのでそれと同じ感じで

modelのリファクタ系developとかv10なんたらとかのブランチにマージしておきたい気がします

↑今はmodel配下のマッピングの差分がわからなくなってしまったので

今まで models にあったものは models/entities と models/repositories に分割されました
entiriesにはスキーマ定義だけあって、repositoriesにはそのスキーマに属するメソッドが定義されます

例えば今までこのように書いていたものはこうなります

before

import Note, { INote, pack } from './models/note';

const foo: INote = bar();
const note = Note.findOne({ _id: x });
return pack(note);

after

import { Note } from './models/entities/note';
import { Notes } from './models';

const foo: Note = bar();
const note = Notes.findOne(x);
return Notes.pack(note);

entity と repository を区別するために、上の例にあるように repository の方は複数形になってます
Note が entity で Notes が NoteのRepostiry

entity は、上述のようにスキーマ定義であってメソッドなどは何も持ってないので、コード上で entity を使うのは型注釈のためだけになると思います
repository は実際にデータベースからデータを持ってきたり更新したりなどのいろいろなメソッドを持っているので頻繁に使います

30%くらい終わった

各種IDにULIDを使おうと思うんですけどどう思いますか?
IDが数値だと、例えばアクセスされたURLに含まれているIDを元にデータベースに問い合わせるようなシナリオで、プログラム上でいちいち文字列から数値に変換する必要があってめんどい

@syuilo
noteIdとかuserIdでもULIDにして、既存IDとの連続性とかは考慮しない感じでしょうか?

う~~~む

ランダムではないので連続性は保たれそう

ちゃんと元の順番通りinsertしていけば

ULIDほどのユニークさは求めてないのでULIDをちょっと改造したものになりそう

そもそもnoteIdとかuserIdって (同じIDで) 移行しますか?
Hex 24文字 => Base32 26文字 にするということは移行なしですかね?

IDは変わると考えてます

ID変えてしまうとユーザー移行が難しくなるのではと思います (AP的に)

こまこまのこまり

  • (固定値1 1bit) + (Timestamp 47bits UNIX ms) + Random 48bits = 96bits (12Bytes, 24 Hex chars)
  • エンコードはHex

にすれば旧IDと重複しないので旧IDがそのまま使えて
順番も旧ID→新IDとつながると思います

So is there any plan to support other databases?
Database server is much cheaper than VPS. But not all ISPs provide MongoDB(or even PostgreSQL)... (MySQL and MS SQL Server are most widely used) If I were building my own site, I would like to just buy a database server instead of a VPS that I need to install MondoDB myself. 😄

Support is difficult because MySQL doesn't support arrays or JSON columns.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

syuilo picture syuilo  ·  3Comments

tamaina picture tamaina  ·  3Comments

2vg picture 2vg  ·  3Comments

tamaina picture tamaina  ·  3Comments

tamaina picture tamaina  ·  3Comments