
目次
Notionのデータベースを外から読むとき、1回で全部は返ってきません。
これ自体は知られています。ただ、取りこぼしたことに気づけない場面がいくつもありました。エラーが出ないまま、件数だけが足りなくなります。
220行のデータベースを作って、読み方を変えながら測りました。
1回に返るのは100件までです
まず、基本のところから。
220行を読むのに、3回かかりました
1回目で100件、2回目で100件、3回目で20件。合計3回で全部取れました。
| 件数 | 回数 | 時間 | |
|---|---|---|---|
| 読む | 220 | 3回 | 1.7秒 |
| 書く(同じ数を作る) | 217 | 217回 | 43.7秒 |
読むのは速いです。書くのが遅いのとは対照的で、読む側で困ることはほとんどありません。
続きを取るための目印が返ります
まだ続きがあるときは、「まだある」という印と、続きを取るための目印が一緒に返ってきます。
次の呼び出しでその目印を渡すと、続きから返ってきます。「まだある」が消えるまで繰り返せば、全件取れます。
今回、重複は1件もありませんでした。同じ行が2回返ってくる心配はしなくて大丈夫です。
何回かかるかは、先に計算できます
1回100件なので、回数は件数から決まります。
| 行数 | かかる回数 |
|---|---|
| 100行まで | 1回 |
| 101〜200行 | 2回 |
| 1,000行 | 10回 |
| 5,000行 | 50回 |
5,000行でも50回です。今回の速さから見積もると、それでも1分はかかりません。
回数が増えること自体は、心配しなくて大丈夫です。問題になるのは、回数ではなく書き方のほうです。ここから先がその話になります。
101を送っても、エラーになりません
ここから、気をつけるところです。
黙って100に丸められます
1回に取る件数は、こちらから指定できます。では、100より大きい数を送るとどうなるか。
| 送った数 | 返った件数 |
|---|---|
| 100 | 100 |
| 101 | 100 |
| 150 | 100 |
| 0 | 100 |
| 1 | 1 |
エラーは返りません。黙って100件になります。
「150件ずつ取る」と書いたつもりで、実際は100件ずつしか取れていない。それでも動いてしまいます。
「まだある」を見ていないと、落ちます
これが問題になるのは、1回で取り切るつもりで書いた場合です。
150件のデータベースに対して「150件ください」と送る。100件返ってくる。それを全件だと思って処理する。50件が消えます。
エラーも出ませんし、返ってきたデータも正しいものです。足りないことに気づく手がかりがありません。
しかも、データベースが小さいうちは正しく動きます。100行までは、1回で全部返るからです。
行が101行を超えた日から、静かに壊れます。作ったときには問題がなく、運用が進んでから発覚するという形になります。
| 行数 | 「150件ください」と送ると |
|---|---|
| 80行 | 80件返る。正しい |
| 100行 | 100件返る。正しい |
| 150行 | 100件返る。50件足りない |
| 300行 | 100件返る。200件足りない |
増えるほど、落ちる割合も増えます。気づいたときには、ずっと間違った集計をしていたことになります。
マイナスを送ると、止まらなくなります
もう1つ、危ない値がありました。マイナスの数です。
| 送った数 | 返った件数 | 「まだある」 |
|---|---|---|
| 100 | 100 | あり |
| -1 | 0 | あり |
0件しか返ってこないのに、「まだある」と言われます。
「まだある」が消えるまで繰り返す、という書き方をしていると、1件も取れないまま永遠に回り続けます。
変数から件数を渡していて、計算の結果がマイナスになった。そういうときに起きます。回数の上限を決めておくのが安全です。
たとえば「残り件数」を計算して渡す作りにしていると、引きすぎてマイナスになることがあります。その瞬間に止まらなくなります。
防ぐのは簡単で、いつも100と書いてしまえば済みます。100より大きい数には意味がなく、小さい数にも利点がありません。計算する理由がそもそもありません。
取得の途中で足した行は、出てきません
ここがいちばんの発見でした。
3回試して、3回とも出てきませんでした
1回目を取ったあとに行を追加して、それから続きを取る。この流れを、条件を変えて3回試しました。
| 試した条件 | 追加した3行が出たか |
|---|---|
| 並べ替えなしで追加 | 0件 |
| 並べ替えて、まだ取っていない側に入る値で追加 | 0件 |
| 並べ替えて、明らかに最後に来る値で追加 | 0件 |
3つ目が決め手です。これから取る範囲に入るはずの行でも、出てきませんでした。
1回目の時点で、中身が決まっています
つまり、順番の問題ではありません。最初に呼んだ時点で、何を返すかが決まっています。
3つ目の試行では、全部で224行あったのに221行しか返ってきませんでした。最初に呼んだときの221行です。
「全件取った」と思っていても、増えています
読んでいる最中に、ほかの人が行を足すことはあります。外部の処理が動いていることもあります。
そのとき、取った側には何も起きません。エラーも警告もなく、少ない件数で正常に終わります。
取り終わったあとに件数を数え直すと、合いません。これが唯一の手がかりです。
どんなときに起きるか
| 場面 | 起きやすさ |
|---|---|
| ほかの人が画面から行を足している | チームで使っていれば、いつでも |
| 別の同期処理が同時に動いている | 決まった時刻に集中しがち |
| フォームからの登録が流れ込む | 受付中はずっと |
| 自分の処理が、読みながら書いている | 毎回 |
最後が見落とされがちです。読みながら同じデータベースに書いていると、自分で自分の取得を狂わせます。
読むのと書くのは、分けてください。先に全部読み切って、それから書く。この順番にするだけで防げます。
消した行は、ちゃんと消えます
追加と対になる話です。こちらは逆でした。
減ったぶんだけ、減ります
1回目を取ったあと、まだ取っていない行を5つ削除しました。
続きを取り切ると、5件少なく返ってきました。消した行は混ざっていません。そのときの実際の件数と一致しました。
追加と削除で、扱いが違います
| 取得の途中で | 結果 |
|---|---|
| 行を足した | 出てこない |
| 行を消した | ちゃんと消える |
どちらか片方に揃っていれば分かりやすいのですが、揃っていません。
「最初の時点の写しが返る」わけでもなく、「そのときの最新が返る」わけでもない。追加だけが反映されない、と覚えてください。
実務では、どちらも困ります
この非対称が、件数のずれ方を分かりにくくします。
| 途中で起きたこと | 取れる件数 |
|---|---|
| 5行足された | 元のまま(5行足りない) |
| 5行消された | 5行少ない(正しい) |
| 5行足されて、5行消された | 10行少ない |
3つ目が分かりにくいところです。件数としては増減なしなのに、取れる数は10行減ります。
数え直したときに「10行足りない」と出ても、何が起きたのかは分かりません。足りないという事実だけを見て、取り直すのが正解です。原因を追う必要はありません。
図にすると、こういう挙動です
カーソルは、間違えてもエラーになりません
続きを取るための目印についても測りました。
3通り試しました
| 送ったもの | 結果 |
|---|---|
| さっき使った目印を、もう一度 | 通る(同じ100件が返る) |
| 形は合っているが、存在しない目印 | 通る(0件が返る) |
| まったく形の違う文字列 | 断られる |
問題は真ん中です。存在しない目印を送っても、エラーになりません。0件が返るだけです。
「取り終わった」と見分けがつきません
0件が返って「まだある」も付かない。これは、正常に取り終わったときとまったく同じ形です。
目印をどこかに保存して、あとで続きを取る。そういう作りにしていると危ないです。保存した値が壊れていても、「もう続きはありません」という顔で返ってきます。
形が違う場合は、教えてくれます
まったく形の違う文字列を送った場合は、きちんと断られました。送った値もそのまま返ってきます。
つまり、形さえ合っていれば通ってしまうということです。途中で1文字変わったような壊れ方は、見つけられません。
使い回しはできます
一度使った目印をもう一度送ると、同じ100件が返ってきました。1回きりではありません。
途中で失敗したときに、同じところからやり直せるということです。失敗した回だけを投げ直す作りにできます。
ただし、ここまでに書いたとおり目印を長く持ち回るのは避けたほうがいいです。やり直すなら、その場ですぐに。
安全な取り方
ここまでを、実務の書き方にまとめます。
4つ守れば、事故は防げます
| やること | 防げる事故 |
|---|---|
| 件数は100と書く | 黙って丸められて気づかない |
| 回数に上限を決める | 止まらなくなる |
| 取り終わったら件数を数え直す | 途中で増えた行の取りこぼし |
| 目印は保存せず、一気に取り切る | 壊れた目印で0件になる |
3つ目がいちばん効きます。取った件数と、あらためて数えた件数を比べるだけです。
ずれていたら、どうするか
数えた結果がずれていたら、増えたということです。やることは1つで、もう一度取り直します。
読むのは速いので、220行なら1.7秒です。やり直す費用は、ほとんどかかりません。
何度やってもずれ続けるなら、書き込みが止まらない時間帯だということです。その場合は、処理する時間をずらすほうが早いです。
| 取り直した結果 | やること |
|---|---|
| 合った | そのまま進む |
| 2〜3回でおさまった | 気にしなくてよい |
| 何度やってもずれる | 実行する時間帯を変える |
夜間や早朝に動かせるなら、それがいちばん簡単です。人が触っていない時間なら、そもそも増えません。
一気に取り切るのが、いちばん簡単です
目印を保存して、あとから続きを取る。この形は、便利そうに見えて危ないです。
保存した目印が壊れていても、0件が返るだけで教えてくれません。途中で終わったことにも気づけません。
件数が多くても、読むのは速いです。1回の処理の中で最後まで取り切ってしまうほうが、結果的に安全です。
エラーにならない4つの場面
最後に、この記事で出てきた「エラーが出ないまま壊れる」場面をまとめます。
| 場面 | 何が起きるか |
|---|---|
| 101や150を送る | 黙って100件になる |
| マイナスを送る | 0件なのに「まだある」。止まらない |
| 取得の途中で行が足される | 足された行が出てこない |
| 壊れた目印を送る | 0件が返り、取り終わりと区別できない |
4つとも、返ってくるデータ自体は正しい形をしています。足りないだけです。
だから、データの中身を見ても見つかりません。件数を数えることでしか気づけません。そこだけ押さえておいてください。
よくある質問(FAQ)
1回に100件より多く取る方法はありますか。
ありませんでした。100より大きい数を送っても、黙って100件になります。回数を増やすしかありません。ただ、読むのは速いので、回数が増えてもそれほど困りません。
同じ行が2回返ってくることはありますか。
今回の範囲では、1件もありませんでした。並べ替えを指定した場合も、取得の途中でデータを変えた場合も、重複は出ていません。
取得の途中で足した行は、次に取れば入りますか。
次に最初から取り直せば入ります。出てこないのは「その回の取得には入らない」という意味です。
何行くらいから気をつけるべきですか。
101行からです。100行までなら1回で終わるので、今回の話はどれも起きません。100を超えた時点で、全部が関係してきます。
書き込みも100件ずつまとめて送れますか。
送れません。書き込みは1件ずつです。今回も217行を作るのに217回かかりました。読むのと書くのは、扱いがまったく違います。
件数を数え直す処理も、100件ずつになりますか。
なります。数えるためだけに全部読むことになるので、そこも含めて時間を見ておいてください。220行で1.7秒ほどでした。
取り終わったら、件数を数え直してください
取得の途中で足された行は出てきません。エラーも警告も出ないので、件数を比べるのが唯一の手がかりです。読むのは速いので、ずれていたら取り直せば済みます。