This page describes Calíope 1.6, the version I am building right now. 1.5 is on the App Store for the Mac, iPad, iPhone, Apple Watch, Apple TV and Apple Vision Pro. The changelog says which version each feature landed in.

Adds an “Open in Calíope” button to every topic. It only works with the app installed.

Redis

Browse keys

How to walk, read and edit the keys of a Redis server.

Where it is: Workspace › Tools › Keys

Applies to: Redis

The browser shows the keys of the database on the left, grouped by prefix, and the detail of the one you pick on the right.

How the keys are walked
Calíope asks for them with SCAN, in batches, and never with KEYS: KEYS walks the whole database in one go and, since Redis serves commands one at a time, it answers nobody else while it runs. The footer says how many keys have been walked and whether the walk finished or keys remain; Load more keys asks for the next batch. In a cluster each primary node is walked and the footer says how many answered; if one is missing, it names it with its reason.

The field at the top is a MATCH pattern —app:*, *:1001—, not a search by content, and the server applies it as soon as you stop typing.

The groups are Calíope’s, not the server’s
Redis has no folders: every key is a flat name. The groups come from splitting the names by the character in the Separator menu on the toolbar (:, /, ., | or None: flat list), and the server knows nothing about them. The separator is chosen per session, and every group says how many keys hang from it. With a keyboard, the arrow keys walk the list and → and ← open and close the selected group; on the iPhone, tapping a key opens its detail.

Type and expiry
Every key carries its type, with its glyph and its name, and its expiry, which counts down in the detail. No expiry and No longer exists are not the same: the first is a key that lasts until someone deletes it; the second, one that expired, or that another client deleted, after it was walked.

The detail
Below the key, the type and the expiry come the size —Length in bytes, Fields or Elements—, the Internal encoding and the Memory, and then the Value. The size is asked before the content: a large value is read in chunks, and a button loads the next one. In a list every element carries its Index, because in Redis the position is part of the data; a set warns that it has no order; a sorted set shows every Member next to its Score, lowest first; and a stream shows its own data first —first and last entry, last generated id, max deleted id—, then its consumer groups with their pending entries and lag, and last its Entries. A value that is not UTF-8 is shown in hexadecimal, and control characters, escaped.

What can be edited
On the selected key, from the toolbar and from the list’s context menu: Expiry… (or Remove Expiry, if the key expires); Rename…, which warns if the name already exists and only overwrites the other key with Replace; and Delete Key…, which always asks and names the key, its type and its size. The Delete key leads to that same question, and so does, on the iPhone and the iPad, swiping the row to the left.

In the value: Edit Value… on a string, which keeps its expiry; Add Field…, Edit… and Remove on a hash; Edit… of a position in a list; Add Member… and Remove on a set; and Add Member…, Edit… —which changes the score— and Remove on a sorted set. Removing the last element warns that Redis deletes the key once it is empty. Every change shows in place, without reloading the list, and is recorded in the log of what Calíope ran.

A binary value is not edited, nor one larger than 64 KiB, nor a stream. And there is no emptying of the database nor deleting by pattern: one key is deleted at a time, knowing which.

Profiles opened with Calíope 1.4
A Redis profile synced to a device that still runs Calíope 1.4 is read there as MariaDB on the same port, because that version does not know Redis; if you save it from that device, it is rewritten that way on all of yours. It is recovered by choosing Redis again in the profile: neither the server nor the password is lost.

Keywords: redis, keys, browse, scan, ttl, expiry, separator, edit, delete

Server status

What the Redis server panel shows and how to read each figure.

Where it is: Workspace › Tools › Server

Applies to: Redis

The panel shows the state of the Redis server as INFO publishes it. It rereads itself every 2 seconds while it is on screen, and Refresh rereads it at once. Under each figure is the INFO field it comes from, so you can check it against redis-cli.

The six cards
Memory used is used_memory, with the limit (maxmemory) and the percentage, or no limit if maxmemory is 0; below it, what the server does when it fills up, and the peak.

Fragmentation is the memory the process takes from the system divided by the memory it uses. Above 1, the process takes more than it uses: gaps the memory allocator has not given back yet. Below 1 usually means part of the data is in swap, and that is worse.

Hit rate and Expired keys are cumulative since the server started, not since the last read: a ratio that barely moves does not mean there are no misses right now. Expired keys come with the keys evicted for lack of memory.

