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

Jos*_*osh 5 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"))))
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

3- def它。尽管运行create-conn实际上并没有执行任何网络活动,但我宁愿不在文件加载时运行它,如果我只是对其进行常规操作def,就会发生这种情况,因此我必须执行以下操作。请注意,该getconn功能是为了方便起见,因此我不必deref conn直接:

(def conn (delay (create-conn)))
(defn getconn [] @conn)
Run Code Online (Sandbox Code Playgroud)

4-使用atom-简单明了,但这不是需要更改的var,它会引入实际上没有必要的状态:

(def conn (atom nil))
(def getconn []
  (if-not @conn (reset! conn (create-conn)))
  @conn)
Run Code Online (Sandbox Code Playgroud)

5- [insert your idea here]

其中,我更喜欢3,因为它使用了不可变的数据,即使delay有点笨重。您是从上面的列表还是自己的解决方案中选择什么?

Ass*_*lov 1

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