• This repository has been archived on 31/Jul/2023
  • Stars
    star
    563
  • Rank 77,348 (Top 2 %)
  • Language
    Clojure
  • License
    Other
  • Created about 5 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A distributed database-system implementing an entity-attribute-value data-model that is time-aware, accumulative, and atomically consistent

What is Eva?

Eva is a distributed database-system implementing an entity-attribute-value data-model that is time-aware, accumulative, and atomically consistent. Its API is by-and-large compatible with Datomic's. This software should be considered alpha for the purposes of quality and stability. Check out the FAQ for more info.

Getting Started

If you are brand new to Eva, we suggest reading through this entire readme to familiarize yourself with Eva as a whole. Afterwards, be sure to check out the Eva tutorial series, which break down and go over almost everything you will want to know.

Development

Required Tools

  1. Java Development Kit (JDK) v8
  2. Leiningen Build Tool

Example: Hello World

First we kick off the repl with:

lein repl

Next we create a connection (conn) to an in-memory Eva database. We also need to define the fact (datom) we want to add to Eva. Finally we use the transact call to add the fact into the system.

(def conn (eva/connect {:local true}))
(def datom [:db/add (eva/tempid :db.part/user)
            :db/doc "hello world"])
(deref (eva/transact conn [datom]))

Note: deref can be used interchangeably with the @ symbol.

Now we can run a query to get this fact out of Eva. We don't use conn to make a query but rather we obtain an immutable database value like so:

(def db (eva/db conn))

Next we execute a query that returns all entity ids in the system matching the doc string "hello world".

