Choosing a data store

Generated by Claude (Fable 5.1) by hand, Scout learning center, format v2 on 2026-10-15, reviewed by a second reader (Claude Fable 5.1), Scout learning center, format v2 C1 on 2026-10-15.

Concept: Relational and non-relational data stores · also covers Data systems (schemas, indexing, partitioning), Storage engines and indexes, System design and architecture · Lesson 5 of 33 in System design · version 1

The data store is the decision a design cannot easily revisit. This lesson reads an access pattern into a kind of store, puts eight criteria against relational, key-value, wide-column, document, search, time-series, graph and object stores in one decision, explains the guarantees that separate them, prices the case where one store is not enough, warns about the migration a wrong choice forces, maps each kind to its AWS managed service from the AWS database decision guide, and walks the news feed through the whole choice.

Why the data store is the decision

Most architecture decisions can be revisited in a sprint. The data store cannot: it fixes the data model, the queries that are cheap, the guarantees the application may assume, and the migration that will one day be needed to leave it. For decades the choice was not made at all, because the only option was a relational database and every application's data was modelled as relational whether it fitted or not; Werner Vogels's account of why Amazon built purpose-built stores makes the point that the database was driving the data model rather than the use case driving the database.[4]

The original evidence is in the same account and in the Dynamo paper. Amazon found that about 70 percent of its operations were key-value lookups that used only a primary key and returned a single row, and the paper lists the services that need only primary-key access, from shopping carts to session management and best-seller lists, for which a relational database would limit scale and availability. A store that offers only that access path can promise what those services need, including staying writeable while disks fail and network routes flap.[4] [5]

The eight kinds of store this lesson chooses between, by the shape of the question each answers best.[1] [12] [11]
store kindunit of datathe question it answers bestexamples
Relationalrows in tables with a schemaany query over several tables, with transactions and joinsPostgreSQL, MySQL, Aurora
Key-valuea value by its keyget or put one item by key, at any scaleDynamoDB, Redis, Dynamo-style stores
Wide-columnrows grouped in a partition, sorted by a clustering keya range within one partition, writes at high rateCassandra, ScyllaDB, Bigtable, Keyspaces
Documenta JSON-like documentfetch or update one document and query its fieldsMongoDB, DocumentDB, PostgreSQL jsonb
Search indexan inverted index over text and fieldsfull-text and faceted search, relevance rankingElasticsearch, OpenSearch
Time seriessamples labelled by metric and tagsranges over time, aggregation, downsamplingPrometheus, Timestream, TimescaleDB
Graphnodes and typed, directed relationshipstraversals across many relationshipsNeo4j, Neptune
Object storean immutable object by keyput and get large blobs cheaply and durablyAmazon S3 and other object stores

The lesson on storage engines explained the mechanism under each kind; this lesson is about the choice. It reads an access pattern into a store kind, puts the criteria against the kinds in one decision, prices the case where one store is not enough, warns about the migration that a wrong choice forces, and maps each kind to the managed service that runs it on AWS. The worked example walks the news feed from this module through the decision.[1]

Reading an access pattern into a store

The DynamoDB design guide states the discipline for every non-relational store in one sentence: do not start designing the schema until you know the questions it will need to answer. A relational schema can be normalised first and queried later; a key-value, wide-column or document schema is the queries, so the queries come first. Cassandra's documentation says the same of its model: modelling is query-driven, each query is best served by a single table, and entities are duplicated across tables to make that true.[2] [3]

Writing down an access pattern

  1. List every read and write the product makes, as a sentence: fetch a user's timeline, append a post to each follower's timeline, search posts by text.
  2. For each one, name the key it arrives with: a user id, a post id, a text query, a time range. A query with no key is a scan or a search, which alone narrows the store.
  3. Estimate its peak rate per second and the size of what it reads or writes.
  4. State what it must see: its own last write, every write before it, or anything recent.
  5. Mark the ones that must happen together or not at all. Those are the transactions, and they decide whether one store can hold the data.
  6. Sort by rate times size. The top two or three are the dominant paths; the store is chosen for them.

[2] [3]

The DynamoDB guide names three properties to know before modelling: the size of the data stored and requested at once, its shape, and its velocity. Those three plus the consistency need sort most patterns into a kind. A key with a small value at a high rate is key-value. A key that owns a growing sorted list is wide-column. A text query is a search index. A time range over labelled samples is time series. A question that walks relationships several hops deep is a graph. A question that joins entities in ways nobody predicted is relational, and so is any set of writes that must be atomic across entities.[2]

