├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.markdown
├── histories
├── huge-scc.edn
├── paper-example.edn
├── si-without-g-single.edn
└── small-slow-scc.edn
├── images
├── anomalies.png
├── g1c-example.png
├── list.dot
├── list.png
├── models.png
├── perf.svg
├── plot-example.png
├── register.dot
├── severity.png
└── watch.sh
├── paper
└── elle.pdf
├── project.clj
├── proof
├── .Traceable-Objects.thy.marks
├── Anomaly.thy
├── DSG.thy
├── FinMap.thy
├── History.thy
├── IDSG.thy
├── InferredAnomaly.thy
├── LocaleTest.thy
├── Object.thy
├── Object2.thy
├── Observation.thy
├── Op.thy
├── Op2.thy
├── PolymorphismTest.thy
├── ProofTest.thy
├── Scratch.thy
├── Traceable-Objects.thy
├── Transaction.thy
├── VersionOrder.thy
└── graphs
│ ├── Arc_Walk.thy
│ ├── Bidirected_Digraph.thy
│ ├── Digraph.thy
│ ├── Digraph_Component.thy
│ ├── Digraph_Component_Vwalk.thy
│ ├── Digraph_Isomorphism.thy
│ ├── Euler.thy
│ ├── Funpow.thy
│ ├── Graph_Theory.thy
│ ├── Kuratowski.thy
│ ├── Pair_Digraph.thy
│ ├── ROOT
│ ├── Rtrancl_On.thy
│ ├── Shortest_Path.thy
│ ├── Stuff.thy
│ ├── Subdivision.thy
│ ├── Vertex_Walk.thy
│ ├── Weighted_Graph.thy
│ └── document
│ ├── root.bib
│ └── root.tex
├── src
└── elle
│ ├── BFSPath.java
│ ├── BitRels.java
│ ├── bfs.clj
│ ├── closed_predicate.clj
│ ├── consistency_model.clj
│ ├── core.clj
│ ├── graph.clj
│ ├── list_append.clj
│ ├── rels.clj
│ ├── rw_register.clj
│ ├── txn.clj
│ ├── util.clj
│ └── viz.clj
└── test
└── elle
├── bfs_test.clj
├── closed_predicate_test.clj
├── consistency_model_test.clj
├── core_test.clj
├── graph_test.clj
├── list_append_test.clj
├── rels_test.clj
├── rw_register_test.clj
├── txn_test.clj
├── util_test.clj
└── viz_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | \#*\#
2 | *.swp
3 | *~
4 | /G1c.txt
5 | /target
6 | /classes
7 | /checkouts
8 | /plots
9 | /test-output
10 | /proof/**/*.pdf
11 | profiles.clj
12 | pom.xml
13 | pom.xml.asc
14 | *.jar
15 | *.class
16 | /.lein-*
17 | /.nrepl-port
18 | /.clj-kondo
19 | /.lsp
20 | /.vscode
21 | /incompatible-order
22 | .hgignore
23 | .hg/
24 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
3 |
4 | ## [Unreleased]
5 | ### Changed
6 | - Add a new arity to `make-widget-async` to provide a different widget shape.
7 |
8 | ## [0.1.1] - 2020-02-24
9 | ### Changed
10 | - Documentation on how to make the widgets.
11 |
12 | ### Removed
13 | - `make-widget-sync` - we're all async, all the time.
14 |
15 | ### Fixed
16 | - Fixed widget maker to keep working when daylight savings switches over.
17 |
18 | ## 0.1.0 - 2020-02-24
19 | ### Added
20 | - Files from the new template.
21 | - Widget maker public API - `make-widget-sync`.
22 |
23 | [Unreleased]: https://github.com/your-name/elle/compare/0.1.1...HEAD
24 | [0.1.1]: https://github.com/your-name/elle/compare/0.1.0...0.1.1
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Eclipse Public License - v 2.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial content
12 | Distributed under this Agreement, and
13 |
14 | b) in the case of each subsequent Contributor:
15 | i) changes to the Program, and
16 | ii) additions to the Program;
17 | where such changes and/or additions to the Program originate from
18 | and are Distributed by that particular Contributor. A Contribution
19 | "originates" from a Contributor if it was added to the Program by
20 | such Contributor itself or anyone acting on such Contributor's behalf.
21 | Contributions do not include changes or additions to the Program that
22 | are not Modified Works.
23 |
24 | "Contributor" means any person or entity that Distributes the Program.
25 |
26 | "Licensed Patents" mean patent claims licensable by a Contributor which
27 | are necessarily infringed by the use or sale of its Contribution alone
28 | or when combined with the Program.
29 |
30 | "Program" means the Contributions Distributed in accordance with this
31 | Agreement.
32 |
33 | "Recipient" means anyone who receives the Program under this Agreement
34 | or any Secondary License (as applicable), including Contributors.
35 |
36 | "Derivative Works" shall mean any work, whether in Source Code or other
37 | form, that is based on (or derived from) the Program and for which the
38 | editorial revisions, annotations, elaborations, or other modifications
39 | represent, as a whole, an original work of authorship.
40 |
41 | "Modified Works" shall mean any work in Source Code or other form that
42 | results from an addition to, deletion from, or modification of the
43 | contents of the Program, including, for purposes of clarity any new file
44 | in Source Code form that contains any contents of the Program. Modified
45 | Works shall not include works that contain only declarations,
46 | interfaces, types, classes, structures, or files of the Program solely
47 | in each case in order to link to, bind by name, or subclass the Program
48 | or Modified Works thereof.
49 |
50 | "Distribute" means the acts of a) distributing or b) making available
51 | in any manner that enables the transfer of a copy.
52 |
53 | "Source Code" means the form of a Program preferred for making
54 | modifications, including but not limited to software source code,
55 | documentation source, and configuration files.
56 |
57 | "Secondary License" means either the GNU General Public License,
58 | Version 2.0, or any later versions of that license, including any
59 | exceptions or additional permissions as identified by the initial
60 | Contributor.
61 |
62 | 2. GRANT OF RIGHTS
63 |
64 | a) Subject to the terms of this Agreement, each Contributor hereby
65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright
66 | license to reproduce, prepare Derivative Works of, publicly display,
67 | publicly perform, Distribute and sublicense the Contribution of such
68 | Contributor, if any, and such Derivative Works.
69 |
70 | b) Subject to the terms of this Agreement, each Contributor hereby
71 | grants Recipient a non-exclusive, worldwide, royalty-free patent
72 | license under Licensed Patents to make, use, sell, offer to sell,
73 | import and otherwise transfer the Contribution of such Contributor,
74 | if any, in Source Code or other form. This patent license shall
75 | apply to the combination of the Contribution and the Program if, at
76 | the time the Contribution is added by the Contributor, such addition
77 | of the Contribution causes such combination to be covered by the
78 | Licensed Patents. The patent license shall not apply to any other
79 | combinations which include the Contribution. No hardware per se is
80 | licensed hereunder.
81 |
82 | c) Recipient understands that although each Contributor grants the
83 | licenses to its Contributions set forth herein, no assurances are
84 | provided by any Contributor that the Program does not infringe the
85 | patent or other intellectual property rights of any other entity.
86 | Each Contributor disclaims any liability to Recipient for claims
87 | brought by any other entity based on infringement of intellectual
88 | property rights or otherwise. As a condition to exercising the
89 | rights and licenses granted hereunder, each Recipient hereby
90 | assumes sole responsibility to secure any other intellectual
91 | property rights needed, if any. For example, if a third party
92 | patent license is required to allow Recipient to Distribute the
93 | Program, it is Recipient's responsibility to acquire that license
94 | before distributing the Program.
95 |
96 | d) Each Contributor represents that to its knowledge it has
97 | sufficient copyright rights in its Contribution, if any, to grant
98 | the copyright license set forth in this Agreement.
99 |
100 | e) Notwithstanding the terms of any Secondary License, no
101 | Contributor makes additional grants to any Recipient (other than
102 | those set forth in this Agreement) as a result of such Recipient's
103 | receipt of the Program under the terms of a Secondary License
104 | (if permitted under the terms of Section 3).
105 |
106 | 3. REQUIREMENTS
107 |
108 | 3.1 If a Contributor Distributes the Program in any form, then:
109 |
110 | a) the Program must also be made available as Source Code, in
111 | accordance with section 3.2, and the Contributor must accompany
112 | the Program with a statement that the Source Code for the Program
113 | is available under this Agreement, and informs Recipients how to
114 | obtain it in a reasonable manner on or through a medium customarily
115 | used for software exchange; and
116 |
117 | b) the Contributor may Distribute the Program under a license
118 | different than this Agreement, provided that such license:
119 | i) effectively disclaims on behalf of all other Contributors all
120 | warranties and conditions, express and implied, including
121 | warranties or conditions of title and non-infringement, and
122 | implied warranties or conditions of merchantability and fitness
123 | for a particular purpose;
124 |
125 | ii) effectively excludes on behalf of all other Contributors all
126 | liability for damages, including direct, indirect, special,
127 | incidental and consequential damages, such as lost profits;
128 |
129 | iii) does not attempt to limit or alter the recipients' rights
130 | in the Source Code under section 3.2; and
131 |
132 | iv) requires any subsequent distribution of the Program by any
133 | party to be under a license that satisfies the requirements
134 | of this section 3.
135 |
136 | 3.2 When the Program is Distributed as Source Code:
137 |
138 | a) it must be made available under this Agreement, or if the
139 | Program (i) is combined with other material in a separate file or
140 | files made available under a Secondary License, and (ii) the initial
141 | Contributor attached to the Source Code the notice described in
142 | Exhibit A of this Agreement, then the Program may be made available
143 | under the terms of such Secondary Licenses, and
144 |
145 | b) a copy of this Agreement must be included with each copy of
146 | the Program.
147 |
148 | 3.3 Contributors may not remove or alter any copyright, patent,
149 | trademark, attribution notices, disclaimers of warranty, or limitations
150 | of liability ("notices") contained within the Program from any copy of
151 | the Program which they Distribute, provided that Contributors may add
152 | their own appropriate notices.
153 |
154 | 4. COMMERCIAL DISTRIBUTION
155 |
156 | Commercial distributors of software may accept certain responsibilities
157 | with respect to end users, business partners and the like. While this
158 | license is intended to facilitate the commercial use of the Program,
159 | the Contributor who includes the Program in a commercial product
160 | offering should do so in a manner which does not create potential
161 | liability for other Contributors. Therefore, if a Contributor includes
162 | the Program in a commercial product offering, such Contributor
163 | ("Commercial Contributor") hereby agrees to defend and indemnify every
164 | other Contributor ("Indemnified Contributor") against any losses,
165 | damages and costs (collectively "Losses") arising from claims, lawsuits
166 | and other legal actions brought by a third party against the Indemnified
167 | Contributor to the extent caused by the acts or omissions of such
168 | Commercial Contributor in connection with its distribution of the Program
169 | in a commercial product offering. The obligations in this section do not
170 | apply to any claims or Losses relating to any actual or alleged
171 | intellectual property infringement. In order to qualify, an Indemnified
172 | Contributor must: a) promptly notify the Commercial Contributor in
173 | writing of such claim, and b) allow the Commercial Contributor to control,
174 | and cooperate with the Commercial Contributor in, the defense and any
175 | related settlement negotiations. The Indemnified Contributor may
176 | participate in any such claim at its own expense.
177 |
178 | For example, a Contributor might include the Program in a commercial
179 | product offering, Product X. That Contributor is then a Commercial
180 | Contributor. If that Commercial Contributor then makes performance
181 | claims, or offers warranties related to Product X, those performance
182 | claims and warranties are such Commercial Contributor's responsibility
183 | alone. Under this section, the Commercial Contributor would have to
184 | defend claims against the other Contributors related to those performance
185 | claims and warranties, and if a court requires any other Contributor to
186 | pay any damages as a result, the Commercial Contributor must pay
187 | those damages.
188 |
189 | 5. NO WARRANTY
190 |
191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
196 | PURPOSE. Each Recipient is solely responsible for determining the
197 | appropriateness of using and distributing the Program and assumes all
198 | risks associated with its exercise of rights under this Agreement,
199 | including but not limited to the risks and costs of program errors,
200 | compliance with applicable laws, damage to or loss of data, programs
201 | or equipment, and unavailability or interruption of operations.
202 |
203 | 6. DISCLAIMER OF LIABILITY
204 |
205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
213 | POSSIBILITY OF SUCH DAMAGES.
214 |
215 | 7. GENERAL
216 |
217 | If any provision of this Agreement is invalid or unenforceable under
218 | applicable law, it shall not affect the validity or enforceability of
219 | the remainder of the terms of this Agreement, and without further
220 | action by the parties hereto, such provision shall be reformed to the
221 | minimum extent necessary to make such provision valid and enforceable.
222 |
223 | If Recipient institutes patent litigation against any entity
224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
225 | Program itself (excluding combinations of the Program with other software
226 | or hardware) infringes such Recipient's patent(s), then such Recipient's
227 | rights granted under Section 2(b) shall terminate as of the date such
228 | litigation is filed.
229 |
230 | All Recipient's rights under this Agreement shall terminate if it
231 | fails to comply with any of the material terms or conditions of this
232 | Agreement and does not cure such failure in a reasonable period of
233 | time after becoming aware of such noncompliance. If all Recipient's
234 | rights under this Agreement terminate, Recipient agrees to cease use
235 | and distribution of the Program as soon as reasonably practicable.
236 | However, Recipient's obligations under this Agreement and any licenses
237 | granted by Recipient relating to the Program shall continue and survive.
238 |
239 | Everyone is permitted to copy and distribute copies of this Agreement,
240 | but in order to avoid inconsistency the Agreement is copyrighted and
241 | may only be modified in the following manner. The Agreement Steward
242 | reserves the right to publish new versions (including revisions) of
243 | this Agreement from time to time. No one other than the Agreement
244 | Steward has the right to modify this Agreement. The Eclipse Foundation
245 | is the initial Agreement Steward. The Eclipse Foundation may assign the
246 | responsibility to serve as the Agreement Steward to a suitable separate
247 | entity. Each new version of the Agreement will be given a distinguishing
248 | version number. The Program (including Contributions) may always be
249 | Distributed subject to the version of the Agreement under which it was
250 | received. In addition, after a new version of the Agreement is published,
251 | Contributor may elect to Distribute the Program (including its
252 | Contributions) under the new version.
253 |
254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
255 | receives no rights or licenses to the intellectual property of any
256 | Contributor under this Agreement, whether expressly, by implication,
257 | estoppel or otherwise. All rights in the Program not expressly granted
258 | under this Agreement are reserved. Nothing in this Agreement is intended
259 | to be enforceable by any entity that is not a Contributor or Recipient.
260 | No third-party beneficiary rights are created under this Agreement.
261 |
262 | Exhibit A - Form of Secondary Licenses Notice
263 |
264 | "This Source Code may also be made available under the following
265 | Secondary Licenses when the conditions for such availability set forth
266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
267 | version(s), and exceptions or additional permissions here}."
268 |
269 | Simply including a copy of this Agreement, including this Exhibit A
270 | is not sufficient to license the Source Code under Secondary Licenses.
271 |
272 | If it is not possible or desirable to put the notice in a particular
273 | file, then You may include the notice in a location (such as a LICENSE
274 | file in a relevant directory) where a recipient would be likely to
275 | look for such a notice.
276 |
277 | You may add additional accurate notices of copyright ownership.
278 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # Elle
2 |
3 | [](https://clojars.org/elle)
4 |
5 | Elle is a transactional consistency checker for black-box databases. Based
6 | purely on client observations of transactions, and given some minimal
7 | constraints on datatypes and operations, it can tell you whether that
8 | observation exhibits a variety of transactional anomalies. Like a clever
9 | lawyer, Elle looks for a sequence of events in a story which couldn't possibly
10 | have happened in that order, and uses that inference to prove the story can't
11 | be consistent.
12 |
13 | In a nutshell, Elle is:
14 |
15 | - _General_: Elle works over a variety of datatypes and places only minimal, practical constraints on transaction structure.
16 | - _Efficient_: Elle is ~linear in history length, and ~constant, rather than exponential, with respect to concurrency.
17 | - _Effective_: Elle has found unexpected anomalies in [every](http://jepsen.io/analyses/yugabyte-db-1.1.9) [database](http://jepsen.io/analyses/tidb-2.1.7) [we've](http://jepsen.io/analyses/yugabyte-db-1.3.1) [checked](https://twitter.com/aphyr/status/1165761686348992513), ranging from internal consistency violations to anti-dependency cycles to dirty read to lost updates to realtime violations.
18 | - _Sound_: Elle can find every (non-predicate) anomaly from Adya, Liskov, & O'Neil's [Generalized Isolation Level Definitions](http://pmg.csail.mit.edu/papers/icde00.pdf).
19 | - _Elucidative_: Elle can point to a minimal set of transactions which witness a consistency violation; its conclusions are easy to understand and verify.
20 |
21 | This repository encompasses a [Clojure implementation](src/elle/) of the Elle
22 | consistency checker and its [accompanying test suite](test/elle/), which you
23 | can use to check your own histories. Our
24 | [paper](https://github.com/jepsen-io/elle/raw/master/paper/elle.pdf) provides
25 | deep insight into the goals and intuition behind Elle, and a rough
26 | formalization of its soundness proof. A nowhere-near-complete formal
27 | [proof](proof/) sketch is written in the
28 | [Isabelle/HOL](https://isabelle.in.tum.de/) proof language.
29 |
30 | If you want to check a database using Elle, see [Jepsen](https://jepsen.io); Elle comes built-in. If you want to use Elle to check your own histories without using Jepsen, you can add Elle as a dependency to any JVM project, and invoke its checker functions directly. If you're working in a non-JVM language, you can write your history to a file or stream, and call a small wrapper program to produce output.
31 |
32 | Elle is still under active development, and we're not 100% confident in its
33 | inference rules yet. Jepsen recommends checking reported anomalies by hand to
34 | make sure they're valid. If you'd like to contribute, we'd especially welcome your help in the [formal proof](proof/), and in [rigorously defining consistency models](src/elle/consistency_model.clj).
35 |
36 | Questions? [Read the paper](https://github.com/jepsen-io/elle/raw/master/paper/elle.pdf)!
37 |
38 | ## Demo
39 |
40 | First, you'll need a copy of Graphviz installed.
41 |
42 | Imagine a database where each object (identified by keys like `:x` or `:y`) is
43 | a list of numbers. Transactions are made up of reads `[:r :x [1 2 3]]`, which
44 | return the current value of the given list, and writes `[:append :y 4]`, which
45 | append a number to the end of the list.
46 |
47 | ```clj
48 | => (require '[elle.list-append :as a]
49 | '[jepsen.history :as h])
50 | nil
51 | ```
52 |
53 | We construct a history of three transactions, each of which is known to
54 | have committed (`:type :ok`). The first transaction appends 1 to `:x` and
55 | observes `:y = [1]`. The second appends 2 to `:x` and 1 to `:y`. The third
56 | observes `x`, and sees its value as `[1 2]`.
57 |
58 | ```clj
59 | => (def h (h/history
60 | [{:process 0, :type :ok, :value [[:append :x 1] [:r :y [1]]]}
61 | {:process 1, :type :ok, :value [[:append :x 2] [:append :y 1]]}
62 | {:process 2, :type :ok, :value [[:r :x [1 2]]]}]))
63 | h
64 | ```
65 |
66 | Now, we ask Elle to check this history, expecting it to be serializable, and
67 | have it dump anomalies to a directory called `out/`.
68 |
69 | ```clj
70 | => (pprint (a/check {:consistency-models [:serializable], :directory "out"} h))
71 | {:valid? false,
72 | :anomaly-types (:G1c),
73 | :anomalies
74 | {:G1c
75 | [{:cycle
76 | [{:process 1,
77 | :type :ok,
78 | :f nil,
79 | :value [[:append :x 2] [:append :y 1]],
80 | :index 1,
81 | :time -1}
82 | {:process 0,
83 | :type :ok,
84 | :f nil,
85 | :value [[:append :x 1] [:r :y [1]]],
86 | :index 0,
87 | :time -1}
88 | {:process 1,
89 | :type :ok,
90 | :f nil,
91 | :value [[:append :x 2] [:append :y 1]],
92 | :index 1,
93 | :time -1}],
94 | :steps
95 | ({:type :wr, :key :y, :value 1, :a-mop-index 1, :b-mop-index 1}
96 | {:type :ww,
97 | :key :x,
98 | :value 1,
99 | :value' 2,
100 | :a-mop-index 0,
101 | :b-mop-index 0}),
102 | :type :G1c}]},
103 | :not #{:read-committed},
104 | :also-not
105 | #{:consistent-view :cursor-stability :forward-consistent-view
106 | :monotonic-atomic-view :monotonic-snapshot-read :monotonic-view
107 | :repeatable-read :serializable :snapshot-isolation :strong-serializable
108 | :strong-session-serializable :strong-session-snapshot-isolation
109 | :strong-snapshot-isolation :update-serializable}}
110 |
111 | ```
112 |
113 | Here, Elle can infer the write-read relationship between T1 and T2 on the basis
114 | of their respective reads and writes. The write-write relationship between T2
115 | and T1 is inferrable because T3 observed `x = [1,2]`, which constrains the
116 | possible orders of appends. This is a G1c anomaly: cyclic information flow. The
117 | `:cycle` field shows the operations in that cycle, and `:steps` shows the
118 | dependencies between each pair of operations in the cycle.
119 |
120 | On the basis of this anomaly, Elle has concluded that this history is not
121 | read-committed---this is the weakest level Elle can demonstrate is violated. In
122 | addition, several stronger isolation levels, such as consistent-view and
123 | update-serializable, are also violated by this history.
124 |
125 | Let's see the G1c anomaly in text:
126 |
127 | ```
128 | $ cat out/G1c.txt
129 | G1c #0
130 | Let:
131 | T1 = {:index 1, :time -1, :type :ok, :process 1, :f nil,
132 | :value [[:append :x 2] [:append :y 1]]}
133 | T2 = {:index 0, :time -1, :type :ok, :process 0, :f nil,
134 | :value [[:append :x 1] [:r :y [1]]]}
135 |
136 |
137 | Then:
138 | - T1 < T2, because T2 observed T1's append of 1 to key :y.
139 | - However, T2 < T1, because T1 appended 2 after T2 appended 1 to :x: a contradiction!
140 | ```
141 |
142 | In the `out/G1c` directory, you'll find a corresponding plot.
143 |
144 | 
145 |
146 | In addition to rendering a graph for each individual cycle, Elle generates a
147 | plot for each strongly-connected component of the dependency graph. This can be
148 | helpful for getting a handle on the *scope* of an anomalous behavior, whereas
149 | cycles show as small a set of transactions as possible. Here's a plot from a
150 | more complex history, involving realtime edges, write-write, write-read, and
151 | read-write dependencies:
152 |
153 | 
154 |
155 | ## Usage
156 |
157 | As a user, your main entry points into Elle will be `elle.list-append/check`
158 | and `elle.rw-register/check`. Both namespaces also have code for generating
159 | sequences of transactions which you can apply to your database; see, for
160 | example, `elle.list-append/gen`.
161 |
162 | Elle has a broad variety of anomalies and consistency models; see
163 | `elle.consistency-model` for their definitions. Not every anomaly is
164 | detectable, but we aim for completeness.
165 |
166 | If you'd like to define your own relationships between transactions, see
167 | `elle.core`.
168 |
169 | ### Observed Histories
170 |
171 | Elle expects its observed histories in the same format as [Jepsen](https://github.com/jepsen-io/jepsen). See [jepsen.history](https://github.com/jepsen-io/history) for the structure of these histories.
172 |
173 | ### Types of Tests
174 |
175 | - `elle.core`: The heart of Elle's inference system. Computes transaction graphs and finds cycles over them. Includes general-purpose graphs for per-process and realtime orders.
176 | - `elle.rw-register`: Write/Read registers. Weaker inference rules, but applicable to basically all systems. Objects are registers; writes blindly replace values.
177 | - `elle.list-append`: Elle's most powerful inference rules. Objects are lists, writes append unique elements to those lists.
178 |
179 | ## Consistency Models
180 |
181 | The following plot shows Elle's relationships between consistency models: an
182 | arrow `a -> b` implies if `a` holds, then so does `b`. Sources for this
183 | structure can be found in `elle.consistency-model`.
184 |
185 | 
186 |
187 | This plot shows the relationships between Elle's anomalies. An arrow `a -> b`
188 | implies if we observe anomaly `a` in a history, then `b` exists in the history
189 | as well.
190 |
191 | 
192 |
193 | ## Soundness
194 |
195 | Elle can check for every non-predicate anomaly from Adya, Liskov, and O'Neil's [Generalized Isolation Level Definitions](http://pmg.csail.mit.edu/papers/icde00.pdf). These include:
196 |
197 | - G0: Write cycle.
198 | - G1a: Aborted read.
199 | - G1b: Intermediate read.
200 | - G1c: Cyclic information flow.
201 | - G-Single: Read skew.
202 | - G2: Anti-dependency cycle.
203 |
204 | There are additional anomalies (e.g. garbage reads, dirty updates, inconsistent version orders) available for specific checkers. Not all of these are implemented fully yet---see the paper for details.
205 |
206 | - Internal Inconsistency: A transaction fails to observe its own prior reads/writes.
207 | - Inconsistent Version Orders: Inference rules suggested a cyclic order of updates to a single key.
208 | - Dirty Updates: A write promotes aborted state into committed state.
209 | - Duplicate Writes: A write occurs more than once.
210 | - Garbage Reads: A read observes a state which could not have been the product of any write.
211 |
212 | In addition, Elle can infer transaction dependencies on the basis of process
213 | (e.g. session) or realtime order, allowing it to distinguish between, say,
214 | strict serializability and serializability.
215 |
216 | For lists, Elle can infer a complete prefix of the Adya version order for a key
217 | based on a single read. For registers, Elle can infer version orders on the
218 | basis of the initial state, writes-follow-reads, process, and real-time orders.
219 |
220 | When Elle claims an anomaly in an observable history, it specifically means
221 | that in any abstract Adya-style history which is compatible with that observed
222 | history, either a corresponding anomaly exists, or something worse
223 | happened---e.g. an aborted read. This is a natural consequence of testing
224 | real-world databases; if the database lies in *just the right way*, it might
225 | appear to exhibit anomalies which didn't actually happen, or mask anomalies
226 | which did. We limit the impact of this problem by being able to distinguish
227 | between many classes of reads, and sampling many anomalies---hoping that
228 | eventually, we get lucky and see the anomaly for what it "really is".
229 |
230 | ## Completeness
231 |
232 | Elle is not complete: it may fail to identify anomalies which were present in
233 | the system under test. This is a consequence of two factors:
234 |
235 | 1. Elle checks histories observed from real databases, where the results of transactions might go unobserved, and timing information might not be as precise as one would like.
236 | 2. Serializability checking is NP-complete; Elle intentionally limits its inferences to those solvable in linear (or log-linear) time.
237 |
238 | In practice, we believe Elle is "complete enough". Indeterminacy is generally
239 | limited to unobserved transactions, or a small set of transactions at the very
240 | end of the history.
241 |
242 | ## Performance
243 |
244 | Elle has been extensively optimized and many of its components are parallelized.
245 | It can check real-world histories of 22 million transactions for (e.g.) strong
246 | session serializability in in roughly two minutes, consuming ~60 GB of heap.
247 | 100-160,000 transactions/sec is readily attainable on modern hardware. Most of Elle's analyses scale linearly or as `n log(n)`.
248 |
249 | 
250 |
251 | These plots, from the original Elle paper before optimization, show Elle's
252 | performance vs the [Knossos](https://github.com/jepsen-io/knossos)
253 | linearizability checker, verifying histories of various lengths (l) and
254 | concurrencies (c), recorded from a simulated serializable snapshot isolated
255 | in-memory database. Lower is better.
256 |
257 | In general, Elle checks real-world histories in a matter of seconds to minutes,
258 | rather than seconds to millennia. Where Knossos is often limited to a few
259 | hundred operations per history, Elle can handle hundreds of thousands of
260 | operations easily.
261 |
262 | Knossos runtimes diverge exponentially with concurrency; Elle is effectively
263 | constant. There's a slight drop in runtime as concurrency increases, as more
264 | transactions abort due to conflicts. Knossos is also mildly superlinear in
265 | history length; Elle is effectively linear.
266 |
267 | ## License
268 |
269 | Elle is copyright 2019--2020 Jepsen, LLC and Peter Alvaro. The Elle library is available under the Eclipse Public License, version 2.0, or, at your option, GPL-2.0 with the classpath exception.
270 |
271 | ## Thanks
272 |
273 | Elle was inspired by conversations with Asha Karim, and Kit Patella (@mkcp) wrote the first prototype of the Elle checker.
274 |
275 | ## See Also
276 |
277 | - [elle-cli](https://github.com/ligurio/elle-cli), a standalone command-line
278 | frontend to Elle (and other checkers)
279 |
--------------------------------------------------------------------------------
/histories/paper-example.edn:
--------------------------------------------------------------------------------
1 | {:index 0 :type :invoke :value [[:append 253 1] [:append 253 3] [:append 253 4] [:append 255 2] [:append 255 3] [:append 255 4] [:append 255 5] [:append 256 1] [:append 256 2]]}
2 | {:index 1 :type :ok :value [[:append 253 1] [:append 253 3] [:append 253 4] [:append 255 2] [:append 255 3] [:append 255 4] [:append 255 5] [:append 256 1] [:append 256 2]]}
3 | {:index 2 :type :invoke, :value [[:append 255 8] [:r 253 nil]]}
4 | {:index 3 :type :ok, :value [[:append 255 8] [:r 253 [1 3 4]]]}
5 | {:index 4 :type :invoke, :value [[:append 256 4] [:r 255 nil] [:r 256 nil] [:r 253 nil]]}
6 | {:index 5 :type :ok, :value [[:append 256 4] [:r 255 [2 3 4 5 8]] [:r 256 [1 2 4]] [:r 253 [1 3 4]]]}
7 | {:index 6 :type :invoke, :value [[:append 250 10] [:r 253 nil] [:r 255 nil] [:append 256 3]]}
8 | {:index 7 :type :ok :value [[:append 250 10] [:r 253 [1 3 4]] [:r 255 [2 3 4 5]] [:append 256 3]]}
9 |
--------------------------------------------------------------------------------
/histories/si-without-g-single.edn:
--------------------------------------------------------------------------------
1 | ; T1 ww T3 on 9
2 | ; T1 wr T4 on 9
3 | ; T2 rw T1 on 9
4 | ; T3 rw T2 on 8
5 | ; T3 rw T4 on 6
6 | ; T4 rw T3 on 9
7 |
8 | ; T1: read by T4
9 | {:index 10, :time 0, :type :invoke, :process 2, :f :txn, :value [[:append 9 2]]}
10 | {:index 11, :time 0, :type :ok, :process 2, :f :txn, :value [[:append 9 2]]}
11 | ; T2: Does not see T1's append to 9
12 | {:index 20, :time 0, :type :invoke, :process 4, :f :txn, :value [[:append 8 1] [:r 9 nil]]}
13 | {:index 21, :time 0, :type :ok, :process 4, :f :txn, :value [[:append 8 1] [:r 9 nil]]}
14 | ; T3: Does not see T2's append to 8 or T4's append to 6, overwrites T1 on 9
15 | {:index 30, :time 0, :type :invoke, :process 7, :f :txn, :value [[:r 6 nil] [:r 8 nil] [:append 9 5]]}
16 | {:index 31, :time 0, :type :ok, :process 7, :f :txn, :value [[:r 6 nil] [:r 8 nil] [:append 9 5]]}
17 | ; T4: does not see T3's append to 9
18 | {:index 40, :time 0, :type :invoke, :process 9, :f :txn, :value [[:append 6 2] [:r 9 nil]]}
19 | {:index 41, :time 0, :type :ok, :process 9, :f :txn, :value [[:append 6 2] [:r 9 [2]]]}
20 |
--------------------------------------------------------------------------------
/images/anomalies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/anomalies.png
--------------------------------------------------------------------------------
/images/g1c-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/g1c-example.png
--------------------------------------------------------------------------------
/images/list.dot:
--------------------------------------------------------------------------------
1 | digraph proof {
2 | // concentrate=true
3 |
4 |
5 | subgraph cluster0 {
6 | label = "Versions"
7 | node [shape="oval"]
8 | v [label="[]"]
9 | v1 [label="[1]",style="dashed"]
10 | v12 [label="[1,2]"]
11 | v123 [label="[1,2,3]"]
12 |
13 | // Version trace
14 | edge [label="tr",color="cyan4",fontcolor="cyan4"]
15 | v123 -> v12
16 | v12 -> v1
17 | v1 -> v
18 |
19 | // Version order
20 | edge [label="≪",color="chartreuse4",fontcolor="chartreuse4"]
21 | v123 -> v12
22 | v12 -> v
23 |
24 | // Inferred version order
25 | edge [label="<",color="darkgreen",fontcolor="darkgreen"]
26 | v12 -> v
27 | }
28 |
29 | subgraph cluster1 {
30 | label = "Observed Transactions"
31 | node [shape=record,height="0.3"]
32 | ot1 [label=" r([])| r([1,2])| c"]
33 | ot2 [label=" w(_, 1)| w(_, 2)"]
34 | ot3 [label=" r(_)| w(_, 3)"]
35 | }
36 |
37 | subgraph cluster2 {
38 | label = "Adya Transactions"
39 | node [shape=record,height="0.3"]
40 | at1 [label=" r([])|r([1,2])| c"]
41 | at2 [label=" w([], 1)| w([1], 2)| c"]
42 | at3 [label=" r []| w([1,2], 3)| c"]
43 | }
44 |
45 | // Actual dependencies
46 | edge [color="slateblue",fontcolor="slateblue"]
47 | at1:op0 -> at2:op1 [label="rw"]
48 | at2:op1 -> at1:op1 [label="wr"]
49 | at2:op1 -> at3:op1 [label="ww"]
50 | at3:op0 -> at2:op1 [label="rw"]
51 | at1:op1 -> at3:op1 [label="rw"]
52 |
53 | // Inferred dependencies
54 | ot1:op0 -> ot2:op1 [label="rw"]
55 | ot2:op1 -> ot1:op1 [label="wr"]
56 |
57 | // Reads
58 | edge [label="read",dir="both",color="maroon3",fontcolor="maroon3"]
59 | v -> ot1:op0
60 | v12 -> ot1:op1
61 |
62 | // Recoverability
63 | edge [dir="forward",label="rec",color="orangered2",fontcolor="orangered2"]
64 | v1 -> ot2:op0
65 | v12 -> ot2:op1
66 |
67 | // Compatibility
68 | edge [label="R",dir="both",color="slategray",fontcolor="slategray"]
69 | ot1 -> at1
70 | ot2 -> at2
71 | ot3 -> at3
72 |
73 | // Adya version/txn relationshiops
74 | edge [dir="none",color="thistle3",style="dashed",label=""]
75 | //at1:op0 -> v
76 | //at1:op1 -> v12
77 | //at2:op0 -> v1
78 | //at2:op1 -> v12
79 | //at3:op0 -> v
80 | //at3:op1 -> v123
81 |
82 | // Purely for appearances
83 | ot2 -> ot3 [style=invis]
84 | at2 -> ot2 [style=invis]
85 | at3 -> ot3 [style=invis]
86 | // I'd love to put adya and observed transactions at the same rank but this
87 | // horribly breaks, complaining about lost edges. Sigh.
88 | }
89 |
--------------------------------------------------------------------------------
/images/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/list.png
--------------------------------------------------------------------------------
/images/models.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/models.png
--------------------------------------------------------------------------------
/images/plot-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/plot-example.png
--------------------------------------------------------------------------------
/images/register.dot:
--------------------------------------------------------------------------------
1 | digraph proof {
2 | // concentrate=true
3 |
4 | subgraph cluster0 {
5 | label = "Versions"
6 | node [shape="diamond"]
7 | v0 []
8 | v1 [style="filled"]
9 | v2 []
10 | v3 []
11 |
12 | // Version trace
13 | edge [label="tr",color="royalblue2",fontcolor="royalblue2"]
14 | v3 -> v2
15 | v2 -> v1
16 | v1 -> v0
17 |
18 | // Version order
19 | edge [label="≪",color="palegreen4",fontcolor="palegreen4"]
20 | v3 -> v2
21 | v2 -> v0
22 |
23 | // Inferred version order
24 | edge [label="<",color="mediumturquoise",fontcolor="mediumturquoise"]
25 | v2 -> v0
26 | }
27 |
28 | subgraph cluster1 {
29 | label = "Observed Transactions"
30 | node [shape=record,height="0.3"]
31 | ot1 [label=" r0| r2| c"]
32 | ot2 [label=" w1| w2"]
33 | ot3 [label=" r_| w3"]
34 | }
35 |
36 | subgraph cluster2 {
37 | label = "Ayda Transactions"
38 | node [shape=record,height="0.3"]
39 | at1 [label=" r0| r2| c"]
40 | at2 [label=" w1| w2| c"]
41 | at3 [label=" r0| w3| c"]
42 | }
43 |
44 | // Actual dependencies
45 | edge [color="slateblue",fontcolor="slateblue"]
46 | at1:op0 -> at2:op1 [label="rw"]
47 | at2:op1 -> at1:op1 [label="wr"]
48 | at2:op1 -> at3:op1 [label="ww"]
49 | at3:op0 -> at2:op1 [label="rw"]
50 |
51 | // Inferred dependencies
52 | ot1:op0 -> ot2:op1 [label="rw"]
53 | ot2:op1 -> ot1:op1 [label="wr"]
54 |
55 | // Reads
56 | edge [label="read",dir="both",color="maroon3",fontcolor="maroon3"]
57 | v0 -> ot1:op0
58 | v2 -> ot1:op1
59 |
60 | // Recoverability
61 | edge [dir="forward",label="rec",color="orangered2",fontcolor="orangered2"]
62 | v1 -> ot2:op0
63 | v2 -> ot2:op1
64 |
65 | // Compatibility
66 | edge [dir="forward",label="R",dir="both",color="slategray",fontcolor="slategray"]
67 | ot1 -> at1
68 | ot2 -> at2
69 | ot3 -> at3
70 |
71 | // Adya version/txn relationshiops
72 | //edge [dir="none",color="thistle3",style="dashed",label=""]
73 | //at1:op0 -> v0
74 | //at1:op1 -> v2
75 | //at2:op0 -> v1
76 | //at2:op1 -> v2
77 | //at3:op0 -> v0
78 | //at3:op1 -> v3
79 | }
80 |
--------------------------------------------------------------------------------
/images/severity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/images/severity.png
--------------------------------------------------------------------------------
/images/watch.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Watches file for changes and rebuilds it when written.
4 | inotifywait -e close_write,moved_to,create -m "$1" |
5 | while read -r directory events filename
6 | do
7 | echo $directory, $events, $filename
8 | if [ ${filename: -4} == ".dot" ] && [ "$events" = "CLOSE_WRITE,CLOSE" ]
9 | then
10 | basename=$(echo "${filename}" | cut -f 1 -d '.')
11 | dot -T png -o "${basename}.png" "$filename"
12 | fi
13 | done
14 |
--------------------------------------------------------------------------------
/paper/elle.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jepsen-io/elle/e9cff9fe2a02cbd6a21a9d1539524813b1c275d5/paper/elle.pdf
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject elle "0.2.5-SNAPSHOT"
2 | :description "Black-box transactional consistency checker based on cycle detection"
3 | :url "https://github.com/jepsen-io/elle"
4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
5 | :url "https://www.eclipse.org/legal/epl-2.0/"}
6 | :dependencies [[slingshot "0.12.2"]
7 | [com.aphyr/bifurcan-clj "0.1.3"]
8 | ; Has to be here for our java classes to use Clojure
9 | [org.clojure/clojure "1.12.0"]
10 | [dom-top "1.0.9"]
11 | [hiccup "1.0.5"]
12 | [org.clojure/tools.logging "1.3.0"]
13 | [rhizome "0.2.9"]
14 | [io.jepsen/history "0.1.5"]
15 | [jepsen.txn "0.1.2"]]
16 | :java-source-paths ["src"]
17 | ; We need jepsen.history.Op available before we can compile our java code
18 | ;:prep-tasks [["compile" "jepsen.history"]
19 | ; "javac"
20 | ; "compile"]
21 | :javac-options ["-target" "1.8" "-source" "1.8"
22 | ]
23 | :profiles {:dev {:dependencies [[com.gfredericks/test.chuck "0.2.15"]
24 | [io.jepsen/history.sim "0.1.1"]
25 | [org.clojure/test.check "1.1.1"]
26 | [spootnik/unilog "0.7.32"]]}}
27 | :jvm-opts ["-server"
28 | "-XX:-OmitStackTraceInFastThrow"
29 | ;"-XX:+PrintGC"
30 | ;"-agentpath:/home/aphyr/yourkit/bin/linux-x86-64/libyjpagent.so=disablestacktelemetry,exceptions=disable,delay=10000,usedmem=50"
31 | ]
32 | :repl-options {:init-ns elle.core}
33 | :test-selectors {:default (fn [m] (not (or (:perf m)
34 | (:interactive m)
35 | (:overflow m))))
36 | :all (fn [m] true)
37 | :perf :perf
38 | :focus :focus
39 | :overflow :overflow
40 | :interactive :interactive})
41 |
--------------------------------------------------------------------------------
/proof/.Traceable-Objects.thy.marks:
--------------------------------------------------------------------------------
1 | !d;832;832
2 |
--------------------------------------------------------------------------------
/proof/Anomaly.thy:
--------------------------------------------------------------------------------
1 | theory Anomaly
2 | imports Main DSG Observation
3 | begin
4 |
5 | section \Non-Cyclic Anomalies\
6 |
7 | text \We are now ready to encode notions of Adya's anomalies. An aborted read, or g1a, implies some
8 | pair of transactions exist such that one wrote v1 and aborted, and another read v1 and committed.\
9 |
10 | definition has_g1a :: "history \ bool" where
11 | "has_g1a h \ (\t1 t2 k v1 a v2 r. (t1 \ (all_atxns h)) \
12 | (t2 \ (all_atxns h)) \
13 | (\(a_is_committed t1)) \
14 | (a_is_committed t2) \
15 | (AWrite k v1 a v2 r) \ (all_aops t1) \
16 | (ARead k v2) \ (all_aops t2))"
17 |
18 | text \An empty history does not have an aborted read.\
19 |
20 | lemma "\(has_g1a (History {(register 1)} {} {(KeyVersionOrder 1 [[0]])}))"
21 | by (simp add:has_g1a_def)
22 |
23 | text "But this history does: we write 1 and fail to commit, then read it! We don't actually
24 | have to constrain the version order for this to happen, either."
25 |
26 | lemma "has_g1a (History {(register 1)}
27 | {(ATxn [(AWrite 1 [0] 1 [1] [])] False),
28 | (ATxn [(ARead 1 [1])] True)}
29 | kvo)"
30 | using has_g1a_def by fastforce
31 |
32 | (* TODO: intermediate read, dirty update *)
33 |
34 | section \Cyclic Anomalies\
35 |
36 | text \In an G0 anomaly, a cycle exists in the DSG composed purely of write dependencies.\
37 |
38 | definition has_g0 :: "history \ bool" where
39 | "has_g0 h \ (\path. (cycle (dsg h) path) \ ((path_dep_types path) = {WW}))"
40 |
41 | text \For example...\
42 |
43 | lemma ww_depends_ex:
44 | assumes "(distinct [x,y]) \ (distinct [v0,v1,v2])"
45 | shows "ww_depends (History objs
46 | {(ATxn [(AWrite x v0 a1 v1 r1), (AWrite y v1 a2 v2 r2)] True),
47 | (ATxn [(AWrite y v0 a1 v1 r1), (AWrite x v1 a2 v2 r2)] True)}
48 | {(KeyVersionOrder x [v0,v1,v2]),
49 | (KeyVersionOrder y [v0,v1,v2])})
50 | (ATxn [(AWrite x v0 a1 v1 r1), (AWrite y v1 a2 v2 r2)] True)
51 | (ATxn [(AWrite y v0 a1 v1 r1), (AWrite x v1 a2 v2 r2)] True)"
52 | apply (simp add:ww_depends_def)
53 | proof-
54 | obtain t1 t2 where "t1 = (ATxn [(AWrite x v0 a1 v1 r1), (AWrite y v1 a2 v2 r2)] True) \
55 | t2 = (ATxn [(AWrite y v0 a1 v1 r1), (AWrite x v1 a2 v2 r2)] True)"
56 | by blast
57 | oops
58 |
59 |
60 | lemma
61 | assumes "(distinct [x,y]) \ (distinct [v0,v1,v2])"
62 | shows "has_g0 (History objs
63 | {(ATxn [(AWrite x v0 a1 v1 r1), (AWrite y v1 a2 v2 r2)] True),
64 | (ATxn [(AWrite y v0 a1 v1 r1), (AWrite x v1 a2 v2 r2)] True)}
65 | {(KeyVersionOrder x [v0,v1,v2]),
66 | (KeyVersionOrder y [v0,v1,v2])})"
67 | apply (simp add:has_g0_def dsg_def path_def cycle_def)
68 | oops
69 |
70 | text \A G1c anomaly is a cycle comprised of write-write and write-read dependencies. We diverge from
71 | Adya here in classifying G0 and G1c as distinct classes; feels more useful to distinguish them.\
72 |
73 | definition has_g1c :: "history \ bool" where
74 | "has_g1c h \ (\path. (cycle (dsg h) path) \ ((path_dep_types path) = {WW,WR}))"
75 |
76 | text \And a G2 anomaly is a cycle involving read-write dependencies.\
77 |
78 | definition has_g2 :: "history \ bool" where
79 | "has_g2 h \ (\path. (cycle (dsg h) path) \ (RW \ (path_dep_types path)))"
80 |
81 |
82 |
83 | end
--------------------------------------------------------------------------------
/proof/DSG.thy:
--------------------------------------------------------------------------------
1 | theory DSG
2 | imports Main History
3 | begin
4 |
5 | section \Transaction Dependencies\
6 |
7 | text \We begin by formalizing three types of dependencies between transactions: wr-depends,
8 | ww-depends, and rw-depends. The wr-depends relation captures the idea of a transaction t2 reading
9 | another transaction T1's write.\
10 |
11 | definition wr_depends :: "history \ atxn \ atxn \ bool" where
12 | "wr_depends h t1 t2 \ \w1 r2. (a_is_committed t1) \
13 | (a_is_committed t2) \
14 | (w1 \ ext_awrites t1) \
15 | (r2 \ ext_areads t2) \
16 | ((key w1) = (key r2)) \
17 | ((post_version w1) = (pre_version r2))"
18 |
19 | text \We say t1 ww-depends t2 if t2 overwrote t1--that is, if t1 installed
20 | some version v1 of k, and t2 wrote v2, such that v1 came immediately before v2 in the version order
21 | of k.\
22 |
23 | definition ww_depends :: "history \ atxn \ atxn \ bool" where
24 | "ww_depends h t1 t2 \ \w1 w2.
25 | (a_is_committed t1) \
26 | (a_is_committed t2) \
27 | (w1 \ ext_awrites t1) \
28 | (w2 \ ext_awrites t2) \
29 | ((key w1) = (key w2)) \
30 | (is_next_in_history h (key w1) (apost_version w1) (apost_version w2))"
31 |
32 | lemma ww_depends_example: "ww_depends
33 | (History objs
34 | {(ATxn [(AWrite x v0 a1 v1 [])] True),
35 | (ATxn [(AWrite x v1 a2 v2 [])] True)}
36 | {(KeyVersionOrder x [v0, v1, v2])})
37 | (ATxn [(AWrite x v0 a1 v1 [])] True)
38 | (ATxn [(AWrite x v1 a2 v2 [])] True)
39 | = True"
40 | apply (simp add:ww_depends_def)
41 | done
42 |
43 | text \An rw-dependency is just like a ww-dependency, only transaction t1 *read* state just prior to
44 | t2's write.\
45 |
46 | definition rw_depends :: "history \ atxn \ atxn \ bool" where
47 | "rw_depends h t1 t2 \ \r1 w2.
48 | (a_is_committed t1) \
49 | (a_is_committed t2) \
50 | (r1 \ ext_areads t1) \
51 | (w2 \ ext_awrites t2) \
52 | ((key r1) = (key w2)) \
53 | (is_next_in_history h (key r1) (apost_version r1) (apost_version w2))"
54 |
55 | section \Direct Serialization Graphs\
56 |
57 | text \Now, we define a Direct Serialization Graph of a history as a graph where nodes are
58 | transactions in that history, and arcs are dependencies between them. We codify these three types of
59 | dependency with a type, and create a dependency type to wrap them. We call these aDeps for abstract
60 | dependencies; later, we'll define analogous observed dependencies.\
61 |
62 | datatype depType = WR | WW | RW
63 |
64 | datatype adep = ADep atxn depType atxn
65 |
66 | primrec adep_head :: "adep \ atxn" where
67 | "adep_head (ADep t _ _) = t"
68 |
69 | primrec adep_tail :: "adep \ atxn" where
70 | "adep_tail (ADep _ _ t) = t"
71 |
72 | class dep_typed =
73 | fixes dep_type :: "'a \ depType"
74 |
75 | instantiation adep :: "dep_typed"
76 | begin
77 | primrec dep_type_adep :: "adep \ depType" where
78 | "dep_type_adep (ADep _ t _) = t"
79 | instance ..
80 | end
81 |
82 | type_synonym dsg = "(atxn, adep) pre_digraph"
83 |
84 | text \The DSG of a history is defined by mapping each dependency to an edge in the graph.\
85 |
86 | definition dsg :: "history \ dsg" where
87 | "dsg h \ \verts = all_atxns h,
88 | arcs = ({(ADep t1 WR t2) | t1 t2. wr_depends h t1 t2} \
89 | {(ADep t1 WW t2) | t1 t2. ww_depends h t1 t2} \
90 | {(ADep t1 RW t2) | t1 t2. rw_depends h t1 t2}),
91 | tail = adep_tail,
92 | head = adep_head\"
93 |
94 |
95 | text \Now, we need to characterize a cycle in a DSG. TODO: I don't know how to use the definitions
96 | in the locales in Arc_Walk.thy, but I assume we should take advantage of them. Copy-pasting with
97 | slight mods for now.\
98 |
99 | type_synonym 'a path = "'a list"
100 |
101 | text \The list of vertices of a walk. The additional vertex argument is there to deal with the case
102 | of empty walks.\
103 |
104 | primrec path_verts :: "('a,'b) pre_digraph \ 'a \ 'b path \ 'a list" where
105 | "path_verts G u [] = [u]"
106 | | "path_verts G u (e # es) = tail G e # path_verts G (head G e) es"
107 |
108 | text \
109 | Tests whether a list of arcs is a consistent arc sequence,
110 | i.e. a list of arcs, where the head G node of each arc is
111 | the tail G node of the following arc.
112 | \
113 | fun cas :: "('a, 'b) pre_digraph => 'a \ 'b path \ 'a \ bool" where
114 | "cas G u [] v = (u = v)" |
115 | "cas G u (e # es) v = (tail G e = u \ cas G (head G e) es v)"
116 |
117 | definition path :: "('a,'b) pre_digraph \ 'a \ 'b path \ 'a \ bool" where
118 | "path G u p v \ u \ verts G \ set p \ arcs G \ cas G u p v"
119 |
120 | text \Is the given path a cycle in the given graph?\
121 |
122 | definition cycle :: "('a,'b) pre_digraph \ 'b path \ bool" where
123 | "cycle G p \ \u. path G u p u \ distinct (tl (path_verts G u p)) \ p \ []"
124 |
125 | text \We would like a cycle whose edges are all of the given set of dependency types.\
126 |
127 | primrec path_dep_types :: "('a::dep_typed) path \ depType set" where
128 | "path_dep_types [] = {}" |
129 | "path_dep_types (x # xs) = (Set.insert (dep_type x) (path_dep_types xs))"
130 |
131 | end
--------------------------------------------------------------------------------
/proof/FinMap.thy:
--------------------------------------------------------------------------------
1 | theory FinMap
2 | imports Main HOL.Map
3 | begin
4 |
5 | section \Finite Maps\
6 |
7 | text \A finite map can be thought of as a set of (k,v) pairs, with unique keys. Think an erlang
8 | proplist. We use this as a wrapper around Isabelle's Maps because those maps use optional, and it
9 | makes quantification over kv pairs kind of messy.\
10 |
11 | definition keys :: "('a,'b) map \ 'a set" where
12 | "keys m = dom m"
13 |
14 | definition vals :: "('a,'b) map \ 'b set" where
15 | "vals m = ran m"
16 |
17 | definition kv_pairs :: "('a,'b) map \ ('a \ 'b) set" where
18 | "kv_pairs m = {(k,v) | k v. (k \ (keys m)) \ ((Some v) = (m k))}"
19 |
20 | text \A function for building map literals out of keys and values.\
21 | definition map1 :: "'a \ 'b \ ('a,'b) map" where
22 | "map1 k v \ (Map.empty(k:=Some v))"
23 |
24 | definition map2 :: "'a \ 'b \ 'a \ 'b \ ('a,'b) map" where
25 | "map2 k1 v1 k2 v2 \ (Map.empty(k1:=Some v1, k2:=Some v2))"
26 |
27 | end
--------------------------------------------------------------------------------
/proof/History.thy:
--------------------------------------------------------------------------------
1 | theory History
2 | imports Main HOL.Map Transaction VersionOrder
3 | begin
4 |
5 | section \History\
6 |
7 | text \An abstract history, as defined by Adya, is a set of objects, transactions over them,
8 | and a version order.\
9 |
10 | datatype history = History "object set" "atxn set" "versionOrder"
11 |
12 | class all_objects =
13 | fixes all_objects :: "'a \ object set"
14 |
15 | instantiation history :: all_objects
16 | begin
17 | primrec all_objects_history :: "history \ object set" where
18 | "all_objects_history (History objs txns vo) = objs"
19 | instance ..
20 | end
21 |
22 | primrec all_atxns :: "history \ atxn set" where
23 | "all_atxns (History obs txns vo) = txns"
24 |
25 | text \It's going to be handy to get every version, operation, every write, and read out of a
26 | history. We say a history's versions and writes are those in its transaction set, not every
27 | version in its objects.\
28 |
29 | instantiation history :: all_versions
30 | begin
31 | primrec all_versions_history :: "history \ version set" where
32 | "all_versions_history (History objs txns vo) = \{all_versions t | t. t \ txns}"
33 | instance ..
34 | end
35 |
36 | instantiation history :: all_aops
37 | begin
38 | primrec all_aops_history :: "history \ aop set" where
39 | "all_aops_history (History objs txns vo) = \{all_aops t | t. t \ txns}"
40 | instance ..
41 | end
42 |
43 | text \We constrain the version order for each object to be compatible with some trace in that
44 | object's version graph.\
45 |
46 | primrec key_version_order_is_in_object :: "keyVersionOrder \ object \ bool" where
47 | "key_version_order_is_in_object (KeyVersionOrder k vl) obj = (
48 | (k = key obj) \ (\tr. (is_trace_of obj tr (last vl))))"
49 |
50 | text \A well-formed history satisfies this property for every key in the version order; there's got
51 | to be a corresponding object where that version order is a legal trace.\
52 |
53 | primrec version_order_is_in_corresponding_object :: "history \ bool" where
54 | "version_order_is_in_corresponding_object (History objs txns vo) =
55 | (\ kvo. kvo \ vo \ (\obj. (obj \ objs) \ (key_version_order_is_in_object kvo obj)))"
56 |
57 | text \Histories also need to ensure that their transactions are over their objects. For every op
58 | in the history, we ensure that op's key identifies an object in the history, that that op's versions
59 | are in that object, and if it's a write that the write is in the transaction graph of that object.\
60 |
61 | primrec transactions_are_over_objects :: "history \ bool" where
62 | "transactions_are_over_objects (History objs txns vo) =
63 | (\ op. (op \ (all_aops (History objs txns vo))) \
64 | (let k = (key op) in
65 | (\obj. (k = (key obj)) \
66 | (obj \ objs) \
67 | ((all_versions op) \ (all_versions obj)) \
68 | (if Read = (op_type op) then True else op \ (all_aops obj)))))"
69 |
70 |
71 |
72 | text \We need to be able to discuss whether a specific transaction wrote a given key and version, in
73 | a history.\
74 |
75 | (*
76 | primrec wrote :: "history \ atxn \ key \ version \ bool" where
77 | "wrote h t k v = "
78 | *)
79 |
80 | text \A well-formed history is made up of well formed objects, transactions, and a version order,
81 | and ensures transactions are over objects, and the version orders are in their corresponding
82 | objects.\
83 |
84 | primrec wf_history :: "history \ bool" where
85 | "wf_history (History objs txns vo) = (let h = (History objs txns vo) in
86 | (\obj. (obj \ objs) \ (wf_object obj)) \
87 | (\t. (t \ txns) \ (wf_atxn t)) \
88 | (wf_version_order vo) \
89 | ((transactions_are_over_objects h) \
90 | (version_order_is_in_corresponding_object h)))"
91 |
92 | text \