InfoQ
Separating transactional writes from immutable, segment-based reads allows application servers to execute queries locally and cache data indefinitely without coordination.
AI-assisted summary of Rich Hickey: Deconstructing the Database — original source. Check the original for full context.
Source published:
Key ideas
- Traditional databases conflate process (writing and coordinating novelty) with perception (querying and indexing), binding both to a monolithic server box with destructive in-place updates. (9:58)
- Datomic deconstructs database architecture into three decoupled roles: a dedicated single-writer transactor, commodity key-value storage (such as DynamoDB or an RDBMS), and stateless peer query engines executing inside the application tier. (38:36)
- Because storage is treated as immutable index segments structured as persistent trees, index data can be cached indefinitely in application memory or Memcached without requiring cache invalidation logic. (43:21)
- Preserving time as a first-class dimension in atomic facts ([Entity, Attribute, Value, Transaction]) enables zero-cost historical queries (as-of), speculative what-if branching, and coordinated asynchronous workflows using a fixed database basis. (17:28)
Detailed notes
## Background This talk by Rich Hickey, author of Clojure and designer of Datomic, was presented at JaxConf 2012 and published on YouTube by InfoQ on August 23, 2012. According to the page description, Hickey explores database architectures by deconstructing traditional models to present a new approach to information systems.
## TL;DR Hickey deconstructs traditional monolithic databases by critiquing update-in-place mutation, the coupling of query processing with transaction coordination, and the loss of historical information. Drawing on functional programming and persistent data structures, he introduces an information model centered on immutable atomic facts, known as datoms, preserved across an append-only timeline. The resulting architecture decouples the database into distinct roles: a single-writer transactor for coordination, commodity storage services for durable persistence, and peer application servers that run expressive queries locally against cached immutable segments. This design eliminates read locks, enables zero-cost time-travel and speculative querying, and allows elastic horizontal scaling of query workloads.
## Atmosphere & style The presentation carries the measured, analytical, and uncompromisingly direct tone characteristic of Rich Hickey's keynote lectures. Blending architectural philosophy with dry, deadpan humor, Hickey challenges longstanding industry defaults—from the pitfalls of polling to the trade-offs of key-value stores—with pragmatic skepticism. Watching the talk feels like an intellectual masterclass where entrenched systems engineering assumptions are dismantled layer by layer, building toward an elegant and coherent reimagining of data architecture.
## Bulletin - [09:58](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=598s) Traditional databases conflate process (writing and coordinating novelty) with perception (querying and indexing), binding both to a monolithic server box with destructive in-place updates. - [38:36](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2316s) Datomic deconstructs database architecture into three decoupled roles: a dedicated single-writer transactor, commodity key-value storage (such as DynamoDB or an RDBMS), and stateless peer query engines executing inside the application tier. - [43:21](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2601s) Because storage is treated as immutable index segments structured as persistent trees, index data can be cached indefinitely in application memory or Memcached without requiring cache invalidation logic. - [17:28](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1048s) Preserving time as a first-class dimension in atomic facts ([Entity, Attribute, Value, Transaction]) enables zero-cost historical queries (as-of), speculative what-if branching, and coordinated asynchronous workflows using a fixed database basis.
## Key takeaways - [03:02](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=182s) Traditional client-server databases suffer from the 'basis problem' because treating the database as a single mutable place prevents running multiple independent queries against a stable snapshot of reality. - [07:48](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=468s) In-place updates destroy historical context, whereas pre-digital record keeping modeled novelty via immutable append-only ledgers. - [09:58](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=598s) Traditional monolithic database architectures couple transactions, indexing, disk I/O, and query processing onto a single server, forcing developers to build fragile external caching tiers. - [17:28](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1048s) Datomic models information as atomic facts called 'datoms' comprising Entity, Attribute, Value, and Transaction [E A V Tx], making transactions first-class entities that record provenance. - [22:22](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1342s) State changes are fundamentally represented by two primitive operations—assertions and retractions—where retractions record that a fact ceased to hold without erasing its historical existence. - [25:54](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1554s) Separating process (transaction coordination) from perception (query execution) allows queries to run locally within application processes without taking read locks or consuming transactor resources. - [28:13](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1693s) Datomic applies persistent data structures to disk through structural sharing and path copying, organizing indexes into trees of immutable segments rather than flat files or mutable B-trees. - [30:14](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1814s) Network storage latency is negligible compared to the RAM-to-disk gap, meaning systems should optimize for in-memory locality while treating disk persistence as an abstract key-value service. - [40:28](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2428s) Commodity key-value stores like Amazon DynamoDB or standard relational databases serve as redundant, decoupled storage backends without requiring specialized hardware affinity with the transactor. - [43:21](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2601s) Moving the query engine directly into the application server tier as a library enables aggressive caching of immutable segments in local memory and Memcached with zero cache invalidation. - [45:41](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2741s) Peers pull only their active working sets on demand and resolve queries via an in-memory merge-join between cached persistent index trees and live incoming novelty. - [51:15](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3075s) Atomic compound updates are achieved through transaction functions that execute as pure functions against an immutable database value, returning primitive assertions and retractions. - [56:19](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3379s) A dedicated single-writer transactor dedicates all resources to consistency and write coordination, enabling stateless peer query engines to scale horizontally via application auto-scaling groups. - [58:37](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3517s) The as-of query capability enables time-travel analysis across past snapshots without modifying query logic or adding complex timestamp filters to joins. - [01:01:06](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3666s) Peers can construct speculative database values locally to execute hypothetical 'what-if' queries in memory without writing to persistent storage or locking the transactor. - [01:03:16](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3796s) Communicating a database basis t allows distributed processes to coordinate asynchronous workflows deterministically against an immutable, epochal snapshot.
## Detailed walkthrough ### [00:11](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=11s) Tackling Complexity and the "Out of the Tar Pit" Miracle
Rich Hickey opens by clarifying that while this talk explores the underpinnings of Datomic—the database system he spent the previous two years designing—it is fundamentally not a product sales pitch. Instead, it is an exploration of the design ideas and trade-offs needed to solve the core problem facing software developers: the pervasive complexity of programming with databases. When Hickey playfully asks the audience how many people find working with databases "easy and trouble-free," virtually nobody agrees.
To diagnose this friction, Hickey points to the influential paper *Out of the Tar Pit* by Ben Moseley and Peter Marks. In analyzing why software becomes tangled, the authors argued that nearly all programming complexity boils down to two culprits: state and control. Their proposed remedy was to adopt functional and declarative programming, paired with an internal relational model for application data. Hickey agrees with their diagnosis and praises the paper, but highlights a gaping omission in their proposal. He likens their design to the classic cartoon of a mathematician covering a blackboard in equations, only to write in the corner: *“and then a miracle occurs.”*
The paper sketched a clean functional and declarative relational model for applications, but completely punted on the mechanism of change: how data actually gets updated when something happens in the real world. Hickey terms this omitted issue "process." Real-world applications encounter ongoing novelty, and an architecture cannot simply wave away the dirty mechanics of state transitions if it hopes to provide a coherent programming model.
> “The authors sort of identified a bunch of problems related to complexity in programming and they basically said that all complexity boils down to, you know, two flavors: one has to do with state and the other has to do with control.” — Rich Hickey
- The paper *Out of the Tar Pit* identifies state and control as the primary drivers of system complexity. - While functional programming and relational logic eliminate many bugs, real-world systems must handle "process"—the arrival of novel information over time.
---
[03:02](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=182s) Declarative Programming, Client-Server Distance, and the Basis Problem
Hickey argues that a fresh look at databases must fully embrace declarative programming. Outside of artificial intelligence researchers, SQL remains the most common declarative tool everyday programmers encounter. Relational set logic is vastly superior for data manipulation compared to the step-by-step iteration common in general-purpose languages. Hickey emphasizes that this remains true even when compared to functional languages, and is infinitely better than object-oriented languages, which handle data poorly by comparison.
However, traditional client-server database architectures put declarative power "over there" across a network wire. This separation creates the "basis problem." When an application needs to make a calculation or business decision based on data, it lacks an immutable foundation: the database in its entirety is treated as a mutable "place" that shifts underfoot from one moment to the next. Developers fear network round trips not merely because of network latency, but because making two or three sequential queries risks querying different database realities.
This lack of a stable basis forces developers into bad software design choices. Hickey points to the common anti-pattern of coupling business queries with display reporting. An application might need to decide which products to put on sale, and then separately determine what visual fields to render for them. Instead of keeping those concerns independent, developers bundle them into a single monolithic query out of fear that the underlying rows will change or vanish between queries.
> “We use object-oriented languages because we have them, not because they're better at this; they really are much worse at this.” — Rich Hickey
> “Can I ask the database three independent questions over time and then make get my answer? No. Why not? Because stuff has happened to that place in between those calls and it makes us do weird stuff.” — Rich Hickey
```mermaid flowchart TD accTitle: The Database Basis Problem accDescr: Diagram illustrating how concurrent mutations destroy the stable basis across independent application queries.
subgraph App["Application Logic"] Q1["Query 1: Select items on sale"] Q2["Query 2: Fetch display details"] Dec["Composite Decision / UI"] end
subgraph DB["Mutable Database (A 'Place')"] S1[("State at T1")] Mut["Concurrent Mutation / Overwrite"] S2[("State at T2")] end
Q1 -->|"1. Request basis"| S1 S1 -.-> Mut Mut -.-> S2 Q2 -->|"2. Unstable basis!"| S2 Q1 --> Dec Q2 --> Dec ```
- Relational declarative set logic outperforms imperative or object-oriented procedural manipulation. - In client-server architectures, treating the whole database as a mutable place destroys the temporal "basis" needed for multi-step calculations. - Fear of inconsistent multi-query results forces developers to tightly couple selection logic with presentation logic in ad-hoc queries.
---
[06:00](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=360s) Distributed Scalability vs. Relational Rigidity
Surveying the database landscape of 2012, Hickey looks at the rise of NoSQL architectures, Amazon Dynamo, and Google Bigtable. Monolithic relational servers struggle to scale horizontally, prompting teams to embrace distributed storage to achieve elastic scale. However, this trade-off comes at a steep price: many teams abandon consistency altogether, importing substantial application complexity to cope with eventual consistency. Hickey asks whether a modern architecture can retain the essential value propositions of traditional databases—such as consistency and rich declarative queries—while harvesting the elasticity and arbitrary scalability of distributed storage engines.
At the same time, traditional relational databases impose immense schema rigidity. Developers spend significant effort creating artificial intersection tables, rigid rectangular schemas, and complex mapping layers that leak into application logic. These rectangular schemas are poorly suited for representing sparse data, irregular structures, or deeply nested hierarchies, directly undermining developer agility.
> “One of the questions we have in revisiting the architecture of a database is what's possible: how much of the value propositions of databases can we retain while tapping into some of the new value propositions of distributed systems, in particular their arbitrary scalability and elasticity?” — Rich Hickey
- Distributed key-value stores provide scale and elasticity, but often force applications to sacrifice ACID guarantees and consistent reads. - Monolithic relational systems require awkward workarounds (e.g., cross-reference/join tables) that do not natively support hierarchical or sparse data formats.
---
[07:48](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=468s) Real Memory, Timestamp Nightmares, and Reactive Eventing
Hickey turns to how systems handle information and time. Prior to the computer era, human record-keeping preserved history: ledger entries were appended, not erased. By contrast, traditional databases default to in-place mutation, overwriting historical facts with current state. Hickey stresses that true record-keeping, auditing, and decision-support require tracking history by default.
To overcome this limitation, teams routinely add custom `timestamp` columns to their relational tables. Hickey connects with the audience's shared pain by asking how many people have had to write and tune the query that fetches the "current" row from such a table. Under heavy online write loads, index contention and lock overhead make tuning that query an absolute nightmare.
Finally, Hickey critiques how client-server databases handle perception and reaction. While "perception" means obtaining a stable view of facts to make decisions, "reaction" means responding to real-world events. In conventional architectures, reaction is handled through continuous polling. Hickey mockingly calls polling "the big four-letter word that begins with P and ends with L." Polling is inefficient and clumsy, but building custom trigger-based eventing systems fails just as badly. If a trigger fires and notifies a service that a record changed, the service has no reliable temporal anchor when it queries the database: by the time the follow-up query executes, further writes may have altered the state again, leaving the application with no way to know whether the retrieved data matches the state that triggered the event.
> “How many people have written the query that gets you 'now' out of that table? Yeah. How many people have tuned that? Yeah, that's a nightmare. Anybody like that query? Tuning that query is brutal, right? The contention is terrible, especially if it's an online system.” — Rich Hickey
> “What do we do? It's the big four-letter word begins with p and ends with l: poll. We poll; very gross.” — Rich Hickey
- Overwriting data destroys the historic records and audit trails natural to pre-digital record keeping. - Manually simulating historical state via timestamp columns creates high write contention and notoriously difficult query optimization bottlenecks. - Polling is inefficient, while database triggers lack temporal context, meaning event consumers cannot reliably query the exact point-in-time state that triggered the notification.
[09:58](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=598s) The Anatomy and Constraints of Traditional Databases
Rich Hickey begins deconstructing the traditional database by looking closely at how standard relational systems are architected. At the absolute bottom sits the durable storage layer: a disk housed within a physical server managed by an I/O subsystem. Above this base layer, databases provide two core services: a transactional component that accepts and integrates novelty into the existing worldview, and a query engine that answers questions based on that data. Crucially, the query engine relies heavily on storage layouts. Hickey points out that simply appending writes to a flat file yields an unusable query engine; true query leverage comes entirely from indexing and maintaining sorted views of data to answer questions rapidly. Because of this, he questions modern key-value stores: “I think we've gone to key-value stores that have almost no leverage and we're still calling them databases, but in my mind, this is what made a database a database. Otherwise we had file systems and all other kinds of things before we had databases, and we didn't call them databases. Why are we calling key-value stores databases now?”
Traditionally, all these pieces were consolidated into a massive, monolithic, and expensive server box. Because memory and computing power were dear, systems relied on a single centralized resource communicating with lightweight clients over a network. This interaction typically happens through strings in a foreign language (such as SQL statements), with the server returning an opaque result set. As read-dominated workloads strain this monolithic database, application developers invariably introduce an external caching tier to avoid the costly round-trip. However, Hickey notes the operational trap this creates: deciding what goes into the cache, what format it takes, and when it gets invalidated becomes entirely the application developer's problem, or gets obscured behind an Object-Relational Mapper (ORM), compounding complexity instead of solving it.
```mermaid flowchart TD accTitle: Traditional Monolithic Database Architecture accDescr: Clients communicate with a single monolithic database server via foreign strings, with an external caching tier added to mitigate read strain.
subgraph Clients["Client Tier"] C1["Client Application"] C2["Client Cache"] end
subgraph Server["Monolithic Database Server"] direction TB TX["Transactional Component<br/>(Novelty / Writes)"] QRY["Query Engine<br/>(Reads / Indices)"] IO["I/O Subsystem & B-Trees"] DISK[("Disk Storage<br/>(Update-in-Place)")]
TX --> IO QRY --> IO IO --> DISK end
C1 -->|"Check / Miss"| C2 C1 -->|"SQL Strings"| TX C1 -->|"SQL Queries"| QRY QRY -.->|"Result Sets"| C1 ```
- Databases historically coupled transactional ingestion, indexing, and query evaluation into one monolithic process running on a single dedicated machine. - External caching layers (e.g., Memcached or ORM level-2 caches) shift invalidation policies and data representation problems entirely onto client applications. - Relational algebra offers a mathematical foundation for querying a fixed snapshot of data, but the update mechanism lacks a corresponding mathematical model: “How do you get a new state of the database? Well, a miracle occurs, and then you have a new relational world.”
[14:20](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=860s) The Missing Information Model and Update-in-Place
Hickey contrasts the relational model's algebraic rigor with its underlying state model, which typically relies on update-in-place mechanics. Decades ago, disk space and system memory were exceptionally scarce, making it impossible to store complete historical records. Database designers accommodated these hardware limits by creating block-oriented B-trees that overwrite and reuse disk blocks, closely coupling the storage layout with operating system memory management. This architectural decision requires extensive, fine-grained concurrency coordination and locking, creating substantial performance bottlenecks for multi-client environments.
Beyond the low-level mechanics, Hickey emphasizes that traditional databases lack a true *information model*. While relational schemas model relations and document stores organize composite payloads, neither treats facts as first-class historical entities. In an update-in-place system, a mutation overwrites the previous value, discarding the temporal context that explains what the world looked like at an earlier point. Hickey argues that modern distributed architectures need a model built from immutable values, where statements about the world are preserved rather than erased.
- Traditional B-trees depend on reusing and overwriting fixed-size disk blocks, mandating heavy locking and coordination protocols. - An update-in-place model destroys past knowledge, preventing systems from determining the historical context or causality of changes. - A true information model requires immutability and an explicit temporal dimension, representing accumulated facts over time rather than mutable placeholders.
[17:28](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1048s) Deconstructing the Database: Facts, Immutability, and Datoms
To resolve these architectural bottlenecks, Hickey outlines core principles for deconstructing the database: adopting an information model, splitting process (novelty processing) from perception (querying), utilizing underlying storage strictly immutably, and holding novelty in memory for a window of time before merging it into indexes. He highlights the architectural leverage gained by shifting to immutable values: “And if you take away nothing from this talk, I would hope you would take away that this is a real example of that—that in action—that adopting a value-oriented model has architecture benefits, really substantial ones.”
```mermaid flowchart LR accTitle: Core Principles for Deconstructing the Database accDescr: The four design principles used to take apart the traditional monolithic database.
P1["1. Information Model<br/>(Atomic Facts / Datoms)"] P2["2. Split Roles<br/>(Process vs. Perception)"] P3["3. Immutable Storage<br/>(Append-Only, No Overwrites)"] P4["4. In-Memory Novelty<br/>(Window of Time)"]
P1 --> P2 --> P3 --> P4 ```
Implementing this information model requires stripping away structural overhead to store fine-grained, atomic facts. Composite constructs like relational rows or JSON documents bundle unrelated attributes together; updating a single column (such as an email address) affects the entire composite row rather than recording a isolated fact. Hickey examines RDF (Resource Description Framework) triples—composed of subject, predicate, and object—and notes that while they capture atomic structure, they lack a temporal dimension, making them incapable of modeling facts as they change over time. Datomic addresses this by organizing data into atomic components called *datoms* (spelled d-a-t-o-m to distinguish the singular and plural clearly from "data"). Each datom represents an atomic fact comprised of an entity ID, an attribute, a value, and a transaction-based temporal component rather than a raw wall-clock timestamp alone.
- Composite records (database rows or documents) represent multiple facts bundled together rather than atomic statements of truth. - RDF triples (`subject, predicate, object`) capture atomic data shapes but omit time, preventing accurate tracking of updates over history. - A datom contains four elements: Entity, Attribute, Value, and a temporal transaction reference (`[E A V Tx]`). - Using a transaction entity for the temporal dimension provides far greater metadata and analytical power than a bare clock timestamp.
[20:23](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1223s) Transactions as First-Class Entities and the Smallest Fact
Rich Hickey explains how transaction metadata transforms the way systems track the origin of information. Because transactions in Datomic are treated as first-class entities, a transaction can store not only the exact point in time ($t$) when data was committed, but also rich contextual metadata: identity, provenance, and audit trails. Hickey points out that you can attach who said it, where it came from, whether it has been audited, or any other characteristic of its origin directly to the transaction record itself. For the scope of this architecture talk, however, he simplifies this component: “The $t$ part is time; it's a path to when, and that's the smallest fact.”
[20:59](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1259s) The Database State Problem: Tree Rings and Accretion
Treating an entire database fundamentally as an immutable value immediately brings up an apparent dilemma: running a business inevitably involves novel activity. New sales are booked, new products launch, and new customers arrive. If the database is an immutable value, where does that novelty go? Hickey resolves this contradiction using the metaphor of tree rings: the database is an ever-expanding value that never updates data in place. It expands outward purely through the accretion of facts. Just as a growing tree never modifies its interior rings to add wood, a system accreting facts never rewires its past. The core upon which each new layer builds remains permanent.
Hickey emphasizes that this model provides the core property developers expect from values: anything observed before will never change. Rather than constantly overwriting predefined memory addresses or disk locations, the system decouples data from fixed locations. As Hickey observes:
> “We're going to get a lot of freedom by moving away from places.”
- Novelty requires allocating new space rather than overwriting existing locations ("new stuff means new space"). - Immutability guarantees that any reference to past data remains stable, reliable, and immune to concurrent alteration. - The fundamental storage contract shifts from localized mutation to continuous accretion.
[22:22](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1342s) Representing Process: Assertions, Retractions, and Reification
Moving away from place-oriented updates—such as targeting an address or primary key with an `UPDATE` statement—requires defining a new fundamental unit of change. Hickey argues that at the lowest layer, novelty can be completely represented by two operations: assertions and retractions of facts. A datum is asserted as true at a given point in time; if it later ceases to apply, it is retracted. Even after a retraction, the historical fact remains true: that statement was valid from the initial moment until the moment of retraction. Hickey calls this the minimal possible representation of process, explaining that any complex data transformation can ultimately expand into a series of basic assertions and retractions.
Beyond merely recording changes, Hickey argues for reifying process—turning process itself into a first-class, inspectable thing. He compares this to event sourcing. In a conventional database that has run for years through thousands of updates, inspecting the database value reveals no explanation of how it reached its current state. At best, an administrator has to parse raw transaction logs that merely document successive byte manipulations on physical places. Reifying process solves this by turning changes into explicit facts:
> “When you add a new customer, there's going to be something that says there is a new customer; that customer's name is Sally.”
Crucially, accretion does not produce disjointed snapshot files (such as an isolated dump from Tuesday, Wednesday, or Thursday). The database preserves history cumulatively within a single expanding view. Querying the system allows developers to step back into the inner tree rings and perform longitudinal analyses—such as measuring how values trended or counting events across arbitrary windows of time—without juggling disconnected backup records.
[25:54](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1554s) Deconstructing the Server: Separating Process from Perception
To translate this conceptual model into an architecture, Hickey decomposes the traditional, monolithic database server—which bundles transactions, indexing, query evaluation, disk I/O, and storage—into two distinct, decoupled halves: process and perception. Process handles novelty: processing new transactions, outputting data to storage, and coordinating order. Perception handles reading: posing queries, leveraging indexes, and reading from durable storage. Because the underlying model relies strictly on immutable information, perception does not need to sit inside the same machine or process boundary as the transaction coordinator.
```mermaid flowchart TD accTitle: Separation of Process and Perception accDescr: An architecture diagram showing how Process and Perception are split across distinct application and storage boundaries.
subgraph ProcessSide["Process (Novelty)"] A["New Transactions"] --> B["Transaction Coordinator"] B --> C["Durable Storage Engine"] end
subgraph PerceptionSide["Perception (Reading)"] D["Application Servers"] -->|"Queries & Indexes"| E["Immutable State View"] C -->|"Reads Data"| D end
D -.->|"Submit Novelty"| A ```
This separation empowers external applications—whether web backends or dedicated analytics engines—to handle their own perception, reactions, and query workloads locally. Applications observe changes, maintain independent working memory for decision-making, and then submit coordinated novelty back to the shared process pipeline when needed. Scaling query capacity no longer requires scaling an all-in-one database server; it simply means adding more stateless application workers.
> “If we want more query capability, what do we need to do? Just add more of these guys; we don't really care about this growing, because we're not asking it to do much, just some coordination.”
- Coordination is confined strictly to transaction sequencing and novelty submission. - Perception and query execution are offloaded to independent nodes, freeing shared bottlenecks. - Shared storage engines provide the common bedrock while minimizing runtime locking and contention.
[28:13](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1693s) Implementing State: Durable Persistent Data Structures
Moving to implementation details, Hickey addresses how an immutable, expanding database state can be physically indexed to support efficient querying. Query capability fundamentally requires sorted data. To achieve this immutably, Datomic adopts persistent data structures—using "persistent" in the functional programming sense rather than meaning disk durability. In functional programming, structures like maps, vectors, and sorted sets are commonly organized as balanced trees that rely on structural sharing.
```mermaid flowchart TD accTitle: Structural Sharing via Path Copying accDescr: Diagram showing how a new root shares existing child nodes while copying only the direct path to an added node.
subgraph OldTree["Original State (Root A)"] RootA["Root Node A"] --> NodeB["Internal Node B"] RootA --> SharedC["Shared Node C"] NodeB --> LeafD["Leaf Node D"] end
subgraph NewTree["New State (Root A')"] RootA2["Root Node A'"] --> NodeB2["Internal Node B'"] RootA2 --> SharedC NodeB2 --> LeafD NodeB2 --> NewLeaf["New Leaf Node E"] end ```
When modifying a persistent tree (such as adding a leaf node to a sorted set), the system does not mutate existing nodes in place. Instead, it creates the new leaf node and copies only the path of nodes leading from that leaf back up to a brand-new root. The new tree root points to this freshly allocated path while referencing all the untouched branches of the previous tree. This operation works safely because the existing nodes are entirely immutable. Hickey notes that while functional languages have traditionally leveraged structural sharing inside volatile RAM, Datomic applies this exact model to disk storage, creating what he calls "durable persistent data structures."
> “We can do this in memory; it's done in memory by most functional programming languages. What we're going to start to do, though, is do this on disk, so we'll have a really persistent—or persistent persistent, or I'd rather say durable—persistent data structures that have this kind of shape.”
[30:14](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=1814s) Rethinking Disk Locality and Abstracting Storage
Rich Hickey challenges the traditional architectural obsession with physical disks and I/O locality. A recent paper aptly titled "Disk Locality Considered Irrelevant" reflects an old notion that is rapidly dying. Historically, computing systems operated under the assumption that whichever machine physically housed the disk held a massive computational advantage. Because it possessed a dedicated controller card attached directly to the drive, it could read data into memory with lightning speed, whereas remote machines suffered severe overhead trying to fetch that same data over the network.
Today, modern high-speed networks have flipped this equation on its head. The performance disparity between accessing local RAM and hitting a spinning disk or block device remains enormous, dwarfing network latencies. Consequently, anyone forced to touch a physical disk is already losing the performance battle, and the machine that hosts the disk locally is barely losing any less than a remote machine. Rather than obsessing over disk proximity, systems should focus on getting data into memory and maintaining good in-memory locality. By packaging I/O and disk hardware into an abstract black box termed "storage," the architecture gains immense flexibility.
```mermaid flowchart LR accTitle: Abstracting Storage accDescr: Incoming novelty and persistent index trees are stored in an abstract key-value storage service.
subgraph StorageService["Storage Service (Black Box)"] direction TB Log["Log (Append-Only Novelty)"] Trees["Index Segments (Immutable Blobs)"] RootPtr["Root Pointer (Consistent Read)"] end
Input["New Facts"] -->|"Append"| Log Indexer["Indexing Process"] -->|"Write Blobs"| Trees Indexer -->|"Update Pointer"| RootPtr ```
- Storage is responsible for holding two core categories of information: an append-only log recording incoming novelty (e.g., "I sold this," "Sally gave me an email") as quickly as possible, and persistent, immutable tree index segments. - Index segments are treated as immutable blobs keyed by an identifier, mirroring the persistent data structures familiar from memory, elevated to an inter-system cooperation level. - The storage contract requires only two capabilities: a key-value interface to store and retrieve immutable blobs, and a small modifiable root pointer accessible via consistent reads to track the current database root. - Hickey underscores that this decoupling frees developers from hardware-level concerns: “It ends up that anybody that needs to access the disk is losing and the guy who has the disk in the same box is only losing very very slightly less than anybody else would lose so it doesn't matter anymore.” - Because the requirements are strictly key-value access and consistent reads, storage can be backed interchangeably by systems like Amazon DynamoDB or even standard SQL databases storing binary blobs.
[34:01](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2041s) Index Sorting and the Challenge of Live Immutability
The database indexes themselves are standard hierarchical trees: a root points to intermediate nodes, which ultimately point to leaf segments containing sorted datums. In a system modeling facts as entity-attribute-value-transaction tuples, data can be organized in multiple sort orders to support different workloads. For example, sorting primarily by entity allows fast retrieval of entity-centric representations like documents or objects. Inverting the sort to lead with attributes clusters matching attributes together—such as all email addresses or phone numbers—mimicking the performance and analytical strengths of column stores.
```mermaid flowchart TD accTitle: Index Sorting Perspectives accDescr: Datums sorted by entity yield document-like access, while sorting by attribute behaves like a column store.
Root["Index Tree Root"] --> Inner["Internal Index Nodes"] Inner --> Leaf1["Entity-Sorted Leaf (EAVT)<br/>Documents / Objects"] Inner --> Leaf2["Attribute-Sorted Leaf (AEVT)<br/>Columnar / Analytics"] ```
However, incorporating new data into massive immutable index trees poses an efficiency challenge. Performing path copying down the tree for every single arriving fact would lead to catastrophic overhead and thrashing in storage. Maintaining sorted structures live on disk or in remote storage in an immutable manner is fundamentally inefficient if done on a per-transaction basis. Systems must separate transaction durability from the structural integration of data into the master index.
- Facts can be arranged along six possible sort orders of the tuple components, offering purpose-built access paths like columnar aggregation or document retrieval from identical underlying data. - Maintaining trees live in storage for each incoming datum is unviable: “We said anytime you want to incorporate new information into that tree we're going to have to do that path copying job do we want to do that every time we get a new fact? No, that's a disaster.” - Durability is achieved immediately by writing facts to the write log, freeing the indexing process from having to update primary index trees synchronously.
[35:49](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2149s) Dynamic Merging and Segment-Based Indexing
To handle the cost of updating large indexes, Hickey points to strategies pioneered by systems like Google BigTable. BigTable buffers writes into memory until reaching a threshold (such as 64 megabytes), dumps that block to disk, and runs an asynchronous background merge sort against historical sorted flat files to emit a new, immutable flat file. While BigTable avoids modifying files in place, its monolithic flat-file output shares zero structure with older versions, making it expensive to address, manipulate, and cache in fine-grained chunks.
Datomic improves upon this paradigm by decomposing indexes into trees composed of smaller segments rather than monolithic flat files. Because nodes and segments are immutable, new index versions can structurally share the vast majority of untouched subtrees with prior index generations. Cached segments remain valid indefinitely because their contents never mutate. In-memory data is kept sorted in a live index, and a query merges this live memory state with historical index segments fetched from storage via an on-the-fly merge join. Periodically, a background job merges accumulated novelty into persistent storage, allowing peers to flush their local in-memory buffers.
```mermaid flowchart LR accTitle: Index Merging and Structural Sharing accDescr: Novelty is logged and buffered in memory, dynamically merged with storage segments for reads, and periodically indexed into storage trees.
subgraph Memory["Memory (Peer / Transactor)"] LiveIdx["Live Index (Sorted Set)"] end
subgraph StorageLayer["Storage"] Log["Novelty Log"] StoredTrees["Persistent Index Trees (Shared Segments)"] end
Novelty["Incoming Novelty"] -->|"Log Immediately"| Log Novelty -->|"Buffer"| LiveIdx LiveIdx -.->|"Dynamic Merge Join"| ReadView["Perceived View"] StoredTrees -.->|"Dynamic Merge Join"| ReadView LiveIdx -->|"Periodic Batch Indexing"| StoredTrees ```
- Structurally shared persistent tree segments provide fine-grained addressability and optimal granularity for local memory caching compared to monolithic flat files. - Once a background indexing run flushes and updates the tree in storage, processes can drop the merged novelty from memory and reset their live buffers. - Hickey observes: “By using trees you get fine-grained addressability and you get these nice small chunks which are good for caching. You also have the potential for structural sharing.”
[38:36](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2316s) Coordination-Free Perception and System Roles
Decoupling reads from the write pipeline fundamentally simplifies database perception. To query or inspect the state of the world, an application only needs access to the in-memory live index and access to underlying storage. It does not need to communicate with the transaction engine, execute lock handshakes, or sit in physical proximity to the writer. Read transactions vanish entirely from the architecture because perceiving immutable values over time requires no distributed coordination.
Hickey formally introduces the core roles that emerge from deconstructing the database: the transactor, peers, and storage. The transactor acts as the centralized coordinator dedicated solely to processing writes, enforcing validity, ordering novel facts, and updating the append log. The applications consuming the database run as peers—first-class participants possessing full local powers to perceive, remember, decide, and query without gatekeeping. Finally, storage servers (typically distributed, redundantly backed key-value systems) supply the raw data blocks on demand.
```mermaid flowchart TD accTitle: Deconstructed Database Architecture accDescr: Transactor handles writes and logging, peers execute queries and read transactions coordination-free, and storage stores immutable blocks.
subgraph TransactorRole["Transactor"] TxCoord["Process Novelty & Log Writes"] end
subgraph PeerRole["Peers (App Servers)"] Perception["Query, Cache & Decide (Coordination-Free)"] LiveMem["Local Live Index Buffer"] end
subgraph StorageRole["Storage Engine"] KV["Redundant Key-Value Store"] end
TxCoord -->|"Write Log & Indexes"| KV TxCoord -.->|"Broadcast Novelty"| LiveMem Perception -->|"Read Tree Segments"| KV Perception -->|"Read Novelty"| LiveMem ```
- When readers avoid the write path, read contention is eradicated: “Where did they go? They just disappeared because a correct implementation of perception does not require coordination.” - Transactors exclusively handle incoming write coordination, logging novelty and maintaining the live memory index. - Peers represent standard application servers empowered to run expressive queries and logic against locally cached index segments and dynamic novelty without imposing read load on the transactor. - Storage systems provide horizontally scalable, redundant persistence for immutable segments, making the whole system modular and architecturally resilient.
[40:28](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2428s) Deconstructing the Jobs: Transactors, Peers, and Commodity Storage
Commodity key-value stores may falter when forced to act as full-blown relational databases, but when treated purely as key-value stores, they shine: they are redundant, highly available, scalable, distributed, and exceptionally reliable. Datomic harnesses this raw power by rearranging database responsibilities across distinct physical components rather than bundling them into a single monolithic server. In this re-architected model, an application communicates new data (novelty) directly to a dedicated transactor. The transactor logs the incoming novelty immediately, writes it into its local live index in memory, and rebroadcasts that novelty—and only that novelty—to connected peers so that every peer’s live index stays synchronized.
```mermaid flowchart TD accTitle: Datomic Architecture Overview accDescr: Data flow between application peers, the transactor, storage services, and intermediate caches.
subgraph Peers["Peer Nodes (JVM App Tier)"] App1["Peer App (Analytics)\n- Live Index\n- Query Engine\n- Local Cache"] App2["Peer App (Web Server)\n- Live Index\n- Query Engine\n- Local Cache"] end
subgraph TransactorTier["Write Path"] Tx["Transactor\n- Single Writer\n- Live Index & Log\n- Occasional Indexing"] end
subgraph CacheTier["Shared Cache Tier"] MC["Memcached Cluster\n(Optional)"] end
subgraph StorageTier["Pluggable Storage Service (KV Store)"] Store[("Storage Engine\nDynamoDB / Postgres / Infinispan\nImmutable B-Tree Segments")] end
App1 -->|"1. Submit novelty"| Tx Tx -->|"2. Broadcast novelty"| App1 Tx -->|"2. Broadcast novelty"| App2 Tx -->|"3. Write log & periodic index"| Store
App1 <-->|"Read / populate cache"| MC App2 <-->|"Read / populate cache"| MC MC <-->|"Miss: Fetch immutable segment"| Store App1 -.->|"Direct read (bypass cache)"| Store App2 -.->|"Direct read (bypass cache)"| Store ```
Underneath this layer sits a pluggable storage service that acts as an unopinionated black box; it only needs to be a consistent key-value store. This allows Datomic to run seamlessly on top of Amazon DynamoDB, relational databases like PostgreSQL, or distributed in-memory grids such as Infinispan when disk persistence is unnecessary. Crucially, the transactor holds no privileged physical locality to storage over any peer. Because storage is decoupled across the network, no single machine is closer or privileged, allowing all peers to read directly from the underlying storage service independently.
- **Pluggable backends:** Supported targets include DynamoDB, PostgreSQL (or any standard RDBMS), and Infinispan for pure in-memory operations. - **Transactor roles:** Manages serialization, write logging, novelty broadcasting, and periodic batch background indexing. - **Peer responsibilities:** Runs as an in-process library inside a Java or JVM application, hosting its own query engine and in-memory live index.
---
[43:21](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2601s) Moving Query to the Edge and Relentless Caching
Decoupling storage fundamentally transforms query execution. A query engine only requires access to organized data—namely, the index. Because the storage engine and indexes are accessible over the network to everyone, queries do not need to run on centralized, specialized database servers. Instead, Datomic bundles the query engine directly inside an in-process JVM library that lives inside the application server itself. While fetching indexed data over the network introduces latency compared to local memory, it avoids the crippling mechanical cost of local disk head movements.
Furthermore, network penalties are mitigated through aggressive caching. Because historical storage segments are strictly immutable, cached data never expires and can never become stale. Applications can deploy in-process memory caches or intermediate Memcached clusters shared among many peers. Hickey points out that managing this cache is entirely offloaded from the software developer: “It's my problem, it's the system's problem, it's this library's problem, it's not your problem.” The caching layer operates under the hood; when a query evaluates, the peer automatically fetches data from its local cache, checks the shared cache, or retrieves it from storage, with zero custom invalidation logic required from the developer.
“What's safe to cache? Everything. Why? It's immutable. When does it expire? Never. These are the things you want to hear as an architect, right? This is sweet.” — Rich Hickey
---
[45:41](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2741s) On-Demand Working Sets and Merge-Join Queries
Addressing an audience question about whether this model resembles a Lambda architecture that forces nodes to copy the entire database, Hickey clarifies that peers never perform proactive or whole-database replication. Instead, peers lazily pull in only their immediate working sets on demand. For instance, an analytics peer crunching pricing data caches only the pricing indices it accesses, while a customer-facing web server caches only catalog and user profile records. Because index structures are shallow B-trees—rarely exceeding three tiers in depth—caching the small upper branch nodes enables peers to retrieve any previously unseen fact across the entire database in a single storage read or Memcached lookup.
```mermaid flowchart LR accTitle: Query Merge-Join Flow accDescr: How a peer resolves a query by combining historical indexed tree data with the recent live index window.
subgraph PeerMemory["Peer In-Memory"] Live["Live Index (Novelty)\n- In-memory recent window\n(e.g., Sally's new email)"] QEng["Query Engine\n(Merge Join)"] end
subgraph ColdStorage["Storage / Cache"] Tree["Persistent Historical Index\n- Immutable sorted trees\n(e.g., Sally's prior data)"] end
Live -->|"Seek Sally"| QEng Tree -->|"Seek Sally"| QEng QEng --> Result["Unified View of 'Now'\n(New facts overwrite/retract old)"] ```
When an application queries the system, the in-process query engine performs an efficient merge-join between two distinct sources: the historical, persistent index in storage and the live index held in local memory. If a persistent index snapshot was generated an hour ago, any modifications that occurred since that time reside exclusively in the live memory index. - When querying a record (such as Sally's profile where her email address changed 20 minutes ago), the engine seeks Sally in the persistent tree and seeks Sally in the live memory index, merging the two streams. - Retractions in the recent stream seamlessly mask obsolete facts from the historical store, presenting an accurate, consolidated view of the current state. - Once the live index accumulates changes over a given window (such as an hour), an indexing job writes a new persistent tree to storage, shares structural nodes with the older tree, notifies all peers, and resets the live accumulator back to empty.
---
[49:31](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2971s) Peer Topologies and Introducing Transaction Functions
Addressing audience concerns about scaling peer updates, Hickey explains that Datomic peers are not meant to represent tens of thousands of lightweight edge web clients or mobile browsers; they represent middle-tier application servers that in turn serve client requests. While broadcasting changes to tens of thousands of nodes would require alternative distribution mechanisms such as IP multicast, typical enterprise topologies involve dozens or hundreds of application servers, a scale easily handled by standard peer-to-peer broadcast channels.
Turning to the mechanics of writes, Hickey observes that while all state changes ultimately boil down to basic assertions and retractions, real-world domain workflows frequently demand atomic compound transformations. A classic example is crediting a bank balance: - If a user wishes to add $10 to an account, the resulting asserted fact directly depends on reading the account's existing balance at the exact moment of execution. - Simply broadcasting an arbitrary raw assertion from an app server risks concurrency anomalies and race conditions. - To resolve this safely, Datomic introduces **transaction functions**—deterministic functions that execute directly inside the transaction process to calculate new assertions and retractions atomically against current state.
[51:15](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3075s) Transaction Functions and Recursive Expansion
Rich Hickey explains that transaction data is not strictly limited to flat assertions and retractions; it can also consist of transaction functions accompanied by arguments. In this model, a transaction function can be passed the database value itself. For example, to add $10 to Sally's bank account, a function can run inside the transaction with the current value of the database passed directly to it. Because the database is an immutable value, the transaction function acts as a pure function over a real value, allowing it to perform arbitrary queries—such as looking up Sally's current balance of $100—and then produce new transaction data asserting her new balance of $110.
```mermaid flowchart TD accTitle: Transaction Function Expansion accDescr: Diagram illustrating how transaction data expands recursively through functions into primitive assertions and retractions. subgraph Input ["Incoming Transaction"] TX["Transaction Data\n(Functions + Facts)"] end subgraph ExpansionEngine ["Expansion Process"] FN["Transaction Function\n(Evaluated with current DB value)"] REC["Nested Transaction Functions"] PRIMS["Primitive Operations\n(:db/add &:db/retract)"] end subgraph Execution ["Commit"] APPLY["Applied to In-Memory DB"] end
TX --> FN FN -->|"expands into"| REC REC -->|"recurses until"| PRIMS FN -->|"expands directly into"| PRIMS PRIMS --> APPLY ```
Hickey emphasizes that transaction functions can recursively expand into invocations of other transaction functions, but the process always terminates in primitive assertions and retractions. This model gives developers architectural independence regarding where and when work is computed: - **Pure Novelty:** When adding completely new facts that do not depend on existing state, data can be asserted directly without evaluating logic inside the transaction. - **Optimistic Pre-computation:** When contention is low, a client can calculate Sally's new balance locally based on its current view and submit the assertion alongside a precondition stating the transaction should fail if the balance was not $100. This avoids holding up the transactor with expensive calculations. - **Atomic Server-Side Execution:** When absolute consistency under contention is necessary, the function can be sent directly to the transactor to run atomically without interference. As Hickey states, “This expansion happens over and over again until it's all assertions and retractions and this allows you to do any arbitrary transformation on the data in the database.”
[53:35](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3215s) The Transactor, Two-Tier Caching, and Storage Garbage Collection
The transactor receives transaction data, expands any transaction functions, applies the resulting assertions and retractions to its in-memory model of the database, writes the changes to a durable log, acknowledges completion to the client, and broadcasts the changes to all peers. Periodic indexing jobs reorganize accumulated transaction data into permanent storage trees. Hickey points out an exact parallel to runtime memory management: creating new index trees leaves old root structures and obsolete nodes orphaned in persistent storage, requiring an asynchronous storage garbage collection process to clean up discarded segments.
```mermaid flowchart LR accTitle: Datomic Transactor and Peer Architecture accDescr: Flow showing transactions arriving at the transactor, logging to storage, broadcasting to peers, and how peers query via a two-tier cache. subgraph TransactorNode ["Transactor"] TX["Incoming Transaction"] --> EXP["Expand & Apply"] EXP --> LOG["Durable Log"] end
subgraph StorageLayer ["Shared Storage"] LOG --> STORE[("Storage Subsystem\n(DynamoDB / SQL / KV)")] STORE -.->|"Garbage Collects"| GC["Orphaned Index Trees"] end
subgraph PeerNode ["Peer Server"] Q["Query Engine"] --> MEM["Live Memory Index"] Q --> CACHE1["Local Object Cache"] CACHE1 -->|"miss"| CACHE2["Memcached (Segments)"] CACHE2 -->|"miss"| STORE end
EXP -->|"Broadcast Novelty"| MEM ```
Peer servers maintain direct access to storage and execute queries locally using their own query engines and live memory indexes. To minimize storage access latency, Datomic employs a two-tier caching hierarchy: 1. **Local Object Cache:** An in-memory cache running inside the peer process storing deserialized objects. 2. **Segment Cache (Memcached):** An optional out-of-process distributed cache holding immutable index segments. 3. **Storage Subsystem:** The underlying persistence engine, accessed only when a segment misses both cache layers.
Hickey explains that the live database reference is simply a pointer inside a single mutable box that points to immutable persistent data structures. Whenever an indexing job completes or the memory index incorporates new broadcast transactions, the box transitions atomically to point to the new immutable tree structure. Every other component in the system is completely immutable. As Hickey explains, “The only mutable thing in the system is the fact that the contents of this box will move from one of these immutable structures to another whenever we've updated the memory index or whenever an indexing job is completed.” Because a database value acquired by a thread is immutable, long-running queries can execute without locking or fear of the underlying state changing.
[56:19](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3379s) Deconstructing Perception and Process: Scalability and Elasticity
By cleanly decoupling perception (reading and querying) from process (writing and recording transactions), Datomic allows availability and scalability decisions to be made independently for each side. On the process side, Datomic intentionally chooses a single-writer, transactional model to guarantee consistency. Hickey points out that because the transactor is freed from the burden of serving queries, all of its resources are dedicated purely to processing and ordering writes. Addressing the contemporary rise of distributed databases, Hickey remarks, “If you want arbitrary write scalability you're going to have to give up transactions and queries and everybody who's adopting NoSQL databases that make that choice for you is getting that trade-off and I think it's not a great trade-off for a lot of companies.”
On the perception side, Datomic reaps the full benefits of modern distributed systems: - **Pluggable Storage:** Storage can be backed by scalable managed services like Amazon DynamoDB or existing relational databases, delegating replication and throughput knobs to mature distributed systems. - **Decentralized Query Execution:** Queries are not evaluated on a central database machine; they execute directly within the peer applications. - **Elastic Auto-Scaling:** Because peers are stateless query engines that read from shared storage and cached segments, capacity scales dynamically with application traffic using tools like Amazon EC2 Auto Scaling groups without needing complex cluster rebalancing.
[58:37](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3517s) Minimalist Schema and Time-Travel Queries (`as-of`)
Datomic’s schema system is intentionally minimalist. Instead of defining rigid tables or collection schemas, developers define only individual attributes—specifying the attribute's name, type, and cardinality (single or multi-valued). The core unit of data remains the atomic fact, meaning the entity model behaves like a flexible set of key-value attributes or multimaps.
Because all historical facts are preserved and indexed with transactions, the database acts as a value that encompasses its entire past. A consumer can take any current database handle and ask for an `as-of` view corresponding to a past point in time without duplicating or modifying underlying records: - **Zero-Cost Historical Views:** The system simply ignores any index segments and datoms asserted after the specified timestamp or transaction point. - **Query Independence from Time:** In traditional SQL databases, historical reporting requires threading timestamps through every table join, `WHERE` clause, and aggregate `MAX()` subquery. In Datomic, an existing query written for current data can be executed directly against an `as-of` database value without altering a single line of query logic. - **Point-in-Time Windows:** Beyond static snapshots, peers can inspect temporal windows to analyze changes that took place strictly within a defined time bracket.
As Hickey observes regarding conventional approaches, “How many people ever built systems with timestamps and had to do as of queries? It's brutal, right? Because what do you have to do? You have to flow around that time, right? If I want to say as of last week, last week has to be part of every query, has to be part of every join, and it's nasty, right? You have to do Max and all that, very, very tough stuff.”
[01:01:06](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3666s) Speculative What-If Queries and Rich Event Processing
Because the database representation is persistent and held locally in memory, processes can perform hypothetical or “as if” operations completely offline. Hickey explains that a developer or analyst can ask questions like: what if I added these records, or what if I committed this specific transaction? There is no need to make a trip to a remote database server or touch a central lock. Instead, the peer takes its in-memory persistent data structure and derives a new version containing the speculative transaction data alongside references to existing storage segments. As Hickey highlights:
> “I can make a new version of it that includes the transaction data I'm thinking about committing that's now another database value it's all in memory but it can can include stuff from storage right but the novelty is the stolen memory now I can take the same query and ask the query I don't need anybody help anybody's help I'm not bothering anybody right I can do what if and then I can say well that query still works so good now I'll try to commit it or maybe I'm just doing you know what if analysis for people and I never commit it you know we're just trying to make decisions what would happen if we made this decision what would the world look like I don't have to put that into the database to ask answer the question I still have query capability”
Turning to the twin concepts of perception and reaction, Hickey notes that while perception is cleanly covered by issuing queries against immutable database values, reaction becomes remarkably straightforward thanks to Datomic’s architecture. The central transactor broadcasts a continuous feed of novelty to every connected peer. Whenever a change occurs—such as a user named Sally updating her email address—the peer does not merely receive an isolated notification. Because the database is represented as an immutable value, the event delivery can include the database value immediately before the transaction, the novelty payload itself, and the database value immediately following the transaction. Peers can run arbitrary queries against these states and against local in-memory data structures simultaneously, allowing developers to implement sophisticated event filtering without dedicated stream-processing infrastructure.
```mermaid flowchart LR accTitle: Speculative What-If Evaluation Flow accDescr: Deriving a speculative in-memory database value from a base state and novelty data without contacting storage.
subgraph BaseState["Base State"] DB["Immutable DB Value<br/>(Current Basis)"] end
subgraph Speculation["In-Memory Speculation"] TxData["Candidate Novelty<br/>(Uncommitted Tx)"] SpecDB["Speculative DB Value<br/>(db.with / as-if)"] end
subgraph LocalQuery["Query Engine"] QE["Local In-Memory Query<br/>(No server contact)"] end
DB --> SpecDB TxData --> SpecDB SpecDB --> QE ```
[01:03:16](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3796s) Epochal State, Time-Travel Debugging, and Communicating a Basis
Hickey underscores that this deconstruction yields profound architectural simplicity. The underlying state model is epochal, meaning state transitions happen cleanly from one fully valid, consistent point to another, with zero intermediate visibility or dirty reads. Read operations require no coordination whatsoever; coordination is restricted entirely to the transaction process managed by the transactor. Identical queries executed against the same database value always return the exact same answers, providing an unshakeable foundation for decision-making. Hickey presents a relatable debugging dilemma to illustrate the point:
> “How many people have ever had that happen the quer is returning some weird results at like 5 o'clock everybody wants to go home so we'll like look into this tomorrow and then you come back tomorrow and the quer is fine just because more data was added into the system how are you ever going to find out what was wrong your toast with this it's straight forward right we can say ask that query as if it was five o'clock because I know at five o'clock that query was screwy and giving me weird results and you could figure out the answer to your problem uh because you can get a basis anytime you want”
Transactions themselves become purely defined as mathematical functions over database values. Furthermore, treating state as an explicit value changes how distributed peers coordinate work. Instead of telling another process to loosely "go look in the database"—where concurrent updates could mutate the records before the worker inspects them—a process passes a specific database basis `t`. The recipient reconstructs and inspects the exact historical reality seen by the sender, ensuring absolute consistency across asynchronous workflow steps.
[01:04:42](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3882s) The Architectural Payoff of Deconstruction
By breaking apart monolithic database responsibilities, teams gain total freedom over physical topology. Query evaluation, durable storage, and transaction coordination no longer need to reside on the same box or even on the same network tier. For instance, durable storage can live on Amazon DynamoDB, while peers run on an on-premises local area network (LAN) backed by a local Memcached cluster, shielding local query execution from public internet latencies and disruptions.
Hickey summarizes the ultimate gains of the architectural shift:
> “The net result of deconstructing the database uh is that you're able to treat the database as a value um in in precisely the way I was talking about in my talk you have a real information model you have a system that's substantially less complex right it's easier to understand it's easier to change right you want to change your storage you can do that it's easy to replace components it's easy to relocate things um it's more powerful uh it's more scalable right you want more brains you start more peers there's less coordination which also leads to the scalability aspects”
By preserving an append-only timeline where facts are remembered rather than overwritten, businesses retain the complete historical record required for analysis. Finally, because raw informational facts are not prematurely bound to rigid, physical schemas, developers gain the flexibility to project and reshape data into whatever form best suits their applications. With that summary, Hickey wraps up the presentation to applause and opens the floor for questions.
- **Independent tier relocation:** Storage (e.g., DynamoDB), caching (Memcached), indexing/transacting, and peer query execution can all be provisioned on separate hardware tiers or distinct networks. - **Horizontal query scalability:** Because peers execute queries locally against immutable blocks without read locks, increasing query capacity simply requires spinning up more peer processes ("brains"). - **Preserved business history:** Avoiding in-place destructive mutation keeps past facts available permanently for auditing, time-travel analysis, and retroactive debugging.
## Notable quotes > [43:21](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2601s) What's safe to cache? Everything. Why? It's immutable. When does it expire? Never. These are the things you want to hear as an architect, right? — Rich Hickey
> [56:19](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=3379s) If you want arbitrary write scalability you're going to have to give up transactions and queries and everybody who's adopting NoSQL databases that make that choice for you is getting that trade-off and I think it's not a great trade-off for a lot of companies. — Rich Hickey
> [35:49](https://www.youtube.com/watch?v=Cym4TZwTCNU&t=2149s) By using trees you get fine-grained addressability and you get these nice small chunks which are good for caching. You also have the potential for structural sharing. — Rich Hickey
## Mentioned Amazon Dynamo, Amazon DynamoDB, Amazon EC2 Auto Scaling, Ben Moseley, Clojure, Datomic, Google Bigtable, Infinispan, InfoQ, JaxConf 2012, JVM, Memcached, Out of the Tar Pit, Peter Marks, PostgreSQL, RDF, Rich Hickey, SQL
## Video description Rich Hickey, author of Clojure, and designer of Datomic presents a new way to look at database architectures in this talk from JaxConf 2012.
** For more presentations from JaxConf 2012, head to http://mrkn.co/txtch
[Source](https://www.youtube.com/watch?v=Cym4TZwTCNU)