Ramkumar K. discusses idempotent services, and he later expands on the concept. Very informative. There was an interesting bit at the end:
One reader, by the name of David Taylor, mentioned that in their domain they follow a different approach to implement idempotent behavior. Apparently, in their domain, most of the messages contain some unique message identifier. For instance, they have used timestamps as a means to uniquely identify the message. Here is his message.In my industry, we use an API called EPP for Domain Name provisioning (an IETF standard). In that case the protocol has been designed to be idempotent purely by API design (rather than sending unique identifiers).
An example being that when you renew a domain, you need to supply the current expiry date, so if you send a command to renew for 1 year (and then repeat the message) you dont end up with a 2 year renewal.
So the API designers have specifically designed each operation as idempotent without requiring a unique client or server identifier. It words quite well because you dont have to cache the unique ids.
I previously recommended the most excellent book Enterprise Integration Patterns. One of the patterns, aptly titled "Idempotent Receiver" deals explicitly with idempotency in the context of messaging, and discusses both the use of unique message identifiers and idempotent interfaces.
Reading that post digs up some [read: dorky] memories, both from college and work related to idempotent functions. The work related one was a much better memory, because it challenged me to dig back into the math that I had left behind when I decided to do software for a living so that I could speak intelligently on idempotent functions. Suffice it to say that the years had not been so good to my memory, but the refresher was good for my soul.
Idempotency has its roots in mathematics, and has a definition something like:
For any A, A is idempotent if A*A = A
This is commonly referenced in computing literature in the following form:
give a function f, f is idempotent if f(f(x)) = f(x)
another way to state that (which is the way a good partner from a good company stated in the aforementioned work-related memory): for each y in the range of f, f(y) = y
All of those definitions make thinking about designing idempotent services very difficult, if not down-right confusing. First, functions do not have side-effects, but services often do. Services that don't are idempotent. Services that insert and update records into databases have side-effects. Are they idempotent? Answer: Depends.
Second, it is difficult to ascertain what exactly most computer scientists, software engineers, hackers, whatever would consider the domain versus the range for a service, and what is an element of the range, and what does it mean to pass that element to the function?
Is it the document passed to the service? Is the entire state of the system? Is it the subset of the entire state of the system to which the caller of the service has access and influence?
Consider the following soap body as message to a service and then you tell me what is domain, what is range, and whether it is idempotent:
<hire>
<employee name="Jef"/>
</hire>
Inquiring minds want to know.
In computer science, idempotency is often simply reduced to "If you do the same thing multiple times, you get the same result" OR "If you do an operation more than once, it only has the effect of a single operation."
Now.
Back to Ram's article. David Taylor mentioned the domain renewal message and that it was idempotent. The specification he was refering to is RFC 3731: Extensible Provisioning Protocol Domain Name Mapping. In particular, he was refering to the 3.2.2, the EPP <renew> command.
Here is an [slightly modified] example from the RFC:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0
epp-1.0.xsd">
<command>
<renew>
<domain:renew
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
xsi:schemaLocation="urn:ietf:params:xml:ns:domain-1.0
domain-1.0.xsd">
<domain:name>example.com</domain:name>
<domain:curExpDate>2000-04-03</domain:curExpDate>
<domain:period unit="y">5</domain:period>
</domain:renew>
</renew>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
Note how the request accepts a domain name, a current expiration date, and a renewal period. The "current expiration date" reminds me of implementing optimistic locking to manage concurrency issues in database updates (which reminds me, I need to do another Recommending Reading entry).
This operation is idempotent because the system ignores renewals where the passed in "current expiration date" is invalid.
It would be easy to screw up designing this interface. Some alternative interfaces include:
Renew(domain, amountOfTime) -- resends of the same message result in unintended renewals
RenewUntil(domain, aDateInTheFuture) -- this could arguably be considered idempotent, but the result might not be what was expected, especially if the system doesn't guard against the current expiration date being greater than the new renewal date.
Recent Comments