The news feed's access patterns, as the next steps need them. Rates are the estimates from the news feed exercise in this module (assumptions, not measurements).[2]
querykeypeak ratemust seesorts into
create a postpost id230 per secondits own writeany durable store
read my home timelineuser id, newest first11,600 per secondrecenta sorted list per key: wide-column or an in-memory list
append a post to each follower's timelinefollower id46,000 per secondeventuallythe same list per key, write-heavy
list who I follow and who follows meuser idlowrecentkey-value or relational
search posts by texttextlowrecentsearch index
store and serve mediamedia idhigh bytes, low countimmutableobject store

The decision: which store for which workload

The decision block is the centre of the lesson: one row per kind of store, the workload it is built for, the workload that breaks it, and named examples with the AWS managed service after them, mapped from the AWS database decision guide and taken up again at the end.[1]

Choosing a data store. Products are examples of the kind; the AWS service after the semicolon follows the decision guide, except S3, which the guide does not cover.[1] [2] [3] [5] [10] [11] [12]
OptionPick whenAvoid whenExample
Relationalqueries are varied and will change, writes must be atomic across entities, and the data fits one primary with replicas, which is most productsthe dominant path is a single-key lookup at a rate one primary cannot serve, or a sorted list per key that grows without boundPostgreSQL, MySQL; Aurora or RDS, Aurora DSQL for multi-region
Key-valuealmost every access is get or put by a known key, the rate or size is beyond one machine, and the store must stay writeable through failuresthe product needs ad hoc queries, joins or transactions across many items, or the questions are not yet knownRedis, Dynamo-style stores; DynamoDB
Wide-columneach key owns a sorted, growing list read by range, writes dominate, and the partition can be bounded, as a chat channel or a timeline isqueries cross partitions, a partition can become hot, or the team has nobody to run the clusterCassandra, ScyllaDB, Bigtable; Keyspaces
Documenteach entity is read and written whole, its fields vary by record, and queries are on the fields of one documentdata is relational underneath and the joins are hidden in application code, or multi-document transactions are commonMongoDB; DocumentDB, or jsonb columns in PostgreSQL
Search indexthe question is a text query or a faceted filter ranked by relevance, over data owned by another storeit would be the only copy of the data; it is a derived index and must be rebuildableElasticsearch; OpenSearch Service
Time seriessamples arrive at a high rate with a timestamp and labels, are queried by range and aggregated, and age outevery sample must be exact and complete, as for per-request billing, or the queries are not over timePrometheus, TimescaleDB; Timestream
Graphthe questions traverse many relationships several hops deep: fraud rings, social paths, recommendations, knowledge graphsthe graph is shallow and the questions are one hop, which a join or a key lookup answersNeo4j; Neptune, Neptune Analytics
Object storeobjects are large, written once, read whole or by range, and durability per byte must be cheapthe application needs small updates, listing by attributes or transactions across objects; keep those in a metadata storeAmazon S3 and other object stores; S3

The criteria, store by store

The decision rows compress eight criteria. The two tables expand them so that a reviewer can disagree with one cell rather than a row; the cells follow the AWS decision guide, the vendor documentation cited in the captions, and the mechanism taught in the storage-engines lesson.[1]

