This guide shows how to keep commands on a folder of tens of thousands of rows or more answering in milliseconds. Why each step helps is explained in Performance.
The first command in a folder reads and judges every file. Make that command
reldir init --adopt (see
How to put an existing folder of JSON under reldir),
run once, rather than letting the first query of a script or an agent pay for
it. Every command after it costs what changed.
Start reldir serve beside the database and leave it running:
reldir --db ./data serve
It runs until Ctrl-C or SIGTERM. On a server, run it under the service
manager, so it starts with the machine and restarts if it stops. With
systemd, as a user service:
# ~/.config/systemd/user/reldir-data.service
[Unit]
Description=reldir folder monitor for /srv/data
[Service]
ExecStart=/usr/local/bin/reldir --db /srv/data serve
Restart=on-failure
[Install]
WantedBy=default.target
systemctl --user enable --now reldir-data.service
To confirm that commands use it, ask for the status as JSON and look at
monitored:
reldir --db ./data --json status
"monitored": true,
If it reads false while the monitor runs, that command examined the folder
whole; Performance
lists when that happens. A --readonly command never uses the monitor.
.db/ on a local disk, beside the rowsChanges are staged in .db/ and renamed into place, so .db/ must be on the
same filesystem as the table directories (CROSS_DEVICE otherwise). On a
network, cluster, FUSE, FAT or shared-folder filesystem, reldir refuses to
write (UNSAFE_FILESYSTEM; the
list):
allow_remote_filesystem in
.db/config.Keys, unique constraints and the source columns of foreign keys are indexed already. For another column that queries filter or sort on, check the plan:
$ reldir explain "SELECT count(*) FROM posts WHERE title = 'Engines'"
id | parent | detail
---+--------+-----------
3 | 0 | SCAN posts
(1 row)
1 plan step(s)
SCAN reads every row. Add an index:
$ reldir migrate add-index posts title
description
------------------
index posts(title)
(1 row)
action | path | planned
-------+-------------------+--------
write | schema/posts.json | false
(1 row)
migrated: 1 file(s), revision #2 ([..])
$ reldir explain "SELECT count(*) FROM posts WHERE title = 'Engines'"
id | parent | detail
---+--------+---------------------------------------------------------------------[..]
3 | 0 | SEARCH posts USING COVERING INDEX [..] (title=?)
(1 row)
1 plan step(s)
The index is declared in the table’s schema, in x-reldir.indexes, so every
clone has it.
Statistics are refreshed when a table doubles or halves in size. When a bulk change alters what a table holds without changing its size much – a re-import, a mass update of a filtered column – and queries slow down, refresh them:
$ reldir analyze
refreshed query statistics
Each command opens the database, and each commit flushes to disk. Put many changes in one statement:
$ reldir sql "UPDATE posts SET title = title || '.' WHERE user_id IN ('ada', 'grace')"
action | path | planned
-------+---------------+--------
write | posts/p1.json | false
write | posts/p2.json | false
(2 rows)
committed: 2 file(s), revision #3 ([..])
When the changes differ row by row, use one script – statements separated by
;, or a file given with reldir sql -f FILE – which commits once:
$ reldir sql "UPDATE users SET name = upper(name) WHERE id = 'ada'; UPDATE users SET name = upper(name) WHERE id = 'grace'"
action | path | planned
-------+------------------+--------
write | users/ada.json | false
write | users/grace.json | false
(2 rows)
committed: 2 file(s), revision #4 ([..])
For rows that come from elsewhere, use reldir import (see
How to import and export rows in bulk).
From Python, executemany is one transaction; from Node and Rust, use a
transaction.
A query over a large folder can sort or return a large part of it. Bound
queries run by scripts and agents with --timeout, --max-result-rows and
--max-query-memory, or for every command with the matching settings in
.db/config. A query past a bound
ends with RESOURCE_LIMIT rather than a partial answer.
From a clone of the repository, the scale run builds a folder of the size you name, times every operation in Performance against its target, and fails on a miss:
cargo build --release -p reldir
cargo xtask scale --rows 100000