deft/tracker.tmp92sD1A.org

126 lines
4.8 KiB
Org Mode
Raw Normal View History

2020-11-09 15:07:43 +00:00
# Created 2020-11-09 Mon 16:07
#+TITLE: Work Time Tracker
#+AUTHOR: Yann Esposito
* IN-PROGRESS Write issue about config.edn simplification :work:
[2020-11-09 Mon 15:24]
** Problem
we have a lot of duplication between our =config.edn= files in
=tenzin-config=.
This make it easy to be wrong while we copy (5 times) similar values.
We would like to make those configuration file a lot shorter and easier to
manage by only puting into them the field that are really different between
all environment.
** A little bit of short-time history.
At first we relied on a single template that used different =.json=
entries.
Which is kind of duplicating the =config.edn= by adding a layer of logic in
the middle.
After lot of confusions and bugs, we decided (both devs and ops) to duplicate
the config.edn and minimize the amount of templating in the process.
** Abstract of previous discussions
If you look at the problem, there are plenty of different solutions to
handle that.
Here are what we thought about:
1. Use a better templating system than jinja. The best in class in my
opinion (and by far) is dhall.
The issue with dhall is that it is still put a limitation about how we
generate the =config.edn= and this is also another new language to
learn.
2. Use a Clojure project to handle =config.edn= templating/generation.
Mainly re-write a dhall-like project in Clojure better suited for our need.
3. Use another =ConfigService= that would take care of some logic in Clojure.
4. Make every service to use better default values.
So typically some service will depend on some `tk-store` that are named
by something and generally the service is also responsible to know the
intended store to be used. Typically the service know that it should
rely on postgres and not redis nor RAM-only store.
The issue is the service will need to declare its expected configuration
to other services (typically to tk-stores). So we should move from
=init= to =start=. Every of our service should declare its default
config to a centralized =IROHConfigService= during =init= phase.
Then every service should initialize its context during the
=start= phase during which we should use a function similar to
=get-in-config= but from =IROHConfigService= and not TK =ConfigService=.
From an architecture standpoint the conclusion was to prefer the 4th choice
(which would not be incompatible with the 3rd.)
Mainly the service start to take responsibility from some dependencies.
Also the =IROHConfigService= could take care of potential configuration
conflict.
If two services want different value for the same field we should make the
configuration fail (unless the configuration is overwritten in =config.edn=)
** High level technical spec
So technically most (all?) our services have a =init-context= function.
Generally this function use =get-in-config= from TK =ConfigService=.
Also the =init-context= is called in the =init= phase.
We should instead make all our service dependant on the new =IROHConfigService=.
And during the =init= phase the service should send its default
configuration to the =IROHConfigService=.
Example:
#+begin_src clojure
(ns iroh.my-service
(:require [iroh.my-service.core :as core],,,))
(defprotocol MyServiceProt ,,,)
(tk/defservice
[[:IROHConfigService declare-default-conf get-in-conf]]
(init [this context]
(core/init-conf declare-default-conf))
(start [this context]
(into context
(core/start-context get-in-conf ,,,)))
,,,)
(ns iroh.my-service.core ,,,)
(def default-config
{:stores {"foo" {:type :postgres :conf {:table-name "foo"}}
"my-service-cache" {:type :redis :conf {:db 1}}}
:my-service {:default-timeout 3000}})
(defn init-conf
[declare-default-conf]
(declare-default-conf default-config))
(defn get-in-conf
[set-default-config
get-in-config path]
(or (get-in-config path) (get-in default-config path)))
(defn ^:always-validate start-context :- MyServiceContext
[get-in-conf ,,,]
(let [foo (get-in-conf [:my-service :default-timeout])]
))
#+end_src
So the =IROHConfigService= should take care of doing a =deep-merge-with
concat= on all default configurations and the config from =ConfigService=.
It should also throw an exception in case of configuration conflict.
If two services do not agree on the value of some inner field.
Doing so, we should better separate the developer concerns from the ops
concerns.
Currently the ops are responsible to select the type of database, the
default routes.
While this should be configurable and at the same time not the matter of
the ops.
** Last word
This Epic is just here to keep track of the discussions about that
recurring subject and make a proposal of solution.
The objective would be to reach a consensus on a the best way to handle
config.edn simplification and prevent duplication.