Skip to content

API Usage Guide

Chen Zhao edited this page Jan 14, 2026 · 3 revisions

This guide summarizes how to issue the request types defined in eloqstore/include/eloq_store.h. Every operation derives from KvRequest, so they expose helpers such as TableId(), Error(), and IsDone(), and they work with both synchronous (ExecSync) and asynchronous (ExecAsyn) execution paths. Snippets below assume an initialized eloqstore::EloqStore plus a partition handle eloqstore::TableIdent tbl{"demo", 0};.

Request Lifecycle Basics

  • Construct the request object on the stack.
  • Populate arguments via SetArgs(...) (and helpers like SetPagination).
  • Execute through store.ExecSync(&req) to block or store.ExecAsyn(&req, user_data, callback) to run in the background.
  • Inspect results via type-specific members and always check req.Error() for KvError::NoError before consuming the payload.

Starting EloqStore

eloqstore/examples/basic_example.cpp shows the minimum wiring needed to bring the engine up and issue requests:

eloqstore::KvOptions opts;
opts.store_path = {"/tmp/eloq_store"};  // directory per shard/partition
opts.num_threads = 1;                    // spawns 1 shard thread
eloqstore::TableIdent tbl{"t1", 1};     // logical table + partition id

eloqstore::EloqStore store(opts);
if (store.Start() != eloqstore::KvError::NoError) {
  throw std::runtime_error("Failed to start EloqStore");
}

// … submit BatchWriteRequest / ReadRequest etc …

store.Stop();

Key points:

  • Always set KvOptions::store_path (vector of local directories) and choose num_threads to match your CPU/shard count.
  • Build a TableIdent with the logical table name and partition id you plan to address.
  • Call Start() once per process, then issue any mix of sync/async requests. Call Stop() during shutdown to flush background workers cleanly.
  • For the full option list and detailed comments, see the KvOptions reference.

BatchWriteRequest

BatchWriteRequest is the high-throughput mutation primitive. Each WriteDataEntry carries key, value, a caller-supplied timestamp, a WriteOp (Upsert or Delete), and an optional expire_ts (0 = never expire).

Key rules:

  • Sort entries strictly by key and deduplicate them before submitting; EloqStore trusts the caller on ordering guarantees.
  • Provide monotonic timestamps to maintain deterministic ordering.
  • Watch KvOptions::max_write_batch_pages; oversized batches are rejected once they exceed the configured page budget.
eloqstore::BatchWriteRequest write;
std::vector<eloqstore::WriteDataEntry> entries;
uint64_t now_ms = /* wall clock in ms */;
entries.emplace_back("session1", "payload", now_ms,
                     eloqstore::WriteOp::Upsert,
                     /*expire_ts*/ now_ms + 10 * 60 * 1000);
entries.emplace_back("session2", "payload2", now_ms,
                     eloqstore::WriteOp::Upsert);
std::sort(entries.begin(), entries.end());
write.SetArgs(tbl, std::move(entries));
store.ExecSync(&write);
if (write.Error() != eloqstore::KvError::NoError) {
  // inspect write.ErrMessage()
}

You can stream records with write.AddWrite(...) while building the vector, but you still need to sort/deduplicate before executing.

ReadRequest

Fetches a single key and returns its value, write timestamp, and TTL deadline.

  • SetArgs(tbl, key) accepts const char*, std::string_view, or std::string.
  • Missing keys surface as KvError::NotFound.
  • On success, read req.value_, req.ts_, and req.expire_ts_.
eloqstore::ReadRequest read;
read.SetArgs(tbl, "session1");
store.ExecSync(&read);
if (read.Error() == eloqstore::KvError::NoError) {
  std::cout << "value=" << read.value_ << ", ts=" << read.ts_ << std::endl;
} else if (read.Error() == eloqstore::KvError::NotFound) {
  // miss
}

FloorRequest

Finds the greatest key that is less than or equal to the probe.

  • Call SetArgs(tbl, key) with any string type; the probe itself may not exist.
  • When found, inspect req.floor_key_, req.value_, req.ts_, req.expire_ts_.
  • KvError::NotFound indicates all stored keys are greater than the probe.
