Today we are going to talk about idempotency of interfaces.

What is idempotency

Idempotent means that any number of executions have the same effect as a single execution.

In restful specifications, common requests and interface idempotent relationships are as follows:

Request way operation Whether the power etc.
GET Query data is
POST The new data no
PUT Update the data Directly updated to a value that is idempotent, for example, set a = 1; The update of the accumulation operation does not meet the requirements, for example, set a = a+1
DELETE Delete the data Delete according to unique condition, meet idempotent; Otherwise, the data is idempotent. For example, after a batch of data is deleted according to a condition, a new data meeting the condition is added and deleted again. In this case, the new data is deleted

Why does interface idempotence arise

In computer applications, network jitter, temporary failures, or service invocation failures may occur, especially in distributed systems, where interface invocation failures are more common. In order to ensure the integrity of the service, we may initiate retry calls to the interface. If the interface does not handle idempotent, it can have a big impact on the system, so the idempotent design of the interface is especially important.

In the service, the idempotent is usually repeated requests of the interface. Repeated requests refer to the same request being submitted several times for some reason. There are several common scenarios for this to happen:

  1. Front-end repeated submission: when submitting a form, users may fail to respond successfully in time due to network fluctuations, causing users to think that the form has not been submitted successfully, and then click the submit button all the time. In this case, repeated submission request will occur.

  2. Interface timeout retry: When a third party invokes an interface, a retry mechanism is added to prevent request failures due to exceptions such as timeout. As a result, one request is submitted multiple times.

  3. Repeated consumption of messages: When using MQ messaging middleware, repeated consumption occurs if the messaging middleware fails to submit consumption information in a timely manner.

Idempotent solution

So how can we ensure that interfaces are idempotent?

Think about it. In the first scenario, since repeated submission is caused by users, we can find a way to make it impossible for users to repeat submission.

Plan 1: front-end control

Block the front end, such as graying or hiding the button after a single click. But often the front end is unreliable, and the back end is more reliable.

Solution 2: Token mechanism

When entering the form page, the user first invokes the background interface to obtain the token and store it in REDis. When the user submits the form, the token is also used as an input parameter. The backend deletes the token in REDis first.

The reason why we don’t check whether the token exists in Redis before deleting it is to ensure the atomicity of the operation. In extreme cases, the first request finds the token exists in Redis, but before deleting it, the second request also finds the token exists in Redis. That still creates the problem of duplicate submissions.

The token mechanism requires the interface to obtain the token first, which is obviously not appropriate in some cases. Most of our requests are going to the database, so we can start with the database.

Scheme 3. Unique index

This approach is easier to understand. Using a unique index prevents the addition of dirty data, and the database throws exceptions when inserting duplicate data, ensuring that the data is unique. Unique indexes support insert, update, and delete operations.

Scheme four, pessimistic lock

Pessimistic locking is based on the database level. When data is acquired, it is locked. When there are multiple repeated requests at the same time, other requests cannot operate. Pessimistic locking only applies to update operations.

/ / such asselect name from t_goods where id=1 for update;
Copy the code

Note:The id field must be a primary key or unique index, otherwise the entire table will be locked, which will kill people. Pessimistic locking is generally used together with transactions, and the data locking time may be very long. Select it according to the actual situation.

In the case of a large number of requests, the pessimistic lock is obviously not appropriate, so it is time to optimistic lock.

Plan five, optimistic lock

You can use the version number to add a version field to the table. When the data needs to be updated, you can obtain the current version number from the database.

select version from t_goods where id=1
Copy the code

When updating data, compare the version number first. If the version number is not the same, it indicates that there are other requests to update data and the update fails.

update t_goods set count=count+1.version=version+1 where version=#{version}
Copy the code

The other is through a state machine, which is actually the principle of optimistic locking. This approach is suitable for stateful flow situations, such as the creation and payment of an order, which must be created before the payment is made. In this case, we can achieve idempotency by designing the status field with an int and the size of the value type.

update t_goods set status=#{status} where id=1 and status<#{status}
Copy the code

Similarly, optimistic locks are only available for update operations.

Scheme 6, distributed lock

Sometimes our business involves more than just operating databases, but also sending SMS, messages, etc., so database level locking is not appropriate. In this case, code-level locking should be considered, and Java’s built-in locks are not suitable for distributed cluster deployment scenarios, so distributed locks can be used (Redis or Zookeeper).

Take Redis distributed lock for example, if an order initiates a payment request, the payment system will check whether there is a Key of the order number in the Redis cache. If there is no Key, the Key will be inserted into Redis as the order number. Check whether the order has been paid, if not, the payment will be made, and delete the Key of the order number after the payment is completed. Distributed locking is achieved through Redis, until this order payment request is completed, the next request can come in. Of course, you need to set a Key expiration time and delete the Redis Key when exceptions occur.

conclusion

Idempotency of interfaces is a common problem. You need to choose a proper solution based on specific service scenarios.

END

Phase to recommend

Distributed transaction solutions that you must understand

Is this it? Distributed ID transmitter actual combat

Some knowledge of design patterns in factory patterns

Is this it? Spring transaction failure scenarios and solutions

Is this it? An article will help you read Spring transactions