What is idempotent?
Look at what Wikipedia says:
Idempotent: Calling a method or interface multiple times does not change the business state, ensuring that the results of repeated calls are consistent with those of a single call.
Use idempotent scenarios
1. Front-end repeat submission
User registration, user creation of goods and other operations, the front end will submit some data to the background service, the background needs to create records in the database according to the data submitted by the user. If the user accidentally multiorders and the back end receives multiple commits, multiple records will be created repeatedly in the database. This is a bug caused by the lack of idempotence of interfaces.
2. Retry the interface timeout
For the interface that is called to a third party, the call may fail due to network reasons. In this case, the interface call is usually designed with the mechanism of failure retry. If a network exception occurs halfway through the first call. When called again, there will be an exception due to the presence of dirty data.
3. Message repeat consumption
When message-oriented middleware is used to process message queues and a manual ACK confirms that the message is being consumed normally. If the consumer suddenly disconnects, half-executed messages are put back on the queue.
When the message is re-consumed by other consumers, if there is no idempotent, it will lead to abnormal results in the repeated consumption of the message, such as database duplicate data, database data conflict, resource duplication and so on.
Third, solutions
1. Token mechanism implementation
The idempotent of interface is realized by token mechanism, which is a more general method.
The schematic is as follows:
Specific process steps:
-
The client will first send a request to obtain the token, and the server will generate a globally unique ID as the token stored in Redis and return the ID to the client
-
The client must carry this token the second time it invokes the business request
-
The server verifies the token. If the verification is successful, the service is executed and the token in redis is deleted
-
If the verification fails, there is no corresponding token in redis. The operation is repeated and the specified result is directly returned to the client
Note:
-
It is recommended to use Lua script to implement the code logic of whether the token exists in Redis and delete it to ensure atomicity
-
The global unique ID can be generated by Baidu’s UID-Generator and Meituan’s Leaf
2, based on mysql implementation
This implementation takes advantage of the unique index feature of mysql.
The schematic is as follows:
Specific process steps:
-
Create a table with a unique index for each field
-
The client requests the server, and the server inserts some information about the request into the table
-
Because a field in the table has a unique index, if the insert is successful, the table does not have the requested information, and the subsequent business logic is executed
-
If the insert fails, the current request has been executed and is returned directly
3. Implementation based on Redis
This implementation is based on the SETNX command
SETNX key value: Sets the value of the key to value if and only if the key does not exist. If the given key already exists, SETNX does nothing.
This command returns 1 on success and 0 on failure.
Specific process steps:
-
The client first requests the server and gets a unique field that represents the requested business
-
The field is stored in redis in the form of SETNX, and the corresponding timeout period is set according to the business
-
If the setting is successful and this proves to be the first request, the subsequent business logic is executed
-
If the setting fails, the current request has been executed and is returned directly
Four,
In fact, these several ways to achieve idempotent are much the same, similar to the use of state machine, pessimistic lock, optimistic lock way to achieve, are relatively simple.
All in all, idempotences are important when designing interfaces, especially when you are designing interfaces involving money, such as transfers and payments.