Docs
View as markdown

Limits and sizing

Every limit relate enforces on graph size, commits, values, statements and parameters, with its default, the flag that sets it, and the error it raises.

clinodecypher

relate holds the whole graph of an open store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary in memory, so the first limit is the machine's memory. On top of that, relate enforces the limits on this page. Most protect the process from one statement exhausting memory or the stack, and each refusal names what it protected and how to clear it. The graph's size has no limit unless one is set.

Synopsis

relate [--logical-graph-size-limit <size>] [--expression-memory-limit <size>|none]
       [--concurrent-logical-workspace-limit <size>] [--max-traversal-depth <n>]
       [--checkpoint-interval-secs <n>] [--database <path> [--read-only]] [<statement>]

Each flag has an environment variable that sets its default; the flag wins over the variable. A <size> is a whole number, optionally followed by a unit:

UnitBytes
No unit, b, bytes1
mb1,000,000
mib1,048,576
gb1,000,000,000
gib1,073,741,824

Units are case-insensitive and may follow the number with or without a space. Any other unit, kb included, is a usage error (exit code The number a relate run returns to the shell; each nonzero value names a class of failure, such as 2 for usage, 5 for conflict, 6 for a rejected statement. Read more in the glossary 2).

Limits you can set

LimitDefaultSet withRangeMet as
Live graph and index size, in logical bytes (not process memory)Unlimited--logical-graph-size-limit, RELATE_LOGICAL_GRAPH_SIZE_LIMITA size; 0 is unlimitedcapacity exceeded: used=… limit=…, exit code 1
One value an expression builds: a list, string or map from range, concatenation, collect, a comprehension or an UNWIND256 MiB--expression-memory-limit, RELATE_EXPRESSION_MEMORY_LIMITA size; none or 0 turns the limit offThis statement tried to build a single value of <size>. …, exit code 1
Workspace shared by the statements running at onceOff--concurrent-logical-workspace-limit, RELATE_CONCURRENT_LOGICAL_WORKSPACE_LIMITA positive size; 0 is a usage errorresource limit exceeded: concurrent-logical-workspace-limit limit=<bytes>, exit code 1
Explicit upper bound of a variable-length path (*1..N, {1,N})500 hops--max-traversal-depth, RELATE_MAX_TRAVERSAL_DEPTH1 to 2000syntax error: TraversalDepthExceeded: …, exit code 6

The graph size limit refuses only writes that grow the graph, when they reach the WAL. DELETE and a SET that shrinks a value still run, and opening a store and replaying its WAL always succeed, even over the limit.

The expression limit applies to each value on its own, not to a statement's total, so a long statement that builds many small values never accumulates against it.

The traversal depth limit applies to an explicit upper bound. A path pattern with no upper bound (*, *1..) is not checked against it, and neither is shortestPath.

Fixed limits

These limits have no flag or environment variable.

LimitValueMet as
One commit's record in the write-ahead log (WAL)64 MiB (67,108,864 bytes)This statement tried to commit a single WAL record of at least <size>. …, exit code 1; nothing is written
Statement text4 MiB (4,194,304 bytes)syntax error: query text of <n> bytes exceeds the maximum query size of 4194304 bytes, exit code 6
Nesting of a statement's expressions and clauses500 levelssyntax error: parse error: maximum nesting depth of 500 exceeded, exit code 6
Nesting of a list or map a statement builds at run time500 levelsValueDepthExceeded: value nesting exceeds 500 levels; container nesting compounds per level (security limit)., exit code 1
Evaluation passes of nested subqueries, EXISTS and FOREACH in one statement100,000NestedEvaluationBudgetExceeded: …
One file read by LOAD CSV8 MiB (8,388,608 bytes)LOAD CSV file exceeds byte limit, exit code 1
A store downloaded by --load <url>1 GiB, and 300 secondsA message containing the remote database exceeded the 1 GiB cap
One source fetched by --fetch-params64 MiB, and 60 secondsA message containing source exceeded the 64 MiB cap
Graphs in a store3 fixed namespaces: neo4j, system, system_ontologies; a statement runs against onesemantic error: DatabaseNotFound: Graph not found: <name>, exit code 6

Every property value is subject to two limits on this page: the expression limit while a statement builds it, and the 64 MiB WAL record limit when it is committed, together with everything else in the same commit. A commit over 64 MiB is refused whole; splitting the write into smaller transactions clears it.

Parameters in the Node.js client

