今回の記事では、Bubbleにおけるアーキテクチャ最適化とBubbleエンジンの限界についての詳細を説明していきます。
Bubbleの仕組みがより分かる内容になっているので、是非今回の記事も参考にして、よりBubbleを効率良く扱っていきましょう。
パフォーマンスとスケーリング概要
Bubbleチームは、スケーラビリティとパフォーマンスの最適化を常に模索しています。
これは、何千ものBubbleアプリを処理するためのBubbleプラットフォーム(スケーラビリティとパフォーマンス)と、Bubbleアプリがエンドユーザーに良い体験を提供するためのプラットフォームの両方を改善することを意味します。
Bubbleアプリのパフォーマンスとスケーラビリティは、アプリがどのように構築されているかによって大きく影響を受けます。
このページでは、アプリのパフォーマンスとスケーラビリティの概要と具体的なヒントを説明します。
パフォーマンスに関する一般的な原則とヒント
■フェッチされるデータが少なければ少ないほど、パフォーマンスは速くなります。
ページは多くの場合、ページロード時にいくつかのデータをフェッチする必要があります。ページロード時に100個のデータをフェッチするページは、100万個のデータアイテムをフェッチするページよりも速くロードされます。
■同様に、小さくてシンプルなページを多く持つことは、複雑なページを少なく持つよりも速くなります。
■ソートやフィルタリングは可能な限り元の検索に近づける – Bubbleはすでに様々な方法でデータベースクエリを最適化していますが、データベースレベルでソートやフィルタリングを実行することは非常に効率的です。
つまり、結果を操作した後にソートやフィルタリングを行うクエリよりも、:sortや :filterを適用したクエリの方が効率的である傾向があります(例:search:count を行う方がsearch:group by:countよりも効率的です)。
■高度なフィルタを使用するとクエリが遅くなることがあります – 基本的な原理は、フィルタ(またはソート)が「データベース上で」実行できる場合、バブルがデータベースからデータの初期セットを取得した後に実行しなければならないフィルタ(またはソート)よりも高速になるということです。
検索パレット(“Do a search for “をクリックするとスライドして出てくる追加のサイドバー)に表示されるフィルタは、データベース上で実行されるので、一般的に高速です。
filterで適用されるフィルタは一般的に「高度な」フィルタであり、一般的には遅くなります。
■連鎖されたクエリは並列ではなく直列に実行されます – Bubbleでは、ある検索結果を別の検索の制約として使用することができます。
これらの検索は並列ではなく直列に実行されるため、最初の検索で多くのデータが返ってくると、2回目の検索が遅くなります。
■Bubbleはすでに多くのパフォーマンス最適化を行っています-Bubbleは、データベース上でクエリを実行したり、サーバー上で画像のサイズを変更したり、ブラウザにJavascriptをキャッシュするように指示したりなど、適切な方法で実行しようとしています。
比較的単純なクエリを実行して、比較的少量のデータを取得した際に、実行が非常に遅いと感じる場合は、ここにあるガイダンスのいくつかに基づいて、アプリのデータ階層またはクエリにできる最適化があるかどうかを確認してください。
■一般的に、クエリを表現する方法はシンプルな方が速い-必ずしもそうとは限らないが、経験則としては良いだろう。
Bubbleは、最も一般的なパターンのためのデータベースの最適化に常に取り組んでいます。
■ページを読み込むたびにデータを変更しないようにする – 要素の状態を変更することは、同じ動作を行うために追加のデータベース呼び出しを行うよりもパフォーマンスが向上します。
■高度な計算を裏でスケジュールされたワークフローに移行してみてください – スケジュールされたワークフローは、重いクエリを実行し、その結果をどこかに保存して後で使用することができます。
■ワークフローアクション”Make changes to a list of X”を慎重に使用する – このアクションは、短いリストに素早く変更を加える場合に便利ですが、リストの数が増えてくると、ワークフローがタイムアウトするリスクがすぐに高まります。
このアクションでタイムアウトが発生している場合は、代わりに”Schedule API Workflow on a list”を検討してみてください。
ページ読み込み時に何が起こるのか?
Bubbleがページをロードすると何が起こるのか、大まかな流れは以下の通りです。
①Bubble はすべての要素(可視と不可視)のコードを送信します。
②Bubbleは、ページ上のすべての可視要素を描画します。
③Bubbleは、可視要素に必要なすべての動的データを取得します。
つまり、
■見えない要素は後で表示されるまで描画されません。見えている項目が見えない項目のデータソースを参照していない限り。(1つの可視要素を使って別の可視要素をカバーしても、この文脈では後者を”不可視”にはしないことに注意してください)
そして、ページの読み込み速度のために、要素の種類よりも要素の数の方が大きくなります。
すべての要素タイプは、2つの例外を除いて、パフォーマンスの点では互いにかなり似ています。
①Repeating groupsは、Layout Style プロパティによって異なる量のデータをロードします。また、繰り返しグループの各セルに要素が多いほど、ページのレンダリングにかかる時間が長くなることにも注意してください。
2つの要素を持つ10個のセルを持つ繰り返しグループは、20個の独立した要素よりも高速ですが、3つの要素よりも低速です。入れ子になったリピートグループは要素数に乗算効果があります。
②プラグインは、使用されているかどうかに関係なく、各ページの読み込みにそのコードが含まれています。
これは使用されていない場合、Bubbleはプラグインをレンダリングしないので、パフォーマンスへの影響はそれほど大きくありませんが、一般的には、アプリが使用していないプラグインをアンインストールすることをお勧めします。
パフォーマンスに関するその他の注意事項
再利用の力
・1つのページが複数の場所で同じ検索をしている場合、バブルは自動的にそれらを組み合わせてクエリを実行する
・スタイルを活用することでパフォーマンスの向上に貢献する
・特に重い検索を最初の数回実行したときは、今後の実行のために少し遅くなるかもしれない
どっちが良いか
・1つのフィールドを変更する12個のアクションよりも、12個のフィールドを変更するアクションの方が効率的
・比較的小さなリストの場合はリストの変更が高速ですが、大きなリストの場合はAPIワークフローの方がワークフローのタイミングアウトのリスクがないので、よりスケーラブルになる
・大規模なリストを変更する場合、リスト上の後続の項目のためにAPIワークフローを再帰的に呼び出した方が、リスト全体のAPIワークフローを一度に実行するよりも、少し時間はかかりますが、よりスケーラブル
・リンク要素を介して新しいページに移動することは、一般的には少し速くなります。
・データ型Aが複数のBに接続している状況(例えば、投稿にはカテゴリがあるが、投稿ごとにカテゴリは1つしかない、A=カテゴリ、B=投稿)では、Bにはそれが属するAを参照するフィールドを持つ方が一般的には良い。
Aにフィールドを持ち、そのフィールドに属するすべてのBをリストアップすることは、リストが非常に長くなる場合にはうまく機能しません。
・APIワークフローでは、ワークフローがアクションを起こさなければならないアイテムの数は、各アイテムのサイズよりもパフォーマンスに大きな影響を与えます。
容量
非専門用語では、「容量」とは、アプリが一定期間にどれだけ「何か」を行うことができるかを測定するものです。
あなたのウェブサイトに来るユーザーは少しの容量を使用しますが、大量のユーザーがあなたのウェブサイトに来ると、より多くの容量を使用します。データベースの呼び出しは容量を使用します。
特定のワークフロー(サーバー上で発生するもの)を実行するとキャパシティが使用され、同様にアプリのAPIを呼び出すとキャパシティが使用されます。
Bubble全体を通して、キャパシティの「単位」への言及があります。
単位とは、バブルのシステムが使用するさまざまな希少なリソースの重み付けされた尺度で、サーバーのCPU時間、データベースのCPU時間、他のバックエンドシステムなどの要因が含まれます。
単位の正確な計算式は、バブルがバックエンドシステムを追加、削除、または改善すると、時間の経過とともに変化します。
バブルの目標の1つは、容量の単位が提供するユーザー目線のパフォーマンスを向上させることです。
特定のBubbleプラン(すなわち、HobbyとPersonal)では、アプリは「ベーシック」サーバーの容量を持ちます。
アプリが「Professional」と「Production」のプランにアップグレードされると、そのアプリのために予約された専用の容量または「予約済み」の単位が与えられます。
容量を超えると、アプリはレート制限されます。これも非専門的な用語で、アプリが特定の期間に多くの「もの」を実行できないことを意味し、アプリ上のユーザーの要求は事実上遅くなります。
このように、一般的には、より多くの容量を持つことは、多くの「何か」が行われている場合に、アプリがより多くの「何か」を行うことができることを意味します。
これには少しひねりがあります。容量は、食料品店のレジの列の数に例えることができます。店がより多くの行を追加する場合、それは同時にチェックアウトするより多くの顧客を処理することができます。
しかし、顧客が何百もの商品を積んだカートを持ってやってきた場合、その顧客はしばらくの間、レジの列全体を占有することになります。
同様に、より多くの容量を持っていても、非常に複雑なデータベースクエリの実行をそれほど速くすることはできません – これは、一人の顧客がカートにたくさんのアイテムを入れてチェックアウトしているようなものです。(これには注意点があります。大規模なクエリがアプリの容量をすべて消費することをBubbleが検出した場合、Bubbleはそのクエリをスローダウンして、アプリの残りの部分の合理的なユーザーエクスペリエンスを維持しようとします。したがって、特定の状況では、容量を追加することで大規模なクエリの実行が速くなることがあります。)
ユーザーは、左側のナビのログに行くと、自分のアプリがどれくらいの容量を使用しているかを見ることができます。
最初のチャートは、アプリが最大容量に到達した時間を示し、2番目のチャートは、アプリが最大容量に対してどのくらいの容量を使用したかを示しています。
ページのさらに下には、過去24時間以内にアプリのさまざまな部分で使用された容量の内訳を示すサーバー容量使用量の詳細チャートがあります。
アプリの動作が遅く、容量の限界に達している場合は、予約された追加容量を購入することで解決する場合があります。
専用インスタンスはどうか?
専用インスタンスは、主に次の3つの方法でパフォーマンスを向上させることができます。
①地理 – 専用インスタンスは、地理的にユーザーの近くに配置することができるため、大規模な静的アセットのパフォーマンスを向上させるのに役立ちます。
②重いデータ処理 – 専用インスタンスでは大幅に高速化できます。
③安定性 – 専用インスタンスでは、専用インスタンスをアップグレードする前に、メインのBubbleクラスタ上でアプリをテストすることができます。これは、新しいバージョンのBubbleでアプリの安定性を確保したり、Bubble全体が停止するリスクを排除するのに役立ちます。
しかし、これは、パフォーマンスを向上させる唯一の方法は専用プランを利用することだと言うことではありません。ほとんどの場合、それには程遠いです。
「容量」セクションの上のすべてのヒントは、アプリが専用のインスタンス上にあるかどうかに関係なく適用されます。
最後に
結局のところ、上記は一般的なガイドラインであり、パフォーマンスに影響を与える要因についてある程度の透明性を提供することを目的としています。
アプリの特定のケースでパフォーマンスが重要な場合は、経験的にさまざまなアプローチをテストしてみて、何がより速くなるかを確認してください。
バブルの限界と既知の問題
以下に、アプリの構築中にまれに遭遇する可能性のある既知の問題を示します。これらのバグは定期的にレビューされ、解決したら削除されます。
一般的な問題
Bubbleの「ドロップダウン」要素はネイティブHTMLの「<select>」タグを使用しているため、一般的に、拡張されたオプションのリストにスタイル(オプションの整列、ボーダーの丸みなど)を適用することはできません。
これは一般的なCSSの制限になります。別の方法としては、プラグインを検討してみてください。
プラットフォーム/ブラウザに特化した問題
・Android(モバイル)→入力の通貨フォーマットはサポートされていません。
・Safari(モバイル)→ページをリロードするか、デバイスを傾けるまでポップアップが表示されない。また、入力カーソルは、入力中に数行オフセットされます。
・Microsoft Edge→エクスプローラからブラウザにファイルをドロップすると、ブラウザの互換性の問題で期待通りに動作しない。
まとめ
今回は、Bubbleおいてのパフォーマンスとスケーリング、限界問題について解説していきました。
やはりシンプルに構築するほどBubble側もより効率良く最適化してくれることが読み取れると思います。
また、まだ正しくはサポートされていない部分も理解できたのではないでしょうか。Bubbleのことをより理解しながら、アプリを構築していきましょう。