How to ignore multiple clicks from an impatient user?

拜拜、爱过 提交于 2019-12-19 12:08:09

问题


I have a query to answer remote clients' standard request. Standard in the sense that it takes no params from outside the server. Whenever anyone submits a request to a URL, say http://www.example.com/query, s/he gets the contents of an reply.xml in the body of the response, depending on what the database delivers at that time. The contents of reply.xml changes only across the contents of the database on the server, and doesn't change on anything external like who does the query, on which input, etc, hence takes no params from the clients. I'm not even checking any authentication-- we're leaving everything to the firewall.

So i wrote a @POST method, say query() to get invoked on such a request posted to http://www.example.com/query, and deliver the result. I used Jersey at it and all is working fine to the specs, except that

the requests should be exclusive across time. that is, one request should be served at a time-- subsequent user-clicks should be getting a message with HTTP status 309 unless the server isn't running a query process invoked by my method query().

How to achieve this? I tried making query() serve @PUT rather than @POST responses, and got the same results.

Maybe a naive Q on the topic. However, not very familiar with Restful services.

I can do this by threading on a token to control that only one query is running at a time and make simultaneous requests receive the HTTP 309. however there must be a better, easier way to achieve this on the server.

I'm using Tomcat 8, Jersey 1.19.

TIA.

Note: I've read PUT vs POST in REST among some other useful discussions.

//=====================

EDIT:

Which user submits the quer doesn't make any difference at any time.

Suppose userA submitted a query. while that query is still running, i.e., before query() returns the response to userA, userB submitted a query. userB should be getting a 309-- just because there's a query being processed at the time.

Whether userA = userB or userA <> userB is immaterial here, a 309 should be returned just because there's a query request while one is already running. and thats the only time a user gets a 309.

//==============================================

EDIT-2:

I'm aware of solutions w/concurrency control. I'm guessing that there is one using the Restful features. this is rather an academic Q.


回答1:


  • @Koos Gadellaa is correct in saying the client should be blocking the second request until the response arrives. Let me expound on why this is the best thing to do. Architecturally, it relates to concerns. The server does not have context awareness as to why two requests have arrived in parallel. Therefore, it is relying on out-of-band knowledge to know parallel requests are bad. Any out-of-band knowledge creates a coupling, meaning if you change how one part of the system works, you have to change the other. RESTful architectures are popular because they reduce coupling. If the same user is logged into two clients then the system breaks. You never want to build systems with this type of client-server coupling.

  • Regarding the server's responsibility, good coding practices come into play, the best thing to do would be to ensure the service is not impeded by multiple parallel requests from the user. Caching may be your friend. Based on query parameters, the response could be written to a cache file on disk. The client would always be redirected with HTTP 303 to the cache file URL. 304 Not Modified could be used so the client would not have to download the answer twice. In this scenario, the only out-of-band knowledge is proper implementation of the HTTP spec which is well specified and robust.

  • The appropriate response code seems to be 503 if the service is overloaded.

    10.5.4 503 Service Unavailable

    The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of the delay MAY be indicated in a Retry-After header. If no Retry-After is given, the client SHOULD handle the response as it would for a 500 response.

         Note: The existence of the 503 status code does not imply that a
         server must use it when becoming overloaded. Some servers may wish
         to simply refuse the connection.
    
  • Since you directed me here from my response here, it would appear you want to know the correct RESTful approach. This will be more complex than the solutions above and my guess would be you don't want to take this route but here it is.

    If the server needs to communicate to the client that a request is being performed and when it completes, then resources need to be created for those concepts. This feels weird because they concepts exist in HTTP and it is odd to reimplement them at a higher level (architectural smell).

    1. The client would POST a Request or Query resource to the server. This resource would have a Response property that is empty and a Status property which is empty. The server would respond with a body that has the Status property set to "processing".
    2. The client would then perform GETs to this resource (possibly use long-polling here) to check for updates.
    3. When the response has been created, the Response property would then link to a response resource or have the response embedded.

    This approach communicates what is happening using resources. Therefore, the only out-of-band knowledge is the valid states of the resource.




回答2:


I think you're framing it wrong. A user clicking multiple times is a user using the frontend client to connect to your server. Hence, it is up to the client to ensure that no multiple clicks occur. Javascript can perfectly fend off such requests.

Having said that, onto your request: the fact that a 309 should be returned. This is a concurrency problem.
If I read your specs correctly, it is related to a database query being performed. Consider the following code:

result = do_database_thing();
out = make_up_result(result);
return out;

As per these specs, the only request is that the critical section is on the do_database_thing. If make_up_results is slow, multiple requests are allowed. So the natural solution is to use locking on the database. If it should encompass the entire query() method (i.e. not the database query itself), request the lock early, release the lock at the end. Then the code becomes:

boolean locked = get_lock();
if(!locked) return 302;
result = do_database_thing();
release_lock();
out = make_up_result(result);
return out;


来源:https://stackoverflow.com/questions/39057416/how-to-ignore-multiple-clicks-from-an-impatient-user

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!