Skip to content

Commit ca363f4

Browse files
committed
Initial code
0 parents  commit ca363f4

10 files changed

+482
-0
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.clje linguist-language=Clojure

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
_build/
2+
_checkouts/
3+
rebar.lock
4+
ebin/

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Selling
2+
3+
Selling is an implementation of [riemann.io](http://riemann.io) index and streams using [clojerl](http://clojerl.org).
4+

rebar.config

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{deps, [
2+
{clojerl, {git, "https://github.com/clojerl/clojerl", {branch, "master"}}}
3+
]
4+
}.
5+
6+
{plugins, [
7+
{'rebar3_clojerl', {git, "https://github.com/clojerl/rebar3_clojerl", {branch, "master"}}}
8+
]
9+
}.
10+

src/selling.app.src

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{ application, 'selling'
2+
, [ {vsn, git}
3+
, {description, "A Riemann.io core processing implementation"}
4+
, {modules, []}
5+
, { applications
6+
, [ stdlib
7+
, kernel
8+
, clojerl
9+
]
10+
}
11+
, {mod, {'selling.app', []}}
12+
]
13+
}.

src/selling/common.clje

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
(ns selling.common
2+
"Utility functions. Time/date, some flow control constructs, protocol buffer
3+
definitions and codecs, some vector set ops, etc."
4+
(:use [selling.time :only [unix-time]]))
5+
6+
(defn pkey
7+
"Primary key for an event."
8+
[event]
9+
[(:host event) (:service event)])
10+
11+
(defn expire
12+
"An expired version of an event."
13+
[event]
14+
(into (select-keys event [:host :service])
15+
[[:time (unix-time)] [:state "expired"]]))
16+
17+
(defrecord Event [host service state description metric tags time ttl])
18+
19+
(defn event
20+
"Create a new event from a map."
21+
[opts]
22+
(let [t (int (erlang/round (or (opts :time)
23+
(unix-time))))]
24+
(map->Event (merge opts {:time t}))))

src/selling/folds.clje

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
(ns selling.folds
2+
"Functions for combining states.
3+
4+
Folds usually come in two variants: a friendly version like sum, and a strict
5+
version like sum*. Strict variants will throw when one of their events is
6+
nil, missing a metric, or otherwise invalid. In typical use, however, you
7+
won't *have* all the necessary information to pass on an event. Friendly
8+
variants will do their best to ignore these error conditions where sensible,
9+
returning partially complete events or nil instead of throwing.
10+
Called with an empty list, folds which would return a single event return
11+
nil."
12+
(:use [selling.common])
13+
(:refer-clojure :exclude [count]))
14+
15+
(defn sorted-sample-extract
16+
"Returns the events in seqable s, sorted and taken at each point p of points,
17+
where p ranges from 0 (smallest metric) to 1 (largest metric). 0.5 is the
18+
median event, 0.95 is the 95th' percentile event, and so forth. Ignores
19+
events without a metric."
20+
[s points]
21+
(let [sorted (sort-by :metric (filter :metric s))]
22+
(if (empty? sorted)
23+
'()
24+
(let [n (clojure.core/count sorted)
25+
extract (fn [point]
26+
(let [idx (min (dec n) (int (math/floor (* n point))))]
27+
(nth sorted idx)))]
28+
(map extract points)))))
29+
30+
(defn sorted-sample
31+
"Sample a sequence of events at points. Returns events with service remapped
32+
to (str service \" \" point). For instance, (sorted-sample events [0 1])
33+
returns a 2-element seq of the smallest event and the biggest event, by
34+
metric. The first has a service which ends in \" 0\" and the second one ends
35+
in \" 1\". If the points is a map, eg (sorted-sample events {0 \".min\" 1
36+
\".max\"}, the the values will be appened to the service name directly.
37+
Useful for extracting histograms and percentiles.
38+
39+
When s is empty, returns an empty list."
40+
[s points]
41+
(let [[points pnames] (if (vector? points)
42+
[points (map #(str " " %) points)]
43+
(apply map vector points))]
44+
(map (fn [pname event]
45+
(assoc event :service
46+
(str (:service event) pname)))
47+
pnames
48+
(sorted-sample-extract s points))))
49+
50+
(defn non-nil-metrics
51+
"Given a sequence of events, returns a compact sequence of their
52+
metrics--that is, omits any events which are nil, or have nil metrics."
53+
[events]
54+
(keep (fn [event]
55+
(when-not (nil? event)
56+
(:metric event)))
57+
events))
58+
59+
(defn fold*
60+
"Fold with a reduction function over metrics. Throws if any event or metric
61+
is nil."
62+
[f events]
63+
(assoc (first events) :metric (reduce f (map :metric events))))
64+
65+
(defn fold
66+
"Fold with a reduction function over metrics. Ignores nil events and events
67+
with nil metrics.
68+
69+
If there are *no* non-nil events, returns nil."
70+
[f events]
71+
(when-let [e (some identity events)]
72+
(assoc e :metric (reduce f (non-nil-metrics events)))))
73+
74+
(defn fold-all
75+
"Fold with a reduction function over metrics.
76+
77+
If the first event has a nil :metric, or if any remaining event is nil, or
78+
has a nil metric, returns the first event, but with :metric nil and a
79+
:description of the error.
80+
81+
If the first event is nil, returns nil."
82+
[f events]
83+
(when-let [e (first events)]
84+
(try
85+
(assoc e :metric (reduce f (map :metric events)))
86+
(catch _ ex
87+
(merge e
88+
{:metric nil
89+
:description "An event or metric was nil."})))))
90+
91+
(defn sum*
92+
"Adds events together. Sums metrics, merges into first of events."
93+
[events]
94+
(fold* + events))
95+
96+
(defn sum
97+
"Adds events together. Sums metrics, merges into first event with a metric,
98+
ignores nil events and nil metrics."
99+
[events]
100+
(fold + events))
101+
102+
(defn product*
103+
"Multiplies events. Returns the first event, with its metric multiplied by
104+
the metrics of all other events."
105+
[events]
106+
(fold* * events))
107+
108+
(defn product
109+
"Multiplies events. Returns the first event with a metric, with its metric
110+
being the product of all events with metrics."
111+
[events]
112+
(fold * events))
113+
114+
(defn difference*
115+
"Subtracts events. Returns the first event, with its metric reduced by the
116+
metrics of all subsequent events."
117+
[events]
118+
(fold* - events))
119+
120+
(defn difference
121+
"Subtracts events. Returns the first event, with its metric reduced by the
122+
metrics of all subsequent events with metrics. Returns nil if the first event
123+
is nil, or its metric is nil."
124+
[events]
125+
(fold-all - events))
126+
127+
(defn quotient*
128+
"Divides events. Returns the first event, with its metric divided by the
129+
product of the metrics of all subsequent events. Like quotient, but throws
130+
when any metric is nil or a divisor is zero."
131+
[events]
132+
(fold* / events))
133+
134+
(defn quotient
135+
"Divides events. Returns the first event, with its metric divided by the
136+
product of the metrics of all subsequent events."
137+
[events]
138+
(when-let [event (first events)]
139+
(try
140+
(fold-all / events)
141+
(catch _ e
142+
(merge event
143+
{:metric nil
144+
:description "Can't divide by zero"})))))
145+
146+
(defn quotient-sloppy
147+
"Like quotient, but considers 0/0 = 0. Useful for relative rates, when you
148+
want the ratio of two constant values to be zero."
149+
[events]
150+
(if (and (first events)
151+
(some zero? (map :metric events)))
152+
(assoc (first events) :metric 0)
153+
(quotient events)))
154+
155+
(defn mean
156+
"Averages events together. Mean metric, merged into first of events. Ignores
157+
nil events and nil metrics."
158+
[events]
159+
(when-let [e (some identity events)]
160+
(let [metrics (non-nil-metrics events)]
161+
(when (seq metrics)
162+
(assoc e :metric (/ (reduce + metrics)
163+
(clojure.core/count metrics)))))))
164+
165+
(defn adjust-occurence
166+
[state {:keys [metric] :as event}]
167+
(update-in state [metric] conj event))
168+
169+
(defn modes
170+
"Keep track of repeating metrics. Yields a sequence of events with the highest
171+
occuring metrics."
172+
[events]
173+
(let [occurs (reduce adjust-occurence {} events)
174+
freqs (for [o (vals occurs)] [(clojure.core/count o) (first o)])]
175+
(->> (sort-by first > freqs)
176+
(partition-by first)
177+
(first)
178+
(map second))))
179+
180+
(defn mode
181+
"Yield any mode returned by modes."
182+
[events]
183+
(first (modes events)))
184+
185+
(defn median
186+
"Returns the median event from events, by metric."
187+
[events]
188+
(first (sorted-sample-extract events [0.5])))
189+
190+
(defn extremum
191+
"Returns an extreme event, by a comparison function over the metric."
192+
[comparison events]
193+
(reduce (fn [smallest event]
194+
(cond (nil? (:metric event)) smallest
195+
(nil? smallest) event
196+
(comparison (:metric event) (:metric smallest)) event
197+
:else smallest))
198+
nil
199+
events))
200+
201+
(defn minimum
202+
"Returns the minimum event, by metric."
203+
[events]
204+
(extremum <= events))
205+
206+
(defn maximum
207+
"Returns the maximum event, by metric."
208+
[events]
209+
(extremum >= events))
210+
211+
(defn std-dev
212+
"calculates standard deviation across a seq of events"
213+
[events]
214+
(when-let [e (some identity events)]
215+
(let [
216+
samples (non-nil-metrics events)
217+
n (clojure.core/count samples)
218+
mean (/ (reduce + samples) n)
219+
intermediate (map #(math/pow (- %1 mean) 2) samples)]
220+
(assoc e :metric (math/sqrt (/ (reduce + intermediate) n))))))
221+
222+
(defn count
223+
"Returns the number of events."
224+
[events]
225+
(let [events (remove nil? events)]
226+
(if-let [e (first events)]
227+
(assoc e :metric (clojure.core/count events))
228+
(event {:metric 0}))))

src/selling/time.clje

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(ns selling.time
2+
"Clocks and scheduled tasks. Provides functions for getting the current time.")
3+
4+
(defn unix-time-real
5+
"The current unix epoch time in seconds, taken from
6+
System/currentTimeMillis."
7+
[]
8+
(/ (erlang/system_time :millisecond) 1000))
9+
10+
(def unix-time unix-time-real)

test/selling/common_test.clje

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(ns selling.common-test
2+
(:require [selling.common :refer :all]
3+
[clojure.test :refer :all]))
4+

0 commit comments

Comments
 (0)