Liking cljdoc? Tell your friends :D

Triplox logo

Website License Discord

🚧 WARNING: Alpha Software 🚧 Triplox is alpha software. The index encoding has not yet stabilized. There will be bugs. There will be ingestion and congestion issues. Incremental queries will likely be slow. Do not use this in production.

Triplox

Triplox is a Datomic-inspired general-purpose database on top of object storage. The backbone of the engine is SlateDB, a key-value store on top of object-storage. Datomic is the main inspiration and the data model, transaction semantics and query API closely follow Datomic.

The goals of Triplox are roughly the following (in no particular order):

  • Object storage first. In it's final version Triplox should simply need a single S3 bucket for deployment. This is currently not the case. See Architecture below.
  • The Datomic Data model and API as main inspiration. Datomic is awesome. Lets bring it directly to object storage.
  • A Client/Server architecture. I hope that this will open the door to ecosystems outside of the JVM (where Datomic has had it's main success).
  • Incremental Datalog queries. You should be able to dynamically subscribe and detach from incremental Datalog queries. This is the most experimental part of Triplox and will need quite a bit of engineering effort to get right, make fast, and fully support of all features of Datalog.

Getting started

The easiest way to test Triplox is to just pull the docker image and start an in-memory or local node.

docker pull ghcr.io/fiv0/triplox:0.1.0-alpha.5
docker run -p 5490:5490 ghcr.io/fiv0/triplox:0.1.0-alpha.5

This will start a Triplox server with an in-memory DB to which you can connect at 5490. If you want an persistent local node, you start the image with

docker run -p 5490:5490 -e TRIPLOX_STORAGE=local -v triplox-data:/var/lib/triplox  ghcr.io/fiv0/triplox:0.1.0-alpha.5

In case you are already convinced and want to deploy Triplox in a distributed setting I suggest you have a look at the Operations section on the website.

There is also the option to run Triplox in dev mode which is particular useful for testing.

docker run -p 5490:5490 -e TRIPLOX_STORAGE=dev ghcr.io/fiv0/triplox:0.1.0-alpha.5

In this case a new in-memory DB is created on every connection.

Afterwards you connect with your favorite client.

(require '[xyz.triplox.api :as tc])

(def conn (tc/connect "localhost" 5490))

;; schema
(tc/transact conn [{:db/ident :name
                    :db/valueType :db.type/string
                    :db/cardinality :db.cardinality/one}
                   {:db/ident :age
                    :db/valueType :db.type/long
                    :db/cardinality :db.cardinality/one}])

;; data
(tc/transact conn [{:name "alice" :age 30}
                   {:name "bob" :age 25}])

;; query
(def db (tc/db conn))
(tc/q db '{:find [?e ?name ?age]
           :where [[?e :name ?name]
                   [?e :age ?age]]})
;; => [[8796093022209 "alice" 30] [8796093022208 "bob" 25]]

Clients

If you would like to create another client there is a PROTOCOL design doc that should help. Currently the HTTP/2 server only supports a custom MessagePack encoding scheme. We plan to add support for transit+json and transit+msgpack in the future.

Architecture

By making object storage the single source of truth, you get separation of storage and compute. SlateDB has a single writer and many readers architecture and that directly translates to Triplox. In that sense it's similar to Datomic. Triplox sits more in the traditional client/server camp compared to Datomic where the peer library gets embedded into the application code. Queries run on the server in Triplox and this makes certain query patterns different to Datomic. For example limiting your query results would be done with :limit in a EDN Datalog query instead of doing it application code side.

The layout of the indices and also SlateDB's focus are OLTP queries. If you expect columnar layout performance for OLAP style queries, Triplox is not the right fit. This doesn't mean Triplox doesn't support aggregates, it will just not beat something like DuckDB on these types of queries. Also keep in mind that the architecture favors read heavy workloads. Triplox is currently single writer.

A typical setup for Triplox with 3 nodes (1 primary writer node + 2 readers nodes) would then roughly look like the diagram below. Transactions get send to the log to get a total-order and then indexed through the primary indexing node into SlateDB. Reads can then be served from the primary node immediately and form any reader node as soon as the reader nodes receive new WALs (Write-Ahead Logs) from Object Storage.

There are some design decisions that still are up for grabs. In particular the external log has been a thorn in my side and I would like to get rid of it. See Open Questions > Log on the website.


                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚                   Object Storage (S3)                       β”‚
                   β”‚                                                             β”‚
                   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                   β”‚  β”‚   SlateDB   β”‚      β”‚   SlateDB   β”‚      β”‚   SlateDB   β”‚  β”‚
                   β”‚  β”‚  (Writer)   β”‚      β”‚  (Reader 1) β”‚      β”‚  (Reader 2) β”‚  β”‚
                   β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β”‚
                   β”‚         β”‚                    β”‚                    β”‚         β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚                    β”‚                    β”‚
     Queries/Indices         β–² read/write         β–Ό read               β–Ό read
                             β”‚                    β”‚                    β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
        β”‚         Writer Node         β”‚  β”‚  Reader Node 1  β”‚  β”‚  Reader Node 2 β”‚
        β”‚                             β”‚  β”‚                 β”‚  β”‚                β”‚
        β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚  β”‚                 β”‚  β”‚                β”‚
  β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β–Άβ”‚   Indexer    β”‚       β”‚  β”‚                 β”‚  β”‚                β”‚
  β”‚     β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β”‚                 β”‚  β”‚                β”‚
  β”‚     β”‚                             β”‚  β”‚                 β”‚  β”‚                β”‚
  β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚                   β”‚
  β”‚  Transactions     β”‚ write
  β”‚                   β–Ό
  β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚     β”‚                                                                      β”‚
  β”‚     β”‚                 Log (Kafka, S2, WAL3, etc.)                          β”‚
  β”‚     β”‚                                                                      β”‚
  β”‚     β”‚    β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”                 β”‚
  └─────┼───── tx0 β”‚ tx1 β”‚ tx2 β”‚ tx3 β”‚ tx4 β”‚ tx5 β”‚ tx6 β”‚ ... β”‚                 β”‚
   read β”‚    β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜                 β”‚
        β”‚                                                                      β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Getting involved

This project is very much WIP and any help is appreciated. Discussions and feedback happen on Discord.

There is lots of things to work on. Feel free to open tickets for features, bugs or general ideas. If you are building something more involved it's likely better to discuss it first (either on Discord or in a ticket) and see if it fits the projects scope.

Acknowledgements

The primary inspiration is Datomic and you will see it's impact throughout the project. Mentat (from which the edn crate is copied) has been a great inspiration and help in designing the transaction pipeline of Triplox. The folks at Feldera have created the very clean and elegant DBSP theory without the incremental queries would not be possible.

Compatibility

The goal is not to have a 1-to-1 correspondence of features with Datomic. Datomic is the main inspiration and we'll strive to stay close to the Datomic APIs, but don't guarantee feature parity nor identical behavior in all cases. The main differences will currently show up in the transaction pipeline. I am currently not dealing with schema updates. There are also some edge cases with tempid + upsert resolution that I am currently not dealing correctly with.

There are some more open questions that I tried to write down on the website

Licence

Triplox is licensed under the Apache License, Version 2.0.

The edn crate was orginally copied from mentat and is also licenced under Apache Licence, Version 2.0.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
β†’Move to next article
Ctrl+/Jump to the search field
Γ— close