The Node.js client passes parameter A named value a statement refers to as $name and the caller binds separately, such as with --param name=value on the CLI. Read more in the glossary to the engine through a bounded builder:

LimitValueMet as
Entries in one list or map100,000parameter container exceeds the entry limit of 100000
Length of one parameter name4,096 bytesparameter name exceeds the 4096-byte limit
All parameters of one statement16 MiB (16,777,216 bytes)string parameter exceeds the builder's total byte budget of 16777216 (for a string)

This program trips each one:

limits.cjs
const { Database } = require("@relate/relate");

const conn = Database.open(":memory:").connect();
const cases = {
  "100,001 list entries": { xs: Array.from({ length: 100001 }, (_, i) => i) },
  "a 4,097-byte name": { ["n".repeat(4097)]: 1 },
  "a 17 MiB string": { s: "a".repeat(17 * 1024 * 1024) },
};
for (const [what, params] of Object.entries(cases)) {
  try {
    conn.query("RETURN 1 AS ok", params);
    console.log(`${what}: accepted`);
  } catch (e) {
    console.log(`${what}: ${e.message}`);
  }
}
100,001 list entries: parameter container exceeds the entry limit of 100000
a 4,097-byte name: parameter name exceeds the 4096-byte limit
a 17 MiB string: string parameter exceeds the builder's total byte budget of 16777216

Sizing

WhatSize
Memory while a store is openThe whole graph and its indexes, held in memory; relate has no buffer pool and does not page to disk. Size a store and its memory has a worked sizing
Snapshot on diskDepends on the codec; the storage formats reference has measured sizes
WAL on diskThe commits since the last checkpoint. The current WAL segment closes, and a checkpoint runs, at 64 MiB, at 10,000 commits, or when the checkpoint timer fires (when a checkpoint runs)
Checkpoint timer300 seconds by default; --checkpoint-interval-secs, RELATE_CHECKPOINT_INTERVAL_SECS; 0 turns off the timer only; relate serve takes only RELATE_CHECKPOINT_INTERVAL_SECS

Examples

A graph size limit of 1mb is 1,000,000 bytes, and 1mib is 1,048,576; the limit appears in the message:

relate --mode envelope --logical-graph-size-limit 1mb "UNWIND range(1, 20000) AS i CREATE (:Item {i: i})"
relate --mode envelope --logical-graph-size-limit 1mib "UNWIND range(1, 20000) AS i CREATE (:Item {i: i})"

The output alternates stderr and stdout:

capacity exceeded: used=258 limit=1000000
{"error":{"code":"fatal_error","exit_code":1,"kind":"capacity","message":"capacity exceeded: used=258 limit=1000000","retryable":false}}
capacity exceeded: used=258 limit=1048576
{"error":{"code":"fatal_error","exit_code":1,"kind":"capacity","message":"capacity exceeded: used=258 limit=1048576","retryable":false}}

kb is not a unit:

relate --logical-graph-size-limit 64kb "RETURN 1 AS ok"
Error: invalid --logical-graph-size-limit: semantic error: invalid memory limit unit 'kb'; use bytes, mb, mib, gb, or gib

A single string property of 67,108,864 characters makes a commit record over 64 MiB, so the commit is refused:

relate --mode envelope "WITH reduce(s = 'a', i IN range(1, 26) | s + s) AS s CREATE (:Blob {s: s})"
This statement tried to commit a single WAL record of at least 64 MB. This is a safety limit protecting against memory exhaustion — the per-record WAL payload cap is 64 MB; commit the data in smaller transactions.
{"error":{"code":"fatal_error","exit_code":1,"kind":"capacity","message":"This statement tried to commit a single WAL record of at least 64 MB. This is a safety limit protecting against memory exhaustion — the per-record WAL payload cap is 64 MB; commit the data in smaller transactions.","retryable":false}}

The message writes the 64 MiB limit as 64 MB. A string of half that length commits:

relate --mode envelope "WITH reduce(s = 'a', i IN range(1, 25) | s + s) AS s CREATE (:Blob {s: s}) RETURN size(s) AS characters"
{"columnTypes":["INTEGER"],"columns":["characters"],"notifications":[],"planCache":"miss","rows":[{"characters":33554432}],"timings":{"elapsed_ms":46.503208,"execute_ms":46.268917,"lex_parse_ms":0.007208,"lower_ms":0.009000000000000001,"parse_ms":0.2215,"plan_ms":0.007042,"result_generation_ms":0.000583,"rewrite_ms":0.0005},"truncated":false}

See also