Examples

Guided Redis migration walkthrough

Source

This exercise moves a disposable Redis source to an existing empty Redis or Valkey target. It uses the workflow preparation surface present in the current source baseline, explicit execution approval and durable status checks. Review Redis support and evidence before adapting it to customer data or a clustered/HA deployment.

Prerequisites

Use a running service with Redis migration/workflow features, curl, jq, a CONFIGURE-capable organization token, two independent disposable backends, redis-cli for the smoke traffic below and a network path to the Eden listener. The guided Redis API stores its state in the configured control database; this single-container path uses embedded Turso and DuckDB and does not require a separate DBOS system database. Other DBOS-backed product workflows have their own dependency requirements. Verify your image's supported profile and live API.

Complete Quick start and export:

bash
set -euo pipefail
export EDEN_ORIGIN='http://127.0.0.1:8000'
export EDEN_REDIS_PORT=6200
# EDEN_TOKEN is the private organization token from login.

Use backend addresses reachable from Eden. The following endpoint payloads are for isolated non-TLS, unauthenticated fixture servers. For secured backends, use Redis credentials and TLS. Avoid client AUTH or SELECT commands on the ordinary native proxy path.

The Docker quickstart publishes localhost port 6200. For Kubernetes, expose that listener through a private TCP Service or port-forward; the HTTP API on 8000 does not carry native Redis connections. Your license must permit both registered endpoints and the selected product capabilities. localhost in an endpoint's backend configuration refers to the Eden container/pod. Supply the real reachable backend names below, not your laptop's loopback address.

Register source and target

bash
EDEN_SOURCE_UUID=$(curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: redis-exercise-source-001' \
  --data '{"endpoint":"redis-source","kind":"Redis","config":{
    "target":{"host":"redis-source.internal.example","port":6379},
    "read_credentials":{},"write_credentials":{},"admin_credentials":{}}}' \
  "$EDEN_ORIGIN/api/v1/endpoints" | jq -er '.uuid')

EDEN_TARGET_UUID=$(curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: redis-exercise-target-001' \
  --data '{"endpoint":"redis-target","kind":"Redis","config":{
    "target":{"host":"redis-target.internal.example","port":6379},
    "read_credentials":{},"write_credentials":{},"admin_credentials":{}}}' \
  "$EDEN_ORIGIN/api/v1/endpoints" | jq -er '.uuid')

These commands expect synchronous resource creation. For an HA 202 response, retain and follow operation_url, confirm activation, then read the endpoint's UUID before continuing. Do not extract .data.uuid or treat acceptance as completion. Ensure the target is empty and belongs only to this exercise; Replace below permits replacement of colliding keys.

Seed a value with an expiry:

bash
curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
  --data '{"request":{"type":"Set","key":"eden:migration:sample",
    "value":"fixture-value","EX":{"seconds":3600}}}' \
  "$EDEN_ORIGIN/api/v1/endpoints/redis-source/write"

Record the seed time and expected expiry. Include all workload-relevant types, TTL patterns and commands in a real qualification; one string is only a smoke check.

Create and configure the guided run

bash
EDEN_RUN_ID=$(curl --fail --silent --show-error --request POST \
  -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
  --data '{}' "$EDEN_ORIGIN/api/v1/migration-workflows/redis" \
  | jq -er '.run.id')

jq -n --arg source "$EDEN_SOURCE_UUID" --arg target "$EDEN_TARGET_UUID" \
  '{source_endpoint_uuid:$source,target_choice:"existing_endpoint",
    target_endpoint_uuid:$target,strategy:"blue_green",conflict_policy:"Replace",
    data_movement_mode:"scan",write_consistency:"BothRequired",
    preserve_ttl:true,require_manual_approval:true}' \
  | curl --fail --silent --show-error \
      -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
      --data-binary @- \
      "$EDEN_ORIGIN/api/v1/migration-workflows/redis/$EDEN_RUN_ID/setup"

Create the source-serving listener at the port you exposed. Discover the real node UUID rather than generating another one:

bash
EDEN_NODE_UUID=$(curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" \
  "$EDEN_ORIGIN/api/v1/ha/deployment/nodes" \
  | jq -er '.nodes | if length == 1 then .[0].node_uuid else error("select the intended node") end')

EDEN_INTERLAY_UUID=$(jq -n --arg id "migration-workflow-interlay-$EDEN_RUN_ID" \
  --arg source "$EDEN_SOURCE_UUID" --arg node "$EDEN_NODE_UUID" \
  --argjson port "$EDEN_REDIS_PORT" \
  '{id:$id,endpoint:$source,eden_node_uuid:$node,port:$port,tls:null,settings:{}}' \
  | curl --fail --silent --show-error \
      -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
      --data-binary @- "$EDEN_ORIGIN/api/v1/interlays" | jq -er '.uuid')

This listener uses the source endpoint. The target endpoint remains the separate destination selected in workflow setup. Confirm a native GET through the listener returns fixture-value before moving application traffic:

bash
redis-cli -h 127.0.0.1 -p "$EDEN_REDIS_PORT" GET eden:migration:sample

For a remote/private deployment, use its reachable listener address instead. This no-TLS example is only for isolated disposable backends and loopback access.

Read the returned run, orchestration, allowed_actions and blocked_reason. The setup submission stores selections; it does not prove readiness. Preparation resolves endpoints/interlay, creates and binds the underlying migration, then runs its governed checks:

bash
curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" -H 'Content-Type: application/json' \
  --data "$(jq -n --arg interlay "$EDEN_INTERLAY_UUID" '{interlay_uuid:$interlay}')" \
  "$EDEN_ORIGIN/api/v1/migration-workflows/redis/$EDEN_RUN_ID/prepare"

curl --fail --silent --show-error \
  -H "Authorization: Bearer $EDEN_TOKEN" \
  "$EDEN_ORIGIN/api/v1/migration-workflows/redis/$EDEN_RUN_ID" \
  | jq '{run,orchestration,allowed_actions}'

Poll the durable run with a bounded interval/deadline, or observe its /events SSE route and re-read state after reconnect. Preparation acceptance is not completion. Inspect compatibility, validation results and stage dispositions. Repair blocked conditions rather than submitting blanket approvals. Optional analysis unavailability is reported with a capability reason and is not a successful safety check.

Optional analysis is unavailable

If preparation reports migration_telemetry_not_configured, analysis did not run. A required-analysis request must remain blocked. Recording the disposition below requires both CONFIGURE and PROMOTE permission; a delegated CONFIGURE-only operator must involve a separately authorized operator for this decision. For an explicitly chosen no-analysis migration, an authorized operator can inspect the linked migration's /stages, review the analysis stage and use its advertised disposition action. The request to PUT /api/v1/migrations/{migration_id}/stages/analysis/disposition contains the current expected_revision, action: "skip", a specific review reason, the accountable rollback_owner and any applicable expiry. Re-read the stage after the change: it must show skipped, not passed. A revision conflict requires a fresh read and review. Then retry the workflow's preparation through its allowed /retry action. Do not skip tests or compatibility checks with it.

Resolve compatibility findings individually using their actual issue IDs, advertised options and current revisions. Fix unsupported configurations rather than accepting every warning automatically. For a customer migration, record why any permitted risk is acceptable and who owns rollback.

Route traffic and approve execution

Record the linked migration_id and interlay_uuid from run. Read the interlay resource and identify its actual listener addresses/ports; do not assume a fixed port or that an advertised loopback address is reachable by your application. Confirm the source route works before sending representative traffic through it. All relevant live writes must traverse the managed path.

Only when the workflow reports awaiting_approval, its allowed actions permit approval, and the owner accepts the preflight evidence:

bash
curl --fail --silent --show-error --request POST \
  -H "Authorization: Bearer $EDEN_TOKEN" \
  "$EDEN_ORIGIN/api/v1/migration-workflows/redis/$EDEN_RUN_ID/approve"

Observe the guided run and its linked migration. Monitor sink-confirmed progress, failed batches, gaps, mismatches, source/target latency and lease pressure. Read independent source/target values and TTLs while keeping the observation boundary explicit. Running status, a fixed wait or a target key appearing is insufficient to establish completed copy or safe cutover.

While the copy is running, write uniquely named disposable values through the listener and independently confirm every acknowledged value on the source and target. Keep application traffic on Eden; direct writes to the old source are not captured by the listener. A brief smoke example is:

bash
redis-cli -h 127.0.0.1 -p "$EDEN_REDIS_PORT" SET eden:migration:live:1 during-copy

A retry is safe only when the response proves the command was rejected before dispatch. A disconnect or timeout after a write leaves an uncertain outcome; inspect it before retrying. A single sample is not proof of a full data copy.

Validate and complete

Read the linked migration and its operation/job state until historical copy and required live delivery are complete. Use the linked migration's /cutover/preflight and inspect every blocker. Verify data/TTL parity, no unresolved delivery gap, application smoke checks, correct target routing and the agreed rollback boundary. A completion request cannot bypass the raw migration's cutover gates.

For a Blue/Green read switch, use the linked migration's PATCH /api/v1/migrations/{migration_id}/toggle with {"activate_new":true,"reason":""} only after the applicable lifecycle/preflight permits it. Verify reads use the target and required writes reach both sides before committing completion.

Only after the required checks and operator acceptance, submit POST /api/v1/migration-workflows/redis/{run_id}/complete if its allowed actions permit completion. A successful completion POST, including HTTP 202, is an acknowledgement. Poll GET on the same workflow and linked migration until both are completed; also check the operation is terminal and its workers are quiesced. Do not resubmit complete merely because the first response was nonterminal. Record an unknown or failed outcome and inspect it rather than repeating earlier steps.

With target-active live traffic, target-only post-cutover writes can legitimately increase its key count. Verify expected key membership/content and expiry, not just raw DBSIZE equality. Retain source until the decommission decision.

Failure and cleanup

Finish the migration before a routine image upgrade. After an idle restart, confirm the same workflow/migration IDs, endpoint records and listener port, then verify completed traffic reads/writes only the target. A crash during active copy can leave a durable restart-required fence; recovery must reconcile it rather than silently starting another worker.

For failed preparation, inspect the failed step and explicit retry policy. For stalled execution, gaps or restart-required fences, follow Recovery runbooks. Do not replay an uncertain write, clear a lease or delete state to force success.

Before removing this disposable exercise, stop its traffic generator, cancel unfinished work using the guided run's /cancel, and verify workers/listeners have stopped. Preserve the run IDs and validation results. Delete migration, interlay and endpoint resources only when their lifecycle allows it and no application uses them. Resource deletion is separate from deleting Redis data. Keep production backups and source retention under your own policy.

Help improve Eden Docs

Find something unclear or incomplete? Review the source and propose an update.

View on GitLab Updated November 4, 2026