0

I'm building a RESTful server, and writing a javascript client for it. The front page in the client shows some statistics of the data in the database. In particular: I have a 6×3 table where each cell is initialized with a '…' text, and has a query data field. Upon document.ready, client side, I invoke this code:

$('#home-page table td').map(function (i, x) {
  $.getJSON("/url/count?q=" + $(x).data('query'),
            function(data){
              $(x).text(data['__total__'])
            });
})

and what this does, it executes the internal unnamed function on each of the 18 table cells, sending the corresponding query to my server and registering an equally unnamed callback to handle the result. The server executes the database query, then counts the items in the result, and returns a one-element dictionary.

Now the funny thing was that Firefox would hold the 7th request, waiting for one of the first 6 to be handled. I had thought it was something in the server, but thanks to this question and this page I can confirm it's a limit in my web browser.

Splitting my server over three or four ports sounds terrible.

Also tweaking the settings, I would not want to request each of my users to do that.

This graph shows the response time if I can put the 18 GET requests at the same time, compared to respecting the default 6-limit. Red is the blocked time.

network timings

Or I give up, and update the table all at once, but I like the effect of the cells getting updated one by one, and this would take as much time as at least the maximum for all requests.

Or I could sort the cells so that it is most likely that the fastest cells will be requested first (the 6 cells in the first of the three columns show the result of select count(*) from table_i). Still the next two columns would have the same problem.

So how could I approach the problem otherwise, and in general, fulfilling the requirements: all requests placed at the same time; each result received as soon as it's ready.

I have an idea, and I will elaborate it into an answer, but I wonder what you people would do.


This was the initial text of the question, before I found the real source of the problem.


This is what it looks like from the logs:

$ mix phx.server
Generated luke app
[info] Running LukeWeb.Endpoint with Cowboy using http://0.0.0.0:4000
15:00:56 - info: compiled 6 files into 2 files, copied 3 in 2.8 sec
[info] GET /
[info] Sent 200 in 56ms
[info] GET /url/count
[info] GET /url/count
[info] GET /url/count
[info] GET /url/count
[info] GET /url/count
[info] GET /url/count
[info] Sent 200 in 678ms
[info] GET /url/count
[info] Sent 200 in 1675ms
[info] GET /url/count
[info] Sent 200 in 2859ms
[info] GET /url/count
[info] Sent 200 in 1895ms
[info] GET /url/count
[info] Sent 200 in 3956ms
[info] Sent 200 in 3963ms
[info] GET /url/count
[info] GET /url/count
[info] Sent 200 in 657ms
[info] GET /url/count
[info] Sent 200 in 4801ms
[info] GET /url/count
[info] Sent 200 in 1196ms
[info] GET /url/count
[info] Sent 200 in 1511ms
[info] GET /url/count
[info] Sent 200 in 4882ms
[info] GET /url/count
[info] Sent 200 in 2950ms
[info] GET /url/count
[info] Sent 200 in 1306ms
[info] Sent 200 in 1099ms
[info] Sent 200 in 1880ms
[info] Sent 200 in 2349ms
[info] Sent 200 in 4885ms
[info] Sent 200 in 3558ms

Server-side, the most relevant code is this (I've not yet implemented anything, this is just a random sleeping time):

defmodule LukeWeb.CountController do
  use LukeWeb, :controller

  def index(conn, _params) do
    sleeping = :crypto.rand_uniform(500, 5000)
    :timer.sleep(sleeping)
    result = %{"__total__" => sleeping}
    json conn, result
  end
end

And sections of router.ex:

defmodule LukeWeb.Router do
  use LukeWeb, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/url", LukeWeb do
    pipe_through :api

    get "/count", CountController, :index
  end
end
mariotomo
  • 9,438
  • 8
  • 47
  • 66

1 Answers1

0

My idea is based on tokens, and recursion. I would place all requests at once, as an array of queries, and I would immediately receive a token, by which to identify the requests set.

Then a cash_token function would offer the token to the server, and register a callback for handling it, with this structure:

function cash_token(token, args) {
  var itr, itd, ispan, btn;
  $.getJSON("/browse/cash-token/{}".format(token), function(data) {
   .
   .
   .
    if (not_yet_done) {
         setTimeout(partial(cash_token, token, args), 1);
    }
})}

These are only guidelines client-side, I obviously haven't written the final code yet, and server side, I need to learn more of Elixir.

(partial and format are not standard javascript)

mariotomo
  • 9,438
  • 8
  • 47
  • 66