мета-данные страницы
  •  

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
agbis_idempotency [31.05.2022 10:28]
Anatoly [Обработка в агенте]
agbis_idempotency [31.05.2022 10:44]
Строка 1: Строка 1:
-====== Идемпотентность запросов ====== 
  
-Можно ознакомится со статьей от Яндекс:​ [[https://​habr.com/​ru/​company/​yandex/​blog/​442762/​]] 
----- 
-[[#​Обработка в агенте|Обработка в агенте]]\\ 
-[[#​Обработка на клиенте|Обработка на клиенте]]\\ 
-[[#​Пример МП Courier|Пример МП Courier]]\\ 
----- 
- 
-===== 1. Пишущая транзакция (на сервере) ===== 
- 
-===== Обработка в агенте ===== 
- 
-  * Новые property: 
-    * Himstat.IdempotencyText - ключ идемпотентности,​ берется из заголовка запроса и устанавливается на прямую (доступно на чтение и запись);​ 
-    * Himstat.IdempotencyHash - хэш ключ идемпотентности,​ устанавливается при установке IdempotencyText (доступно только на чтение);​ 
-  * в модуле <font color="​blue"​ size="​+1">​ErrorRes.pas</​font>​ новый тип Exception TIdenpotencyException. 
- 
- 
-**Пример запроса:​** 
- 
-<sxh Delphi> 
-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; 
-</​sxh>​ 
- 
-   ​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 ===== 
- 
-<code pascal> 
-... 
- 
-... 
-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); 
-...  
- 
-... 
-</​code>​