мета-данные страницы
Это старая версия документа!
Идемпотентность запросов
Можно ознакомится со статьей от Яндекс: 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;
THimstatDM.ReplyWithPayPlanOrders - Himstat.IdempotencyText := Srvr.Request.Query.Text; Получаем IdempotencyText IdempotencyHash - Himstat.DoPayPlanOrders - QueryIdempotency Записываем в таблицу Hash и время - Нет таблицы IDEMPOTENCY_QUERY - выходим из проверки - получаем старый ОТВЕТ (предыдущего АНАЛОГИЧНОГО запроса) ИЛИ получаем ОТВЕТ="" - Идет запись - ОТВЕТ=null - пытаемся записать HASH - ERROR Присваиваем ОТВЕТ:="" - Идет запись - FINALY - (1) ЕСЛИ ОТВЕТ=null - то у нас уникальный запрос записанный в таблицу - (2) ЕСЛИ ОТВЕТ<>null - запрос не уникален - возбуждаем исключение TIdenpotencyException.Create(ОТВЕТ); - (1) UpdateIdempotency сохраняем ОТВЕТ в таблицу - (2) TIdenpotencyException - Если исключение содержит НЕ ПУСТОЕ сообщение то это ОТВЕТ и мы его отправляем повторно - Если исключение содержит ПУСТОЕ сообщение то снова возбуждаем TIdenpotencyException и отправляем как ошибку ОДНОВРЕМЕННОГО ЗАПРОСА (ErrorCode = 5) - Если ОШИБКА произошла в теле обработки до сохранения ОТВЕТа то HASH запроса удаляется
Клиент
- Организовать уникальность запроса
- Обработку 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); ... ...