Embedded mode¶
shard-db can run in-process inside your application — no daemon, no TCP
socket, no separate process. You open a data directory, run queries
synchronously, and close when you're done. All the same storage, indexes,
and query capabilities are available; the only things missing are the TCP
server and its config knobs (PORT, TLS_*, THREADS, etc.).
When to use it¶
| Use daemon mode | Use embedded mode |
|---|---|
| Multiple applications share one database | Single application owns the data |
| Remote access required | All access is in-process |
| Multi-language clients (Python, Go, …) | C, C++, or Node.js / Bun |
| Hot reload / live config changes | Restart is acceptable |
C API¶
Include shard_db.h (exported alongside libshard-db.a during ./build.sh):
Open¶
db_root must be an existing, writable directory. One instance may be open at
a time (single-instance guard enforced with an atomic flag); after close, the
same process may open another instance. Open returns NULL if required
background infrastructure, including an enabled durability thread, cannot be
started.
Query¶
char *out = NULL;
size_t out_len = 0;
int rc = shard_db_query(db,
"{\"mode\":\"count\",\"dir\":\"default\",\"object\":\"users\"}",
&out, &out_len);
if (rc == 0) {
printf("%.*s\n", (int)out_len, out); /* e.g. 42 */
shard_db_free_result(out);
}
shard_db_query is thread-safe — multiple threads may call it concurrently
on the same handle. Returns 0 on success, -1 on allocation failure. The
response is the same JSON string the daemon would return over TCP; parse it
with any JSON library.
Always free the result with shard_db_free_result(out).
Close¶
Stops and joins durability sync, auto-vacuum, auto-reshard, and any explicitly enabled async warmup before shutting down the CPU/I/O pools and flushing caches. After this call the pointer is invalid. A maintenance operation already in progress may therefore extend close latency, but it cannot race cache or mutex teardown.
Log handler¶
By default embedded mode is silent — no log files, no ring buffer. Register a callback to receive events:
void my_log(int type, const char *msg, void *userdata) {
/* msg: "YYYY-MM-DD HH:MM:SS LEVEL [subsystem] text\n" */
if (type <= SHARD_DB_LOG_WARN)
fprintf(stderr, "[shard-db] %s", msg);
}
shard_db_set_log_handler(db, my_log, NULL);
Log type constants:
| Constant | Value | When |
|---|---|---|
SHARD_DB_LOG_ERROR |
1 | Internal errors |
SHARD_DB_LOG_WARN |
2 | Warnings |
SHARD_DB_LOG_INFO |
3 | General info |
SHARD_DB_LOG_DEBUG |
4 | Verbose debug |
SHARD_DB_LOG_AUDIT |
5 | Auth / write audit trail |
SHARD_DB_LOG_SLOW |
6 | Slow-query threshold crossed |
Thread safety: the handler is called from whichever thread emits the event — including shard-db's internal worker threads. The handler must be thread-safe.
Timing: set the handler after shard_db_open returns. Log events that
occur during open itself (schema load, recovery) are emitted before the
handler is registered and will not be delivered. Periodic durability ticks and
background maintenance events emitted afterward are delivered normally.
Configuration¶
shard-db reads db.env from the process's current working directory on
shard_db_open, if the file exists. DB_ROOT inside the file is
ignored — the db_root argument to shard_db_open is always authoritative.
Useful knobs for embedded mode:
# db.env (place in your app's working directory)
SLOW_QUERY_MS=200 # default 500 — log queries slower than this
GLOBAL_LIMIT=10000 # default 100000 — max records returned per query
FCACHE_MAX=4096 # default 4096 — entries each for kfcache/segcache; derives bt_cache/bm_cache at /4
DURABILITY_SYNC_MS=1000 # 0=off; otherwise >=50ms target dirty-age/attempt interval
TOKEN_CAP=1024 # default 1024 — token table buckets
LOG_LEVEL=3 # default 3 — 1=ERROR 2=WARN 3=INFO 4=DEBUG
# WARMUP=async # embedded default is off unless explicitly configured
Periodic durability sync is enabled by default. Cached mmap mutations are
marked dirty; the background thread attempts blocking MS_SYNC writes on the
configured cadence. Failed syncs stay dirty for retry and are reported through
the log callback. The interval is a target, not a transactional or hard
real-time guarantee: an active writer, an earlier slow sync, or storage errors
can extend it. This does not provide WAL semantics or complete directory-entry
metadata durability.
Daemon warmup still defaults to async. Embedded mode deliberately defaults
warmup to off; explicit WARMUP=off, sync, or async is honored. All
asynchronous maintenance threads are joined by shard_db_close.
If no db.env is present all settings stay at their compiled defaults.
The file is only read once at open time; changing it after shard_db_open
has no effect until the next open.
Linking¶
./build.sh produces build/bin/libshard-db.a and build/bin/shard_db.h.
CFLAGS += -I/path/to/build/bin
LDFLAGS += -L/path/to/build/bin -lshard-db -lpthread
# On Linux also: -lm
# With TLS enabled: -lssl -lcrypto
Limitations¶
- One instance open at a time. A second
shard_db_opencall returns NULL until the first is closed; sequential open → close → open is supported. - No TCP server. Daemon config knobs that relate to the TCP layer
(
PORT,TLS_*,THREADS,WORKERS,MAX_REQUEST_SIZE) are parsed fromdb.envif present but have no effect.MAX_CONCURRENT_QUERIESdoes not cap concurrency in embedded mode but does influence the per-query memory budget auto-tune. - Startup logs not delivered. Events emitted during
shard_db_openoccur before the handler is registered. If you need to capture them, start the daemon, redirect its log to your log pipeline, and use the TCP API instead.