データを持ち、そして消す
保存期間とは、アプリが種類ごとのデータをどれだけ持つかを決めた決まりです。その期間が過ぎたら、データを消します。
会話をいつまでも持つチャットアプリは、年ごとに危険が増えます。このレッスンでは、古いデータを掃除し、本人に頼まれて消し、消えずに残ったものを数えます。
このレッスンのゴール
メモ
決まりに従って、また本人の頼みで、データを消すことができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 保存期間 | データを持っておく長さ | |
| 掃除 | 古くなりすぎたものを消す仕事 | |
| 削除の依頼 | 本人が、自分のデータを消してほしいと頼むこと | |
| 写し | 同じデータを置いてある、別の場所 | キャッシュ |
| バックアップ | 故障から戻すために取っておく、保存した写し |
学生が入力するセルは、このプレビューでは空白です。
保存期間の決まりが、長さを決める
データの種類で、必要な長さは違います。会話は 30 日、請求は 365 日、のように決めます。請求は会計に要るので、長く持ちます。実際の請求書は 1 年より長く持つことが多いですが、このレッスンでは 365 日にします。
記録には、作った日があります。掃除は、今日からその日を引いて年齢を出します。年齢が期間より大きければ、記録は消えます。ちょうど期限の日の記録は残ります。
例: 100 日目の掃除
| 記録 | 種類 | 作った日 | 年齢 | 期間 | 結果 |
|---|---|---|---|---|---|
| c1 | chat | 10 | 90 | 30 | 消す |
| c2 | chat | 80 | 20 | 30 | 残す |
| b1 | billing | 20 | 80 | 365 | 残す |
| c3 | chat | 70 | 30 | 30 | 残す、ちょうど期限 |
学生が入力するセルは、このプレビューでは空白です。
削除の依頼は、すべての写しに届く
本人が、自分のデータを消してほしいと頼むことがあります。メッセージは、本体の保存場所にも、速くするためのキャッシュにも、バックアップにもあるかもしれません。
本体だけを消しても、ほかが残ります。先に、置き場所をすべて書き出しましょう。決まりは国や使い方で違い、限りもあるので、専門の人に聞いてください。これは法律の助言ではありません。
よくある間違い
注意
本体から消せば、本人のデータは消えた、と考える人が多いです。そうではありません。キャッシュ、ログ、バックアップに写しが残ります。バックアップは書き換えられないので、期限が来て消えるまで残ります。日付を添えて、本人に伝えましょう。
助っ人 model.js を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// 応答のトークン数。なければ null。' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
助っ人 model.js を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
保存期間の掃除を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const rules = { chat: 30, billing: 365 };' 'const today = 100;' 'const records = [' ' { id: \"c1\", user: \"u1\", day: 10, kind: \"chat\" },' ' { id: \"c2\", user: \"u2\", day: 80, kind: \"chat\" },' ' { id: \"b1\", user: \"u1\", day: 20, kind: \"billing\" },' ' { id: \"c3\", user: \"u1\", day: 70, kind: \"chat\" },' '];' > retain.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
保存期間の掃除を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function expired(r) {' ' return typeof rules[r.kind] === \"number\" && today - r.day > rules[r.kind];' '}' 'function ids(list) {' ' return list.map(function (r) { return r.id; });' '}' 'const stores = {' ' live: records.filter(function (r) { return expired(r) === false; }),' ' cache: [records[3]],' ' backup: [records[0], records[2], records[3]],' '};' >> retain.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
保存期間の掃除を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function remaining(user, reached) {' ' const left = {};' ' Object.keys(stores).forEach(function (name) {' ' const mine = stores[name].filter(function (r) { return r.user === user; });' ' if (reached.indexOf(name) < 0 && mine.length > 0) left[name] = ids(mine);' ' });' ' return left;' '}' >> retain.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
保存期間の掃除を書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' console.log(\"今日の期限切れ: \" + JSON.stringify(ids(records.filter(expired))));' ' console.log(\"掃除で残る: \" + JSON.stringify(ids(stores.live)));' ' console.log(\"u1 を live と cache から削除、残り: \" + JSON.stringify(remaining(\"u1\", [\"live\", \"cache\"])));' ' console.log(\"u1 を三つすべてから削除、残り: \" + JSON.stringify(remaining(\"u1\", [\"live\", \"cache\", \"backup\"])));' ' console.log(\"90 日目に作ったバックアップは 35 日保存、あと何日: \" + Math.max(0, 90 + 35 - today));' ' const q = \"短く一文で答えてください。削除したあとも、バックアップにデータが残るのは、なぜですか。\";' ' const sample = \"バックアップは別の写しなので、期限が来るまで変わらないからです。\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 80);' ' console.log(\"モデル: \" + reply.text);' '}' 'main();' >> retain.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
保存期間の掃除を動かす
docker exec -w /home/node/project agent node retain.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run の出力
| 行 | 意味 |
|---|---|
| 今日の期限切れ: ["c1"] | c1 だけが期間を過ぎた |
| 掃除で残る: ["c2","b1","c3"] | 本体に残るもの |
| ... live と cache から削除、残り: {"backup":[...]} | バックアップに u1 が残る |
| ... 三つすべてから削除、残り: {} | すべての写しに届いた |
| あと何日: 25 | バックアップが消える日 |
| モデル: ... | 一文。言葉は毎回変わる |
学生が入力するセルは、このプレビューでは空白です。