The criteria against the four row-shaped stores.[1] [2] [3] [5]
criterionrelationalkey-valuewide-columndocument
access patternany; joins across tablesone item by keya range inside one partitionone document by key, filters on its fields
read to write ratioeither; reads scale with replicaseither; both scale by partitionwrite-heavy; appends are cheapread-mostly, whole documents
consistency needtransactions across rows and tablesper item; always writeable in Dynamo-style storestunable per query; per partitionper document; multi-document transactions are the exception
data shaperegular, normalisedopaque valuesorted rows per partitionnested, varying fields
scale and growthone primary; shard by hand past itlinear by partitionlinear by partition; bound the partition sizeshard by key
query flexibilityhighest; ad hoc SQLlowest; by key onlyby partition and clustering key onlyfields of one document; secondary indexes
operational costwell understood; managed forms everywherelowest when manageda cluster to run and repair unless managedmoderate; managed forms exist
team familiaritynearly universalcommonspecialistcommon
The criteria against the four special-purpose stores.[1] [8] [11] [12]
criterionsearch indextime seriesgraphobject store
access patterntext and faceted queries ranked by relevanceranges over time per metric and label settraversals across relationshipsput and get whole objects, range reads
read to write ratioread-heavy; indexing is batchedwrite-heavy ingest, range readsread-heavy traversalswrite once, read many
consistency neednear real time; a derived copyapproximate is acceptable; not for billingper transaction in most enginesper object; immutable
data shapedocuments with analysed text fieldssamples with timestamps and labelsnodes, typed directed relationships, propertiesopaque bytes with a few metadata fields
scale and growthshards and replicasretention and downsampling bound itgrows with relationships; traversals get costlyeffectively unbounded, cheap per byte
query flexibilityhigh for text; weak for joinshigh over time; weak across metricshigh for paths; weak for scansnone beyond key and prefix listing
operational costa cluster and its reindexingretention managementspecialistlowest when managed
team familiaritycommoncommon for monitoringrareuniversal

Two cells deserve a sentence, and one row a caveat: the team-familiarity row is the lesson's own judgement, not a source's. Query flexibility is the hidden price of every non-relational choice: the DynamoDB guide describes data that can be queried efficiently in a limited number of ways, outside of which queries are expensive, against relational data that can be queried flexibly at a cost that does not scale under high traffic. The time-series consistency cell is the Prometheus project's own warning: where every sample must be exact, as in per-request billing, another store must hold the record.[2] [11]

Guarantees, transactions and the shape of the answer

Three guarantees separate the kinds more sharply than throughput does. The first is atomicity across entities: a relational store commits several rows in one transaction as a matter of course, while a key-value or document store makes a single item the unit of atomicity and offers cross-item transactions, where it offers them at all, as a bounded special case. A product whose core operation moves value between two records, a ledger or an inventory, has this guarantee as its dominant path.[2]

The second is what a read may see. The Dynamo paper's design target is an always-writeable store: a shopping cart must accept an add even while servers and network paths fail, and the price is that two replicas may hold divergent versions to be reconciled later. The relational default is the opposite: one primary accepts writes and a read on it sees every committed write. Neither is better; the question is which failure the product would rather have, a rejected write or a stale read.[5]

The third is exactness of the answer. A search index ranks by relevance, which is a judgement rather than a match; a vector index returns approximate neighbours, and pgvector's documentation states the trade plainly: exact search by default with perfect recall, or an approximate index, HNSW or IVFFlat, that trades some recall for speed and changes the results a query returns. A time-series store may drop or round samples by design. Each is the right answer for its question and the wrong one for a question that needs the exact row.[10] [11]

What each kind promises, and what a product must not ask of it.[5] [10] [11] [2]
kindunit of atomicitywhat a read seesexactness of the answer
relationala transaction over many rows and tablesevery committed write, on the primaryexact
key-value, documentone item or document; bounded multi-item transactions where offeredconfigurable; eventually consistent reads are the cheap default in many storesexact for the item
wide-columnone partitiontunable per queryexact
search indexone documentafter the next refreshranked, not exact
vector indexone rowafter indexingapproximate unless exact search is chosen
time seriesone samplerecentapproximate by design in monitoring stores
object storeone objectthe object as writtenexact bytes

When one store is not enough, and what two cost

Most products end up with a primary store plus derived ones: a cache for the hot reads, a search index for the text queries, a time-series store for the metrics, an analytics copy for the reports, an object store for the files. The rule that keeps this sane is that exactly one of them is the source of truth and every other is rebuildable from it. Discord's search is the pattern done right: messages live in Cassandra, Elasticsearch holds an index that is built lazily, only when someone first searches a server, and even the mapping of servers to index shards is kept in Cassandra as the source of truth with the index as a derived copy.[8]

What a second store adds and what it costs.[8] [1]
second storewhat it addswhat it costs
cachecheap repeated readsinvalidation, cold starts, a stale-read story
search indextext and faceted queriesa pipeline that feeds it, a lag, and a reindex when the mapping changes
time seriesmetrics over time at high ingestretention policy, cardinality limits, a second query language
analytics copyreports without loading the primarya batch or change-data pipeline and a freshness lag
object storecheap, durable blobsa metadata table to keep in step; orphaned objects
any of themthe right shape for one querytwo systems to run, two failure modes, two skill sets, and a dual-write path that can disagree