Connected clients is measured against maxclients, which does not show if the server does not allow CONFIG. If you check it against redis-cli, you get one more there: redis-cli itself.

Operations per second are calculated by the server (instantaneous_ops_per_sec), not by Calíope, and change from one moment to the next.

When memory or clients go past 90 % of their limit, the card says Near the limit, with a glyph as well as the colour.

Persistence
Shows whether AOF is on, which RDB saves are scheduled (save), when the last save was —with date, time and time zone— and how many changes are unsaved; a failed save shows as such, with its glyph. If AOF is off and no saves are scheduled, the card says only that, because the server loses all its data if it restarts. If it is loading its dump from disk, it says so first: until it finishes, the figures are not those of the running server.

Pub/sub
A snapshot taken at each read, not a stream: Calíope does not subscribe to any channel or show messages. It lists the channels that have subscribers now, with how many, and the pattern subscriptions. If the server does not let the connection use PUBSUB, the card says so.

Server
The footer gives the version, mode, role, protocol (RESP2 or RESP3) and uptime. In a cluster the panel is about a single node, and the footer names it.

Keywords: redis, server, info, memory, fragmentation, hits, expired, clients, operations, persistence, aof, rdb, pubsub

Analyze memory

What the memory analyzer measures, what it costs the server, and why its total doesn’t match used_memory.

Where it is: Workspace › Tools › Memory

Applies to: Redis

Analyze walks the database’s keys with SCAN, measures each one with MEMORY USAGE and adds up what it measured by prefix, splitting the names on the same separator as the browser (: unless you picked another). The server doesn’t know about those groups: Calíope builds them.

What it costs
Measuring is one read per key, and in a large database that is real load on a server that handles commands one at a time. That is why the walk goes page by page, has a ceiling and can be stopped. You choose the ceiling before analyzing, in the ceiling menu next to Analyze: from 10,000 to 5,000,000 keys, and 500,000 if you don’t change it. While it runs, the button becomes Stop, which is also in the Activity panel, and closing the tab or the session stops it. Until it finishes, the previous analysis stays on screen. In a cluster it walks every primary and adds them up.

An estimate, not an exact count
A string is measured exactly. A list, a hash, a set, a sorted set or a stream is measured by sampling: the server looks at five elements and estimates the rest (SAMPLES 5). If the analysis stopped at the ceiling or you stopped it, the screen warns that it is a sample, with a glyph as well as the text.

The grid
Prefix, Memory, Keys and Share: the share is of what was measured, not of the database. The sort menu beside it sorts by memory or by keys, largest first; on the Mac and on a wide iPad you can also sort by clicking the column header. Groups open with the chevron, or with → and ← on the keyboard. At the top, the figure always says what it is —how much the measured keys take— and, below it, whether they are all the keys in the database (DBSIZE).

Why the total doesn’t match used_memory
The sum of the keys is always less than the memory the server says it uses (used_memory), and that is not a measuring error. used_memory also counts what the server takes for itself —an empty one already uses about 1 MB (used_memory_startup)—, the client and replication buffers, and the keys of every database, while the analysis walks only the session’s. The closest figure is used_memory_dataset, which leaves out the fixed part, and even that includes the other databases. With few keys the gap is huge; with many, the sum gets closer.

Keywords: redis, memory, analyzer, memory usage, scan, prefix, sampling, used_memory, ceiling, stop

Slow commands

What Redis’s slow command log records, why its threshold is in microseconds and what happens when you clear it.

Where it is: Workspace › Tools › Slow Log

Applies to: Redis

The server records every command that takes longer than a threshold to execute (SLOWLOG), and this tab shows what it has recorded. The duration is the server’s own working time: it doesn’t count the network or the wait in the queue. The tab reloads itself every 5 seconds while it is visible, and Refresh reloads at once.

The threshold is in microseconds
In Redis the threshold (slowlog-log-slower-than) is written in microseconds, not in the seconds or milliseconds of the other engines: 10,000 µs is 10 ms, which is the value Redis ships with. At the top, the Server log card says it in a sentence, along with how many entries it keeps (slowlog-max-len, 128 unless someone changed it). A threshold of 0 records every command, the fast ones too; a negative one turns the log off, and then the empty list says so: that isn’t the same as “no command has gone over the threshold”. In a cluster the card names the node: each node keeps its own log, and this one is the profile’s node.

A window, not a history
The log keeps only the latest entries: when a new one arrives and the log is full, the oldest goes. What you see is what the server keeps now, not everything that was slow since it started.

