Standard Operating Procedure

Redis → SQL Server

Key-value to relational migration from Redis to SQL Server. MigrationBridge reads Redis Hash keys matching a pattern, reconstructs rows by coercing string values to SQL types, and inserts into SQL Server tables.

Source: Redis 5+ (on-prem, Azure Cache, ElastiCache)  ·  Target: SQL Server 2016+ / Azure SQL / RDS  ·  Tool: MigrationBridge v1.3.0

🔍 Overview

MigrationBridge reconstructs relational rows from Redis Hash keys. During a prior SQL Server → Redis export, each table row was stored as a Redis Hash using the key pattern schema:table:PK_value. Each Hash field corresponds to a column name, and every value is stored as a UTF-8 string. This SOP reverses that process.

How Key-Value to Relational Reconstruction Works

MigrationBridge iterates over Redis using SCAN to discover all key prefixes matching the pattern schema:table:*. For each matching key it issues HSCAN to read all Hash fields and values. Field names map directly to SQL Server column names. String values are coerced back to their original SQL types using MigrationBridge's type inference engine (see Type Conversion Reference).

Key Pattern Convention

All keys must follow the three-part colon-delimited convention established during the export phase:

schema:table:PK_value

Examples:
  HumanResources:Employee:1
  HumanResources:Employee:2
  Person:Address:10
  Sales:SalesOrderHeader:43659
🛑
The key naming convention used during import must exactly match the convention used during the original SQL Server → Redis export. If keys were exported with a different separator or prefix structure, MigrationBridge will produce incorrect table names. Verify with redis-cli SCAN 0 COUNT 10 before proceeding.

TRUNCATE Before INSERT

To prevent primary key duplicate errors on re-runs, MigrationBridge issues a TRUNCATE TABLE on the target table before each batch INSERT. This means the migration is not incremental by default — it is a full replacement of the target table's data. If the target table has foreign key constraints, MigrationBridge temporarily disables them during the operation and re-enables them after.

TRUNCATE removes all existing rows in the target table before inserting Redis data. Do not run this against a live production table unless a full replacement is intended and a backup exists.

Validated Run — AdventureWorks2019

This SOP was validated against AdventureWorks2019 on July 8, 2026: 70 key prefixes (tables), 760,836 rows migrated, zero coercion failures, zero PK violations. Total elapsed time: 4 minutes 12 seconds on a local Redis instance + SQL Server 2019 target.

🔒 Prerequisites — Redis (Source)

Driver

MigrationBridge uses the redis-py Python driver. It is bundled with the MigrationBridge installation. No separate driver installation is required when running from the packaged binary or the installed Python environment.

pip show redis
# Expected: redis 4.6.x or 5.x

Connection String Formats

Environment Format Notes
Local / on-prem (no auth) redis://localhost:6379/0 DB number appended as path segment
Local / on-prem (with AUTH) redis://:password@localhost:6379/0 Colon before password is required
Azure Cache for Redis rediss://<name>.redis.cache.windows.net:6380 rediss:// (double-s) enforces TLS 1.2. Password = Access Key from Azure Portal.
AWS ElastiCache (cluster disabled) redis://<endpoint>.cache.amazonaws.com:6379/0 Must run from within the same VPC. Use rediss:// if in-transit encryption is enabled.
AWS ElastiCache (TLS + AUTH) rediss://:token@<endpoint>:6380/0 Requires ElastiCache auth token configured on the cluster

Test Connectivity

redis-cli -h localhost -p 6379 PING
# Expected: PONG

redis-cli -h localhost -p 6379 DBSIZE
# Expected: integer (number of keys in DB 0)

# For Azure Cache (TLS):
redis-cli -h myinstance.redis.cache.windows.net -p 6380 --tls -a "<access-key>" PING

# For ElastiCache:
redis-cli -h mycluster.xxxxxx.cache.amazonaws.com -p 6379 PING
If PING returns NOAUTH Authentication required, the instance requires an AUTH password. Supply it in the connection URI or in the MigrationBridge password field. Azure Cache for Redis always requires AUTH — use the Primary Access Key from the Azure Portal under Authentication.

Verify Key Naming Convention