The cost is rarely in the software. It is in the pipeline between the stores, which is a second write path that can fail half-way; in the lag, which the product must tolerate and the support team must explain; in the on-call surface, which doubles; and in the people, since each kind of store has its own failure modes and tuning. The AWS decision guide's advice on vector search generalises: a team that already runs a hundred PostgreSQL databases should first evaluate the vector extension on the engine it knows, because the same engine lets developers use the skills and tools they have rather than learn a new system.[1]

So the second store has to earn its place with a query the first cannot serve at the required rate or shape, not with a preference. The order of preference is: the primary store's own feature, such as a JSON column, full-text search or a vector extension in PostgreSQL; a managed derived store fed by change capture; and only then a second primary with its own writes.[1] [10]

The migration trap

The wrong store is not discovered at design time; it is discovered when the dataset outgrows it, and leaving it then means migrating live data under full traffic. Discord's message store shows both halves. The first version was a single MongoDB replica set, chosen for speed of iteration, and the team planned from the start for an easy migration out. At about 100 million messages the data and its index no longer fitted in RAM, latency became unpredictable, and the messages moved to Cassandra with a partition key of channel and time bucket sized to stay under 100 MB. Six years later the Cassandra cluster had grown from 12 nodes to 177, was high toil with hot partitions that hurt the whole cluster, and was migrated again, to ScyllaDB, behind a new data-services layer.[6] [7]

Stripe's account of moving hundreds of millions of subscription objects states the method most teams use and the constraint that makes it hard: the migration had to run with every service at full traffic and with users expecting consistency throughout. The four-step dual-writing pattern is the answer, one codebase-wide change at a time.[9]

The four-step dual-writing migration, as Stripe describes it

  1. Dual write: every new write goes to the existing table and the new one, so they stay in sync; then backfill the missing history into the new table, in Stripe's case hundreds of millions of subscription objects, until the two hold identical information.
  2. Change every read path in the codebase to read from the new table.
  3. Change every write path in the codebase to write only to the new table.
  4. Remove the old data that relies on the outdated data model.

[9]

The trap is the cost of the choice that forces this, not the migration itself. A store chosen by preference rather than by the access pattern tends to be left at the worst time, when the dataset is largest and the product busiest. The defence is Discord's: choose the store for the questions you have, but from day one keep the application's data access behind a narrow interface, keep ids and keys that any store can use, and know what the next store will be and what number triggers the move.[6] [9]

On AWS: the managed service for each store

The neutral decision comes first; the product mapping is the last step. On AWS the database decision guide gives it in one table: each managed engine, its data model, when to use it and what it is optimised for. The table below carries that mapping for the eight kinds of this lesson; the alternative column is the lesson's own reading of the guide's other rows, and two cells the guide does not cover are marked; the exercises in this module link back here when they take a design down to AWS.[1]

