Skip to content

Stats

This guide shows how to inspect the state of a Vix KV database.

Use this page when you want to check live keys, tombstones, memory mode, recovery information, and sequence state.

Public header

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

What stats are for

Stats help you observe what is happening inside a KV database.

Use stats() when you want to know:

  • whether the database is open
  • whether it is memory-only
  • how many live keys exist
  • how many tombstones exist
  • the last sequence number
  • how many WAL records were recovered after reopen

Basic stats

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"a"}, "one");
  (void)kv.set({"b"}, "two");
  (void)kv.erase(vix::kv::KeyPath{"a"});

  const auto stats = kv.stats();

  vix::print("open =", stats.open ? "yes" : "no");
  vix::print("memory only =", stats.memory_only ? "yes" : "no");
  vix::print("live keys =", stats.key_count);
  vix::print("tombstones =", stats.tombstone_count);
  vix::print("last sequence =", stats.last_sequence);

  (void)kv.close();

  return 0;
}

Run:

vix run main.cpp

Expected output:

open = yes
memory only = yes
live keys = 1
tombstones = 1
last sequence = 3

Stats fields

FieldMeaning
openWhether the database is currently open.
memory_onlyWhether the database is memory-only.
key_countNumber of live keys.
tombstone_countNumber of delete markers.
last_sequenceLast applied operation sequence.
wal_records_recoveredNumber of WAL records recovered when opening a durable database.

Live keys

Live keys are values currently visible to reads and lists.

cpp
const auto stats = kv.stats();

vix::print("live keys =", stats.key_count);

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"users", "1", "name"}, "Ada");
  (void)kv.set({"users", "2", "name"}, "Grace");

  const auto stats = kv.stats();

  vix::print("live keys =", stats.key_count);
  vix::print("size =", kv.size());

  (void)kv.close();

  return 0;
}

Expected output:

live keys = 2
size = 2

Tombstones

A tombstone represents a delete.

When you erase a key, the live key count decreases and the tombstone count increases.

cpp
auto erased = kv.erase(vix::kv::KeyPath{"users", "1", "name"});

if (erased.is_err())
{
  vix::eprint(erased.error().message());
  return 1;
}

Then inspect stats:

cpp
const auto stats = kv.stats();

vix::print("live keys =", stats.key_count);
vix::print("tombstones =", stats.tombstone_count);

Complete example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"users", "1", "name"}, "Ada");
  (void)kv.set({"users", "2", "name"}, "Grace");

  auto erased = kv.erase(vix::kv::KeyPath{"users", "1", "name"});

  if (erased.is_err())
  {
    vix::eprint(erased.error().message());
    return 1;
  }

  const auto stats = kv.stats();

  vix::print("live keys =", stats.key_count);
  vix::print("tombstones =", stats.tombstone_count);

  (void)kv.close();

  return 0;
}

Expected output:

live keys = 1
tombstones = 1

Open state

Use stats.open or kv.is_open() to check whether the database is open.

cpp
const auto stats = kv.stats();

vix::print("open =", stats.open ? "yes" : "no");

You can also use:

cpp
vix::print("open =", kv.is_open() ? "yes" : "no");

Memory-only state

Use stats.memory_only to know whether the database is temporary.

cpp
const auto stats = kv.stats();

vix::print("memory only =", stats.memory_only ? "yes" : "no");

Memory-only databases are useful for tests and temporary state.

cpp
auto opened = vix::kv::open_memory();

Durable databases are backed by a filesystem path.

cpp
auto kv = vix::kv::open("data/app");

Sequence state

last_sequence tracks the last applied operation sequence.

Each write or delete advances the operation history.

cpp
const auto stats = kv.stats();

vix::print("last sequence =", stats.last_sequence);

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"a"}, "one");
  (void)kv.set({"b"}, "two");
  (void)kv.erase(vix::kv::KeyPath{"a"});

  const auto stats = kv.stats();

  vix::print("last sequence =", stats.last_sequence);

  (void)kv.close();

  return 0;
}

Expected output:

last sequence = 3

Recovery stats

For durable databases, stats can show recovery information after reopening.

cpp
const auto stats = kv.stats();

vix::print("wal recovered =", stats.wal_records_recovered);
vix::print("last sequence =", stats.last_sequence);

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  {
    auto kv = vix::kv::open("data/recovery-stats");

    kv.put("name", "Ada");
    kv.put("theme", "dark");

    (void)kv.flush();
    (void)kv.close();
  }

  {
    auto kv = vix::kv::open("data/recovery-stats");

    const auto stats = kv.stats();

    vix::print("wal recovered =", stats.wal_records_recovered);
    vix::print("last sequence =", stats.last_sequence);
    vix::print("live keys =", stats.key_count);

    (void)kv.close();
  }

  return 0;
}

