Created
September 19, 2014 17:10
-
-
Save drbobbeaty/d7e7e13ae0e83dc7658b to your computer and use it in GitHub Desktop.
Bucketing Service code for experiment variants
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns finch-experiments.bucket | |
"Namespace to deal with all the bucketing issues with the experiments and | |
users so that we can respond as the finch client would." | |
(:require [clj-endpoints :as ep] | |
[clj-endpoints.logging :refer [log-execution-time!]] | |
[clj-endpoints.util :refer [md5 to-uuid nil-if-empty compact]] | |
[finch-experiments.routing :as routing] | |
[finch-experiments.web :as web] | |
[finch-experiments.data :refer [experiment-list]])) | |
(def min-in-millis (* 1000 60)) | |
(defn make-bucket | |
"Function to take a UUID and a layer name and use the same method as the finch | |
code does now to compute a bucket number for this UUID." | |
[id & [ln]] | |
(if id | |
(let [hv (md5 (str id "," (or ln "layer-name-for-test")))] | |
(mod (Long/parseLong (.substring hv 24) 16) 1000)))) | |
(defn grab-config | |
"Function to get a map of all the *active* experiment config data. This is | |
the core of the bucketing as we need to know the layer, the experiment, and | |
the buckets in order to make a decision about any one person. This will be | |
cached for 5 mins at a time to save load on the services and speed access." | |
[cluster] | |
(let [cfg (ep/config cluster) | |
exps (experiment-list)] | |
(merge (select-keys (web/get-config cfg) exps) | |
(select-keys (routing/get-config cfg) exps)))) | |
(defn- variant-for-population | |
"Function to take a bucket number, a population, a sequence of variants, | |
and a country code and return the appropriate variant for this bucket in | |
this population - given that the bucket is in the experiment. If not, | |
this function will return `nil`." | |
[b p vs cc] | |
(if (and b p vs cc) | |
(let [cvs (count vs) | |
roll (nil-if-empty (:rollout p)) | |
ok-cc? ((set (:countries p)) cc) | |
[lbn hbn] (:buckets p)] | |
(or roll | |
(if (and ok-cc? (<= lbn b hbn)) | |
(nth vs (mod (- b lbn) cvs) nil)))))) | |
(defn to-variants | |
"Function to take a very brute-force approach to the bucketing of a user | |
into the active experiments. This looks at all the configured experiments | |
from all sources, and then buckets the privided user's b-cookie into each | |
of the experiments and returns a list of all the experiments that the user | |
is involved in (and it's not all of them) - based on the variants and the | |
bucketing of the b-cookie." | |
[cluster bcookie cc] | |
(let [cfg (grab-config cluster) | |
bId (to-uuid bcookie)] | |
(compact (for [[en edata] cfg | |
:when (:unique edata) | |
:let [ln (:layer-name edata) | |
vns (:variant-names edata) | |
bn (make-bucket bId ln)] | |
pd (:populations edata) | |
:let [vn (variant-for-population bn pd vns cc)]] | |
(if vn | |
{ :layer ln | |
:experiment en | |
:bucket bn | |
:variant vn }))))) | |
(log-execution-time! to-variants {:msg-fn (fn [ret c bc cc] (format "(%s) %s in %s" c bc cc))}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment