Subchapter 62.7
references/mode-4-sql-migration.mdMarkdown6 KBView on GitHub
Use when the user provides SQL CREATE TABLE statements and wants a Keyspaces migration plan. This mode translates the relational schema into three Keyspaces data models, prices each via calculate.ts, and recommends the best fit.
Scripts
scripts/7 filesExtract:
(source_table, source_col) → (target_table, target_col).SELECT statements provided. These drive partition-key choice.| SQL type | Bytes |
|---|---|
BOOL / BOOLEAN | 1 |
SMALLINT | 2 |
INT / INTEGER / SERIAL / DATE / FLOAT / REAL | 4 |
BIGINT / DOUBLE / TIMESTAMP / DATETIME / DECIMAL / NUMERIC | 8 |
UUID | 16 |
VARCHAR(n) / CHAR(n) | n |
VARCHAR / TEXT / CLOB (no length) | 64 |
BLOB / BINARY | 512 |
row_size_bytes per table = sum of all column byte sizes.
If not already supplied, ask for:
us-east-1).Storage-only reasoning misses the dominant pricing driver, so do not skip rates.
Merge all foreign-key-related tables into one.
merged_row_size_bytes = sum of all unique column sizes (deduplicate FK columns).merged_row_count = row count of the many-side table (the table with the FK column). For 1:many relationships, this equals the child table row count since each child row maps to exactly one parent. For many:many relationships through a join table, use the join table row count.storage_gb = (merged_row_count × merged_row_size_bytes) / (1024^3).reads_per_sec = sum of reads across original tables.writes_per_sec = sum of writes across original tables.Keep original tables; add one lookup table per FK for application-side joins.
storage_gb = (row_count × row_size_bytes) / 1024^3 per table.(source.col → target.pk), named target_by_source:
lookup_row_size_bytes = size(FK col) + size(target PK col).lookup_storage_gb = (target_row_count × lookup_row_size_bytes) / 1024^3.total_storage_gb = sum of original + all lookup storage.reads_per_sec = sum of original reads + (FK lookups required per query × reads using them).writes_per_sec = sum of original writes + (1 extra write per lookup table per insert).Same merged table as Option A, plus one reverse-index table per non-PK FK column.
reverse_row_size_bytes = merged_row_size_bytes.reverse_row_count = merged_row_count.total_storage_gb = merged_storage_gb × (1 + number_of_reverse_indexes).reads_per_sec = same as Option A (no extra read; correct table picked per query).writes_per_sec = Option A writes_per_sec × (1 + number_of_reverse_indexes).cd scripts
npx ts-node --project tsconfig.scripts.json calculate.ts \
<region> <reads/s> <writes/s> <avg_row_size_bytes> <storage_gb> 0 false \
| tee /tmp/keyspaces-sql-optionA.json
# Repeat for B → /tmp/keyspaces-sql-optionB.json
# Repeat for C → /tmp/keyspaces-sql-optionC.jsonExtract provisioned.total, on_demand.total, and provisioned_savings_plan.total from each JSON.
| Option A — Denorm | Option B — Normalized | Option C — Reverse Index | |
|---|---|---|---|
| Storage | — | — | — |
| Reads/s | — | — | — |
| Writes/s | — | — | — |
| Bytes/row (avg) | — | — | — |
| Backup | off / on | off / on | off / on |
| Lookups per query | — | — | — |
| Provisioned + Savings Plan/mo | $— | $— | $— |
| On-demand + Savings Plan/mo | $— | $— | $— |
pitr_enabled input (PITR on / off).Generate the full table definitions for each option.
Pick based on:
State the recommended option first, then the one-line reason.
After displaying the comparison, ask the user whether they want a PDF. If yes, use one invocation with all three --input flags (see pdf-reporting.md):
npx ts-node --project tsconfig.scripts.json generate-pdf.ts \
--input /tmp/keyspaces-sql-optionA.json --label "Option A — Denorm" \
--input /tmp/keyspaces-sql-optionB.json --label "Option B — Normalized" \
--input /tmp/keyspaces-sql-optionC.json --label "Option C — Reverse Index" \
--output /tmp/keyspaces-sql-comparison.pdf