Changing the settings
Change settings… opens a sheet with What gets logged (What goes over the threshold, Everything or Nothing), Threshold (µs) and Entries it keeps. It is written with CONFIG SET and read back, and what is shown afterwards is what the server says it has. It isn’t saved to its configuration file: a restart puts back whatever that file says.

The list
On the Mac it is a grid with Started (server clock), Duration, Command and Client —the last one only if the server sends the client—, sorted by clicking the header. On the iPhone and the iPad it is a list, and the order is chosen in the sort menu in the toolbar: Most recent or Slowest. The Filter by command or client field filters what has already been read, without going back to the server. The server keeps at most 32 arguments of each command and trims each one to 128 bytes, and says so inside the command itself (... (N more arguments)): Calíope shows it as it is. An AUTH is masked.

An entry’s details
When you select an entry —on the Mac it appears below the grid; on the iPhone and the iPad, by tapping it, in a sheet— you see the whole command, the time with its time zone (the clock is the server’s), the duration in ms and in µs, the client’s address and name, and the entry number. Copy command copies it; Open in console leaves it typed in the console, without running it. Both are also in the context menu, and on the Mac a double-click or Return opens the console.

Clearing
Clear log, in the toolbar, deletes every entry on the server (SLOWLOG RESET). It asks first, saying how many will go, counted at that moment with SLOWLOG LEN. It can’t be undone.

Keywords: redis, slowlog, slow commands, threshold, microseconds, slowlog-log-slower-than, slowlog-max-len, config set, clear, console

Connected clients

Who is connected to the Redis server, what connected, idle and the flags mean, and what happens when you disconnect a client.

Where it is: Workspace › Tools › Clients

Applies to: Redis

This tab shows what CLIENT LIST answers: every connection open against the server, whether from an application, from redis-cli or from Calíope itself. The list rereads itself every 5 seconds while it is on screen, and Refresh rereads it at once. The Filter by name, address or command field filters what has already been read —name, address, last command, user, library and id— without going back to the server. In a cluster the list is the one of the profile’s node, and a notice says so: each node has its own clients.

The columns
On the Mac it is a grid with Client, ID, Address, Connected, Idle, Database, Last command, Flags and Memory, sorted by clicking the header; User and Library only appear if some connection carries them (the ACL user exists since Redis 6; the library, since Redis 7.2 and only if the client introduced itself). On iPhone and iPad each client is a row with the same data, labelled, and the order is chosen in the sort menu on the toolbar: By ID, Most idle, Most recently connected or Most memory.

Connected and idle are not the same
Connected (age) counts the seconds since the connection was opened; Idle (idle), the seconds since its last command. A pool connection has been connected for hours and idle for a few seconds; one that has been idle for a long time is open doing nothing, and still takes memory on the server. A client blocked waiting for data —a BLPOP, for example— also adds idle time while it waits, and its last command says so. Memory (tot-mem) is what the connection takes on the server, input and output buffers included.

The flags
Flags (flags) is shown exactly as the server writes it, one letter per flag. The most common: N, none in particular (a normal connection); b, blocked waiting for data (BLPOP, XREAD BLOCK…); P, subscribed to pub/sub; x, inside a MULTI not yet executed; S, a replica connected to this server; M, the primary, on the connection a replica keeps with it; and O, a client in MONITOR.

Calíope’s connections are in the list
Calíope is a client too, so its connections show up in the list, with the name Caliope and the library caliope; their last command is usually the read itself (client|list). They are marked with a glyph and the label This session, and cannot be disconnected from here: closing them would close the session. Calíope recognises them by their id, not by name: another Calíope instance connected to the same server has the same name, and that one is a different client.

Disconnecting
Disconnect… closes the connection of the selected client (CLIENT KILL ID). It always asks first, naming the client, its address, its id and its last command, and warning that anything it had half done —a transaction, a subscription or a blocked command— is lost; you confirm with Disconnect. On the Mac you get there from the toolbar and the context menu; on iPhone and iPad, from the context menu and by swiping the row to the left, which asks rather than disconnects. Afterwards the row is removed in place and a notice says so. The client may connect again —many pools do it on their own—, and then it reappears in the next read with a different id. The context menu also has Copy address.

When the server answers 0
If the client had already left between the read and the confirmation, the server answers that it closed 0 connections. That is not a failure: Calíope says it was no longer connected and removes the row.

Keywords: redis, clients, client list, client kill, connections, idle, age, flags, disconnect

