Использование агентов для выполнения побочных эффектов в транзакциях STM

Я знаю, что, как правило, нежелательно размещать функции с побочными эффектами в транзакциях STM, поскольку они потенциально могут быть повторены и вызваны несколько раз.

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

e.g.

(dosync
  // transactional stuff
  (send some-agent #(function-with-side-effects params))
  // more transactional stuff
  )

Это хорошая практика?

Какие плюсы/минусы/подводные камни?


person mikera    schedule 22.01.2011    source источник
comment
Одной из основных идей STM является атомарность отказов. Как это поможет в этом?   -  person Sai Venkat    schedule 22.01.2011
comment
Дело в побочных эффектах, которые должны возникать после успешной транзакции, но сами по себе не являются частью транзакции, например. отправка письма с подтверждением. Очевидно, вы не хотите делать это каждый раз, когда транзакция повторяется, иначе вы можете получить очень рассерженного / сбитого с толку получателя!   -  person mikera    schedule 23.01.2011


Ответы (3)


Оригинал:

Кажется, это должно сработать для меня. В зависимости от того, каковы ваши побочные эффекты, вы можете использовать отправку (для операций, связанных с вводом-выводом) вместо отправки (для операций, связанных с процессором). Отправка/отправка поставит задачу в очередь в один из внутренних пулов исполнителей агента (существует пул фиксированного размера для процессора и пул неограниченного размера для операций ввода-вывода). Как только задача поставлена ​​в очередь, работа отключается от потока dosync, поэтому в этот момент вы отключаетесь.

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

Обновление (см. комментарии):

Отправки агента в рамках транзакции ссылки удерживаются до тех пор, пока транзакция ссылки не будет успешно завершена, и выполняются один раз. Таким образом, в моем ответе выше отправка НЕ ​​будет происходить несколько раз, однако она не будет происходить во время транзакции ref, что может быть не тем, что вы хотите (если вы ожидаете ведения журнала или выполнения побочных эффектов).

person Alex Miller    schedule 22.01.2011
comment
@mikera, @Alex, отправка гарантированно произойдет только один раз после успешной транзакции и не должна повторяться несколько раз. (Утверждение 5 в clojure.org/agents) - person bmillare; 22.01.2011
comment
@bmillare - я думаю, ты говоришь о чем-то другом. Этот оператор относится к тому, как будет выполняться функция, переданная при отправке/отправке. Я имею в виду, что если сделать этот вызов внутри транзакции ref, транзакция ref может быть повторена, вызывая несколько вызовов отправки/отправки, каждый из которых выполняется ровно один раз. - person Alex Miller; 22.01.2011
comment
@alex, извините, я указал не на тот момент, далее в тексте говорится, что агенты интегрированы с STM - любые отправки, сделанные в транзакции, удерживаются до тех пор, пока она не будет зафиксирована, и отбрасываются, если она повторяется или прерывается. - person bmillare; 23.01.2011
comment
@bmillare хммм .... я понимаю твою точку зрения. Я все еще не уверен, что согласен с тем, что произойдет, поскольку я не знаю, что отправка транзакции ссылки будет иметь такое же ограничение. Может быть, пришло время написать небольшой код, чтобы узнать... - person Alex Miller; 23.01.2011
comment
@bmillare действительно, ты прав. Агент, отправленный из транзакции ref, будет задержан и выполнен в конце успешной транзакции ref. Итак, @mikera, мой первоначальный ответ выше был неверным. - person Alex Miller; 23.01.2011
comment
Спасибо, ребята - очень полезно и, похоже, делает именно то, что я хочу (т.е. побочный эффект срабатывает только один раз, если и только если транзакция прошла успешно) - person mikera; 23.01.2011

Это работает и является обычной практикой. Однако, как справедливо заметил Алекс, вам следует подумать об отправке, а не об отправке.

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

(let [[x y z] (dosync
                ; do stuff
                [@x @y @z])] ; values of interest to sode effects
  (side-effect x y z))

или вы можете вызвать сброс! на локальном атоме (конечно, за пределами лексической области действия блока dosync).

person cgrand    schedule 23.01.2011

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

Рефы, вероятно, самый чистый способ сделать это, но вы даже можете справиться с этим, используя только атомы!

(def work-queue-size (atom [0]))

(defn add-job [thunk]
  (let [[running accepted?]
        (swap! work-queue-size
               (fn [[active]]
                 (if (< active 3)
                   [(inc active) true]
                   [active false])))]
    (println
     (str "Your job has been "
          (if accepted?
            "queued, and there are "
            "rejected - there are already ")
          running
          " total running jobs"))))

swap! может повторять столько раз, сколько необходимо, но рабочая очередь никогда не превысит трех, и вы всегда будете печатать ровно один раз сообщение, правильно привязанное к принятию вашего рабочего элемента. «Первоначальный дизайн» требовал всего одного int в атоме, но вы можете превратить его в пару, чтобы передать интересные данные обратно из вычислений.

person amalloy    schedule 12.05.2011