8000 Indexed sessions by athos · Pull Request #12 · athos/Postmortem · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Indexed sessions #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ A tiny data-oriented debugging tool for Clojure(Script), empowered by transducer
- [Handling sessions](#handling-sessions)
- [Attaching a transducer](#attaching-a-transducer)
- [`void-session`](#void-session)
- [Indexed sessions](#indexed-sessions)
- [`make-unsafe-session`](#make-unsafe-session)
- [Simple logger](#simple-logger)
- [Instrumentation](#instrumentation)
Expand Down Expand Up @@ -671,6 +672,91 @@ Using it together with `with-session` disables logging temporarily:
;=> [1 4]
```

#### Indexed sessions

When dealing with multiple log entries, it is sometimes useful to have a sequential
number (or index) for each log item throughout all the entries.

An *indexed session* automatically adds an auto-increment index to each log item.
To create a new indexed session, use `make-indexed-session`:

```clojure
(pm/set-current-session! (pm/make-indexed-session))

(pm/spy>> :foo 100)
(pm/spy>> :bar 101)
(pm/spy>> :foo 102)

(pm/logs)
;=> {:foo [{:id 0 :val 100}
; {:id 2 :val 102}]
; :bar [{:id 1 :val 101}]}
```

Calling `reset!` on the indexed session resets the index:

```clojure
(pm/spy>> :foo 100)
(pm/spy>> :foo 101)
(pm/log-for :foo)
;=> [{:id 0 :val 100} {:id 1 :val 101}]

(pm/reset!)

(pm/spy>> :foo 102)
(pm/spy>> :foo 103)
(pm/log-for :foo)
;=> [{:id 0 :val 102} {:id 1 :val 103}]
```

`make-indexed-session` takes an optional function to specify how the indexed
session will attach the index to each log item.

The function must take two arguments, the index and the log item, and return
a new log item. The default function is `(fn [id item] {:id id :val item})`.

The example below shows how it takes effect:

```clojure
(pm/set-current-session!
(pm/make-indexed-session (fn [id item] [id item])))

(pm/spy>> :foo :a)
(pm/spy>> :foo :b)
(pm/spy>> :foo :c)
(pm/log-for :foo)
;=> [[0 :a] [1 :b] [2 :c]]

(pm/set-current-session!
(pm/make-indexed-session #(assoc %2 :i %1)))

(pm/spy>> :point {:x 100 :y 100})
(pm/spy>> :point {:x 200 :y 200})
(pm/spy>> :point {:x 300 :y 300})
(pm/log-for :point)
;=> [{:i 0 :x 100 :y 100}
; {:i 1 :x 200 :y 200}
; {:i 2 :x 300 :y 300}]
```

The `indexed` function is another way to create an indexed session.
`(indexed <session>)` creates a new indexed session based on another session.
In fact, `(make-indexed-session)` is equivalent to `(indexed (make-session))`.

It's especially useful to make an session with base transducer into an indexed session:

```clojure
(pm/set-current-session!
(pm/indexed (pm/make-session (take-while #(< (:id %) 3)))))

(doseq [v [:a :b :c :d :e]]
(pm/spy>> :foo v))
(pm/log-for :foo)
;=> [{:id 0 :val :a}
; {:id 1 :val :b}
; {:id 2 :val :c}]
```

#### `make-unsafe-session`

In Clojure, an ordinary session (created by `make-session`) is inherently
Expand Down
20 changes: 20 additions & 0 deletions src/postmortem/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@
(let [session (session/void-session)]
(fn [] session)))

(defn indexed
"Creates an indexed session based on the given session.
An indexed session manages an auto-incremental index and attaches it to each
log item. How the session attaches the index can be specified by a function f
passed as an optional argument. The function takes two arguments, the index
and the log item, and returns a new log item. The default function is
`(fn [id item] {:id id :val item})`."
{:added "0.5.1"}
([session] (indexed session #(array-map :id %1 :val %2)))
([session f]
(session/indexed session f)))

(defn make-indexed-session
"Creates and returns a new indexed session.
Equivalent to `(indexed (make-session))` or `(indexed (make-session) f)`.
See the docstring for `indexed` for details."
{:added "0.5.1"}
([] (indexed (make-session)))
([f] (indexed (make-session) f)))

(def ^:dynamic *current-session*
"Dynamic var bound to the current session. Don't use this directly, call
(current-session) instead."
Expand Down
27 changes: 27 additions & 0 deletions src/postmortem/session.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,33 @@
(-complete! [this])
(-complete! [this keys])))

(defn indexed [session f]
(let [id (atom -1)]
(reify
proto/ISession
proto/ILogStorage
(-add-item! [_ key xform item]
(proto/-add-item! session key xform (f (swap! id inc) item)))
(-keys [_]
(proto/-keys session))
(-logs [_]
(proto/-logs session))
(-logs [_ keys]
(proto/-logs session keys))
(-reset! [_]
(proto/-reset! session)
(reset! id -1)
nil)
(-reset! [_ keys]
(proto/-reset! session keys))
proto/ICompletable
(-completed? [_ key]
(proto/-completed? session key))
(-complete! [_]
(proto/-complete! session))
(-complete! [_ keys]
(proto/-complete! session keys)))))

#?(:clj
(defn synchronized [session]
(let [^ReentrantLock lock (ReentrantLock.)]
Expand Down
35 changes: 35 additions & 0 deletions test/postmortem/core_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,41 @@
(is (= "" (with-out-str (f 1) (f 2) (f 3))))
(is (= "" (with-out-str (pm/logs sess)))))))

(deftest indexed-session-test
(testing "indexed session logs items with auto-incremental index"
(let [sess (pm/make-indexed-session)]
(pm/spy>> sess :foo identity 100)
(pm/spy>> sess :bar identity 101)
(pm/spy>> sess :foo identity 102)
(is (= {:foo [{:id 0 :val 100}
{:id 2 :val 102}]
:bar [{:id 1 :val 101}]}
(pm/logs sess)))))
(testing "indexed session accepts an optional fn that specifies how to attach the given index to the item"
(let [sess (pm/make-indexed-session vector)]
(pm/spy>> sess :foo identity 100)
(pm/spy>> sess :bar identity 101)
(pm/spy>> sess :foo identity 102)
(is (= {:foo [[0 100] [2 102]] :bar [[1 101]]}
(pm/logs sess)))))
(testing "calling reset! on an indexed session resets the index"
(let [sess (pm/make-indexed-session)]
(pm/spy>> sess :foo identity 100)
(pm/spy>> sess :foo identity 101)
(is (= [{:id 0 :val 100} {:id 1 :val 101}]
(pm/log-for sess :foo)))
(pm/reset-key! sess :foo)
(pm/spy>> sess :foo identity 102)
(pm/spy>> sess :foo identity 103)
(is (= [{:id 2 :val 102} {:id 3 :val 103}]
(pm/log-for sess :foo)))
(pm/reset! sess)
(is (= {} (pm/logs sess)))
(pm/spy>> sess :foo identity 104)
(pm/spy>> sess :foo identity 105)
(is (= [{:id 0 :val 104} {:id 1 :val 105}]
(pm/log-for sess :foo))))))

#?(:clj

(deftest ^:eftest/synchronized synchronization-test
Expand Down
0