Before starting the migration, confirm keys follow the schema:table:PK convention:

redis-cli SCAN 0 COUNT 20
# Sample output should include keys like:
#   HumanResources:Employee:1
#   Person:Address:10
#   Sales:SalesOrderHeader:43659

# Count keys for a specific prefix:
redis-cli KEYS "HumanResources:Employee:*" | wc -l
The key naming convention must match what was used during the SQL Server → Redis export. If the source used a different pattern (e.g., table_schema.table_name:PK or flat keys without schema prefix), MigrationBridge will misinterpret the prefix and create incorrect table names. Contact the export operator to confirm the original pattern before proceeding.

Required Redis Permissions

📊 Prerequisites — SQL Server (Target)

Permissions

The SQL login used by MigrationBridge requires the following minimum permissions on the target database:

PermissionPurposeScope
db_ownerAll operations (simplest)Target database
— or — CREATE TABLECreate tables if they don't existTarget database
INSERTLoad rowsAll target tables
TRUNCATE TABLEClear table before reloadAll target tables
ALTER TABLEDisable/re-enable FK constraintsAll target tables with FK
VIEW DATABASE STATESpace and file group checksTarget database

ODBC Driver

MigrationBridge requires ODBC Driver 17 for SQL Server or later. Verify installation:

# Windows — check installed ODBC drivers:
odbcad32.exe  (via Run dialog → Drivers tab)

# Or from PowerShell:
Get-OdbcDriver -Name "ODBC Driver 17 for SQL Server"

# Download if missing:
# https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server

Pre-flight Space Check

Run this query against the target instance to confirm sufficient free space before migration:

-- Pre-flight: check data file free space on target database
SELECT
    DB_NAME() AS DatabaseName,
    SUM(size * 8 / 1024) AS AllocatedMB,
    SUM(FILEPROPERTY(name, 'SpaceUsed') * 8 / 1024) AS UsedMB,
    SUM((size - FILEPROPERTY(name, 'SpaceUsed')) * 8 / 1024) AS FreeMB
FROM sys.database_files
WHERE type_desc = 'ROWS';
💡
For AdventureWorks2019 scale (760,836 rows), plan for at least 2 GB free in the target data file group. NVARCHAR(MAX) columns for unmapped types can inflate storage compared to the original typed schema.

Table Creation

MigrationBridge will create target tables automatically if they do not exist, using the inferred column definitions from the first 100 Hash keys of each prefix. If the target tables already exist with the correct schema, MigrationBridge skips CREATE TABLE and proceeds directly to TRUNCATE + INSERT. If the target schema differs from the inferred schema, a warning is logged and the operation continues with best-effort column matching.

For best results, run the migration into a pre-created schema using the original DDL from the source SQL Server. This ensures data types, nullability, and constraints are preserved exactly. Auto-created tables use permissive types (NVARCHAR(MAX), FLOAT) which may differ from the originals.
1

Configure Connections

Launch MigrationBridge and navigate to New Migration. Select Redis as Source and SQL Server as Target.

MigrationBridge v1.3.0
Jobs
New Migration
Settings
Logs

Source — Redis

