clojure:类似单身数据连接的最佳方法

时间:2016-09-19 17:38:18

标签: clojure

我正在创建与elasticsearch的连接(但替换您喜欢的任何其他数据源),它将基于运行时的环境或配置文件参数。它看起来像这样:

(defn create-conn
  "Connect to the given uri. This is a persistent conn managed by clj-http (apache)."
  ([uri]
   ( ;;; create a persistent connection using clj-http / elastic
   ...)
  ([]
   (create-conn (or (System/getenv "ES_URL")
                    (cfg/get-url-from-config-file)
                    "http://localhost:9200"))))

因为连接在服务器的生命周期内不会改变,所以我只需要运行一次这个函数并缓存结果。有几种方法可以做到这一点:

1 - memoize它 - 虽然这有效,但它并不像正确的方法,因为我只是缓存一件事

2 - 使用像Component或mount这样的状态管理器;因为我不是真正管理国家,只是设置和遗忘,而只是将它用于这一件事,感觉有点矫枉过正。例如,以下如何优于下面的#3?

; mount version -- good, but how is this better than #3 below?
(defstate conn :start (getconn))
(mount/start #'elastic/conn)   ;; somewhere else, must start mount

3 - def它。即使运行create-conn实际上并不执行任何网络活动,我也不会在文件加载时运行它,如果我只是在其上执行常规def会发生这种情况,所以我必须执行以下操作...请注意,getconn功能是为了方便起见,因此我不必直接deref conn

(def conn (delay (create-conn)))
(defn getconn [] @conn)

4 - 使用atom - 直截了当,但这不是一个需要改变的var,并且它引入了不必要的状态:

(def conn (atom nil))
(def getconn []
  (if-not @conn (reset! conn (create-conn)))
  @conn)

5 - [insert your idea here]

在上面,我更喜欢3,因为它使用不可变数据,即使delay有点笨拙。您的偏好是什么,无论是来自上面的列表,还是您自己的解决方案?

2 个答案:

答案 0 :(得分:2)

Mountcomponent完全是为此而构建的 - 控制状态大多是固定的(有时需要更新连接)。 Mount是两个库中最简单的一个,允许您定义连接一次,并在需要的地方“需要”它。

答案 1 :(得分:0)

即使conn在启动后没有发生变化,如果您重视集成测试,也可能希望针对不同的数据库运行相同的存储库功能。在REPL中加载项目并使用某个数据库开始提供请求后,您可能希望编写针对不同数据库运行的一些集成测试。如果您开发测试并并行处理请求,那么只是一个原子还不够好。最简单的,我认为最常用的方法是def连接并将其作为参数传递给每个存储库函数(在测试中传递另一个值)。具有动态变量和原子的东西可以导致更少的代码,但结果更复杂。