4

ここに画像の説明を入力

Rails プロジェクトの一部として、ユーザーが FB の友達に招待状を発行できる機能があります。私は API 呼び出しに fb_graph を使用しています。以下は、ユーザーが招待ページにアクセスしたときのコントローラーからのコードのサンプルです。

この操作は非常にコストがかかります。1000 人以上の友達を持つユーザーの場合、30 秒以上かかることがわかりました。また、このコードは、ユーザーが招待ページにアクセスするたびに再実行されます。ユーザーの FB フレンド リストは厳密には静的ではありませんが、リクエストごとにこれを再計算しなくても問題ありません。

だから私がやりたいのは、このコードを改善してより効率的にすることです。これを行う可能性のあるいくつかの異なる方法を考えることができますが、この場合、何が最も理にかなっていますか? これは、私が通常 SO で質問するよりも少しオープンエンドですが、私はまだプログラミングに比較的慣れていないので、あなたが何をするか/しないかについて、どのように行うかと同じくらい興味があります。それ。

私ができる最適化に関するいくつかのアイデアを次に示します。

1) セッション内でのみ最適化します。このコードは、ページが最初に表示されたときに実行され、セッションの残りの間保持されます。私は実際にこれを行う方法がわかりません。

2) データベースに永続化します。友達のハッシュを保持するユーザー テーブルに列を追加します。バックグラウンド ジョブを使用して、このデータを定期的に更新できます (週に 1 回程度でしょうか?)。

3) キャッシュを保持します。これに何が関係しているのか、またはこれが適切なユースケースであるかどうかも正確にはわかりません。オプション 2 には多くの手作業によるメンテナンスが必要であり、有効期限などを処理する適切なキャッシュ ソリューションがあるのではないかと思いますが、よくわかりません。

他のアイデア?オプションについてのあなたの考えに感謝します。

  # fetch full array of facebook friends
  @fb_friends = current_user.facebook.fetch.friends

  # strip out only id, name, and photo for each friend
  @fb_friends.map! { |f| { identifier: f.identifier, name: f.name, picture: f.picture }}

  # sort alphabetically by first name
  @fb_friends.sort! { |a,b| a[:name].downcase <=> b[:name].downcase }

  # split into two lists. those already on vs not on network
  @fb_friends_on_network =  Array.new

  @fb_friends.each do |friend|
    friend_find = Authorization.find_by_uid_and_provider(friend[:identifier], 'facebook')
    if friend_find
      @fb_friends_on_network << friend_find.user_id
      @fb_friends.delete(friend)
    end 
  end

更新 # 1

私が行った最初の実験にもう少し追加します。@fb_friends 配列を保持するユーザー テーブルに列を追加しました (上記の変換の後処理)。基本的に、上記のコントローラー コードは単純に @fb_friends = current_user.fbfriends に置き換えられます。上記のすべての処理は言うまでもなく、Facebook への呼び出しがなくなるため、これにより負荷が大幅に削減されると思いました。これにより、多少の時間は節約されましたが、期待したほどではありませんでした。私自身の友達リストはローカル マシンにロードするのに約 6 秒かかりましたが、これらの変更が 4 秒に短縮された後です。負荷の問題で、ここでもっと大きな何かが欠けているに違いありません。

更新 #2

さらに調査したところ、データ転送のほぼ半分が「招待」ボタンに使用していたフォームによるものであることがわかりました。フォームは友達ごとに 1 回読み込まれ、次のようになります。

<%= form_for([@group, @invitation], :remote => true, :html => { :'data-type' => 'html', :class => 'fbinvite_form', :id => friend[:identifier]}) do |f| %>
    <%= f.hidden_field :recipient_email, :value => "facebook@meetcody.com" %>

    <div class = "fbinvite btn_list_right" id = "<%= friend[:identifier] %>">
    <%= f.submit "Invite", :class => "btn btn-medium btn-primary", :name => "fb" %>
    </div>
<% end %>

フォームを削除し、内部にシンプルなボタンを配置することにしました。

<div class = "fbinvite_form" id = "<%= friend[:identifier] %>" name = "fb">
    <div class = "btn btn-small">
        Invite
    </div>
</div>

次に、ajax を使用してクリックを検出し、適切なアクションを実行しました。この変更により、データ転送が文字通り半分になりました。約 500 人の友達をロードする前は ~650kb かかりましたが、現在は ~330kb に減少しています。

前処理を行うために、更新 # 1 で試したことをもう一度試してみようと考えています。組み合わせると、これを最大2秒の操作にできることを願っています。

更新 #3

Miniprofilerをインストールして、この操作が遅くなる原因を詳しく調べたところ、上記の for ループがすべての友人の DB に移動するため、非常に非効率的であることがわかりました。私は別の質問に投稿し、旅行を1つに減らすための助けを得ました. 次に、アップデート #1 で述べた前処理を実装しました。これらすべての変更により、このパスをたどる前に +20 秒以上かかっていたことを考えると、驚くべきことに 700 ミリ秒まで短縮されました!

4

1 に答える 1