分析のため、機密情報をいい感じにマスクしたプロダクションDBのコピーを毎日作っている
こんにちは、shokaiですshokai.icon
Cosense.icon Scrapboxのプロダクトマネージャーをしています
実態
コンセプトを考えて設計して実装して運用して〜みたいな色々をやっています
最近
分析環境を作り始めました
今日はその話をします
https://gyazo.com/099c489da6edcc6572ca8d71179dfe43
タイトルや本文テキストをマスクしている
md5 文字列の長さという形式
アクセスできる人
今はビジネス系の1人だけ
将来増える
転送にかかる時間
1時間ぐらい
なぜそんな事をしているのか
サービスの利用状況を分析したい
操作ログでは扱えない領域がある
例
どういうサイズのページを作ってる?上と下どっちに追記するの?共同編集者の数は?調べてみました!
production DBそのものを分析したい
md5 文字数という形式でマスクする
リンクの構造や編集頻度、ページの規模などは分析できる
転送batchを速くしたい
なるべく最新のデータを反映したい
毎日production DBからコピーする
まちがえて壊しても24時間で復活するし、最高
実装
ダメだった構成
MongoDB Atlasの自動バックアップから定期的にリストア
production全体だとデータが大きすぎて、リストアに時間がかかる
どちらも、巨大なexportファイルができてつらい
3工程を順にやるので効率悪い
exportしたファイルを自力でparseしてマスク処理するの大変
最終的にこうなった
1つのプログラムで2つのDBをつなぐパイプライン
1. production DBからのread
2. 本文のマスク処理
3. 分析用DBへのwrite
これを4スレッドでやる
read/writeのI/O待ちの間に、CPUを使うマスク処理がちょうど走る
実装のポイント
mongooseはDB1からreadして、DB2にそのまま同じdocumentを書き込むような処理が書けない
_idを指定した新規documentのinsertができない為
node-mongodb-nativeでやる
code:js
for await (const page of srcDb.collection('pages').find(condition, option)) {
/* ここにfieldをマスクする処理 */
await destDb.collection('pages').insertOne(page)
}
久しぶりに使ったけど、mongooseと使用感はそれほど変わらなかったshokai.icon
安全装置が無いだけ
fieldの型情報、validator、hook機構など
いい感じに並列処理
こう書くと、規模が異なるprojectが混在していても良い感じになる
project IDの配列を作っておいて
code:js
const projectIds = (await srcDb.collection('projects').find({ plan: 'business' }, { _id: 1 }).toArray()).map(({ _id }) => _id)
project ID配列をタスクのキューとし、空になるまで4並列で処理していく
code:js
await Promise.all(Array(4).fill().map(async () => {
while (projectIds.length > 0) {
const projectId = projectIds.shift()
await transferProject({ projectId, srcDb, destDb })
}
}))
転送するfieldはallow listで指定する
deny listはダメ
fieldが増えた時に転送対象になってしまう
データの投入が終わってからindexを付ける
先にindexが付いていると
insert毎にソートが走って、CPUが詰まる
index後付けにしたら
全体の処理時間が40%高速化した
まとめ
操作ログではなく、現状のDBのデータそのものを、マーケやCSの人にも分析させたい
名前や本文などの機密情報はマスクしたい
色々やったら速くて安全なのやつができた