(eva/q '[:find ?e :where [?e :db/doc "hello world"]] db)

If we want to return the full representation of these entities, we can do that by adding pull to our query.

(eva/q '[:find (pull ?e [*]) :where [?e :db/doc "hello world"]] db)

Project Structure

  1. project.clj contains the project build configuration
  2. core/* primary releasable codebase for Eva Transactor and Peer-library
    1. core/src clojure source files
    2. core/java-src java source files
    3. core/test test source files
    4. core/resources non-source files
  3. dev/* codebase used during development, but not released
    1. dev/src clojure source files
    2. dev/java-src java source files
    3. dev/test test source files
    4. dev/resources non-source files
    5. dev/test-resources non-source files used to support integration testing

Development Tasks

Running the Test Suite

lein test

Configuration

Eva exposes a number of configuration-properties that can be configured using java system-properties. Some specific configuration-properties can also be configured using environment-variables.

The eva.config namespace, linked here, contains descriptions and default values for the config vars.

About the Eva Data Model

Entity-Attribute-Value (EAV)

EAV data-entities consist of:

  1. a distinct entity-id
  2. 1-or-more attribute-value pairs associated with a single entity-id

EAV data can be represented in the following (equivalent) forms:

  1. as an object or map:
    {:db/id 12345,
     :attribute1 "value1",
     :attribute2 "value2"}
  2. as a list of EAV tuples:
    [
      [12345, :attribute1, "value1"],
      [12345, :attribute2, "value2"]
    ]

Time-Aware

To make the EAV data-model time-aware, we extend the EAV-tuple into an EAVT-tuple containing the transaction-id (T) that introduced the tuple:

[
;;  E      A            V         T
   [12345, :attribute1, "value1", 500],
   [12345, :attribute2, "value2", 500]
]

Accumulative

To make the EAVT data-model accumulative, we extend the EAVT-tuple with a final flag that indicates if the EAV information was added or removed at the transaction-id (T).

[
;;  E      A            V         T    added?
   [12345, :attribute1, "value1", 500, true],
   [12345, :attribute2, "value2", 500, true]
]

Under this model, common data operations (create, update, delete) are represented like this:

  • Create: a single tuple with added? == true
[[12345, :attribute1, "create entity 12345 with field :attribute1 at transaction 500", 500, true]]
  • Delete: a single tuple with added? == false
[[12345, :attribute1, "create entity 12345 with field :attribute1 at transaction 500", 501, false]]
  • Update: a pair of deletion and creation tuples
[
 ;; At transaction 502
 ;;   invalidate the old entry for :attribute2
      [12345, :attribute2, "old-value", 502, false]
 ;;   add a new entry for :attribute2
      [12345, :attribute2, "new-value", 502, true]
]

The complete history of the database is the cumulative list of these tuples.

Atomic Consistency

Data-updates are submitted as transactions that are processed atomically. This means that when you submit a transaction, either all the changes in the transaction are applied, or none of the changes are applied.

Transactions

Transactions are submitted as a list of data-modification commands.

The simplest data-modification commands (:db/add, :db/retract) correspond to the accumulative tuples described above:

[
  [:db/retract 12345 :attribute2 "old-value"]
  [:db/add 12345 :attribute2 "new-value"]
]

When this transaction is committed it will produce the following tuples in the database history (where <next-tx> is the next transaction-number):

[
  [12345, :attribute2, "old-value", <next-tx>, false]
  [12345, :attribute2, "new-value", <next-tx>, true]
]

Using Object/Map form in transactions

In addition to the command-form, you can also create/update data using the object/map form of an entity:

[
  {:db/id 12345
   :attribute1 "value1"
   :attribute2 "value2"}
]

This form is equivalent to the command-form:

[
  [:db/add 12345 :attribute1 "value1"]
  [:db/add 12345 :attribute2 "value2"]
]

Schemas

Because all stored data reduces to EAVT tuples, schemas are defined per Attribute, rather than per Entity.

Schemas definitions are simply Entities that have special schema-attributes.

Defining the schema for :attribute1:

[
  {:db/id #db/id[:db.part/db]
   :db/ident :attribute1
   :db/doc "Schema definition for attribute1"
   :db/valueType :db.type/string
   :db/cardinality :db.cardinality/one
   :db.install/_attribute :db.part/db}
]

Taking each key-value pair of the example in turn:

  • :db/id #db/id[:db.part/db]: declares a new entity-id in the :db.part/db id-partition
  • :db/ident :attribute1: declares that :attribute1 is an alias for the entity-id
  • :db/doc "Schema definition for attribute1": human-readable string documenting the purpose of :attribute1
  • :db/valueType :db.type/string: declares that only string values are allowed for :attribute1
  • :db/cardinality :db.cardinality/one: declares that an entity may no-more-than one :attribute1. This means that for an given entity-id, there will only ever be one current tuple of [<entit-id> :attribute1 <value>]. Adding a new tuple with this attribute will cause any existing tuple to be removed.
  • :db.install/_attribute :db.part/db: declares that this :attribute1 is registered with the database as an installed attribute

Components

Running with Docker

The included docker compose can be used to spin up a completely integrated Eva environment. This includes:

To spin up said environment run the following commands:

make gen-docker-no-tests # to build Eva with the latest changes
make run-docker

To shut down the the environment use the following command:

make stop-docker

In order to open a repl container that can talk to the environment use:

make repl

And run the following to initially setup the repl environment:

(require '[eva.catalog.client.alpha.client :as catalog-client])
(def config (catalog-client/request-flat-config "http://eva-catalog:3000" "workiva" "eva-test-1" "test-db-1"))
(def conn (eva/connect config))

Finally, test that everything is working with an empty transaction:

(deref (eva/transact conn []))

A similar result to this should be expected:

{:tempids {}, :tx-data (#datom[4398046511105 15 #inst "2018-06-06T17:35:07.516-00:00" 4398046511105 true]), :db-before #DB[0], :db-after #DB[1]}

Additional Resources

FAQ

Is this project or Workiva in any way affiliated with Cognitect?

No. Eva is its own project we built from the ground up. The API and high-level architecture are largely compatible with Datomic, but the database, up to some EPL code, was entirely built in-house. We have a list of the most notable API differences here.

Should I use Eva instead of Datomic?

If you are looking for an easy system to move to production quickly, almost certainly not. Eva is far less mature and has seen far less time in battle. Datomic Cloud is an amazing (and supported) product that is far easier to stand up and run with confidence. Eva is provided as-is.

What are the key differences between Eva and Datomic?

There are a handful of small API differences in the Peer, whereas the Clients are quite distinct. For example, a Connection in Eva is constructed using a baroque configuration map, not a string. From an operational standpoint, Datomic is far more turn-key. There are also likely some low-level architectural differences between the systems that will cause them to exhibit different run-time characteristics. For example, our indexes are backed by a persistent B^𝜀-tree, whereas Datomic's indexes seem to exhibit properties more like a persistent log-structured merge-tree. For a more detailed list check here.

Why did Workiva build Eva?

Workiva's business model requires fine-grained and scalable multi-tenancy with a degree of control and flexibility that fulfill our own evolving and unique compliance requirements. Additionally, development on Eva began before many powerful features of Datomic were released, including Datomic Cloud and Datomic Client.

Why is Workiva open sourcing Eva?

The project by-and-large is nearly feature complete and we believe is generally technically sound. Workiva has decided to discontinue closed development on Eva, but sees a great deal of potential value in opening the code base to the OSS community. It is our hope that the community finds value in the project as a whole.

What will Workiva's ongoing contributions to Eva be?

Eva will likely still continue to be maintained and matured in 10% time and by previous contributors on their personal time.

Maintainers and Contributors

Active Maintainers

Previous Contributors

Listed, in transaction log style, in order of addition to the project:

More Repositories

1

go-datastructures

A collection of useful, performant, and threadsafe Go datastructures.
Go
7,347
star
2

over_react

A library for building statically-typed React UI components using Dart.
Dart
422
star
3

react-dart

Dart Bindings for React JS
JavaScript
410
star
4

frugal

Thrift improved
Go
140
star
5

built_redux

an implementation of redux written in dart that enforces immutability
Dart
125
star
6

dart_dev

Centralized tooling for Dart projects. Consistent interface across projects. Easily configurable.
Dart
74
star
7

dart_codemod

A library that makes it easy to write and run automated code modifications on a codebase.
Dart
60
star
8

state_machine

Easily create a finite state machine and define legal state transitions. Listen to state entrances, departures, and transitions.
Dart
59
star
9

dependency_validator

A tool to help you find missing, under-promoted, over-promoted, and unused dependencies.
Dart
55
star
10

go-rest

A Go library that makes it easy to build a flexible and (mostly) unopinionated REST API with little ceremony.
Go
48
star
11

opentelemetry-dart

Dart
48
star
12

furious

Fast and modular async task library for Google App Engine.
Python
37
star
13

w_flux

A Dart app architecture library with uni-directional data flow inspired by RefluxJS and Facebook's Flux.
Dart
25
star
14

morphe

A Clojure utility for defining and applying aspects to function definitions.
Clojure
25
star
15

w_transport

A platform-agnostic transport library for sending and receiving data over HTTP and WebSocket.
Dart
22
star
16

aws-lambda-fsm-workflows

A Python framework for developing finite state machine-based workflows on AWS Lambda.
Python
21
star
17

dart_to_js_script_rewriter

A pub transformer that Rewrites Dart script tags to JavaScript script tags, eliminating 404s and speeding up initial loads. Use when building for deployment.
Dart
21
star
18

w_module

Base module classes with a well defined lifecycle for modular Dart applications.
Dart
19
star
19

go-hystrimp

An ergonomic implementation of Hystrix fault-tolerance principles for Go developers.
Go
19
star
20

platform_detect

A library for detecting browser and platform type and version.
Dart
18
star
21

fluri

Fluri is a fluent URI library for Dart built to make URI mutation easy.
Dart
18
star
22

gae-financials

Simple demo app to illustrate developing real world applications on Google App Engine.
Python
18
star
23

thrift-nats

A library that adds support for using NATS as a Thrift RPC transport.
Go
14
star
24

wf-uicomponents

Mobile-optimized, composable UI components that support a rich HTML5 user experience.
JavaScript
13
star
25

wGulp

Opinionated Suite of Gulp Tasks for JavaScript and TypeScript projects.
JavaScript
12
star
26

scip-dart

Dart
11
star
27

utiliva

A collection of helpful little utilities for Clojure.
Clojure
11
star
28

webdev_proxy

A simple HTTP proxy for the webdev serve command (a tool authored by the dart team) that adds support for rewriting certain requests, namely rewriting 404s to instead serve the root index (/index.html). This allows developers to work on browser applications (like our own) that use HTML5 routing while still being able to refresh and/or navigate directly to deep links.
JavaScript
11
star
29

r_tree

A recursive RTree library written in Dart.
Dart
10
star
30

opentracing_dart

This library is the Open Tracing API written in Dart. It is intended for use both on the server and in the browser.
Dart
10
star
31

w_common

A collection of helpful utilities for use in Dart projects.
Dart
10
star
32

styleguide

9
star
33

pdfjs_dart

Dart bindings for Mozilla's PDF.js library
JavaScript
7
star
34

flowgraph

A Clojure library for fast, concurrent, asynchronous data processing using directed graphs. Decursus supports cyclic graphs. Though it is not distributed, it does have primitive tools for minimizing communication overhead in the event that some processing steps involve calls to distributed resources.
Clojure
6
star
35

wf-common

A collection of helpful utilities for use in JavaScript projects.
JavaScript
6
star
36

over_react_test

A library for testing OverReact components
Dart
6
star
37

dart_transformer_utils

Utilities relating to code generation, Dart analyzer, logging, etc. for use in Pub transformers.
Dart
6
star
38

w_service

Dart
5
star
39

react_testing_library

Dart bindings for the JS dom-testing-library and react-testing-library packages, which provide simple and complete DOM/React testing utilities that encourage good testing practices.
Dart
5
star
40

tesserae

A Clojure library that abstracts over promises, futures, delays, etc. with chaining and cancellations.
Clojure
4
star
41

w-mobile-kit

A Swift library containing various custom UI components to provide functionality outside of the default libraries.
Swift
4
star
42

wf-grunt

Opinionated Suite of Grunt Tasks for JavaScript and TypeScript projects.
JavaScript
4
star
43

goverge

A golang multi package coverage reporting tool.
Python
3
star
44

workiva_analysis_options

Workiva's shared Dart static analysis options
Dart
3
star
45

autoPaw

JavaScript
3
star
46

paw

A touch / touch gesture simulation library for JavaScript.
JavaScript
3
star
47

sockjs-dart-client

A Dart client library for SockJS.
Dart
3
star
48

lsif_indexer

Dart
2
star
49

eva-client-service

The Eva client service is an Eva peer which exposes Eva functionality to other languages through a REST interface.
Java
2
star
50

over_react_codemod

Codemods to help consumers of over_react automate the migration of UI component code.
Dart
2
star
51

font_face_observer

Load and unload fonts in the browser with a Promise based API. Simple, small and efficient. It will use the FontFace api if available, otherwise it falls back to a Dart port of https://github.com/bramstein/fontfaceobserver
Dart
2
star
52

sockjs_client_wrapper

A Dart wrapper around the SockJS Client. Uses the js Dart package to interop with the JS lib.
JavaScript
2
star
53

eva-catalog

Provides a central repository (and client) for handling the configuration maps used to connect to EVA
Clojure
1
star
54

lazy-tables

A set of tools for lazy relational algebra
Clojure
1
star
55

test_html_builder

Dart builder that generates HTML files from templates for dart tests.
Dart
1
star
56

abide

A command line tool to manage analysis_options.yaml and check if it abides by requirements.
Dart
1
star
57

barometer

A thin clojure wrapper over Coda Hale's metrics library for the JVM.
Clojure
1
star
58

ichnaie

A handful of Clojure utilities for easing project integration with the OpenTracing API
Clojure
1
star
59

recide

Provides utilities for defining standard ex-info forms, as well as the capacity for checking at compile-time that they are being used as intended
Clojure
1
star