eloqstore::FloorRequest floor;
floor.SetArgs(tbl, "k042");
store.ExecSync(&floor);
if (floor.Error() == eloqstore::KvError::NoError) {
  process(floor.floor_key_, floor.value_);
}

ScanRequest

Iterates over a key range and supports pagination plus read-ahead.

  • SetArgs(tbl, begin, end, begin_inclusive=true) defines the half-open range [begin, end); pass begin_inclusive=false to start strictly greater than begin.
  • SetPagination(max_entries, max_bytes) bounds how much work each scan round performs.
  • SetPrefetchPageNum(pages) issues additional asynchronous reads so later ExecSync calls hit warm data (default kDefaultScanPrefetchPageCount).
  • After each execution iterate over req.Entries() (a tcb::span<KvEntry>) and check req.ResultSize() for the returned count/bytes.
  • When req.HasRemaining() is true, resume scanning by calling SetArgs(tbl, req.EndKey(), end).
eloqstore::ScanRequest scan;
scan.SetArgs(tbl, "k0000", "k9999");
scan.SetPagination(/*entries*/500, /*bytes*/1 << 20);
scan.SetPrefetchPageNum(4);
while (true) {
  store.ExecSync(&scan);
  if (scan.Error() != eloqstore::KvError::NoError) break;
  for (auto &entry : scan.Entries()) {
    consume(entry);
  }
  if (!scan.HasRemaining()) break;
  scan.SetArgs(tbl, scan.EndKey(), "k9999");
}

TruncateRequest

Deletes the tail of a partition by removing every key greater than or equal to a boundary. Useful for rollbacks or reclaiming space quickly.

  • SetArgs(tbl, position) accepts std::string_view or owning std::string and marks the first key that will be removed.
  • Execute synchronously or asynchronously like any other request.
  • Pair with DropTableRequest to wipe all partitions of a logical table.
eloqstore::TruncateRequest truncate;
truncate.SetArgs(tbl, "k5000");
store.ExecSync(&truncate);
if (truncate.Error() != eloqstore::KvError::NoError) {
  // inspect truncate.ErrMessage()
}

DropTableRequest

Removes every partition belonging to a logical table by dispatching a TruncateRequest per partition internally.

  • Call SetArgs("table_name"); partitions are auto-discovered on local disks or via ListObjectRequest in cloud mode.
  • The drop completes once each partition truncate finishes. req.Error() reports the first failure (others are logged).
  • No TableIdent is required in advance because the drop spans all partitions.
eloqstore::DropTableRequest drop;
drop.SetArgs("tenant_cache");
store.ExecSync(&drop);
if (drop.Error() != eloqstore::KvError::NoError) {
  // inspect drop.ErrMessage()
}

ArchiveRequest

Creates an immutable snapshot (archive) for a partition when running in append mode with KvOptions::num_retained_archives > 0. The background writer compacts cold data files before writing the archive to object storage.

  • Use it to force an on-demand snapshot outside the periodic archiver.
  • Requires KvOptions::data_append_mode and an object store configuration. Returns KvError::NotFound when the partition has no data.
eloqstore::ArchiveRequest archive;
archive.SetTableId(tbl);
store.ExecSync(&archive);

Error Handling

Every request returns a KvError (see include/error.h) via ExecSync and req.Error().

Error Description
NoError Success.
NotFound Point operations (read/floor) or archives against empty partitions.
InvalidArgs Bad inputs such as unsorted batch writes or invalid scan ranges.
NotRunning Store was never started or already stopped.
EndOfFile Iterator-style calls (scan/TTL sweeps) reached the end of range.
OutOfSpace Host ran out of disk space; free capacity and retry.
OpenFileLimit File descriptor budget exhausted or io_uring buffer ring initialization failed; raise ulimit -n.
OutOfMem Buffer pool exhausted; enlarge the pool or shrink requests.
Busy / TryAgain / Timeout Transient backpressure (e.g., -EBUSY, throttling).
CloudErr Remote object store failure (S3/MinIO/GCS).
IoFail Local IO error reading/writing files.
Corrupted On-disk structure verification failed; repair/restore needed.
NoPermission Underlying operation returned -EPERM.

Clone this wiki locally