The AWS managed service for each kind of store. The service and use-when columns follow the AWS database decision guide except where a cell says otherwise; the alternative column is the lesson's own reading of the guide's other rows.[1]
store kindAWS servicethe guide's use-whenalternative on AWS
RelationalAmazon Aurora (MySQL and PostgreSQL compatible)migrating or modernising a relational workload, or less predictable query patternsAmazon RDS for six engines; Aurora DSQL for active-active multi-region with strong consistency; Aurora PostgreSQL Limitless for writes beyond one instance
Key-valueAmazon DynamoDBsession stores and shopping carts; very high throughput for millions of simultaneous users; serverlessElastiCache or MemoryDB for in-memory latency
Wide-columnAmazon Keyspaces (for Apache Cassandra)Cassandra workloads, heavy reads and writes at single-digit-millisecond latency with linear scalabilityDynamoDB, when the Cassandra model is not required
DocumentAmazon DocumentDB (with MongoDB compatibility)JSON-like documents with rich querying across their fieldsjsonb in Aurora PostgreSQL
Search indexAmazon OpenSearch Servicefrom the guide's vector table: native k-NN; RAG at scale, hybrid search, log analyticsnone in the guide; OpenSearch is the search service
Time seriesAmazon Timestreamlarge amounts of time series data from many sources: IoT, metrics, asset trackinga managed Prometheus for monitoring metrics (the lesson's own; not in the guide)
GraphAmazon Neptunecomplex networks of objects: social graphs, fraud detection, recommendationsNeptune Analytics for graph analytics and GraphRAG
Object storeAmazon S3not a row in the guide; the lesson's own mapping: large, immutable objects at low cost per bytenone; the metadata belongs in one of the stores above

Vector search is where the guide argues most clearly from team familiarity. Rather than a separate vector database, it lists vector capability inside the existing services, ranked by latency and fit, and advises a team that already runs relational databases to evaluate the vector extension on the engine it has before adding a new system.[1]

Vector search options on AWS, from the decision guide's capability table.[1]
servicevector capabilitylatency, per the guidebest for, per the guide
OpenSearch Servicenative k-NNlow to mediumretrieval-augmented generation at scale, hybrid search, log analytics
Aurora PostgreSQLpgvector extensionlowrelational applications that need vectors and transactional data in one database
MemoryDBnative vector searchsub-millisecondsemantic caching, real-time inference, lowest-latency retrieval
Neptune Analyticsvector similarity plus graph analyticsmediumGraphRAG and knowledge-graph-enhanced retrieval
DocumentDBnative vector search with HNSW and IVFFlat indexeslowMongoDB-compatible applications with vectors

Worked example

Worked example: choosing the stores for the news feed

The news feed exercise in this module sizes a product with 10 million daily active users, where each user follows about 200 accounts. Its estimates are the inputs here; every number is an assumption from that exercise, not a measurement. The decision is made per access pattern, and the stores are then counted.[2]

From the access patterns to the stores

  1. Posts: 230 creates per second at peak. Any durable store serves this; the posts table and its author index stay relational, because posts are also read by id, edited, deleted and joined to their authors.
  2. Home timeline reads: 11,600 per second at peak, each wanting the newest entries for one user. A sorted list per key, read by range: a wide-column partition or an in-memory capped list, not a relational query that sorts at read time.
  3. Fan-out appends: 46,000 per second at peak, each adding one entry to one follower's list. Write-heavy, by key, eventually consistent: the same per-key list, on a store that makes appends cheap.
  4. Timeline size: 800 entries per user at 16 bytes is 12.8 kB, and the cache for all active users is about 128 GB; an in-memory store holds it, with the wide-column store or a rebuild from the posts table behind it.
  5. Social graph: who follows whom, read when a post is created to find its followers and when a timeline is rebuilt. One hop, by user id: a key-value or relational table, not a graph database, because no question walks further than one hop.
  6. Search: posts by text, low rate, ranked by relevance. A search index derived from the posts table through its change stream, rebuildable, never the only copy.
  7. Media: images and video, large and immutable. An object store, with the media id and attributes in the posts row.
  8. Metrics: the fan-out lag, the cache hit ratio and the request latencies, at a high ingest rate and queried by range. A monitoring time-series store, approximate by design.
  9. Count: one relational primary as the source of truth, one in-memory timeline cache, one wide-column or list store for timelines, one derived search index, one object store and one metrics store, each with a query the primary could not serve at that rate or shape.

[2] [3] [5] [11]

The news feed's stores, one row per decision.[1] [2]
datastore kindon AWS (the mapping in the on-AWS subtopic)why this kind
posts, users, followsrelational, the source of truthAurora PostgreSQLvaried queries, joins, transactions across entities; the rates fit one primary with replicas
home timelinesa sorted list per user: in-memory cache in front of a wide-column storeElastiCache in front of Keyspaces or DynamoDB46,000 appends and 11,600 range reads per second at peak, eventually consistent
post searchsearch index, derivedOpenSearch Servicetext queries ranked by relevance; rebuildable from the posts table
mediaobject storeS3large immutable blobs, cheap per byte; metadata stays in the posts row
service metricstime seriesa managed Prometheus or Timestreamhigh ingest of labelled samples, range queries, approximate is acceptable

The decision that changes the architecture is the timeline. If it were kept relational, every home timeline read would sort and merge posts from about 200 followed accounts at 11,600 requests per second, and the choice of fan-out on write would be pointless. Everything else in the table is a derived store that earns its place with one query shape; the relational primary stays the only source of truth, and each derived store can be rebuilt from it.[2] [3]

In practice

Amazon built Dynamo, and later DynamoDB, because it measured its own access patterns: about 70 percent of operations were key-value lookups by primary key returning one row, and services such as the shopping cart needed a store that stayed writeable through disk, network and data-centre failures. The Dynamo paper lists the services that need only primary-key access and states that a relational database would have limited their scale and availability.[4] [5]

Discord's message store across three generations, from its two engineering posts.[6] [7]
MongoDB (2015)Cassandra (2017)ScyllaDB (2023)
chosen becausefastest to iterate on; a single replica setlinear scalability, automatic failover, no manual reshardingsame model without the garbage-collection toil; hot partitions still possible
left becauseat about 100 million messages the data and index no longer fit in RAM and latency became unpredictable177 nodes, frequent pages, hot partitions that slowed the whole clustercurrent
keycompound index on channel and created timepartition (channel, 10-day bucket) sized under 100 MB, clustered by message idthe same, behind data services with request coalescing
scale100 million messages12 nodes in 2017, 177 by 2022, trillions of messages72 nodes at 9 TB each
tail latency for history readsunpredictablep99 of 40 to 125 msp99 of 15 ms

Discord's search is a derived store by design: Elasticsearch was chosen over Solr for its self-contained discovery and automatic shard rebalancing, messages are indexed lazily only when a server is first searched, and the mapping from servers to index shards is stored in Cassandra, the primary store, as the source of truth with a cache in front of it.[8]

Stripe migrated hundreds of millions of Subscriptions objects to a new data model with every service at full traffic, using the four-step dual-writing pattern: dual write to the existing and new tables and backfill the history, change every read path to the new table, change every write path to write only to the new table, then remove the old data that relies on the outdated model.[9]

Technologies

PostgreSQLAmazon DynamoDBApache CassandraMongoDBElasticsearchPrometheusNeo4jpgvectorAmazon S3Redis

Sources

  1. docAWS Decision Guides, Choosing an AWS database service (docs.aws.amazon.com, read 2026-10-15): the table of engines by data model with when to use each, the vector-capability table, and the team-familiarity guidance (docs.aws.amazon.com) checked 2026-10-15
  2. docAmazon DynamoDB Developer Guide, NoSQL design for DynamoDB: access-pattern-first modelling, know the questions before the schema, as few tables as possible (docs.aws.amazon.com) checked 2026-10-15
  3. docApache Cassandra documentation, Data modeling, Introduction: query-driven modelling, one table per query, denormalisation (cassandra.apache.org) checked 2026-10-15
  4. articleWerner Vogels, A one size fits all database doesn't fit anyone (All Things Distributed, 2018-06-21): the use case drives the database; about 70 percent of Amazon's operations were key-value lookups (www.allthingsdistributed.com) checked 2026-10-15
  5. paperGiuseppe DeCandia et al., Dynamo: Amazon's Highly Available Key-value Store, SOSP 2007 (PDF): services that only need primary-key access, the always-writeable shopping cart (www.allthingsdistributed.com) checked 2026-10-15
  6. articleDiscord Engineering, How Discord Stores Billions of Messages (2017): MongoDB until the data and index outgrew RAM, the move to Cassandra, the (channel, bucket) partition key sized to stay under 100 MB (discord.com) checked 2026-10-15
  7. articleDiscord Engineering, How Discord Stores Trillions of Messages (Bo Ingram, 2023-03-06): 177 Cassandra nodes, hot partitions, the move to 72 ScyllaDB nodes, the data services layer with request coalescing, before-and-after tail latencies (discord.com) checked 2026-10-15
  8. articleDiscord Engineering, How Discord Indexes Billions of Messages (2017): Elasticsearch chosen over Solr, lazily indexed, Cassandra kept as the source of truth (discord.com) checked 2026-10-15
  9. articleStripe Engineering, Online migrations at scale (2017): the four-step dual-writing pattern applied to hundreds of millions of Subscriptions objects with every service at full traffic (stripe.com) checked 2026-10-15
  10. repopgvector README (GitHub, maintained): exact search by default with perfect recall, HNSW and IVFFlat approximate indexes that trade recall for speed, vectors beside the rest of the data (github.com) checked 2026-10-15
  11. docPrometheus documentation, Overview: the multi-dimensional time series model, designed for reliability, and when it does not fit (per-request billing) (prometheus.io) checked 2026-10-15
  12. docNeo4j documentation, What is a graph database: nodes, relationships with a type and a direction, properties on both (neo4j.com) checked 2026-10-15

Related lessons