artifact: "engineering-report" database: "wordpress" job_id: "docs-sample" generated: "2026-10-07T05:24:34+00:00" filename: "wordpress_engineering-report_docs-sam_20261007.md" source_artifact: "wordpress/docs-sample/synthesis/v2/report.json"
Database Modernization — Engineering Report
Source database: wordpress. This is the build companion to the Decision Report: the source-to-target mapping, the per-engine target schemas, and the query groups.
Target engines
| Engine | Role | Workload | Why |
|---|---|---|---|
| dynamodb | Migration target | 54.2% | 97% mean fit across 58 queries (18 rated tables), led by key-value lookups (25 of 58). estimated $98.41/month. |
| aurora_mysql | Migration target | 45.8% | 88% mean fit across 49 queries (21 rated tables; 2 queries without table-level evidence), pinned by a utility or DDL statement, aggregation, full-text or pattern search and multi-table joins (35 of 49). estimated $318.80/month. |
| elasticache | Cache layer | 20 cached reads · 83.4% of calls | Cache layer for 20 hot reads (83.4% of calls), mostly point lookups, cache-aside in front of Aurora MySQL in wave 2, then DynamoDB once wave 3 moves these reads; it owns no queries; 76% mean cache fit across 20 cached reads (12 rated tables). estimated $165.55/month (185.07 calls/s combined across these reads clears the 1 calls/s hot-read floor). |
Cost
- Current monthly (source): source cost not provided
- Projected monthly (target): $582.76
- Savings: not available
- Already removed from this comparison: $367.39/mo (DocumentDB), $240.96/mo (OpenSearch), retired before this projection (see Migration trade-offs below for why).
Cache layer (elasticache)
elasticache fronts 20 hot reads (83.4% of calls, 185.1 calls/s) cache-aside. It owns none of the workload: each cached read stays with its owner engine, which serves every miss and every write. It fronts Aurora MySQL with no data migration of its own, and keeps serving the same reads as their owners move in later waves: invalidation follows the engine that owns each cached table.
- Owner engines: aurora_mysql 3, dynamodb 17
- Read shapes: point lookup 19, reference read 1
- Eligible: a SELECT at ≥ 1 calls/s returning ≤ 100 rows on average, with a point, top-N, session or small reference-read shape, on tables that are not write-heavy.
Migration roadmap (3 waves)
One suggested adoption path, not the only one — to modernize in one step instead, adopt the target architecture in the Target engines table above directly.
1 table name in the assignment did not resolve to a table or view in the collected schema (a CTE alias, a system catalog, a sequence, a keyword, or a column the SQL parser mistook for a table) and is not shown in any wave.
Wave 1: Move to Aurora MySQL
- Engines: Aurora MySQL
- all 107 queries at cutover; 49 (45.8%) remain on Aurora MySQL at the end
- 50 source tables and views:
wordpress.wp_actionscheduler_actions,wordpress.wp_actionscheduler_claims,wordpress.wp_actionscheduler_groups,wordpress.wp_actionscheduler_logs,wordpress.wp_commentmeta,wordpress.wp_comments,wordpress.wp_links,wordpress.wp_options,wordpress.wp_postmeta,wordpress.wp_posts,wordpress.wp_term_relationships,wordpress.wp_term_taxonomy,wordpress.wp_termmeta,wordpress.wp_terms,wordpress.wp_usermeta,wordpress.wp_users,wordpress.wp_wc_admin_note_actions,wordpress.wp_wc_admin_notes,wordpress.wp_wc_category_lookup,wordpress.wp_wc_customer_lookup(+30 more) - Moves from: the source MySQL database
- Why: The whole source database (MySQL 8.0.45) moves 1:1 to Aurora MySQL: same engine family. The collector reports engine and version only; feature compatibility is not assessed here. 49 queries (45.8%) remain on Aurora MySQL once later waves have moved their share; later waves move subsets of these 50 tables and views out of Aurora MySQL.
- Gate before the next wave: Schema and data parity validated against the source database before cutover; decommission the legacy source once replication lag is zero.
Wave 2: Cache hot reads with ElastiCache
- Engines: ElastiCache
- 20 queries (83.4% of calls)
- Fronts: Aurora MySQL
- Why: 20 hot reads (83.4% of calls), cache-aside in front of Aurora MySQL in wave 2, then DynamoDB once wave 3 moves these reads: no data migration, fully reversible. It takes the read pressure off before the data migrations that follow. These reads belong to queries that move in wave 3 (DynamoDB) and wave 1 (Aurora MySQL); this share overlaps the owner shares, which alone sum to 100%.
- Gate before the next wave: Cache hit rate and invalidation verified against Aurora MySQL.
Wave 3: Move key-value and point-lookup queries to DynamoDB
- Engines: DynamoDB
- 58 queries (54.2% of query patterns)
- 8 source tables:
wordpress.wp_actionscheduler_actions,wordpress.wp_actionscheduler_groups,wordpress.wp_options,wordpress.wp_postmeta,wordpress.wp_termmeta,wordpress.wp_wc_tax_rate_classes,wordpress.wp_woocommerce_api_keys,wordpress.wp_woocommerce_order_itemmeta - 1 table group:
- 29 queries, independent:
wordpress.wp_actionscheduler_actions,wordpress.wp_actionscheduler_groups,wordpress.wp_options,wordpress.wp_postmeta,wordpress.wp_termmeta,wordpress.wp_wc_tax_rate_classes,wordpress.wp_woocommerce_api_keys,wordpress.wp_woocommerce_order_itemmeta - Moves from: Aurora MySQL
- Why: 58 key-value and point-lookup queries (54.2% of query patterns) across 1 table group, respecting co-dependent tables where possible: the pattern DynamoDB fits best, and the smallest-blast-radius data migration available once the cache wave has absorbed the read pressure.
- Gate before the next wave: Dual-write/backfill validated and query parity confirmed per table group. 7 tables (wordpress.wp_actionscheduler_actions, wordpress.wp_actionscheduler_groups, wordpress.wp_options, wordpress.wp_postmeta, wordpress.wp_termmeta, wordpress.wp_woocommerce_api_keys, wordpress.wp_woocommerce_order_itemmeta) stay dual-read by Aurora MySQL queries, which remain on Aurora MySQL; keep a copy in sync via CDC. 29 queries read 10 tables (wordpress.wp_actionscheduler_logs, wordpress.wp_comments, wordpress.wp_posts, wordpress.wp_term_relationships, wordpress.wp_term_taxonomy, wordpress.wp_terms, wordpress.wp_users, wordpress.wp_wc_reserved_stock, wordpress.wp_woocommerce_downloadable_product_permissions, wordpress.wp_woocommerce_order_items) Aurora MySQL owns (wave 1) and keeps owning; keep a DynamoDB copy of them in sync via CDC.
Risk register (2)
dynamodb
- RISK-001 · HIGH · performance degradation — Complex GROUP BY / HAVING / multi-table aggregations. DynamoDB doesn't support server-side aggregation — these need to move to application layer, pre-computed aggregates, or a separate analytics store. (0% of queries resolved by schema design, 1 remaining)
- Mitigation: Pre-compute aggregates on write (DynamoDB Streams + Lambda), or export to S3/Athena for analytics.
- Affects:
wordpress.wp_posts - RISK-002 · HIGH · migration complexity — Schema design gap: 9 queries assigned to DynamoDB have no in-scope access pattern: UPDATE
wp\_woocommerce\_api\_keysSETlast\_access= ? WHEREkey\_id= ?; SELECTkey\_id,user\_id,permissions,consumer\_key,consumer\_secr...; UPDATEwp_woocommerce_downloadable_product_permissionsSETuser_id` = ? , ...; and 6 more (0% of queries resolved by schema design, 9 remaining) - Mitigation: Add in-scope access patterns for these queries to the DynamoDB schema design, or route them to an engine that serves them.
- Affects:
wordpress.wp_actionscheduler_logs,wordpress.wp_comments,wordpress.wp_options,wordpress.wp_wc_reserved_stock,wordpress.wp_wc_tax_rate_classes,wordpress.wp_woocommerce_api_keys,wordpress.wp_woocommerce_downloadable_product_permissions
Resolved by the assignment (6)
- HIGH · DynamoDB → Aurora MySQL — Complex GROUP BY / HAVING / multi-table aggregations. DynamoDB doesn't support server-side aggregation — these need to move to application layer, pre-computed aggregates, or a separate analytics store.
- Resolved because the queries run as SQL on Aurora MySQL.
- HIGH · Aurora MySQL → DynamoDB — Single-row SELECT by primary key at very high frequency (>100 calls/second). DynamoDB provides single-digit millisecond latency for key-value lookups at any scale — Aurora adds unnecessary overhead for this access pattern.
- Resolved because the Aurora MySQL analysis recommended DynamoDB for these queries and the assignment moved them there.
- MEDIUM · Aurora MySQL → Aurora MySQL — Kept on Aurora MySQL because of a utility or DDL statement and aggregation on this table.
- Resolved because Aurora MySQL is also required for a utility or DDL statement and aggregation on this table, so keeping it there is a deliberate routing decision, not an open risk; revisit DynamoDB for this table's simple key-value reads in a later wave if those other queries are retired or move too.
- MEDIUM · Aurora MySQL → DynamoDB — Table with no foreign keys and no join participation — all queries are single-table CRUD. This workload has no structural relational requirement and could run on a simpler, purpose-built engine (DynamoDB for key-value, ElastiCache for hot lookups).
- Resolved because the Aurora MySQL analysis recommended DynamoDB for these queries and the assignment moved them there.
- MEDIUM · Aurora MySQL → Aurora MySQL — Kept on Aurora MySQL because of a utility or DDL statement on this table.
- Resolved because Aurora MySQL is also required for a utility or DDL statement on this table, so keeping it there is a deliberate routing decision, not an open risk; revisit DynamoDB for this table's simple key-value reads in a later wave if those other queries are retired or move too.
- MEDIUM · Aurora MySQL → DynamoDB — Table accessed by at most 2 distinct query patterns, all single-table, all by primary key or single column filter. This is a key-value workload wearing a relational costume — no query optimizer benefit.
- Resolved because the Aurora MySQL analysis recommended DynamoDB for these queries and the assignment moved them there.
Migration trade-offs (4)
reality-check
- Consolidated 23 queries from aurora_mysql → dynamodb: aurora_mysql provides no unique capabilities — 23 queries can be served by existing engines (35 retained due to capability requirements). No operational savings are claimed: this move does not remove aurora_mysql from the architecture.
- Consolidated 4 queries from documentdb → aurora_mysql: documentdb provides no unique capabilities — 6 queries can be served by existing engines. Removes $367.39/mo of infrastructure cost (documentdb's own analysed estimate before removal), plus a qualitative reduction in operational overhead -- one fewer engine's worth of team expertise, monitoring, backups and failover to maintain -- by avoiding a dedicated documentdb cluster.
- Consolidated 2 queries from documentdb → dynamodb: documentdb provides no unique capabilities — 6 queries can be served by existing engines. documentdb leaves the architecture.
- Consolidated 10 queries from opensearch → aurora_mysql: 10 opensearch queries moved to aurora_mysql; no in-scope opensearch query remains (4 queries of these, moved by the justification floor: no search predicate with relevance ranking, fuzzy matching or a facet in this query). Removes $240.96/mo of infrastructure cost (opensearch's own analysed estimate before removal), plus a qualitative reduction in operational overhead -- one fewer engine's worth of team expertise, monitoring, backups and failover to maintain -- by avoiding a dedicated opensearch cluster.
Assignments are deterministic. The complete machine-readable assessment is the Assessment Data (JSON) artifact.