使用代理完成STM交易中的副作用

发布于 2024-10-13 16:35:24 字数 296 浏览 4 评论 0原文

我知道,将具有副作用的函数放入 STM 事务中通常是不好的做法,因为它们可能会被重试和调用多次。

然而,我想到您可以使用代理来确保仅在事务成功完成后才执行副作用。

例如,

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

这是好的做法吗?

有哪些优点/缺点/陷阱?

I'm aware that it is generally bad practice to put functions with side-effects within STM transactions, as they can potentially be retried and called multiple times.

It occurs to me however that you could use agents to ensure that the side effects get executed only after the transaction successfully completes.

e.g.

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

Is this good practice?

What are the pros/cons/pitfalls?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

£烟消云散 2024-10-20 16:35:24

原文:

看来这对我应该有用。根据副作用的不同,您可能需要使用 send-off(对于 IO 密集型操作)而不是 send(对于 cpu 密集型操作)。发送/发送会将任务排入内部代理执行器池之一(有一个用于 cpu 的固定大小池和用于 io 操作的无限大小池)。一旦任务入队,工作就会脱离 dosync 的线程,因此此时您已断开连接。

当然,您需要将事务中所需的任何值捕获到发送函数中。并且您需要处理由于重试而可能多次发生的发送。

更新(参见评论):

代理在 ref 事务内发送的消息将被保留,直到 ref 事务成功完成并执行一次。因此,在我上面的回答中,发送不会多次发生,但是在引用事务期间不会发生,这可能不是您想要的(如果您希望记录或执行副作用)。

Original:

Seems like that should work to me. Depending on what your side effects are, you might want to use send-off (for IO-bound ops) instead of send (for cpu-bound ops). The send/send-off will enqueue the task into one of the internal agent executor pools (there is a fixed size pool for cpu and unbounded size pool for io ops). Once the task is enqueued, the work is off the dosync's thread so you're disconnected at that point.

You'll need to capture any values you need from within the transaction into the sent function of course. And you need to deal with that send possibly occurring multiple times due to retries.

Update (see comments):

Agent sends within the ref's transaction are held until the ref transaction successfully completes and are executed once. So in my answer above, the send will NOT occur multiple times, however it won't occur during the ref transaction which may not be what you want (if you expect to log or do side-effecty stuff).

嘴硬脾气大 2024-10-20 16:35:24

这是有效的并且是常见的做法。然而,正如亚历克斯正确指出的那样,你应该考虑送别而不是发送。

有更多方法可以捕获承诺值并将其从交易中移出。例如,您可以以向量(或地图或其他形式)返回它们。

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

或者你可以打电话重置!在本地原子上(当然是在 dosync 块的词法范围之外定义的)。

This works and is common practice. However, like Alex rightly pointed out you should consider send-off over send.

There are more ways to capture commited-values and hand them out of the transaction. For example you can return them in a vector (or a map or whatever).

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

or you can call reset! on a local atom (defined outside the lexical scope of the dosync block of course).

执手闯天涯 2024-10-20 16:35:24

使用代理并没有什么问题,但简单地返回副作用计算所需的交易值通常就足够了。

引用可能是最简洁的方法,但您甚至可以仅使用原子来管理它!

(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,但您可以将其变成一对,以便将有趣的数据从计算中传递回来。

There's nothing wrong with using agents, but simply returning from the transaction values needed for the side-effecting computation is often sufficient.

Refs are probably the cleanest way to do this, but you can even manage it with just atoms!

(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"))))

The swap! can retry as many times as needed, but the work queue will never get larger than three, and you will always print exactly once a message that is tied correctly to the acceptance of your work item. The "original design" called for just a single int in the atom, but you can turn it into a pair in order to pass interesting data back out of the computation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文