INFO

INFO [section]

Returns a bulk string of server and memory statistics in the standard Redis # Section / field:value (CRLF-separated) format, so redis-cli, monitoring agents, and benchmark harnesses read it unchanged. Goblin Core emits a deliberately small subset: # Server, # Replication, # Kafka, and # Memory. The optional section argument is accepted but ignored: INFO and INFO memory both return the full payload.

# Server

field value
redis_version 7.4.0 — the wire/behaviour compatibility level
redis_mode standalone
list_implementation concrete backend selected for standard list commands (pma or segmented; default segmented)
hash_implementation representation selected for hashes created by standard commands (efficient or rt)
keyspace_index top-level key index (swiss, or incremental linear under --real-time)

# Replication

field meaning
role master, or slave when an upstream firehose is configured
master_replid the 128-bit replication lineage ID, rendered as 32 hexadecimal digits
master_repl_offset highest logical mutation offset represented by this server
kafka_acknowledged_offset last broker offset acknowledged by Kafka and eligible for inclusive snapshot recovery; -1 until one is known
goblin_ready 1 for a primary or live replica; 0 while a replica is connecting, replaying, or degraded
master_link_status replica upstream link: up only after a complete handoff, otherwise down
master_sync_in_progress 1 while Kafka replay or buffered-firehose handoff is active
replica_state connecting, reconnecting, replaying_kafka, buffering_firehose, live, or degraded
slave_repl_offset replica's locally applied logical offset
upstream_repl_offset latest offset observed in an upstream hello or firehose batch
replica_lag known upstream-minus-local logical offset; -1 when disconnected before a newer upstream offset is known
replica_reconnect_attempts connection attempts made after startup lost or could not establish the source
replica_successful_reconnects reconnects that completed a safe handoff and returned to live state
master_last_io_seconds_ago seconds since a successful upstream handshake or frame; -1 before either occurs
replica_last_error most recent connection, lineage, replay, buffering, or apply failure; empty while live

The logical and Kafka offsets are different coordinate systems. See Firehose replication and Kafka recovery before using them in restart automation. The replica-only fields are omitted on a primary except for goblin_ready, which is always present.

# Kafka

field meaning
kafka_journal_enabled 1 when the server is producing its write journal to Kafka; otherwise 0
kafka_ack_mode queued, broker, or off; broker mode withholds successful client replies and firehose delivery until Kafka acknowledges the corresponding write
kafka_acknowledged_logical_offset highest Goblin replication offset whose Kafka record has been acknowledged by the broker
kafka_pending_records produced records still awaiting a broker delivery result
kafka_pending_bytes command and key bytes retained for those pending records
kafka_oldest_pending_age_ms age of the oldest unacknowledged record
kafka_retained_batch_bytes mutation payload retained until a broker acknowledgement makes its firehose batch releasable
kafka_input_backpressured 1 while retained Kafka payload has reached --kafka-pending-bytes and command input is paused

The pending-byte ceiling bounds journal payload retained by the server, not librdkafka's own queue. A single atomic command, transaction, or script may cross the ceiling; Goblin then stops reading additional commands until acknowledgements release enough memory. See Kafka write log and recovery for the acknowledgement contract and its retry implications.

# Memory

field meaning
used_memory committed data-store capacity: arenas, fixed index/table structures, object state, TTL tables, and actual upstream blob-pool capacity
used_memory_rss the OS resident set size (ps-equivalent)
used_memory_peak currently mirrors used_memory — Goblin does not yet track a separate high-water mark
mem_reclaimable_bytes the dead subset of used_memory: bytes orphaned by deletes/overwrites that a compaction would free
hash_heap_allocated_bytes heap-resident state owned by promoted hashes; compact hash handles and blobs live in the keyspace slot/arena instead
realtime_hash_index_pool_bytes committed bytes in the fixed, prefaulted arena shared by all RT hash field indexes; 0 until that arena is initialized
realtime_keyspace_index_pool_bytes committed bytes in the separate fixed RT keyspace-index arena selected by --real-time; otherwise 0
blob_pool_requested_bytes live compact zset/list blob bytes requested through the shared blob allocator
blob_pool_capacity_bytes bytes the blob allocator currently holds from its upstream allocator, including live direct blobs over 1 KiB; this amount is included once in used_memory
blob_pool_fragmentation_bytes retained pool capacity beyond live requested blob bytes; unlike arena dead bytes, GOBLIN.OPTIMIZE does not reclaim it
blob_pool_live_allocations number of live blobs in the pool
blob_pool_upstream_allocations cumulative allocations the pool has requested from its upstream resource
maxmemory configured --maxmemory byte ceiling; 0 means unlimited
maxmemory_policy noeviction — capacity-growing writes return OOM instead of evicting
mem_fragmentation_ratio internal fragmentation — see below

