Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Windows Tipps & SecurityGarmin Cirqa im Test: Fitness-Tracker ohne Display(22.09.2026 um 10:30 Uhr)
Windows Tipps & SecuritySicher online bezahlen: 7 Methoden im Praxis-Check(22.09.2026 um 09:00 Uhr)
Sichere Programmierunghas_tokens: true is a boolean. 476 of 791 have no market behind them.(22.09.2026 um 10:00 Uhr)
Sichere ProgrammierungClaude Code Hooks – Safety Through Invariants(22.09.2026 um 10:04 Uhr)
Sichere ProgrammierungYour MCP Tool Just Returned a Secret. Did It Need To?(22.09.2026 um 10:05 Uhr)
Windows Tipps & SecurityGarmin Cirqa im Test: Fitness-Tracker ohne Display(22.09.2026 um 10:30 Uhr)
Windows Tipps & SecuritySicher online bezahlen: 7 Methoden im Praxis-Check(22.09.2026 um 09:00 Uhr)
Sichere Programmierunghas_tokens: true is a boolean. 476 of 791 have no market behind them.(22.09.2026 um 10:00 Uhr)
Sichere ProgrammierungClaude Code Hooks – Safety Through Invariants(22.09.2026 um 10:04 Uhr)
Sichere ProgrammierungYour MCP Tool Just Returned a Secret. Did It Need To?(22.09.2026 um 10:05 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

How I Solved DynamoDB + Amplify Search Problem with OpenSearch for $27/Month Instead of Migrating to Aurora

TL;DR: One OpenSearch instance. $27/month. DynamoDB search and multi-tenancy — solved. We Were About to Migrate to Aurora I had the Aurora estimate ready. I was planning to rewrite our Amplify Data models into SQL schemas. We h…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

TL;DR: One OpenSearch instance. $27/month. DynamoDB search and multi-tenancy — solved.






We Were About to Migrate to Aurora



I had the Aurora estimate ready. I was planning to rewrite our Amplify Data models into SQL schemas.



We had two problems. The first was search — once filter conditions exceeded 5 fields, DynamoDB's Query/Scan couldn't keep up. Full-text search was simply impossible. The second was multi-tenancy — we needed to safely isolate data across multiple products and multiple environments (staging / prod / sandbox). With Amplify Gen2's sandbox spinning up per-developer environments, naively adding a search engine would multiply instances and blow up costs.



In the end, we didn't migrate to Aurora. We added OpenSearch as a "search layer" alongside DynamoDB, and used index naming conventions to consolidate multiple products and environments into a single instance. The additional cost: $27/month. Both search and multi-tenancy, solved.



This article shares how we implemented this in a real-world real estate tech product running on Amplify Gen2.






The Pain Points of DynamoDB-Only



We're running a real estate tech product built on Amplify Gen2. Property and lot data lives in DynamoDB, with search and listing features for users. Multiple products share the same AWS account, each with staging, prod, and per-developer sandbox environments.



Early on, DynamoDB Query with GSIs (Global Secondary Indexes) was fine. But as the product grew, we hit walls on both search and multi-tenancy.






Search Problems






1. Complex Filter Conditions Don't Scale



Real estate search means combining "area + price range + floor size + walking distance to station + building age" — easily 5+ conditions. DynamoDB Query only filters on partition key and sort key.



You can add FilterExpression for extra conditions, but filters apply after the Query runs. So you might fetch 1,000 records just to filter down to 10. That's wasteful.






2. No Full-Text Search



Searching for "Shibuya office pet-friendly" is impossible in DynamoDB. The contains operator exists but requires a full Scan — not practical at scale.






3. Inflexible Sorting and Pagination



DynamoDB sorting depends on the sort key. Switching between "sort by price," "sort by size," and "sort by newest" each requires a separate GSI. The GSI limit is 20, but once you factor in filter combinations, it's nowhere near enough.



Pagination is cursor-based only (ExclusiveStartKey) — you can't jump directly to page 3.






Multi-Tenancy Problems






4. Data Isolation Across Products and Environments



Our products share Amplify backend patterns. DynamoDB naturally isolates data by table, so cross-product contamination isn't an issue there.



The problem emerges when you add a search engine. If you create a separate search instance per product and environment, instances multiply fast: 2 products x 3 environments (staging / prod / sandbox) = 6 instances. With 5 developers, sandbox alone means 10 instances. At $27 each: $432/month — just for adding search.






5. Amplify Sandbox Proliferation



Amplify Gen2's npx ampx sandbox creates isolated environments per developer. This works perfectly for DynamoDB, but it's devastating for always-on services like OpenSearch. Each sandbox creates an instance, takes minutes to boot, and if someone forgets to shut it down, costs pile up.






Keep DynamoDB, Add a Search Layer



"If search is the problem, just migrate to an RDB" — we considered it seriously. Aurora Serverless v2 is the strongest managed RDB option on AWS. But even with Aurora, multi-tenancy still needs separate design work, and we didn't see enough upside to justify abandoning DynamoDB + Amplify's developer experience.






Cost Comparison

































DynamoDB + OpenSearch Aurora Serverless v2
Monthly cost (min config) DynamoDB: pay-per-use (~$5) + OpenSearch t3.small: ~$27
Min 0.5 ACU: ~$60–70 (Tokyo region)
Total ~$30–50/month ~$60–100/month
Storage DynamoDB: $0.25/GB + OpenSearch: $0.122/GB (gp3) $0.12/GB (Aurora storage)
Scaling DynamoDB: auto-scale, OpenSearch: fixed instance ACU-based auto-scaling


Aurora Serverless v2 charges even at minimum 0.5 ACU. In Tokyo region, that's ~$0.20/hour × 0.5 ACU × 730 hours = ~$73/month — even when idle.



DynamoDB is pay-per-use (nearly zero when idle), and OpenSearch t3.small.search runs at ~$27/month. Combined, it's less than half of Aurora.






Amplify Compatibility



Amplify Gen2 uses DynamoDB as its default data store. Define your schema with defineData() and GraphQL APIs + DynamoDB tables are auto-generated. This developer experience is excellent.



Migrating to Aurora means giving that up: self-managed SQL schemas, migrations, ORM configuration. Keeping DynamoDB as the primary data store and adding OpenSearch as a search layer minimizes architectural changes.






Search Performance



OpenSearch is purpose-built for search: full-text search, faceted search, geo search, scoring — all things Aurora's LIKE clause or full-text indexes can't match. For complex real estate search with many filter combinations, OpenSearch is the clear winner.






Architecture: DynamoDB Streams + Lambda + OpenSearch



The architecture is straightforward:




[DynamoDB] → DynamoDB Streams → [Lambda] → [OpenSearch]

Search API reads from here









Data Flow





  1. Write: App writes to DynamoDB as before (Amplify Data models unchanged)


  2. Sync: DynamoDB Streams detects changes and triggers a Lambda function


  3. Index: Lambda upserts/deletes documents in OpenSearch


  4. Search: Search API queries OpenSearch and returns results



The key benefit: zero changes to the existing write path. DynamoDB CRUD goes through Amplify's GraphQL API as always — only search reads from OpenSearch.






Sync Lambda (Conceptual)






// DynamoDB Streams → Lambda → OpenSearch
export const handler = async (event: DynamoDBStreamEvent) => {
for (const record of event.Records) {
const tableName = extractTableName(record.eventSourceARN);
const indexName = tableToIndex(tableName); // Table name → index name mapping

switch (record.eventName) {
case 'INSERT':
case 'MODIFY':
await opensearchClient.index({
index: indexName,
id: record.dynamodb.Keys.id.S,
body: unmarshall(record.dynamodb.NewImage),
});
break;
case 'REMOVE':
await opensearchClient.delete({
index: indexName,
id: record.dynamodb.Keys.id.S,
});
break;
}
}
};









Search Query: Before / After



Before: DynamoDB (multi-condition filtering)




// DynamoDB: Partition key + FilterExpression workaround
const result = await dynamoClient.query({
TableName: 'Properties',
KeyConditionExpression: '#area = :area',
FilterExpression: '#price BETWEEN :minPrice AND :maxPrice AND #size >= :minSize',
ExpressionAttributeValues: { ':area': 'Shibuya', ':minPrice': 5000000, ':maxPrice': 10000000, ':minSize': 50 },
// → Fetches 1,000 records, filters down to 10. Inefficient.
});






After: OpenSearch (same conditions + full-text search + sort)




// OpenSearch: Combine any conditions + full-text + sort in one query
const result = await opensearchClient.search({
index: 'projectA-prod-properties',
body: {
query: {
bool: {
must: [
{ match: { description: 'pet-friendly office' } }, // Full-text search
{ term: { area: 'Shibuya' } },
{ range: { price: { gte: 5000000, lte: 10000000 } } },
{ range: { size: { gte: 50 } } },
],
},
},
sort: [{ price: 'asc' }], // Sort by any field
from: 0, size: 20, // Pagination
},
});
// → Returns exactly 20 matching records. 100–200ms response time.






Full-text search + multi-condition filtering + sorting + pagination — all in one query. This was impossible with DynamoDB alone.






The Gotcha: Japanese Full-Text Search Doesn't Work by Default



The first thing that bit us after adding OpenSearch: Japanese full-text search returned zero results.



Searching for "Shibuya office" returned nothing. The root cause: OpenSearch's default analyzer (standard analyzer) doesn't support Japanese morphological analysis. "渋谷オフィス" (Shibuya Office) was treated as a single token, so partial matches failed.



The fix: configure ICU analyzer + kuromoji tokenizer when creating the index.




{
"settings": {
"analysis": {
"analyzer": {
"japanese_analyzer": {
"type": "custom",
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_baseform", "lowercase"]
}
}
}
},
"mappings": {
"properties": {
"description": { "type": "text", "analyzer": "japanese_analyzer" }
}
}
}






The moment we added this config, Japanese keyword search started working correctly. OpenSearch's Japanese search doesn't work out of the box — not knowing this was our biggest time waste.




Note for English-language deployments: If your data is English-only, the default standard analyzer works fine. The kuromoji tokenizer is specifically needed for CJK (Chinese/Japanese/Korean) text segmentation.







Operational Considerations



DynamoDB Streams + Lambda sync comes with a few operational caveats:





  • Sync latency: Streams → Lambda → OpenSearch propagation typically takes a few hundred ms to a few seconds. For screens requiring real-time data, add a fallback that reads directly from DynamoDB immediately after writes


  • Lambda error retries: If the Streams-triggered Lambda fails repeatedly, records can expire and be lost. Set up a DLQ (Dead Letter Queue) to catch failures


  • Index rebuilds: When you change OpenSearch mappings, existing data needs re-indexing. Keep a script ready that does a full DynamoDB Scan → OpenSearch Bulk Insert






Multi-Tenancy Design: 1 Instance × Index Isolation



Search was solved. Next: multi-tenancy — how to safely isolate multiple products × multiple environments while keeping costs down.






Solution: Share One Instance, Isolate by Index



Instead of creating an OpenSearch instance per environment, we share a single instance across all environments and isolate by index name.




OpenSearch Domain (1 instance)
├── projectA-stg-properties ← Product A staging properties
├── projectA-stg-sections ← Product A staging lots
├── projectA-prod-properties ← Product A prod properties
├── projectA-prod-sections ← Product A prod lots
├── projectB-stg-properties ← Product B staging properties
├── projectB-prod-properties ← Product B prod properties
└── sandbox-{user}-properties ← Sandbox (only when needed)






Key decisions:





  • No OpenSearch resources for sandbox — In CDK/Amplify backend definitions, skip OpenSearch resource creation for sandbox environments


  • Sandbox Lambda points to shared dev OpenSearch — Inject the OpenSearch endpoint and index name via environment variables. Sandbox either uses the stg instance or falls back to direct DynamoDB reads


  • Index names include project and environment{project}-{env}-{entity} naming convention prevents collisions



This approach keeps OpenSearch instances at the absolute minimum (1 shared) while safely isolating data across multiple products and environments. What would have been $432/month with 16 instances is now $27/month with one.






What We Gained






Search






































Before (DynamoDB only) After (DynamoDB + OpenSearch)
Filtering GSI + FilterExpression (2–3 conditions max) Combine any number of conditions
Full-text search Not possible Japanese morphological analysis supported
Sorting Sort key dependent (needs GSI) Sort by any field
Pagination Cursor-based only from/size for direct page jumps
Response time Scan could take seconds Complex queries in 100–200ms





Multi-Tenancy

































Before (separate instances) After (1 instance × index isolation)
Instances 1 per product × environment 1 total
Monthly cost Up to $432/month (16 instances) $27/month (1 instance)
Sandbox startup Wait for OpenSearch boot (minutes) Skip (uses shared stg instance)
Data isolation Instance-level Index naming convention (logical)





Getting Started: 5 Steps





  1. Create an OpenSearch domain — t3.small.search, single instance. ~$27/month


  2. Create indexes with Japanese analyzer — Configure kuromoji tokenizer. Skip this and Japanese search won't work


  3. Enable DynamoDB Streams — Set NEW_AND_OLD_IMAGES on target tables


  4. Deploy sync Lambda — Streams-triggered Lambda that upserts/deletes to OpenSearch. Don't forget the DLQ


  5. Add a search API — Query OpenSearch from a new endpoint. Keep existing DynamoDB CRUD APIs untouched



When your GSI count exceeds 5, or your FilterExpression hit rate drops below 10% — that's when to consider adding OpenSearch.






Wrapping Up



We were ready to migrate to Aurora. We had the estimate. But in the end, we didn't need to abandon DynamoDB.



One OpenSearch instance solved both search and multi-tenancy. Writes go to DynamoDB, search reads from OpenSearch. DynamoDB Streams + Lambda keeps them in sync. Index naming ({project}-{env}-{entity}) consolidates multiple products and environments into a single instance. Amplify Gen2's DynamoDB-centric developer experience stays intact, and search capabilities scale independently.



The beauty of this "patch the weakness" approach: you don't have to rewrite your architecture all at once. Start with DynamoDB. When search requirements grow, add OpenSearch. When products multiply, add indexes. At $27/month, there was no reason to migrate to Aurora.






When NOT to Use This Approach



This pattern isn't universal. Consider a full RDB migration if you need complex relational queries across entities, ACID transactions spanning multiple tables, or your search requirements involve heavy aggregations that OpenSearch alone can't serve back to the write path. If your product is SQL-first from the start, don't force DynamoDB into the picture just to save a few dollars.



But if you're already running DynamoDB and your pain is search — try patching the weakness with OpenSearch before rewriting everything. The best architecture isn't always the newest one. Sometimes it's the cheapest complement to what you already have.






References



Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How I Solved DynamoDB + Amplify Search Problem with OpenSearch for $27/Month Instead of Migrating to Aurora

Thematisch verwandte Begriffe: Solved, DynamoDB, Amplify, Search · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94426 | A vulnerability was determined in xuxueli xxl-job up to 3.5.0. The impac…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick