Migrating
This page describes how to upgrade an existing Korvet 0.12.5 deployment to the current version. The upgrade involves three kinds of changes:
-
Renamed configuration options (
KORVET_SERVER_*is nowKORVET_BROKER_*). -
A new Redis key layout for metadata and stream storage.
-
A one-time, offline data migration performed with the
korvet migratecommand.
Only 0.12.5 is supported as a full migration source. The migrate command detects the source layout and refuses to run against any other version, with one exception: against a deployment that already uses the current layout it converts leftover per-partition committed-offset keys, as described in Upgrading from 0.13–0.16.
|
Upgrading from 0.13–0.16
Two breaking changes apply when upgrading a deployment that already uses the current key layout (Korvet 0.13 through 0.16):
-
The Redis database must provide the Search capability, alongside JSON. The broker creates a search index over committed-offset group hashes at startup and fails to start with a clear error if Search is missing. On Redis Enterprise, enable the Search module on the database before upgrading.
-
Committed offsets moved from one String per group and partition (
{namespace}:broker:commit:{topic:partition:group}) to one Hash per group ({namespace}:broker:commit:{group}). The broker does not read the old keys; runkorvet migrate --executeonce (with the broker offline) to convert them. The conversion is idempotent and re-running it converges. If the broker finds leftover old-format keys at startup it logs a prominent warning directing to the migrate command, and consumer groups appear to have no committed offsets until the conversion has run.
$ korvet migrate -u redis://localhost:6379 --execute
Detected Korvet Redis layout: <current version>
Target Korvet version: <current version>
Migration executed.
MIGRATE committed offsets korvet:broker:commit: (6 offsets)
Committed-offset keys whose topic no longer exists cannot be converted unambiguously and are skipped with a SKIP line; they are ignored by the broker and can be deleted manually.
Configuration Changes
The configuration model was reorganized between 0.12.5 and the current version.
The most visible change is that the Kafka listener moved from korvet.server.* to korvet.broker.* (environment variables: KORVET_SERVER_* to KORVET_BROKER_*), but several other areas were renamed, restructured, or removed.
Review every korvet.* property and environment variable in your deployment against the tables below; see Configuration for the full current reference with defaults.
The tables list properties in their dotted form. The environment-variable form follows Spring Boot’s relaxed binding in both versions: uppercase the property name and replace . and - with _. For example, korvet.broker.max-request-bytes is set with KORVET_BROKER_MAX_REQUEST_BYTES, and the same rule yields the 0.12.5 name on the left-hand side of each row. Sections below call out the cases where the resulting environment variable is not a mechanical rename.
Broker Listener (korvet.server.* → korvet.broker.*)
| 0.12.5 | Current | Notes |
|---|---|---|
|
|
Moved to the root |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Renamed. |
|
|
Renamed. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
removed |
No longer user-configurable. |
For environment variables, the mapping is mechanical: KORVET_SERVER_MAX_REQUEST_SIZE becomes KORVET_BROKER_MAX_REQUEST_BYTES, KORVET_SERVER_ADVERTISED_HOST becomes KORVET_BROKER_ADVERTISED_HOST, and so on.
Redis Connection (korvet.redis.*)
| 0.12.5 | Current | Notes |
|---|---|---|
|
unchanged |
|
|
|
Now has an explicit default of |
|
|
Renamed. |
|
|
Now nested under |
|
|
Now nested under |
|
removed |
The per-purpose connection pools (metadata, archival source, committed offsets) were consolidated into the single |
|
removed |
|
|
removed |
|
|
removed |
|
|
removed |
|
|
unchanged |
Note that because . and - both map to _, the environment variables for the pool settings are unchanged despite the nesting: KORVET_REDIS_POOL_SIZE and KORVET_REDIS_POOL_MAX_WAIT keep working. The renamed I/O thread setting becomes KORVET_REDIS_IO_THREAD_POOL_SIZE → KORVET_REDIS_IO_THREADS.
Topic Configuration (korvet.topics.*)
In 0.12.5, korvet.topics.* was a single, flat set of defaults applied to all topics.
It is now a *list* of glob patterns evaluated first-match-wins, with the same flat per-topic setting names inside each list entry.
A flat 0.12.5 default block translates to one catch-all name: "*" entry:
# 0.12.5
korvet:
topics:
auto-create: true
partitions: 3
retention-time: 7d
retention-bytes: -1
# Current
korvet:
topics:
- name: "*"
auto-create: true
partitions: 3
retention-time: 7d
retention-bytes: -1
| 0.12.5 | Current | Notes |
|---|---|---|
|
|
Now per-pattern. |
|
|
|
|
|
|
|
|
|
|
|
Now per-pattern. |
|
|
Now per-pattern. |
|
|
Now per-pattern. |
|
|
Now per-pattern. |
|
|
|
|
|
Buckets were replaced by segments; a |
|
removed |
At-rest value compression is now configured server-wide with |
|
removed |
The legacy RAW/JSON/AUTO value layout no longer exists; all records use the new envelope format. |
|
removed |
No longer user-configurable; average message size is measured at runtime. |
|
removed |
In environment-variable form, the list entries are addressed by index, and each entry needs a NAME. The flat 0.12.5 variables translate to a 0 catch-all entry:
# 0.12.5
export KORVET_TOPICS_AUTO_CREATE=true
export KORVET_TOPICS_PARTITIONS=3
export KORVET_TOPICS_RETENTION_TIME=7d
export KORVET_TOPICS_RETENTION_BYTES=-1
# Current
export KORVET_TOPICS_0_NAME='*'
export KORVET_TOPICS_0_AUTO_CREATE=true
export KORVET_TOPICS_0_PARTITIONS=3
export KORVET_TOPICS_0_RETENTION_TIME=7d
export KORVET_TOPICS_0_RETENTION_BYTES=-1
Storage (korvet.storage.*)
In 0.12.5 the only storage configuration was the remote (cold) tier under korvet.storage.remote.\*, backed by Delta Lake.
The current version splits storage configuration into three areas:
-
korvet.storage.local.*— new: the Redis (hot) tier, including the at-rest compression codec and an optional dedicated Redis client for message storage (see New Configuration Areas). -
korvet.storage.worker.*— new: the background worker that handles retention and segment tiering, replacing the 0.12.5 archiver. -
korvet.storage.remote.*— the cold tier, which moved from Delta Lake to Apache Iceberg:
| 0.12.5 | Current | Notes |
|---|---|---|
|
|
Unchanged, but now points at an Iceberg warehouse. |
|
unchanged |
A new |
|
removed |
Credential resolution is now automatic. |
|
removed |
Archiver internals are no longer exposed; the storage worker ( |
|
removed |
Replaced by Iceberg writer settings |
|
removed |
The cold index is no longer user-configurable. |
New Configuration Areas
These did not exist in 0.12.5; defaults are generally sensible, but review them as part of the upgrade:
-
korvet.broker.enabled,korvet.broker.response-queue-timeout,korvet.broker.produce-timeout,korvet.broker.rebalance-threads— new listener tuning options. -
korvet.broker.sasl.*— SASL authentication (PLAIN, SCRAM-SHA-256). -
korvet.broker.metrics.*— consumer-offset gauge publishing. -
korvet.redis.circuit-breaker.*— circuit breaker for stream operations (enabled by default). -
korvet.storage.local.compression.codec— server-wide at-rest compression codec (defaultnone). -
korvet.storage.local.redis.*— optional separate Redis client/pool for message storage. -
korvet.storage.worker.*— background worker for retention and segment management. -
korvet.schema-registry.*— embedded schema registry. -
korvet.admin.— Admin API bootstrap credentials. The defaults areadmin/admin; *change these in production.
Redis Key Layout Changes
The current version uses a different Redis key layout than 0.12.5, for both metadata and message storage. This is why a data migration is required: a current server cannot read 0.12.5 keys directly.
Metadata Keys
| Area | 0.12.5 | Current |
|---|---|---|
Topic registry |
|
|
Broker registry |
|
|
Credentials |
|
|
Committed offsets |
|
|
Stream Storage Keys
Messages are still stored in Redis Streams, but both the key names and the per-entry field layout changed.
Keys:
-
0.12.5:
{namespace}:stream:{topic}:{partition} -
Current:
{namespace}:storage:local:{topic}:{partition}
Stream entry fields:
In 0.12.5, each record was stored as multiple stream fields: the record key under __key, the value either under __value (raw) or flattened into one field per top-level JSON attribute, and each header under __header.<name>.
The current format stores each record as a fixed envelope:
| Field | Content |
|---|---|
|
Record value bytes, stored with the configured at-rest codec ( |
|
Record key bytes, verbatim |
|
All record headers encoded into one self-delimiting blob |
|
Kafka record timestamp as a decimal string |
Stream entry IDs (and therefore Kafka offsets) are preserved by the migration.
0.12.5 did not store the producer’s record timestamp, so migrated records carry no timestamp (-1) rather than a fabricated one.
|
The korvet migrate Command
The migrate command reads a 0.12.5 keyspace and rewrites it in place into the current layout.
It migrates, in order:
-
Topic registry — converts the topic Set and per-topic Hashes into the new JSON document, and pins each topic’s
storageCompressionTypeto the codec used during migration. -
Broker registry — converts the broker Set and per-broker Hashes into the
broker:nodesJSON document. -
Credentials — moves each credential Hash to the new key and renames its fields.
-
Committed offsets — rewrites each per-partition committed-offset String into the owning group’s Hash, preserving values.
-
Local streams — rewrites every
{topic}:{partition}stream into the new location, converting each entry from the legacy field layout to the new envelope format (decompressing legacy values where needed and re-compressing with the chosen codec), preserving stream entry IDs.
By default the command is a dry run: it prints what it would migrate without writing anything.
Pass --execute to apply the migration.
Successfully migrated source keys are deleted unless --keep-source is given.
Options
| Option | Default | Description |
|---|---|---|
|
(required) |
Redis connection URI of the deployment to migrate. |
|
|
Korvet keyspace/namespace used by the legacy deployment. |
|
off |
Apply the migration. Without this option the command only prints a dry run. |
|
off |
Replace existing destination keys. Without it, the command skips any area whose destination key already exists (useful when re-running after a partial migration). |
|
off |
Keep legacy source keys after successful migration. By default, migrated source keys are deleted. |
|
|
At-rest codec to write migrated stream values with. Must match the target server’s |
|
|
Number of streams (topic-partitions) to migrate in parallel. Each transfer rewrites one stream, in order, on its own Redis connection. Increase this to speed up migrations with many topic-partitions. |
The command also accepts the standard Redis connection options (TLS, credentials, timeouts) shared by all korvet commands; run korvet migrate --help for the full list.
Output and Exit Code
The command prints the detected source layout, the target version, and one line per migrated (MIGRATE), skipped (SKIP), or failed (ERROR) item:
$ korvet migrate -u redis://localhost:6379 --execute
Detected Korvet Redis layout: 0.12.5
Target Korvet version: {page-component-version}
Migration executed.
MIGRATE topic registry korvet:topics (3 topics)
MIGRATE broker registry korvet:broker:nodes (1 brokers)
MIGRATE credentials korvet:broker:credentials: (2 credentials)
MIGRATE committed offsets korvet:broker:commit: (6 offsets)
MIGRATE local streams korvet:storage:local: (9 streams)
The exit code is 0 on success (including dry runs) and 1 if any error was reported.
Step-by-Step Migration
The migration must be performed offline: no 0.12.5 broker may be writing to Redis while data is being rewritten, and the new broker must not start until the migration has completed.
-
Stop producers and consumers. Drain or pause all Kafka clients so no data is lost while the broker is down.
-
Shut down the 0.12.5 broker(s). Stop every Korvet server process or container that uses this Redis database.
-
Back up the Redis database. Take an RDB snapshot (or use your usual backup mechanism) so you can roll back if needed:
redis-cli -u redis://localhost:6379 BGSAVE -
Run a dry run. Inspect what will be migrated without changing anything:
korvet migrate -u redis://localhost:6379Review the
MIGRATE/SKIPlines. If you used a custom namespace, pass--namespace <name>. -
Execute the migration:
korvet migrate -u redis://localhost:6379 --executeFor large deployments with many topic-partitions, parallelize stream migration:
korvet migrate -u redis://localhost:6379 --execute --transfers 8If you want to keep the legacy keys around until you have verified the new deployment, add
--keep-source(note that this temporarily doubles the memory used by stream data).Verify that the command exits with code
0and reports noERRORlines. If it fails partway, fix the cause and re-run with--execute --replaceto overwrite partially written destinations. -
Update the broker configuration. Apply all renames from Configuration Changes above:
korvet.server.*tokorvet.broker.*(includingkorvet.server.keyspacetokorvet.namespace), thekorvet.redispool restructure, and the flatkorvet.topics.\*block to a pattern list. Remove any settings listed as removed. If you passed a non-default--storage-compression, setkorvet.storage.local.compression.codecto the same value. -
Start the new broker version. Deploy and start the current Korvet release against the same Redis database.
-
Verify. Check that the broker starts cleanly, then validate with a Kafka client: list topics, consume existing messages from an old topic, produce and consume a new message, and confirm consumer groups resume from their committed offsets.
-
Resume traffic. Re-enable producers and consumers. Once you are satisfied, you can delete the legacy keys if you used
--keep-source, and remove the backup per your retention policy.