The command console

How to type and read a command in the Redis console, which commands it won’t run and why, and which ones ask you to type their name first.

Where it is: Workspace › Tools › Console

Applies to: Redis

The console sends Redis the command you type, over the same session as the other tabs. Run —or Return— runs it; on the Mac, Option-Return breaks the line without running it —a long command can span several, and the break counts as a space—, and on iPhone and iPad the return key always runs. The line is split as in redis-cli: arguments are separated by spaces, and single or double quotes keep text with spaces in one argument; inside double quotes, \n, \t and \xHH work. A quote left open isn’t sent, and the output says why.

History
The up arrow brings back the previous command when the cursor is on the first line, and the down arrow the next one when it’s on the last; the Previous command in history and Next command in history buttons do the same without a keyboard. It keeps the last 200 lines, including the ones the console didn’t run, without repeating the same one twice in a row. Clear empties the output, not the history. The output, the history and whatever you were typing are still there when you switch tabs and come back.

Reading a reply
- A status such as OK or PONG shows as is.
- An integer is preceded by (integer).
- Text is always quoted, with control characters escaped as in redis-cli ("a\r\nb"); text that isn’t UTF-8 shows in hexadecimal, with its length in bytes.
- (nil) means no value, and it isn’t the same as an empty string, which shows as "".
- A list is numbered, and an empty one shows (empty array). A map —what HGETALL or CONFIG GET return— shows as numbered field => value pairs. A set has a header saying how many members it has and that they have no order, and its members are bulleted, not numbered.
- An error shows in red, with a glyph and (error) in front, and with the prefix the server puts on it (WRONGTYPE, ERR…).
- A reply longer than 2000 lines is cut, and the notice points to “Keys”, which walks keys page by page.

In a cluster, each reply says who answered: the primary for the slot of the first key or, if the command has no key, the profile’s node. A command without a key —DBSIZE, INFO— counts only that node.

What the console won’t run, and why
Each refusal shows in the output with its reason, and the command never reaches the server. Below it, “What not to do” opens the DBA Handbook topic that explains why:
- KEYS walks every key at once and blocks the server while it runs. To find keys, use “Keys”, which walks them page by page with SCAN.
- MONITOR degrades the server while it runs and can’t be stopped from outside.
- SUBSCRIBE, PSUBSCRIBE and SSUBSCRIBE would deliver channel messages mixed in with the replies to other commands. Active channels and their subscribers are in “Server”.
- SHUTDOWN shuts the server down.
- DEBUG has subcommands that stall or crash the server (DEBUG SLEEP, DEBUG SEGFAULT).
- RESET and HELLO change the protocol underneath the connection Calíope is using, and CLIENT REPLY OFF or SKIP stop replying: the next command would wait forever for its reply. SYNC and PSYNC turn the connection into a replica link that receives everything written to the server.
- SELECT would leave the interface showing one database and reading another: the database is chosen with the tool’s selector.
- MULTI, EXEC, DISCARD, WATCH and UNWATCH: a transaction lives on a single connection, and the console sends each line over whichever one is free, so the next commands could go to another one and the transaction would be left open. For several commands that run together, with nothing in between, there’s EVAL with a script.
- A command that waits for data —BLPOP, BRPOP, BLMOVE, BZPOPMIN, XREAD … BLOCK, WAIT…— with a timeout of 0, which means waiting forever, or longer than 10 seconds (over 10000 milliseconds in XREAD BLOCK, WAIT and WAITAOF): while it waits it holds one of the connections every tool in the session uses. With a shorter timeout it runs.

Commands that ask you to type their name
FLUSHDB and FLUSHALL delete every key —in the database, or in every database— and can’t be undone; REPLICAOF, SLAVEOF, CLUSTER RESET and CLUSTER FAILOVER change the topology, and other clients may lose access to their data. For these, a strip appears above the field saying what will be deleted or changed and on which server, with a field where you type the command’s name —case doesn’t matter— and a button that stays off until you do. Cancel leaves it unsent, and the output says so.

Everything else runs, including EVAL, CONFIG SET and CLIENT KILL. What runs goes into the command log, with the AUTH password masked.

This list lives in Calíope’s code, in a single place, and every path that runs a command you typed checks it: if it changes, this topic changes with it.

Keywords: redis, console, commands, redis-cli, keys, monitor, subscribe, flushdb, flushall, forbidden, history, resp

The server topology

