мета-данные страницы
Это старая версия документа!
Идемпотентность запросов
Можно ознакомится со статьей от Яндекс: https://habr.com/ru/company/yandex/blog/442762/
Обработка в агенте Обработка на клиенте Пример МП Courier Пример Бонусы Онлайн
1. Пишущая транзакция (на сервере)
Обработка в агенте
- Новые property:
- Himstat.IdempotencyText - ключ идемпотентности, берется из заголовка запроса и устанавливается на прямую (доступно на чтение и запись);
- Himstat.IdempotencyHash - хэш ключ идемпотентности, устанавливается при установке IdempotencyText (доступно только на чтение);
- в модуле <font color="blue" size="+1">ErrorRes.pas</font> новый тип Exception TIdenpotencyException.
Пример запроса:
procedure THimstatDM. ...(Srvr: TRtcDataServer);
var
...
begin
...
try
...
try
Himstat := THimstat(GlobalConnectionPoolClass.LockFreeConnection(THimstat)); // Получаем IdempotencyText -> (IdempotencyHash)
Himstat.IdempotencyText := Srvr.Request.Query.Text;
Himstat.Do...(SessionID, s); // Выполняем команду
finally
GlobalConnectionPoolClass.UnlockConnection(TObject(Himstat));
end;
...
except
on E: TIdenpotencyException do
begin
...
s := Format('{"error": ' + IntToStr(ErrorIdenpotency) + ', "Msg": "%s"}',
[AEncodeURL(Utf8Encode('Повторный запрос. Обновите приложение!'))]);
...
end;
on E: Exception do
begin
...
end;
end;
...
end;
procedure THimstat.Do...(const nSessionID: string; out Str: string);
var
...
begin
...
try
...
QueryIdempotency; // Записываем в таблицу Hash и время
(*
*Нет таблицы IDEMPOTENCY_QUERY - выходим из проверки
* - получаем старый ОТВЕТ (предыдущего АНАЛОГИЧНОГО запроса) ИЛИ получаем ОТВЕТ="" - Идет запись
* - ОТВЕТ=null - пытаемся записать HASH
* - ERROR Присваиваем ОТВЕТ:="" - Идет запись
* - FINALY
* - (1) ЕСЛИ ОТВЕТ=null - то у нас уникальный запрос записанный в таблицу
* - (2) ЕСЛИ ОТВЕТ<>null - запрос не уникален - возбуждаем исключение TIdenpotencyException.Create(ОТВЕТ);
*)
// Выполняем действия
...
UpdateIdempotency(Str); // сохраняем ОТВЕТ в таблицу
except
on E: TIdenpotencyException do
begin
Str := e.Message;
if Trim(Str) = '' then
raise TIdenpotencyException.Create('Нет ответа на запрос - повторяющийся запрос!');
(*
*Если исключение содержит НЕ ПУСТОЕ сообщение то это ОТВЕТ и мы его отправляем повторно
*Если исключение содержит ПУСТОЕ сообщение то снова возбуждаем TIdenpotencyException и отправляем как ошибку ОДНОВРЕМЕННОГО ЗАПРОСА (ErrorCode = 5)
*)
end;
on E: Exception do
begin
...
DeleteIdempotency; // Если ОШИБКА произошла в теле обработки до сохранения ОТВЕТа то HASH запроса удаляется
end;
end;
...
end;
Клиент
- Организовать уникальность запроса
- Обработку ErrorCode = 5
Обработка на клиенте
Пример МП Courier
... ... var PayIdempotency: string;/ /'Переменная сохраняет состояние текущей транзакции /запрос(dor_id, debet) + ответ(JSON)/' ... ... JSONAnswer := KassaFiscalRegistar.PrintFiscalCheck(JSONResultObj); / /'Печать чека' ... except on E: Exception do begin PayIdempotency := ''; / /'Если ошибка при печати - обнуляем текущую транзакцию' ... ... procedure TmoneyPayFramePanel.OnShow(curVisible: TuniFramePanel); begin PayIdempotency := '';/ / 'При входе обнуление предыдущей транзакции' ... ... ClnDM.GetFiscalCheck(sJSON, procedure (AJObject: TJSONObject) var CurrentPayIdempotency: string; / /'Текущая транзакция' begin CurrentPayIdempotency := THashMD5.GetHashString(sJSON + '=' + AJObject.ToString); if PayIdempotency = CurrentPayIdempotency then / /'Текущую сравниваем с запомненой' begin / / 'Ничего не делаем если транзакция все еще ТА ЖЕ' WriteLog("TmoneyPayFramePanel.WorkOfPay PayIdempotency = CurrentPayIdempotency Abort"); end else begin / /'Транзакция другая - выпонить оплату' PayIdempotency := CurrentPayIdempotency; AnswerFromWeb('GetFiscalCheck', '', 0, AJObject); end; end); ... ...