简体   繁体   中英

Ruby on Rails - Multiple POST requests

Good afternoon,

I have an internal ruby on rails (4.1.14) app running on passenger/nginx with 3 processes allocated for it. Very occasionally (<5% of the time) I get multiple POSTs from a single submit action. Here's what the log looks like:

1) User visits build_item_interaction#new using Google Chrome. This spawns 3 (!) GET requests.

I, [2017-02-14T13:27:49.679558 #19777]  INFO -- : Started GET "/build_item_interactions/new?serial_number=1702R022&type=end_of_line" for 192.168.45.105 at 2017-02-14 13:27:49 -0600
I, [2017-02-14T13:27:49.681263 #19777]  INFO -- : Processing by BuildItemInteractionsController#new as HTML
I, [2017-02-14T13:27:49.681354 #19777]  INFO -- :   Parameters: {"serial_number"=>"1702R022", "type"=>"end_of_line"}
I, [2017-02-14T13:27:49.690776 #19777]  INFO -- :   Rendered build_item_interactions/_new_interaction.html.erb (4.7ms)
I, [2017-02-14T13:27:49.690956 #19777]  INFO -- :   Rendered build_item_interactions/new.html.erb within layouts/application (5.1ms)
I, [2017-02-14T13:27:49.693310 #19777]  INFO -- :   Rendered layouts/_login_header.html.erb (1.4ms)
I, [2017-02-14T13:27:49.693779 #19777]  INFO -- : Completed 200 OK in 12ms (Views: 8.6ms | ActiveRecord: 1.0ms)
I, [2017-02-14T13:27:49.712790 #19777]  INFO -- : Started GET "/build_item_interactions/new?serial_number=1702R022&type=end_of_line" for 192.168.45.105 at 2017-02-14 13:27:49 -0600
I, [2017-02-14T13:27:49.714741 #19777]  INFO -- : Processing by BuildItemInteractionsController#new as HTML
I, [2017-02-14T13:27:49.714852 #19777]  INFO -- :   Parameters: {"serial_number"=>"1702R022", "type"=>"end_of_line"}
I, [2017-02-14T13:27:49.722208 #19777]  INFO -- :   Rendered build_item_interactions/_new_interaction.html.erb (3.5ms)
I, [2017-02-14T13:27:49.722370 #19777]  INFO -- :   Rendered build_item_interactions/new.html.erb within layouts/application (3.8ms)
I, [2017-02-14T13:27:49.724351 #19777]  INFO -- :   Rendered layouts/_login_header.html.erb (1.2ms)
I, [2017-02-14T13:27:49.724693 #19777]  INFO -- : Completed 200 OK in 10ms (Views: 6.4ms | ActiveRecord: 0.7ms)
I, [2017-02-14T13:27:49.746169 #19777]  INFO -- : Started GET "/build_item_interactions/new?serial_number=1702R022&type=end_of_line" for 192.168.45.105 at 2017-02-14 13:27:49 -0600
I, [2017-02-14T13:27:49.747577 #19777]  INFO -- : Processing by BuildItemInteractionsController#new as HTML
I, [2017-02-14T13:27:49.747658 #19777]  INFO -- :   Parameters: {"serial_number"=>"1702R022", "type"=>"end_of_line"}
I, [2017-02-14T13:27:49.754470 #19777]  INFO -- :   Rendered build_item_interactions/_new_interaction.html.erb (3.6ms)
I, [2017-02-14T13:27:49.754647 #19777]  INFO -- :   Rendered build_item_interactions/new.html.erb within layouts/application (3.9ms)
I, [2017-02-14T13:27:49.756809 #19777]  INFO -- :   Rendered layouts/_login_header.html.erb (1.3ms)
I, [2017-02-14T13:27:49.757209 #19777]  INFO -- : Completed 200 OK in 9ms (Views: 6.9ms | ActiveRecord: 0.7ms)

2) The user then clicks the [Submit] button on the page which spawns 2 consecutive POST requests:

I, [2017-02-14T13:27:59.692934 #19777]  INFO -- : Started POST "/build_item_interactions?serial_number=1702R022&type=end_of_line" for 192.168.45.105 at 2017-02-14 13:27:59 -0600
I, [2017-02-14T13:27:59.693996 #19777]  INFO -- : Processing by BuildItemInteractionsController#create as HTML
I, [2017-02-14T13:27:59.694105 #19777]  INFO -- :   Parameters: {"utf8"=>"?", "authenticity_token"=>"FFs8Uh07oKpvZ57wHzTll/PQxVp0eaK3bVIsOKUDGPU=", "build_item_interaction"=>{"build_item_id"=>"4501
", "interaction_type_id"=>"5", "data"=>"", "badge"=>["SuxY3ATMMjs"]}, "commit"=>"Save Checkpoint", "serial_number"=>"1702R022", "type"=>"end_of_line"}
I, [2017-02-14T13:27:59.708826 #19777]  INFO -- : Redirected to http://fbdbms/build_items/4501
I, [2017-02-14T13:27:59.709030 #19777]  INFO -- : Completed 302 Found in 15ms (ActiveRecord: 7.0ms)
I, [2017-02-14T13:27:59.733202 #19777]  INFO -- : Started POST "/build_item_interactions?serial_number=1702R022&type=end_of_line" for 192.168.45.105 at 2017-02-14 13:27:59 -0600
I, [2017-02-14T13:27:59.734318 #19777]  INFO -- : Processing by BuildItemInteractionsController#create as HTML
I, [2017-02-14T13:27:59.734412 #19777]  INFO -- :   Parameters: {"utf8"=>"?", "authenticity_token"=>"FFs8Uh07oKpvZ57wHzTll/PQxVp0eaK3bVIsOKUDGPU=", "build_item_interaction"=>{"build_item_id"=>"4501
", "interaction_type_id"=>"5", "data"=>"", "badge"=>["SuxY3ATMMjs"]}, "commit"=>"Save Checkpoint", "serial_number"=>"1702R022", "type"=>"end_of_line"}
I, [2017-02-14T13:27:59.747852 #19777]  INFO -- : Redirected to http://fbdbms/build_items/4501
I, [2017-02-14T13:27:59.748100 #19777]  INFO -- : Completed 302 Found in 14ms (ActiveRecord: 5.7ms)

3) Which creates two records and then the user gets forwarded to the resource's "owner" page:

I, [2017-02-14T13:27:59.756761 #19777]  INFO -- : Started GET "/build_items/4501" for 192.168.45.105 at 2017-02-14 13:27:59 -0600
I, [2017-02-14T13:27:59.757784 #19777]  INFO -- : Processing by BuildItemsController#show as HTML
I, [2017-02-14T13:27:59.757850 #19777]  INFO -- :   Parameters: {"id"=>"4501"}
I, [2017-02-14T13:27:59.809268 #19777]  INFO -- :   Rendered shared/_rfid_scans_table.html.erb (0.1ms)
I, [2017-02-14T13:27:59.809422 #19777]  INFO -- :   Rendered build_items/show.html.erb within layouts/application (41.4ms)
I, [2017-02-14T13:27:59.811722 #19777]  INFO -- :   Rendered layouts/_login_header.html.erb (1.3ms)
I, [2017-02-14T13:27:59.812213 #19777]  INFO -- : Completed 200 OK in 54ms (Views: 12.3ms | ActiveRecord: 35.6ms)

The relevant code in the controller looks like this:

class BuildItemInteractionsController < ApplicationController
    def new
        load_build_item
        load_interaction_info
        if @interaction_info then
            @interaction = BuildItemInteraction.new( build_item_id: @build_item.try(:id), 
                                                     interaction_type_id: @interaction_info[:id] )
        end
    end
    def create
        load_build_item
        load_interaction_info
        badge = params[:build_item_interaction][:badge]
        user  = Badge.valid_badge(badge)

        @interaction = BuildItemInteraction.new(params_for_interaction)
        @interaction.user = user
        if @interaction.save then
            flash[:success] = "Record saved"
            redirect_to @interaction.build_item
        else    
            err = @interaction.errors.full_messages
            flash.now[:danger] = "Could not save record: #{err}"
            render "new"
        end
    end
    private
        def load_interaction_info
            @interactions = { "teardown"    => { id: 9, description: "End of Teardown"  },
                          "sta4"        => { id: 6, description: "End of Station 4" },
                          "sta7"        => { id: 7, description: "End of Station 7" },
                          "sta8"        => { id: 8, description: "End of Test" },
                          "end_of_line" => { id: 5, description: "End of Line" } }
            interaction_type = params[:type]
            @interaction_info = @interactions[interaction_type]
        end
        def load_build_item
            sn = params[:serial_number]
            @build_item = BuildItem.where(serial_number: sn).first
        end
        def params_for_interaction
            params.require(:build_item_interaction).permit(:build_item_id, :interaction_type_id, :badge, :data)
        end
end

Here's what the form code looks like:

<h2><%= @interaction_info[:description] %> Scan for <%= @build_item.serial_number%></h2>

<p><%= link_to("Back to Unit Record", @build_item) %></p>

<div class="well">
    <%= simple_form_for(@interaction,
                        :url  => build_item_interactions_path(type: "end_of_line", serial_number: @build_item.serial_number),
                        :html => { :class => "form-horizontal" },
                        wrapper: :horizontal_form,
                        wrapper_mappings: {
                            check_boxes:   :horizontal_radio_and_checkboxes,
                            radio_buttons: :horizontal_radio_and_checkboxes,
                            file: :horizontal_file_input,
                            boolean: :horizontal_boolean}) do |f| %>
        <%= f.association :build_item,       as: :hidden %>
        <%= f.association :interaction_type, as: :hidden %>
        <%= f.input       :data, label: "Comments", as: :text %>


        <div class="form-group select required build_item_interaction_badge">
            <label class="select required col-sm-3 control-label" for="build_item_interaction_badge">
                <abbr title="required">*</abbr>Badge
            </label>
            <div class="col-sm-9">
                <%= password_field "build_item_interaction[badge]", nil, class: "string required form-control",
                                                                            id: "build_item_interaction_badge",
                                                                            placeholder: "XXXXXXXXXXXX" %>
                </div>
        </div>
        <div class="text-center">
            <%= f.submit "Save Checkpoint",  data: {disable_with: "Saving..."} %>
        </div>
    <% end %>
</div>

I can't figure out why I end up getting 2x POST requests. Some things I've tried doing:

1) Adding data: {disable_with: "Saving..." } to the submit button. This appears to work (it'll grey out/disable the button) upon clicking.

2) I've read up on CSRF tokens. It looks like the presence of this token should be enough to prevent that second request from being processed. Alas - the log shows that both requests hit within 0.1s with the same authentication_token and I get two new records.

Any suggestions?

Update: I checked the nginx access logs and there are multiple requests being processed by nginx as well:

192.168.45.105 - - [14/Feb/2017:13:27:49 -0600] "GET /build_item_interactions/new?serial_number=1702R022&type=end_of_line HTTP/1.1" 499 0 "http://fbdbms/build_items/4501" "Mozilla/5.0 (Windows NT 6
.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-"
192.168.45.105 - - [14/Feb/2017:13:27:49 -0600] "GET /build_item_interactions/new?serial_number=1702R022&type=end_of_line HTTP/1.1" 200 1657 "http://fbdbms/build_items/4501" "Mozilla/5.0 (Windows N
T 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-"
192.168.45.105 - - [14/Feb/2017:13:27:49 -0600] "GET /build_item_interactions/new?serial_number=1702R022&type=end_of_line HTTP/1.1" 200 1657 "http://fbdbms/build_items/4501" "Mozilla/5.0 (Windows N
T 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-"
192.168.45.105 - - [14/Feb/2017:13:27:59 -0600] "POST /build_item_interactions?serial_number=1702R022&type=end_of_line HTTP/1.1" 302 107 "http://fbdbms/build_item_interactions/new?serial_number=170
2R022&type=end_of_line" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-"
192.168.45.105 - - [14/Feb/2017:13:27:59 -0600] "POST /build_item_interactions?serial_number=1702R022&type=end_of_line HTTP/1.1" 302 107 "http://fbdbms/build_item_interactions/new?serial_number=170
2R022&type=end_of_line" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" "-"

The multiple GET requests are probably linked to Chrome's predictive browsing functionality prev discussion here . Which is fine, but the multiple POSTs are the problem.

Here is what you need to do to root-cause the culprit

Browser : If you are using Chrome. Press F12 (Ctrl + Shift + c) Check your browser Network tab to see how many requests are being sent from the browser for both GET & POST requests. If you see only one then browser or your front-end is not the problem

Nginx : Check your Nginx access logs that prints all the requests that reached the server. If this is printing multiple requests then I suspect something in-between the browser & server is causing this which might be a Load Balancer . Not sure if you have a LB in your setup. If this is not printing the multiple requests then obviously something has to do with Nginx or Passenger configuration which is causing this to duplicate the requests and sent to your Rails server

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM