Your application can read the migrated rows, but a customer history screen is empty. The loader copied the documents and missed the relationships encoded in their keys. A DynamoDB to PostgreSQL migration needs to preserve the application's behavior as well as its stored values. Moving JSON into a column is only one part of that work.
Begin with the queries and writes the application actually performs. Then design the target tables, extraction boundary and reconciliation checks. The workflow below is an engineering recommendation. It does not imply that DynamoDB exports automatically create a working PostgreSQL database or that a particular downtime window is guaranteed.
Map application access patterns before mapping fields
Inventory the requests that create, update and retrieve each business entity. Record the keys used, expected result order, tenant scope and behavior when a record is missing. Include background workers, reports and administrative tools. A successful web request is not enough if a scheduled writer still depends on the source representation.
For each source item shape, decide whether it becomes a relational row, a child collection or a retained document. Preserve the original source key alongside any new target identifier. If the source uses a composite key, keep both components without ambiguous concatenation. Explain how each source item maps to the target so a failed business check can be traced back.
Do not force every item shape into the same table just because the source stored them together. Equally, do not split every nested field without a query requirement. Treat this as part of the application change described in our code migration approach, not a loader-only project.
Choose and record the extraction boundary
DynamoDB export to S3 requires point-in-time recovery to be enabled. It supports full and incremental exports in DynamoDB JSON or Amazon Ion. Record the source table, requested export time or interval, export identifier and output format. Verify permissions and successful export completion before accepting files as input.
A full export gives the table representation for the requested point within the recovery window. Do not assume this guarantees transactionally consistent business records. AWS explicitly warns that exported data is not transactionally consistent and transaction changes can be split across export outputs. Validate related business records and reconcile the boundary before cutover.
If you use Scan instead, paginate to completion. A Scan reads up to its configured item limit or the documented data boundary before applying its filter. A page can contain no matching items while still returning LastEvaluatedKey. Continue using that key as ExclusiveStartKey rather than treating an empty page as completion.
Keep the export manifest and original types
DynamoDB exports contain summary and files manifests. Use the files manifest as the list of expected data files rather than loading whatever happens to be present under a shared storage prefix. Store the export identity, file identity and processing result in a private migration journal. Restart from that journal without silently skipping failures.
DynamoDB JSON uses type wrappers. A value tagged N is not the same representation as a plain JSON number, and a set is not simply an ordered list. Keep the original tagged record in restricted staging until conversion and reconciliation have passed. Preserve failed rows with an explicit reason and a controlled way to reprocess them.
| Source representation | Target decision | Check before acceptance |
|---|---|---|
| Source key components | Preserved identity columns or mapping record | No collisions between entity shapes or tenants |
| Number wrapper | Reviewed numeric conversion | No intermediate floating point rounding |
| String or numeric set | Explicit set representation | Membership and duplicate handling match requirements |
| Map or list | JSONB or related rows | Nested values and meaningful list order survive |
| Missing or null attribute | Written presence rule | Unknown, absent and explicitly cleared stay distinguishable |
Test JSONB conversion instead of assuming equivalence
PostgreSQL JSONB does not preserve object key order, whitespace or duplicate object keys. It also rejects numbers outside PostgreSQL numeric's range and has Unicode input restrictions. Preserve original input outside the converted representation if exact source text matters for audit or recovery.
Avoid passing precise numeric strings through an ordinary floating point parser before writing them. Test the transformation path end to end with actual source values. Decide explicitly how binary values will be represented and how source set types will remain distinguishable from ordered lists when that difference matters to the application.
JSON null and SQL NULL are different concepts. Add tests for a missing source attribute, an explicit null, an empty collection and an empty string. The target application must know whether to leave a value unchanged, clear it or reject it. A generic cleanup that makes all four identical can change business behavior.
Reconcile inserts, changes and deletions
For an active source, define how changes after the initial extraction reach the target. Incremental exports contain compacted item state for the selected interval, not every intermediate event. Do not use them as an assumed complete event history for billing, notifications or business audit.
Process deletions as well as new images. Keep the intervals and source keys in the migration journal so an older batch cannot silently replace a newer accepted state. Write and test your replay policy before running overlapping loads. If the design cannot account for changes reliably, agree a controlled pause rather than presenting a partial sync as current.
Compare coverage by entity type and tenant. Then compare relationships, values and application results. Raw counts alone are insufficient when one source item can produce several target rows. The broader data migration plan should state which differences are intentional and who accepts them.
Use a cutover checklist with a real recovery decision
- Complete the input inventory and resolve or explicitly accept every rejected transformation.
- Reconcile the final change boundary, including deleted records and related entities.
- Run application reads and writes against the target, including tenant checks and background tasks.
- Switch to one authoritative write path and verify that old workers cannot continue writing unnoticed.
- Record the recovery boundary and how target-only writes would be preserved if a return to the source is required.
Keep source access restricted during the agreed validation period. Retaining the source helps investigation, but it does not automatically make rollback safe. New target writes create another reconciliation problem. Test that recovery path before relying on it.
How Remqa approaches DynamoDB migrations
Remqa can help define the target model, build typed staging and conversion, and connect reconciliation to application cutover checks. Start with representative item shapes and the screens or jobs that use them. Include unusual records, not just the simplest successful export.
Bring a sample schema, access pattern list and current writer inventory to a call. An engineer can discuss a bounded migration plan and the checks needed before moving production traffic.