-
Notifications
You must be signed in to change notification settings - Fork 713
/
delete.clj
104 lines (93 loc) · 4.12 KB
/
delete.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
(ns jepsen.dgraph.delete
"Tries creating an indexed record, then deleting that record, and checking to
make sure that indexes are always up to date."
(:require [clojure.tools.logging :refer [info]]
[dom-top.core :refer [disorderly with-retry]]
[jepsen.dgraph [client :as c]]
[jepsen [client :as client]
[checker :as checker]
[generator :as gen]
[independent :as independent]]
[jepsen.checker.timeline :as timeline])
(:import (io.dgraph TxnConflictException)))
; These operations apply to a single key; we generalize to multiple keys using
; jepsen.independent. Upserts and deletes just upsert and delete {:key
; whatever}. Reads return the set of matching records for the key.
(defn r [_ _] {:type :invoke, :f :read, :value nil})
(defn u [_ _] {:type :invoke, :f :upsert, :value nil})
(defn d [_ _] {:type :invoke, :f :delete, :value nil})
(defrecord Client [conn]
client/Client
(open! [this test node]
(assoc this :conn (c/open node)))
(setup! [this test]
(c/alter-schema! conn (str "key: int @index(int)"
(when (:upsert-schema test) " @upsert")
" .\n")))
(invoke! [this test op]
(let [[k v] (:value op)]
(c/with-conflict-as-fail op
(c/with-txn test [t conn]
(case (:f op)
:read (->> (c/query t (str "{ q(func: eq(key, $key)) {\n"
" uid\n"
" key\n"
"}}")
{:key k})
:q
(independent/tuple k)
(assoc op :type :ok, :value))
:upsert (if-let [uid (c/upsert! t :key {:key k})]
(assoc op :type :ok, :uid uid)
(assoc op :type :fail, :error :present))
:delete
(if-let [uid (-> (c/query t "{ q(func: eq(key, $key)) { uid }}"
{:key k})
:q
first
:uid)]
(do (c/delete! t uid)
(assoc op :type :ok, :uid uid))
(assoc op :type :fail, :error :not-found)))))))
(teardown! [this test])
(close! [this test]
(c/close! conn)))
(defn checker
"We want to verify that every read either finds an empty set, or exactly one
record with both a :uid and a :key."
[]
(reify checker/Checker
(check [_ test model history opts]
(let [k (:history-key opts)
reads (->> history
(filter (fn [{:keys [f type value]}]
; We want an OK read
(and (= :ok type)
(= :read f)
; Which didn't...
(not ; Find nothing
(or (= 0 (count value))
; Or exactly one record
(and (= 1 (count value))
(let [v (first value)]
; With UID and key
(and (= #{:uid :key}
(set (keys v)))
(= k (:key v)))))))))))]
{:valid? (empty? reads)
:bad-reads reads}))))
(defn workload
"Stuff you need to build a test!"
[opts]
{:client (Client. nil)
:generator (independent/concurrent-generator
(* 2 (count (:nodes opts)))
(range)
(fn [k]
(->> (gen/mix [r u d])
(gen/limit 1000)
(gen/stagger 1/10))))
:checker (independent/checker
(checker/compose
{:deletes (checker)
:timeline (timeline/html)}))})