Web Services – How to Implement Idempotent Webservice Calls

soaweb services

I'm developing a wcf based solution for a webservice layer that "occasionally-connected" mobile devices will be using. The service will not be using queuing (at this stage) due to the additional complexities, and so it will instead be operating a simple Request / Response approach.

Of course, being a mobile device, it is possible that the devices may go out of signal mid insert / update. The webservice itself may have completed the transaction, but the client will never receive this message. Due to this, it will resend the request up, at which point the server needs to recognise this as a duplicate request, and return the same response that it tried on the first instance.

Hence I am trying to make the services Idempotent. In order to achieve this, I am implementing Request Paramter DTO objects, which consist of the RequestID, plus the parameters needed to complete the call.

However, my difficulty is in implementing a way of monitoring the request ids. SInce the service is currently stateless, the only way I can currently envision is to have a ServiceRequest table in the database, which will take the RequestID as a primary. Obviously querying on this will indicate whether the request has already been actioned, as the client will be sending up the same RequestID. But the service also needs to know what message to send back. In the case of an insert/update, the affected aggregate root id will also need to be stored somewhere, whether directly on the request table, or in a RequestToObjectLookup table

So, I'm wondering if there is a best practice way of implementing this? My thoughts are to have a ServiceRequest table(s) (specific to service), that stores requestids, additional info, and also a lookup to the result object id (on a save/insert/update). So when a new request comes in, this table can be consulted first before proceeding with the rest of the request, whether that be to perform the save, or just return the previously updated object (that was actioned on the first request attempt).

I'm also thinking (as stated) that I only need to keep the root aggregate id referenced with the request, as I should just be able to use relationships to attain the rest of the info.

Best Answer

Deletion can be made idempotent by only allowing deletion based on IDs. You will need to have some way for IDs to remain "in use" and maybe expire.

With such a reservation system; creating an object can be done by first reserving an ID and then a second call to create the object with that ID (essentially an update on the new empty object). When an ID is requested the server will grab any free ID and create an uninitialized object with that ID, the second call to initialize will include the previously returned ID (plus all data required). This means that the second call is in essence an update call that is idem potent in itself (calling it with the same data multiple times will result the same result).

If reservation happens multiple times then multiple IDs are returned and initialized object will remain. These uninitialized object can be cleaned up by a rhoomba after some time automatically.

This put the onus on the client to ensure that if they do get a ID then they are responsible for calling the initialize. If the client falls off mid transaction it should keep its own log for the operations so it knows for example that he got a ID and was busy with initializing the object.

Update is more difficult to make idempotent but a compare-and-set operation (with true/false result) will help.

Related Topic