What the topology of a Redis session shows: primaries, replicas and shards, why lag is measured in bytes, what slots are, what it means when the cluster fails, and why Sentinel isn’t there.

Where it is: Workspace › Tools › Topology

Applies to: Redis

The topology shows how the session’s server is laid out. A cluster shows as Cluster; a standalone node with replicas, as Standalone node and its replication. At the top is a summary: how many nodes and how many primaries answer, and the ones that don’t, named by their address. Below, Diagram draws one box per node, grouped by shard, with the primary on top and its replicas underneath, and Nodes gives the same in a table. It rereads itself every 5 seconds while it’s on screen, and Refresh rereads it at once.

Lag is in bytes, not seconds
A Redis replica doesn’t confirm a time: it confirms up to which byte of the replication stream it has received. So the Lag (bytes) column is the primary’s position minus the replica’s. Calíope reads the primary twice, before asking anyone and at the end: what it moved between the two reads is the floor, and below it the replica shows In sync, because that difference can’t be told apart from the time it takes to ask another machine. Only above it does it show Behind. The lag= Redis writes next to each replica in INFO replication is something else: the seconds since its last acknowledgement.

When a primary restarts it begins a new stream, and its replica has no measurable lag until it resyncs: the table says On a different replication stream from its primary and the lag “not measured (other stream)”, instead of showing 0 bytes. If the replica lost its link to its primary, it shows Link to its primary is down.

Slots
A cluster spreads keys over 16384 slots: each key lands in one by the CRC16 of its name, and each primary serves a range. The number is fixed and small on purpose: the map of which primary serves each slot travels between nodes with every heartbeat, and has to stay small. The Slots column gives each primary’s range, and the summary says how many of the 16384 have an owner.

When the cluster fails
“Cluster state: fail” means the cluster has stopped accepting commands: some slots have no live primary to serve them, because a primary went down with no replica to take over, or several went down at once. A node that doesn’t answer shows Not responding, with the technical error beside it, and nothing else is claimed about it: neither its role nor its lag.

Sentinel isn’t there
A session opened against Sentinel shows no topology: Sentinel watches the primaries and decides the failover, but holds no data and has no replicas of its own. To see a primary with its replicas, open the session against the primary.

With VoiceOver, the table is the alternative to the diagram: each row is read once, with each column’s name before its value.

Keywords: redis, topology, cluster, replicas, primary, lag, bytes, slots, 16384, sentinel, failover, cluster_state

Redis reports

What a Redis report is, what fields it has and what it costs the server: a key scan on every run.

Where it is: Workspace › Tools › Reports

Applies to: Redis

A Redis report has no query: it’s a key scan turned into a table, one row per key. You create it with New report in a Redis session, and instead of the text editor you get its fields.

What you choose
Pattern is a glob on the key name (app:*, session:??, user:[ab]*); empty means all of them. Type keeps only keys of that type. Columns turns on key, type, ttl, memory, encoding and length, and they come out in the order you turn them on; in Hash fields each field, comma-separated, is one more column with that hash field’s value, empty on a key that isn’t a hash or doesn’t have it. Row cap says how many rows at most; empty is the engine’s cap.

What it costs
Every time you press Run, Calíope walks with SCAN all the keys that match the pattern —on a cluster, on every primary— and only then asks for the columns of the rows that are kept. The cap trims the rows, not the walk: with a broad pattern on a database with millions of keys, the report takes time even if it asks for ten. Stop stops it before the next page of the walk.

The table
Rows come out sorted by key, so two runs give the same table; if there are more than the cap, the first ones in that order are kept, and the table says it was cut. memory is what MEMORY USAGE answers: on a collection it’s a sampled estimate. A key or a field that isn’t UTF-8 comes out as 0x… with its length, and its column is marked as binary.

Parameters
A parameter is written $$name inside the pattern and declared with Declare parameters. Its value is matched literally: a * in the value is an asterisk, not a wildcard. It’s written according to its declared type, and a value that isn’t of that type is rejected before running.

The rest
Export saves it in any of the menu’s formats, PDF included, and History keeps every run, with its rows or with its failure. A Redis report only runs in a Redis session: in any other, the tool says the report doesn’t belong to that session and won’t let you run it. Schedule doesn’t help with it: only reports on a relational database can be scheduled, and on every round the Redis one is recorded in the history as Skipped.

Keywords: redis, report, scan, pattern, glob, keys, cap, columns, hash, parameter, export, history