You can actually query etcd keys not just by prefix, but also by a specific range, which is incredibly useful for understanding and managing your cluster’s state.
Let’s say you have a set of keys in etcd that represent different versions of a configuration. You might have keys like /config/app1/v1, /config/app1/v2, /config/app2/v1, and so on. If you want to see all versions of app1’s configuration, you’d use a prefix query. But what if you wanted to see all configurations that were created or modified between two specific points in time? That’s where range queries come in, and they operate on the revision of the key.
Here’s how you’d typically interact with etcd using etcdctl, the command-line interface.
First, let’s set up some sample data. We’ll put a few keys into etcd.
ETCDCTL_API=3 etcdctl put /config/app1/v1 "config_v1_data"
ETCDCTL_API=3 etcdctl put /config/app1/v2 "config_v2_data"
ETCDCTL_API=3 etcdctl put /config/app2/v1 "app2_config_v1"
ETCDCTL_API=3 etcdctl put /config/app1/v3 "config_v3_data"
Now, let’s query by prefix. This is straightforward.
ETCDCTL_API=3 etcdctl get --prefix /config/app1
This will output something like:
/config/app1/v1
config_v1_data
/config/app1/v2
config_v2_data
/config/app1/v3
config_v3_data
This shows all keys that start with /config/app1. But what if you wanted to see keys within a specific revision range? etcd keys have a revision number associated with them, which is a monotonically increasing counter reflecting the history of operations on the key. You can query based on these revisions.
To do this, you first need to know the revision numbers. You can get the current revision of the cluster like this:
ETCDCTL_API=3 etcdctl get / --rev=$(ETCDCTL_API=3 etcdctl get / --limit=1 -w simple | grep revision | cut -d: -f2)
This command is a bit of a trick. It gets the first key in etcd (/), but requests it at the current revision. The output includes metadata, including the revision number of the transaction that returned the data. Let’s assume the output shows something like revision: 5.
Now, let’s say you want to see all keys that were modified or created between revision 3 and revision 5. You can use the --rev flag with a range.
ETCDCTL_API=3 etcdctl get --rev-start 3 --rev-end 5
This would show you all keys as they existed at revision 3, and then all keys as they existed at revision 5, effectively showing the state transitions. However, a more common and powerful use is to retrieve all versions of a specific key within a revision range.
If you want to see all versions of a particular key, say /config/app1/v2, between two revisions, you’d do this:
ETCDCTL_API=3 etcdctl get /config/app1/v2 --rev-start 1 --rev-end 10 --prev-kv
The --prev-kv flag is crucial here. It tells etcdctl to also return the previous versions of the key that match the revision range. If /config/app1/v2 was created at revision 4 and modified at revision 7 within our hypothetical range of 1-10, this command would show you the state of /config/app1/v2 at revision 4 (if it existed then) and its state at revision 7, and its state at revision 10.
The mental model for etcd is that it’s a distributed, consistent key-value store that also tracks the history of its data. Every operation (put, delete, txn) increments a global revision number. When you query, you can specify which revision of the data you want to see. This is like time-traveling through your data.
The prefix query is essentially a shortcut for fetching keys where the key string itself falls within a lexicographical range (e.g., all keys between /config/app1/ and /config/app10/). The range query, however, operates on the revision dimension. You can combine these. For instance, you can get all keys with a specific prefix at a particular revision:
ETCDCTL_API=3 etcdctl get --prefix /config/app1 --rev 7
This fetches all keys starting with /config/app1 as they existed at revision 7.
The truly counterintuitive aspect of etcd’s revision system is how it enables efficient historical queries without requiring you to explicitly store old versions yourself. When you request a key at a specific revision, etcd doesn’t scan its entire history; it uses its internal data structures (B-trees, typically) to efficiently locate the version of the key that was active at that revision. This makes historical lookups remarkably fast, even for very large datasets and long histories.
The next thing you’ll likely encounter is dealing with leases and watch events, which are intimately tied to the revision history and how etcd handles ephemeral keys and real-time data changes.