Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Understanding cost and reuse

Avoiding execution is only one kind of reuse. A request may still walk dependencies, read metadata, or decode a large stored answer. Ajika exposes these costs separately so you can choose query boundaries from evidence.

Start with operation statistics

After run, inspect database.statistics(). These counters cover the latest operation and its nested calls, not just the root request.

CounterWhat it helps explain
executedQuery functions that actually ran.
validatedRecords whose saved dependencies were examined.
header_reads, metadata_readsDisk metadata access for known-valid answers or full records.
decoded, bytes_readPayload acquisition, including old answers read for equality.
compared, unchangedEquality checks and recomputations that preserved versions.
resident_hits, waitsSharing within the operation and contention for unfinished calls.
bytes_written, metadata_writesEncoded payload work and checkpointed record updates.
session_queries, session_bytesThe retained session working set after collection.

For example, zero executions with a large bytes_read suggests that the request loads more data than it needs. Many unchanged recomputations show useful equality boundaries, but may also expose an expensive producer that could be split.

These are not wall-clock timings or exact RSS measurements. bytes_written counts encoded payload bytes, even if the final commit fails. Metadata-only updates can still happen when it is zero. The Statistics fields specify the individual accounting rules.

Ask why a particular call ran

Ajika emits diagnostic events through the tracing target ajika::diagnostics. Enable it with this filter:

ajika::diagnostics=debug

debug reports executions and the first dependency that failed validation. trace also reports reuse decisions and individual output outcomes.

An illustrative whitespace edit looks like this, with some fields omitted:

op=17 revision=3 query=analyze call=ab12cd34
  invalidated dependency=File::text reason=input_stamp_changed
  execute reason=dependency_invalid
  output=words result=equal evidence=comparison version=preserved
  output=locations result=unequal evidence=comparison version=advanced
op=17 revision=3 query=word_count call=ef56ab78
  reuse reason=dependencies_valid

Every event includes a process-local operation ID and its input revision. Query and input families have names. call is a shortened address hash; call_id retains the full hash for correlation. Named slots include their name; keyed slots show the name and an opaque key hash. No Debug bounds on application keys or values are required.

A new version doesn’t always mean the value changed. The previous value may be unavailable for comparison; that outcome is comparison_unavailable. Conversely, reconstructing a transient result with still-valid dependencies can preserve a version by proof without comparing payloads.

Events describe decisions made during evaluation, before checkpointing. Enabling them does not force additional reads or comparisons. The diagnostic implementation only renders evidence the evaluator already has.

Separate cache budgets from validity

Retention::max_idle controls when unused stored work becomes eligible for collection. It is not how Ajika detects stale answers. Input stamps and dependency versions do that on demand, even one millisecond after an edit.

Entry, age, and byte budgets reclaim whole query records with their outputs. Successful operations collect periodically or when over budget; collect() lets the host request an idle-boundary pass. There is no background timer.

Logical retained bytes, compressed payload bytes, physical files, and allocator memory are different measurements. storage_statistics() exposes physical storage information for a disk backend. compact() performs a full physical maintenance pass; it can take much longer than regular maintenance.

set_compression selects no compression, LZ4, or Zstandard for new payloads. It does not rewrite existing payloads or alter query equality. Measure with representative data: serialization, compression, validation, and computation are different costs.

The storage chapter explains where space is used and how logical eviction becomes physical reclamation.