Параллельное управление - PullRequest
7 голосов
/ 11 марта 2011

Hello
Я хотел бы знать, как лучше всего реализовать управление параллелизмом в трехуровневом приложении? Первая мысль мая:

  1. Клиент хочет редактировать запись из набора данных.
  2. отправить запрос на сервер с запросом блокировки на эту запись
  3. сервер принимает / отклоняет запрос на редактирование на основе таблицы блокировки

На основании этого сценария блокировки должны иметь ссылку как на заблокированную запись, так и на клиента, использующего эту запись.
Клиент должен периодически отправлять сообщения поддержки активности на сервер. Поддержание активности используется для освобождения заблокированных записей в случае, если мы потеряли клиента в процессе редактирования.

Я буду использовать Delphi с datasnap. Может быть, это вопрос новичка, но я должен спросить !!

Ответы [ 3 ]

3 голосов
/ 11 марта 2011

Я опираюсь на Оптимистический контроль параллелизма jachguate ответ, чтобы ответить на вопрос, заданный в комментариях.

Я предпочитаю использовать OCC везде, где могу, потому что реализация проще.Я собираюсь поговорить о трехуровневом приложении, использующем инфраструктуру персистентности объекта .Моя предложенная схема имеет три уровня:

  1. контроль уровня строки или объекта, где уникальный идентификатор версии хранится для каждого объекта.Если вы попытаетесь обновить объект, идентификатор версии будет автоматически изменен.Если ваш идентификатор версии не совпадает с тем, что уже есть, ваше обновление завершится неудачно.

  2. блокировка на уровне поля или столбца.Вы отправляете полную копию оригинального объекта, а также обновленную.Каждое поле в вашем обновлении имеет действительные и старые значения, сравниваемые до применения нового значения.Можно попросить пользователя разрешить конфликты, а не отбрасывать их, но это становится грязным по мере увеличения объема данных в коммите.

  3. пессимистическая блокировка.Каждый объект имеет владельца блокировки, который обычно равен нулю (объект не заблокирован).Когда вы хотите редактировать объект, вы сначала блокируете его.Проблема здесь в том, что необходимо привести в порядок блокировки, а деловые правила могут быть ужасными (какой тайм-аут желателен).

Преимущество этого состоит в том, что большую часть временинедорогой путь OCC взят.Для вещей, которые случаются много, но с низкой конкуренцией, выгоды значительны.Подумайте об отслеживании товаров на складе - товары перемещаются все время, но очень редко одинаковые предметы перемещаются одновременно, и когда они это делают, решение легко (количество осталось = оригинал минус мое удаление и ваше удаление).Для сложного случая, когда (скажем) продукт перемещен, вероятно, имеет смысл заблокировать продукт, пока он находится в пути (потому что это отражает физическую ситуацию).

Когда вам приходится возвращаться к блокировке, эточасто полезно иметь возможность уведомлять обоих пользователей и иметь канал связи.По крайней мере, уведомите пользователя, который хочет блокировку, когда она будет доступна, предпочтительно разрешите ему отправить сообщение держателю блокировки и, возможно, даже разрешите ему принудительно установить блокировку.Затем уведомите проигравшего о том, что «Джо Смит взял вашу блокировку, вы потеряете свои изменения».Позвольте офисной политике разобраться в этом:)

Я обычно управляю процессом отката с помощью жалоб пользователей, а не отчетов об ошибках.Если пользователи жалуются на то, что они слишком часто теряют свои правки в конкретном процессе, измените его.Если пользователи жалуются на то, что записи блокируются слишком часто, вам придется реорганизовать сопоставления объектов, чтобы увеличить степень детализации блокировки или внести изменения в бизнес-процессы.

3 голосов
/ 11 марта 2011

Я проектирую свои приложения с учетом Оптимистического управления параллелизмом , не блокируя какую-либо запись, когда пользователь хочет ее редактировать, и не пытаясь управлять параллелизмом.

Важные вычисления и обновлениявыполняется на стороне сервера (приложение или база данных) после того, как при обработке обновлений, примененных клиентом, установлены надлежащие встроенные функции блокировки базы данных.Автоматический откат транзакции DataSnap предотвращает блокировку других пользователей в случае сбоя.

С DataSnap вы имеете полный контроль над предотвращением потери данных при столкновении правок двух пользователей, соответствующим образом используя ProviderFlags для ваших полей.Задайте pfInWhere для любого поля, которое вы хотите автоматически проверить, чтобы оно имело то же значение во время редактирования / удаления, что и при чтении записи.

Кроме того, при возникновении конфликта вы можете программно реагировать на сервере приложений (поставщикOnUpdateError), на клиенте (событие TClientDataSet OnReconcileError) или даже попросить пользователя для правильного разрешения конфликта (взгляните на ReconcileErrorDialog в репозитории New Item).

Между тем, IMHO избегает требуемой сложностиподдерживать списки блокировок, списки клиентов, списки блокировок для каждого клиента, сообщения поддержки активности, надежное восстановление после сбоя сервера приложений и все возможные сбои, которые в конечном итоге станут более чистым и лучшим решением.

0 голосов
/ 11 марта 2011

Подход, предложенный jachgate , великолепен и, вероятно, лучше, но в случае, если вы захотите реализовать это, вам понадобится TThreadList на сервере, который создается при запуске службы.Используйте TThreadList, потому что это потокобезопасно.Вы можете использовать TThreadList для каждой таблицы, чтобы свести к минимуму снижение производительности при навигации по спискам.Чтобы управлять тем, что заблокировано, вам понадобится объект, который создан и передан в список

  TLockedItem = class(TObject)
  public
    iPK: Integer;
    iClientID: Integer;
  end;

Чтобы сделать фактическую блокировку, вам потребуется что-то вроде этого:

function LockItem(pPK, pClientID: Integer): Boolean;
var
  oLockedItem: TLockedItem;
  oInternalList: TList;
  iCont: Integer;
  bExists: Boolean;
begin
  bExists := False;
  if (Assigned(oLockedList)) then
  begin
    oInternalList := oLockedList.LockList;
    try
      if (oInternalList.Count > 0) then
      begin
        iCont := 0;
        while ((not bExists) and (iCont < oInternalList.Count)) do
        begin
          oLockedItem := TLockedItem(oInternalList[iCont]);
          if (oLockedItem.iPK = pPk) then
            bExists := True
          else
            Inc(iCont);
        end;
      end;
    finally
      oLockedList.UnlockList;
    end;
    if (not bExists) then
    begin
      oLockedItem := TLockedItem.Create;
      oLockedItem.iPK := pPK;
      oLockedItem.iClientID := pClientID;
      oInternalList := oLockedList.LockList;
      try
        oInternalList.Add(oLockedItem);
      finally
        oLockedList.UnlockList;
      end;
    end;
  end;
  Result := bExists;
end;

Это просто идея того, что вам нужно.Вы должны будете сделать метод разблокировки с подобной логикой.Вам, вероятно, понадобится список клиентов, в котором будет храниться точка каждого TLockItem, хранящегося у каждого клиента, в случае потери соединения.Это не окончательный ответ, просто толчок в направлении, если вы хотите реализовать этот подход.
Удачи

...