In a RESTful SOA, suppose I issue a POST request via AJAX but I don't get a response before the request times out. Further suppose a re-submission of the request would be harmful. POST is not idempotent. For example, maybe I am posting a bank transfer of money. If I don't get a response, I don't know if the server processed the request.
What is the best practice to deal with this, assuming I have control over the client-side and the services side?
My initial thought is to include a nonce (i.e. a pseudo-id; some sort of unique identifier) with each POST request; e.g. perhaps a value in the If-None-Match header. With this approach, the client-side can programmatically re-issue a timed out request with the same pseudo-id and the server can reject it if it contains a repeat value.
Timeouts can be easily added to the URL you are requesting. It so happens that, you are using a third-party URL and waiting for a response. It is always a good practice to give a timeout on the URL, as we might want the URL to respond within a timespan with a response or an error.
Best practice is to get the response time of your web application to be under 500ms, this will free up the application for more requests and deliver a high quality user experience to your visitors. Occasionally a web request may hang or take an excessive amount of time to process by your application.
Settings -> general ->XHR Timeout(ms) Show activity on this post. Don't forget to specify timeout in milliseconds.
The 408 Request Timeout error is an HTTP status code that means the request you sent to the website server—e.g., a request to load a web page—took longer than the website's server was prepared to wait. In other words, your connection with the website "timed out."
There are a number of ways I've heard of to try to solve this
Get the current state.
Design the service so that if a POST fails, the client can issue a GET on the same resource and based on the data returned, they can determine if the POST was successful of not.
The problem with this approach is in cases where the POST creates a new item in a collection, it could be difficult for the client to determine if the post was successful or not (i.e., did my post add that item or did someone else's?)
If-Match
Use the If-Match
header to prevent the POST from being repeated. e.g., if the POST is adding an item to a collection, and the collection currently has a ETag
of 737060cd8c284d8af7ad3082f209582d
. If If-Match
of 737060cd8c284d8af7ad3082f209582d
is used on the POST, then the POST will only succeed if the collection's ETag
is still 737060cd8c284d8af7ad3082f209582d
, which will add the item and result in a new ETag
for the collection. Repeating the POST at this stage will just return 412 Precondition Failed
.
The problem with this approach is the when you get a 412 Precondition Failed
, you can't be sure if your POST modified the collection or someone else's.
POST then PUT
Design your service to never persist data from a POST. Instead the POST creates a temporary resource with the POST content, which is in a "Pending" state. This temporary resource is then "committed" with a PUT. With this approach you don't care if your POST time's out, just request the POST again and this time you'll hopefully get your temporary resource. If the PUT to commit the resource time's out, you don't care either, because the PUT is idempotent.
The only real downside of this approach is the extra work required to manage the temporary resources and the extra effort required in the client.
Update
Nonce
The use of a nonce (a.k.a Message ID, Transaction ID, Request ID, Correlation ID) in the request is a common approach for solving this problem; however it can have scalability issues. If you commit to rejecting all POSTs that use a previous nonce, you then need to scan the existing POST records to determine if the nonce has been used before. Not a problem when you have only a few thousand POSTs, but it can become problematic when you have millions (especially if you haven't stored the nonces in a way that is fast to query).
You can mitigate this by reducing your commitment to rejecting all POSTs that use a previous nonce within a certain timeframe (e.g., last 24hrs), but if the first POST times out and the client is disconnected for that timeframe, then they are back in the same old position, where they don't know if the first POST was successful and have no way to determine if they should rePOST or not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With