Example output:

wal recovered = 2
last sequence = 2
live keys = 2

Stats with list

list() returns live entries. stats() tells you about live entries and tombstones.

cpp
auto entries = kv.list({"users"});

if (entries.is_err())
{
  vix::eprint(entries.error().message());
  return 1;
}

const auto stats = kv.stats();

vix::print("listed users =", entries.value().size());
vix::print("live keys =", stats.key_count);
vix::print("tombstones =", stats.tombstone_count);

Complete example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"users", "1", "name"}, "Ada");
  (void)kv.set({"users", "2", "name"}, "Grace");
  (void)kv.set({"users", "3", "name"}, "Linus");

  auto erased = kv.erase(vix::kv::KeyPath{"users", "3", "name"});

  if (erased.is_err())
  {
    vix::eprint(erased.error().message());
    return 1;
  }

  auto users = kv.list({"users"});

  if (users.is_err())
  {
    vix::eprint(users.error().message());
    return 1;
  }

  const auto stats = kv.stats();

  vix::print("listed users =", users.value().size());
  vix::print("live keys =", stats.key_count);
  vix::print("tombstones =", stats.tombstone_count);

  (void)kv.close();

  return 0;
}

Expected output:

listed users = 2
live keys = 2
tombstones = 1

Stats and size

kv.size() returns the number of live keys. stats.key_count also represents live keys.

cpp
vix::print("size =", kv.size());

const auto stats = kv.stats();

vix::print("live keys =", stats.key_count);

Example:

cpp
#include <vix/kv/kv.hpp>
#include <vix/print.hpp>

int main()
{
  auto opened = vix::kv::open_memory();

  if (opened.is_err())
  {
    vix::eprint(opened.error().message());
    return 1;
  }

  auto kv = opened.move_value();

  (void)kv.set({"a"}, "one");
  (void)kv.set({"b"}, "two");

  const auto stats = kv.stats();

  vix::print("size =", kv.size());
  vix::print("live keys =", stats.key_count);

  (void)kv.close();

  return 0;
}

Expected output:

size = 2
live keys = 2

Common workflows

cpp
const auto stats = kv.stats();

vix::print("open =", stats.open ? "yes" : "no");
vix::print("memory only =", stats.memory_only ? "yes" : "no");
vix::print("live keys =", stats.key_count);
vix::print("tombstones =", stats.tombstone_count);
vix::print("last sequence =", stats.last_sequence);

Check live key count

cpp
const auto stats = kv.stats();

if (stats.key_count == 0)
{
  vix::print("database is empty");
}

Check delete markers

cpp
const auto stats = kv.stats();

if (stats.tombstone_count > 0)
{
  vix::print("database has tombstones");
}

Check recovery

cpp
const auto stats = kv.stats();

vix::print("wal recovered =", stats.wal_records_recovered);

Compare list and stats

cpp
auto users = kv.list({"users"});

if (users.is_ok())
{
  const auto stats = kv.stats();

  vix::print("users prefix =", users.value().size());
  vix::print("all live keys =", stats.key_count);
}

Common mistakes

Confusing live keys and tombstones

Live keys are readable values. Tombstones are delete markers.

cpp
const auto stats = kv.stats();

vix::print("live keys =", stats.key_count);
vix::print("tombstones =", stats.tombstone_count);

Expecting deleted keys to appear in size

Deleted keys are not live keys.

cpp
vix::print("size =", kv.size());

Use tombstones when you want to observe deletes.

cpp
const auto stats = kv.stats();

vix::print("tombstones =", stats.tombstone_count);

Expecting memory-only stores to recover WAL records

Memory-only stores are temporary.

cpp
auto opened = vix::kv::open_memory();

Recovery stats are most useful with durable databases.

cpp
auto kv = vix::kv::open("data/app");

Reading stats before operations

Stats reflect the current state. If you read stats before writing, counts may be zero.

cpp
const auto before = kv.stats();

(void)kv.set({"a"}, "one");

const auto after = kv.stats();

Treating stats as application data

Stats are for observability. Store application data with put or set.

cpp
kv.put("settings/theme", "dark");

Best practices

Use stats() when debugging persistence and recovery. Use stats() after deletes to confirm tombstones. Use kv.size() when you only need the live key count. Use list(prefix) when you need the actual entries. Use wal_records_recovered to inspect durable reopen behavior.

PagePurpose
Opening a databaseLearn how to open memory and durable stores.
KeysLearn how to structure keys and prefixes.
ValuesLearn how to write, read, and delete values.
PersistenceLearn how values survive restart.
RecoveryLearn how recovered records and tombstones work.
API ReferenceSee the public KV API surface.

Next step

Continue with the API reference.

Released under the MIT License.