used_memory is a real number, not the RSS: it comes from GOBLIN.MEMORY-style accounting rolled up across every zset, hash, list, and the keyspace. Each arena maintains its live/dead byte counts incrementally, so the rollup reads counters rather than walking members or reparsing blobs. The shared compact-blob pool is tracked at its upstream boundary, which makes size-class rounding and retained slabs visible instead of silently reporting only requested payload bytes.

Fragmentation: a different meaning than Redis

mem_fragmentation_ratio is a Redis field, but Goblin Core measures the opposite end of the stack with it. Same name, different question — worth understanding before you alert on it.

Redis — external fragmentation (rss / used_memory)

Redis asks its allocator (jemalloc) for the bytes it needs. To serve those requests the allocator holds more than that resident — size-class rounding, per-arena free lists it hasn't returned to the OS, and so on. So in Redis:

A ratio of 1.4 means jemalloc/OS holds ~40% more than the live data. Redis fights this with jemalloc active-defrag, which relocates allocations in the background. (A ratio below 1.0 is a separate alarm: part of the process has been swapped to disk.)

Goblin Core — internal fragmentation (used_memory / compacted size)

Goblin manages its own page-aligned mmap arenas for the bulk of its data, so it is the allocator for what matters. used_memory also includes the actual upstream capacity of the remaining compact-blob pool, not merely the live requests inside it. Process code, stacks, shared libraries, and general heap metadata still make RSS larger, so RSS remains the cross-engine benchmark number.

The fragmentation Goblin can actively compact lives above the allocator, inside its own arenas. Those arenas are append-structured: a deleted or overwritten member's bytes are orphaned — still mapped, no longer referenced — until a compaction rewrites the arena. Those mem_reclaimable_bytes are the actionable waste used by the ratio. Pool slack is visible separately as blob_pool_fragmentation_bytes and is not treated as compactable:

mem_fragmentation_ratio = used_memory / (used_memory − mem_reclaimable_bytes)

The remedy differs, and deliberately: where Redis leans on the allocator's probabilistic background defrag, Goblin gives you a deterministic one — GOBLIN.OPTIMIZE key rewrites a key's arena tight. Full hashes instead auto-evacuate fragmented arena chunks in bounded maintenance steps once dead bytes exceed live (past a floor). mem_fragmentation_ratio is precisely the signal for when that's worth doing.

In one line

Redis's ratio asks "how much is my allocator wasting underneath me?"
Goblin's asks *"how much of what I've allocated is reclaimable dead weight I can
compact away?"* — one is the layer below you that you don't control, the other is
your own data layout, which you do.

What this looks like

> INFO memory        # freshly loaded, tightly packed
used_memory:142778
used_memory_rss:3080192
mem_reclaimable_bytes:0
mem_fragmentation_ratio:1.00

# ... after deleting ~60% of a large zset ...
> INFO memory
used_memory:118018
mem_reclaimable_bytes:39000
mem_fragmentation_ratio:1.49      # a third of the allocation is now reclaimable

> GOBLIN.OPTIMIZE board
(integer) 58796                   # bytes reclaimed
> INFO memory
mem_reclaimable_bytes:0
mem_fragmentation_ratio:1.00      # compacted back to tight

See also