redis://localhost:6379/0
0
●●●●●●●●
Off (use rediss:// for TLS)
* (all schemas)
500

Target — SQL Server

localhost\MSSQLSERVER
AdventureWorks2019
SQL Login
mb_migrator
●●●●●●●●
ODBC Driver 17 for SQL Server
Test Source Connection Test Target Connection Clear
[10:42:01] SOURCE Redis PING OK — 760836 keys in DB 0
[10:42:02] TARGET SQL Server connected — AdventureWorks2019 (SQL Server 15.0.4316)
The Database field for the Redis source is the logical DB number (0–15). Redis DB 0 is the default. If your keys were exported to a non-default DB, set this accordingly. The value is appended as the path segment of the URI: redis://host:6379/2 for DB 2.
For Azure Cache for Redis, always use rediss:// (double-s) and port 6380. Plain redis:// on port 6379 will be rejected by Azure Cache because non-TLS access is disabled by default.
2

Assessment

Click Run Assessment. MigrationBridge scans all keys using SCAN 0 MATCH * COUNT 500, groups them by their schema:table prefix, counts rows per prefix, samples the first 20 keys per prefix to infer column names and types, and produces a pre-migration report.

What the Assessment Checks

MigrationBridge — Assessment Report
[10:42:15] ASSESS Starting assessment — DB 0, SCAN COUNT 500
[10:42:16] ASSESS Key convention: schema:table:PK detected (sample: HumanResources:Employee:1)
[10:42:17] ASSESS 70 unique prefixes discovered
[10:42:18] ASSESS HumanResources:Employee — 290 keys, 26 Hash fields, types inferred: INT, NVARCHAR, BIT, DATETIME2
[10:42:18] ASSESS Person:Address — 19614 keys, 9 Hash fields, types: INT, NVARCHAR, FLOAT
[10:42:19] ASSESS Sales:SalesOrderHeader — 31465 keys, 25 Hash fields, types: INT, NVARCHAR, FLOAT, DATETIME2, UNIQUEIDENTIFIER
[10:42:20] ASSESS Production:ProductDescription — field [Description] contains values >4000 chars, will use NVARCHAR(MAX)
[10:42:21] ASSESS Total: 70 prefixes, 760836 rows, ~152167 INSERT batches (batch size 5)
[10:42:21] ASSESS Estimated duration: ~4m 00s (based on SCAN rate 3200 keys/s)
[10:42:21] ASSESS Assessment complete — 0 blocking issues, 1 informational warning

Type Coercion Rules Shown in Assessment

The assessment output includes a coercion preview for each prefix, showing the inferred SQL type for each field. The full coercion reference is in the Type Conversion Reference section. Common coercions flagged during assessment:

CoercionAssessment MessageSeverity
ISO 8601 string → DATETIME2datetime ISO8601 coercion confirmedOK
"1"/"0" → BITbit coercion: "1"/"0" detectedOK
UUID string → UNIQUEIDENTIFIERGUID format confirmed for field [rowguid]OK
Value > 4000 charsNVARCHAR(MAX) required for field [X]INFO
Ambiguous numericambiguous type — defaulting to FLOAT for field [X]WARN
3

Select Key Prefixes

After assessment, the Prefix Selection panel lists all discovered key prefixes. Each prefix maps to exactly one SQL Server table. Select the prefixes to migrate. By default all prefixes are selected.

MigrationBridge — Prefix Selection
Select All Deselect All Filter...
Key Prefix → SQL Server Table Keys (Rows) Hash Fields (Cols) Status
HumanResources:Employee [HumanResources].[Employee] 290 26 Ready
Person:Address [Person].[Address] 19,614 9 Ready
Person:Person [Person].[Person] 19,972 12 Ready
Production:Product [Production].[Product] 504 25 Ready
Production:WorkOrder [Production].[WorkOrder] 72,591 10 Ready
Sales:SalesOrderHeader [Sales].[SalesOrderHeader] 31,465 25 Ready
Sales:SalesOrderDetail [Sales].[SalesOrderDetail] 121,317 11 Ready
Purchasing:PurchaseOrderDetail [Purchasing].[PurchaseOrderDetail] 8,845 10 Ready
Production:TransactionHistory [Production].[TransactionHistory] 113,443 9 Ready
... 61 more prefixes — 372,795 additional rows ...
70 of 70 prefixes selected  |  760,836 total rows
Start Migration Back
💡
To migrate a single schema only, use the Filter input and type the schema name (e.g., Sales:). This filters the list to show only prefixes beginning with that string. You can then use Select All to select only the filtered results.
4

Migration & Log

Click Start Migration. MigrationBridge processes each prefix sequentially. For each prefix it: (1) issues SCAN with the prefix pattern, (2) fetches Hash fields via HSCAN, (3) coerces types, (4) TRUNCATES the target table, (5) bulk-INSERTs rows in configurable batches (default: 500 rows). Progress and all events are written to the log panel in real time.

MigrationBridge — Migration Log
[10:43:00] START Migration started — 70 prefixes, 760836 rows, batch size 500
[10:43:00] ------- [1/70] HumanResources:Employee
[10:43:00] SCAN SCAN 0 MATCH HumanResources:Employee:* COUNT 500 — cursor complete in 1 pass
[10:43:01] HSCAN Read 290 keys × 26 fields each
[10:43:01] COERCE BirthDate: ISO8601 → DATETIME2 (290/290 OK)
[10:43:01] COERCE HireDate: ISO8601 → DATETIME2 (290/290 OK)
[10:43:01] COERCE SalariedFlag: "1"/"0" → BIT (290/290 OK)
[10:43:01] COERCE CurrentFlag: "1"/"0" → BIT (290/290 OK)
[10:43:01] COERCE rowguid: UUID string → UNIQUEIDENTIFIER (290/290 OK)
[10:43:01] SQL TRUNCATE TABLE [HumanResources].[Employee]
[10:43:01] INSERT [HumanResources].[Employee] — 290 rows inserted (1 batch)
[10:43:01] DONE [1/70] HumanResources:Employee — 290 rows — 1.1s
[10:43:02] ------- [2/70] Person:Address
[10:43:02] SCAN SCAN cursor complete in 40 passes (19614 keys)
[10:43:03] HSCAN Read 19614 keys × 9 fields each
[10:43:03] COERCE ModifiedDate: ISO8601 → DATETIME2 (19614/19614 OK)
[10:43:03] SQL TRUNCATE TABLE [Person].[Address]
[10:43:04] INSERT [Person].[Address] — 19614 rows inserted (40 batches)
[10:43:04] DONE [2/70] Person:Address — 19614 rows — 2.3s
[10:43:05] ------- [3/70] Person:Person
[10:43:05] SCAN SCAN cursor complete in 41 passes (19972 keys)
[10:43:06] HSCAN Read 19972 keys × 12 fields each
[10:43:06] COERCE rowguid: UUID string → UNIQUEIDENTIFIER (19972/19972 OK)
[10:43:06] SQL TRUNCATE TABLE [Person].[Person]
[10:43:07] INSERT [Person].[Person] — 19972 rows inserted (41 batches)
[10:43:07] DONE [3/70] Person:Person — 19972 rows — 2.0s
[10:43:08] ------- [14/70] Sales:SalesOrderHeader
[10:43:08] SCAN SCAN cursor complete in 64 passes (31465 keys)
[10:43:09] HSCAN Read 31465 keys × 25 fields each
[10:43:09] COERCE OrderDate: ISO8601 → DATETIME2 (31465/31465 OK)
[10:43:09] COERCE ShipDate: ISO8601 → DATETIME2 (31462/31465 OK, 3 NULL — field absent in key)
[10:43:09] COERCE SubTotal: string → FLOAT — precision may differ from original MONEY type
[10:43:09] COERCE rowguid: UUID string → UNIQUEIDENTIFIER (31465/31465 OK)
[10:43:10] SQL TRUNCATE TABLE [Sales].[SalesOrderHeader]
[10:43:12] INSERT [Sales].[SalesOrderHeader] — 31465 rows inserted (63 batches)
[10:43:12] DONE [14/70] Sales:SalesOrderHeader — 31465 rows — 4.1s
[10:44:05] ------- [50/70] Production:TransactionHistory
[10:44:05] SCAN SCAN cursor complete in 228 passes (113443 keys)
[10:44:09] HSCAN Read 113443 keys × 9 fields each
[10:44:09] COERCE TransactionDate: ISO8601 → DATETIME2 (113443/113443 OK)
[10:44:10] SQL TRUNCATE TABLE [Production].[TransactionHistory]
[10:44:16] INSERT [Production].[TransactionHistory] — 113443 rows inserted (227 batches)
[10:44:16] DONE [50/70] Production:TransactionHistory — 113443 rows — 11.4s
[10:47:12] ------- [70/70] dbo:AWBuildVersion
[10:47:12] SCAN SCAN cursor complete in 1 pass (1 key)
[10:47:12] HSCAN Read 1 keys × 4 fields each
[10:47:12] SQL TRUNCATE TABLE [dbo].[AWBuildVersion]
[10:47:12] INSERT [dbo].[AWBuildVersion] — 1 rows inserted (1 batch)
[10:47:12] DONE [70/70] dbo:AWBuildVersion — 1 rows — 0.1s
[10:47:12] SUMMARY 70 prefixes | 760836 rows inserted | 0 coercion failures | 0 errors
[10:47:12] SUMMARY Elapsed: 4m 12s | Throughput: ~3019 rows/s
[10:47:12] DONE Migration complete
A warning on MONEY type columns is expected. Redis stores all values as strings; MigrationBridge coerces numeric strings to FLOAT. If the target table was pre-created with MONEY columns, SQL Server will implicitly convert FLOAT to MONEY on INSERT, which may cause sub-cent rounding differences. Verify financial columns after migration if precision is critical.
💡
The internal tracking field _redis_key (if present in any Hash from a prior MigrationBridge export) is automatically excluded from INSERT column lists. It will never be written to the SQL Server target table.
5

Verify

After migration completes, run the following validation queries against the SQL Server target to confirm row counts, spot-check individual rows, and verify database integrity.

Row Count Validation — All Tables

-- Row counts for all user tables — compare against MigrationBridge summary log
SELECT
    QUOTENAME(s.name) + '.' + QUOTENAME(t.name) AS TableName,
    p.rows                                              AS RowCount
FROM       sys.tables  t
INNER JOIN sys.schemas s  ON s.schema_id = t.schema_id
INNER JOIN sys.partitions p ON p.object_id = t.object_id
                          AND p.index_id IN (0, 1)
ORDER BY  s.name, t.name;

Spot-Check a Specific Row by Primary Key

-- Spot-check: HumanResources.Employee — BusinessEntityID = 1
SELECT
    BusinessEntityID,
    NationalIDNumber,
    LoginID,
    JobTitle,
    BirthDate,
    MaritalStatus,
    Gender,
    HireDate,
    SalariedFlag,
    rowguid,
    ModifiedDate
FROM [HumanResources].[Employee]
WHERE BusinessEntityID = 1;
-- Spot-check: Sales.SalesOrderHeader — SalesOrderID = 43659
SELECT
    SalesOrderID,
    OrderDate,
    ShipDate,
    Status,
    CustomerID,
    SalesPersonID,
    TerritoryID,
    SubTotal,
    TaxAmt,
    Freight,
    TotalDue,
    rowguid
FROM [Sales].[SalesOrderHeader]
WHERE SalesOrderID = 43659;

Aggregate Spot-Check — Financial Columns

-- Verify financial totals in SalesOrderHeader match expected values
SELECT
    COUNT(*)        AS TotalOrders,
    SUM(SubTotal)   AS TotalSubTotal,
    SUM(TaxAmt)     AS TotalTax,
    SUM(TotalDue)   AS TotalDue,
    MIN(OrderDate)  AS EarliestOrder,
    MAX(OrderDate)  AS LatestOrder
FROM [Sales].[SalesOrderHeader];

DBCC CHECKDB — Database Integrity

-- Run after all inserts are complete; no_infomsgs suppresses informational messages
DBCC CHECKDB ('AdventureWorks2019') WITH NO_INFOMSGS, ALL_ERRORMSGS;
💡
For a large database, DBCC CHECKDB can be I/O intensive. On production systems, consider running it during off-peak hours or using DBCC CHECKDB WITH PHYSICAL_ONLY for a faster structural scan, followed by a full check in a subsequent maintenance window.
Cross-reference the row counts from the SQL Server query against the MigrationBridge summary log line: SUMMARY 70 prefixes | 760836 rows inserted. Any discrepancy indicates a failed INSERT batch — check the log for .err lines.
6

Post-Migration

After successful verification, complete the following post-migration steps to restore SQL Server performance and validate data quality.

Update Statistics

TRUNCATE + bulk INSERT invalidates existing statistics. Rebuild them before the database is used in production:

-- Update statistics on all tables in the database
EXEC sp_updatestats;

-- Or update statistics on a specific high-priority table with full scan:
UPDATE STATISTICS [Sales].[SalesOrderHeader] WITH FULLSCAN;
UPDATE STATISTICS [Sales].[SalesOrderDetail] WITH FULLSCAN;
UPDATE STATISTICS [Production].[TransactionHistory] WITH FULLSCAN;

Rebuild or Add Indexes

MigrationBridge does not create non-clustered indexes. If the target tables were auto-created by MigrationBridge, only a heap (no clustered index) exists. Add indexes to restore query performance:

-- Example: add clustered PK on auto-created HumanResources.Employee
ALTER TABLE [HumanResources].[Employee]
    ADD CONSTRAINT PK_Employee_BusinessEntityID
    PRIMARY KEY CLUSTERED (BusinessEntityID);

-- Example: add non-clustered index on SalesOrderDetail.ProductID
CREATE NONCLUSTERED INDEX IX_SalesOrderDetail_ProductID
    ON [Sales].[SalesOrderDetail] (ProductID)
    INCLUDE (OrderQty, UnitPrice);

-- Rebuild all indexes (fragmentation after bulk insert):
ALTER INDEX ALL ON [Sales].[SalesOrderHeader] REBUILD;
ALTER INDEX ALL ON [Sales].[SalesOrderDetail] REBUILD;
If target tables were pre-created with the original DDL (recommended), the indexes already exist but will be highly fragmented after TRUNCATE + bulk INSERT. Run ALTER INDEX ALL ON [schema].[table] REBUILD for all large tables before opening the database to queries.

Validate Datetime Precision

If the assessment or migration log reported any datetime ISO8601 coercion warnings, validate that DATETIME2 values landed with the correct precision:

-- Check datetime precision — compare truncated vs. full value
SELECT TOP 10
    BusinessEntityID,
    BirthDate,
    CONVERT(VARCHAR(30), BirthDate, 126) AS BirthDate_ISO,
    HireDate,
    CONVERT(VARCHAR(30), HireDate, 126)  AS HireDate_ISO
FROM [HumanResources].[Employee]
ORDER BY BusinessEntityID;

Re-enable Foreign Key Constraints

MigrationBridge disables FK constraints before TRUNCATE and re-enables them after INSERT. Verify they are in an enabled, trusted state:

-- Verify all FK constraints are enabled and trusted
SELECT
    OBJECT_NAME(fk.parent_object_id) AS TableName,
    fk.name                           AS ConstraintName,
    fk.is_disabled,
    fk.is_not_trusted
FROM sys.foreign_keys fk
WHERE fk.is_disabled = 1
   OR  fk.is_not_trusted = 1
ORDER BY TableName;

-- Re-enable any that show as disabled (replace table/constraint names):
ALTER TABLE [schema].[table]
    WITH CHECK CHECK CONSTRAINT constraint_name;
Using WITH CHECK CHECK CONSTRAINT (note the double keyword) forces SQL Server to re-validate all existing rows against the constraint, setting is_not_trusted = 0. This is required for the query optimizer to use the constraint for plan optimization.

Final Row Count Cross-Check

-- Final validation: total rows across all user tables
SELECT
    SUM(p.rows) AS TotalRows
FROM sys.tables t
INNER JOIN sys.partitions p
    ON p.object_id = t.object_id
    AND p.index_id IN (0, 1)
WHERE t.is_ms_shipped = 0;
-- Expected: 760836

🔄 Type Conversion Reference

MigrationBridge uses the following rules to coerce Redis string values back to SQL Server types. Rules are evaluated in the order shown — the first matching rule wins.

Redis String Value Pattern SQL Server Type Example Redis Value Example SQL Value Notes
Integer string (no decimal, no dot) INT "123" 123 Values outside INT range promote to BIGINT
Numeric string with decimal point FLOAT "123.45" 123.45 MONEY columns receive implicit FLOAT→MONEY cast; precision warning issued
"true" or "1" (case-insensitive) BIT = 1 "1", "True" 1 Only applies when target column is BIT or when all values in field are "0"/"1"/"true"/"false"
"false" or "0" BIT = 0 "0", "False" 0 See above
ISO 8601 datetime string DATETIME2 "1969-01-29T00:00:00" 1969-01-29 00:00:00.0000000 Fractional seconds preserved up to 7 digits if present in string
UUID / GUID string (8-4-4-4-12 hex) UNIQUEIDENTIFIER "92c4279f-1207-48a3-8448-4636514eb7e2" 92C4279F-1207-48A3-8448-4636514EB7E2 Validated with regex before coercion
Hex-encoded binary string (prefix 0x) VARBINARY "0x89504e47..." binary data Only when value begins with 0x and remainder is valid hex
All other strings NVARCHAR(MAX) "Mountain Bike" N'Mountain Bike' Default fallback; storage may exceed original column length
Missing Hash field (key does not have this field) NULL (field absent) NULL Logged as informational; column must be nullable in target table
Type inference runs per-field across a sample of 100 keys per prefix during assessment. The inferred type is used for all rows in the migration run. If a value outside the sample set fails coercion at INSERT time, MigrationBridge substitutes NULL and logs a warning — the row is still inserted.
Redis has no native MONEY, DECIMAL, or NUMERIC type. All monetary values were stored as strings during the SQL Server → Redis export. MigrationBridge coerces them to FLOAT. If the target table has MONEY or DECIMAL columns, sub-cent rounding differences are possible. Validate financial totals post-migration against the original source.

⚠️ Known Issues

Key Naming Convention Must Match Export

🛑
The key naming pattern used during import must exactly match the pattern used during the original SQL Server → Redis export. If the exporter used a different separator (e.g., . instead of :, or schema_table:PK), MigrationBridge will parse the prefix incorrectly and create wrong table names or fail to find the schema. Always verify with redis-cli SCAN 0 COUNT 10 and compare against the original export configuration before running the migration.

_redis_key Field Excluded Automatically

MigrationBridge automatically excludes any Hash field named _redis_key from INSERT column lists. This is an internal tracking field written during the SQL Server → Redis export phase and has no corresponding SQL Server column. No action required — it is silently skipped.

Type Coercion Failures

If a value fails type coercion at INSERT time (e.g., a field sampled as INT contains an unexpected non-numeric value in a non-sampled key), MigrationBridge logs a WARN line and substitutes NULL for that column in that row. The row is still inserted. After migration, query for unexpected NULLs in columns that should not be nullable:
SELECT COUNT(*) FROM [schema].[table] WHERE expected_column IS NULL;

Azure Cache for Redis — SSL Required

🛑
Azure Cache for Redis requires TLS. Always use rediss:// (double-s) and port 6380. Using redis:// on port 6379 will result in a connection timeout or immediate disconnect. Non-TLS access is disabled by default and cannot be enabled on the Standard or Premium tier.

AWS ElastiCache — VPC Access Required

🛑
ElastiCache Redis clusters are not publicly accessible. MigrationBridge must run from within the same VPC as the ElastiCache cluster, or from a bastion/jump host with VPC connectivity. Attempting to connect from outside the VPC will result in a connection timeout. If running MigrationBridge from a local machine, use an SSH tunnel or AWS Session Manager port-forward to the cluster endpoint.

Large Hash Fields — NVARCHAR(MAX) Inflation

If any Hash field contains values longer than 4,000 characters, MigrationBridge assigns NVARCHAR(MAX) to that column for the entire table. This may significantly inflate storage if the original column was a fixed-width type. Monitor with:
SELECT SUM(a.total_pages) * 8 / 1024 AS UsedMB
FROM sys.partitions p
JOIN sys.allocation_units a ON a.container_id = p.hobt_id
JOIN sys.tables t ON t.object_id = p.object_id
WHERE t.name = 'YourTable';

Foreign Key Constraint Failures on Re-run

MigrationBridge disables FK constraints before TRUNCATE and re-enables them after INSERT. If the migration is interrupted mid-run (e.g., network loss), FK constraints on partially-migrated tables may remain disabled. Run the FK validation query from Step 6 after any interrupted migration before resuming or using the target database.

🖥️ UI Behavior During Migration

During an active migration run, the MigrationBridge UI enters a busy state to prevent conflicting operations. The following behaviors are expected and by design:

Do not close the MigrationBridge window during an active migration. Closing the window while a migration is running will terminate the Python process mid-batch. The target tables will be left in a partially-loaded state with FK constraints disabled. Use the Cancel Migration button to stop gracefully.

MigrationBridge v1.3.0 · Redis → SQL Server · User Guide · Troubleshooting