diff --git a/docs/architecture/00-baseline/README.md b/docs/architecture/00-baseline/README.md index 64ae9ca..915335b 100644 --- a/docs/architecture/00-baseline/README.md +++ b/docs/architecture/00-baseline/README.md @@ -8,7 +8,7 @@ A URL shortener accepts a long URL and returns a short, human-shareable alias that redirects to the original. The system's primary job is redirection — it is not a metadata store, analytics platform, or user management system. Redirects are perceived as part of normal page navigation; any latency is user-visible. The system must therefore be optimised heavily for the read (redirect) path, while keeping the write (create) path simple and correct. -The deployment target is `us-east-1` (single region). The API and short URL redirects are served at `go.buffden.com` (ALB → EC2 → Spring Boot); the Angular SPA is hosted at `tinyurl.buffden.com` (S3 + CloudFront). v1 establishes a production-minimal baseline. v2 evolves it to handle higher scale and reliability requirements without changing the deployment model. +The deployment target is `us-east-1` (single region). The API and short URL redirects are served at `go.buffden.com` (EC2 → Spring Boot); the Angular SPA is hosted at `tinyurl.buffden.com` (S3 + CloudFront). v1 establishes a production-minimal baseline. v2 evolves it to handle higher scale and reliability requirements without changing the deployment model. --- @@ -102,7 +102,7 @@ These decisions are locked before v1 implementation begins. Changing them requir Goal: ship the smallest correct system. -- Components: DNS → Load balancer → Nginx → App servers → PostgreSQL +- Components: DNS → Nginx → App servers → PostgreSQL - No cache required in v1; DB handles up to 5K QPS peak - Rate limiting at Nginx level (coarse per-IP throttle on `POST /api/urls`) - No soft delete required (optional `is_deleted` field, deferred to v2) diff --git a/docs/architecture/00-baseline/adr/ADR-002-scaling-approach.md b/docs/architecture/00-baseline/adr/ADR-002-scaling-approach.md index 8453bf0..7dd720c 100644 --- a/docs/architecture/00-baseline/adr/ADR-002-scaling-approach.md +++ b/docs/architecture/00-baseline/adr/ADR-002-scaling-approach.md @@ -19,7 +19,7 @@ The system is read-heavy (99:1 read/write ratio in v1, 95:5 in v2). The redirect ## Decision -**v1**: Scale application servers **horizontally** behind a load balancer. Keep the database as a **single PostgreSQL primary** (vertical scaling + indexing). No cache required. +**v1**: Scale application servers **horizontally** behind Nginx. Keep the database as a **single PostgreSQL primary** (vertical scaling + indexing). No cache required. **v2**: Add **Redis cache-aside** on the redirect path (> 90% cache hit target) to absorb the 20K QPS peak without overloading the DB. Enable app autoscaling on CPU + P95 latency. Add a DB read replica only if cache miss pressure on the primary becomes measurable. @@ -28,7 +28,7 @@ The system is read-heavy (99:1 read/write ratio in v1, 95:5 in v2). The redirect ## Consequences - Application instances must be **fully stateless** — no in-process session or shared memory. -- Load balancer and health checks are required infrastructure from v1. +- Nginx and health checks are required infrastructure from v1. - The trigger for adding read replicas is a **measured metric**, not an upfront decision. - v2 introduces Redis as a new operational failure domain; the redirect path must degrade gracefully when Redis is unavailable (fall back to DB, apply stricter rate limiting). diff --git a/docs/architecture/00-baseline/poc-requirements-baseline.md b/docs/architecture/00-baseline/poc-requirements-baseline.md index 08976a6..038a370 100644 --- a/docs/architecture/00-baseline/poc-requirements-baseline.md +++ b/docs/architecture/00-baseline/poc-requirements-baseline.md @@ -80,7 +80,7 @@ Version 1: - Client (browser). - DNS Resolution. -- Load balancer (L4 or L7). +- Nginx (TLS termination + reverse proxy). - Stateless application servers. - Storage system (short -> original mapping) SQL. - Optional in-memory cache for hot keys. @@ -153,7 +153,7 @@ Reason: - Read-heavy workload (80/20). - Stateless redirect logic. -- App instances can scale independently behind load balancer. +- App instances can scale independently behind Nginx. - DB initially vertically scaled, later read-replicated if needed. Avoid premature sharding at this stage. @@ -166,8 +166,7 @@ Avoid premature sharding at this stage. | ------------- | ------------------ | ---------------------- | ---------------------------- | | DB | Slow query | Redirect latency spike | Proper indexing | | DB | Down | All redirects fail | Replication + failover | -| App | Crash | Partial 5xx errors | Multiple instances behind LB | -| Load Balancer | Misroute | Partial outage | Health checks | +| App | Crash | Partial 5xx errors | Multiple instances, health checks | | DNS | Resolution failure | Full outage | DNS caching + low TTL | | Cache | Miss storm | DB overload | Rate limiting + TTL | diff --git a/docs/architecture/00-baseline/v1/url-shortener-v1-hld.excalidraw b/docs/architecture/00-baseline/v1/url-shortener-v1-hld.excalidraw index 8b0bf6a..4176f34 100644 --- a/docs/architecture/00-baseline/v1/url-shortener-v1-hld.excalidraw +++ b/docs/architecture/00-baseline/v1/url-shortener-v1-hld.excalidraw @@ -8,7 +8,7 @@ "type": "text", "index": "a0", "frameId": null, - "x": 56.13725536494985, + "x": 55.87163036494985, "y": -108.39478546066607, "width": 1100, "height": 42, @@ -23,11 +23,11 @@ "groupIds": [], "roundness": null, "seed": 2001, - "version": 58, - "versionNonce": 1383285800, + "version": 59, + "versionNonce": 1106613006, "isDeleted": false, "boundElements": [], - "updated": 1774768960966, + "updated": 1783315633907, "link": null, "locked": false, "text": "URL Shortener v1 — High Level Design (Single Region)", @@ -46,8 +46,8 @@ "type": "rectangle", "index": "a7", "frameId": null, - "x": 143.3891868352821, - "y": 13.80902514497916, + "x": 143.0634872971243, + "y": 13.947970419539615, "width": 1048.395135198751, "height": 799.7149468065028, "angle": 0, @@ -63,11 +63,11 @@ "type": 3 }, "seed": 2008, - "version": 690, - "versionNonce": 1469225578, + "version": 704, + "versionNonce": 2064185422, "isDeleted": false, "boundElements": [], - "updated": 1773362659051, + "updated": 1783315677384, "link": null, "locked": false }, @@ -76,7 +76,7 @@ "type": "text", "index": "a8", "frameId": null, - "x": 162.1497292109566, + "x": 161.8841042109566, "y": 32.47990562037908, "width": 264.6397705078125, "height": 25.454545454545453, @@ -91,8 +91,8 @@ "groupIds": [], "roundness": null, "seed": 2009, - "version": 72, - "versionNonce": 1841621800, + "version": 73, + "versionNonce": 435857294, "isDeleted": false, "boundElements": [ { @@ -100,7 +100,7 @@ "type": "arrow" } ], - "updated": 1774769115761, + "updated": 1783315633907, "link": null, "locked": false, "text": "AWS Region (Single Region)", @@ -114,169 +114,15 @@ "autoResize": true, "lineHeight": 1.2727272727272727 }, - { - "id": "pkg-entry", - "type": "rectangle", - "index": "a9", - "frameId": null, - "x": 270.01, - "y": 74.94015624999997, - "width": 282.26953124999994, - "height": 186.83984375000003, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "#d3f9d8", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 2010, - "version": 27, - "versionNonce": 690928424, - "isDeleted": false, - "boundElements": [ - { - "id": "pkj-bXNKUJ5JkXZEZFy7Q", - "type": "arrow" - }, - { - "id": "qUlTJfuzogxBPf8rzjKsn", - "type": "arrow" - } - ], - "updated": 1774769090197, - "link": null, - "locked": false - }, - { - "id": "entry-label", - "type": "text", - "index": "aA", - "frameId": null, - "x": 286.21484375, - "y": 84.90411931818181, - "width": 91.61593627929688, - "height": 20.363636363636363, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 2011, - "version": 38, - "versionNonce": 1065395285, - "isDeleted": false, - "boundElements": [], - "updated": 1774395443060, - "link": null, - "locked": false, - "text": "Entry Layer", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 11, - "containerId": null, - "originalText": "Entry Layer", - "autoResize": true, - "lineHeight": 1.2727272727272727 - }, - { - "id": "lb", - "type": "rectangle", - "index": "aB", - "frameId": null, - "x": 291, - "y": 113.60546875, - "width": 240.83203125, - "height": 122.39453125, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 2012, - "version": 30, - "versionNonce": 1622554389, - "isDeleted": false, - "boundElements": [ - { - "id": "KPSeq5ElsFWNwCGxW1Dk2", - "type": "arrow" - }, - { - "id": "zIf3jPvo0-VCxKVrFzgjh", - "type": "arrow" - } - ], - "updated": 1774395418521, - "link": null, - "locked": false - }, - { - "id": "lb-label", - "type": "text", - "index": "aC", - "frameId": null, - "x": 314.4833068847656, - "y": 133.4044744318182, - "width": 188.09588623046875, - "height": 101.81818181818181, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 2013, - "version": 99, - "versionNonce": 700368821, - "isDeleted": false, - "boundElements": [], - "updated": 1774395450457, - "link": null, - "locked": false, - "text": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "top", - "baseline": 12, - "containerId": null, - "originalText": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "autoResize": true, - "lineHeight": 1.2727272727272727 - }, { "id": "pkg-apptier", "type": "rectangle", "index": "aD", "frameId": null, - "x": 666.78125, - "y": 117, + "x": 666.390625, + "y": 48.69140624999994, "width": 300, - "height": 340, + "height": 408.30859375000006, "angle": 0, "strokeColor": "#e67700", "backgroundColor": "#fff9db", @@ -290,8 +136,8 @@ "type": 3 }, "seed": 2014, - "version": 69, - "versionNonce": 1573108702, + "version": 83, + "versionNonce": 1817164174, "isDeleted": false, "boundElements": [ { @@ -307,7 +153,7 @@ "type": "arrow" } ], - "updated": 1772922154959, + "updated": 1783315633909, "link": null, "locked": false }, @@ -316,8 +162,8 @@ "type": "text", "index": "aE", "frameId": null, - "x": 682, - "y": 121.81818181818181, + "x": 679.59765625, + "y": 59.91974431818181, "width": 121.56791687011719, "height": 20.363636363636363, "angle": 0, @@ -331,11 +177,11 @@ "groupIds": [], "roundness": null, "seed": 2015, - "version": 32, - "versionNonce": 1719919431, + "version": 79, + "versionNonce": 628971470, "isDeleted": false, "boundElements": [], - "updated": 1773127229309, + "updated": 1783315633909, "link": null, "locked": false, "text": "Application Tier", @@ -354,10 +200,10 @@ "type": "rectangle", "index": "aF", "frameId": null, - "x": 686.765625, - "y": 147.78125, + "x": 686.5, + "y": 94.15625, "width": 260, - "height": 88, + "height": 137.64453125, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", @@ -371,8 +217,8 @@ "type": 3 }, "seed": 2016, - "version": 43, - "versionNonce": 1489978242, + "version": 180, + "versionNonce": 961516750, "isDeleted": false, "boundElements": [ { @@ -384,71 +230,65 @@ "type": "arrow" }, { - "id": "s5tJdSVnNVQCYvd5G_Mke", + "type": "text", + "id": "2DDKQQKb7pZN6-A3nGJAM" + }, + { + "id": "qUlTJfuzogxBPf8rzjKsn", "type": "arrow" }, { - "id": "8SQdDMAXZ3-rxe_G9tuQ4", + "id": "Efn73IUgAM4JpqBCJtgSW", "type": "arrow" } ], - "updated": 1772922023213, + "updated": 1783315687618, "link": null, "locked": false }, { - "id": "nginx-label", + "id": "2DDKQQKb7pZN6-A3nGJAM", "type": "text", - "index": "aG", - "frameId": null, - "x": 687.8557738606532, - "y": 160.06510660347095, - "width": 255.45161735569502, - "height": 61.09090909090909, + "x": 710.444091796875, + "y": 112.978515625, + "width": 212.11181640625, + "height": 100, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, + "fillStyle": "solid", + "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], + "frameId": null, + "index": "aFV", "roundness": null, - "seed": 2017, - "version": 106, - "versionNonce": 237448440, + "seed": 1938164562, + "version": 82, + "versionNonce": 164200338, "isDeleted": false, - "boundElements": [ - { - "id": "zIf3jPvo0-VCxKVrFzgjh", - "type": "arrow" - }, - { - "id": "KPSeq5ElsFWNwCGxW1Dk2", - "type": "arrow" - } - ], - "updated": 1773443532213, + "boundElements": null, + "updated": 1783315655321, "link": null, "locked": false, - "text": "Nginx\n(TLS Termination + Reverse\nProxy + Rate Limiting)", + "text": "Nginx (EC2)\n(TLS :443\nHTTP→HTTPS redirect + \nTLS Termination + Reverse\nProxy + Rate Limiting)", "fontSize": 16, "fontFamily": 5, "textAlign": "center", - "verticalAlign": "top", - "baseline": 12, - "containerId": null, - "originalText": "Nginx\n(TLS Termination + Reverse Proxy + Rate Limiting)", - "autoResize": false, - "lineHeight": 1.2727272727272727 + "verticalAlign": "middle", + "containerId": "nginx", + "originalText": "Nginx (EC2)\n(TLS :443\nHTTP→HTTPS redirect + \nTLS Termination + Reverse Proxy + Rate Limiting)", + "autoResize": true, + "lineHeight": 1.25 }, { "id": "app", "type": "rectangle", "index": "aH", "frameId": null, - "x": 687, + "x": 686.734375, "y": 299, "width": 260, "height": 110, @@ -465,8 +305,8 @@ "type": 3 }, "seed": 2018, - "version": 73, - "versionNonce": 807780138, + "version": 74, + "versionNonce": 1239856910, "isDeleted": false, "boundElements": [ { @@ -502,7 +342,7 @@ "type": "arrow" } ], - "updated": 1773362634202, + "updated": 1783315633909, "link": null, "locked": false }, @@ -511,7 +351,7 @@ "type": "text", "index": "aI", "frameId": null, - "x": 729.4240570068359, + "x": 729.1584320068359, "y": 331.53733290399896, "width": 175.15188598632812, "height": 40.72727272727273, @@ -526,11 +366,11 @@ "groupIds": [], "roundness": null, "seed": 2019, - "version": 77, - "versionNonce": 1886672519, + "version": 78, + "versionNonce": 2089023310, "isDeleted": false, "boundElements": [], - "updated": 1773127244211, + "updated": 1783315633911, "link": null, "locked": false, "text": "URL Shortener App\n(Stateless Instances)", @@ -549,7 +389,7 @@ "type": "rectangle", "index": "aN", "frameId": null, - "x": 279.5323946173464, + "x": 279.2667696173464, "y": 576.873303262185, "width": 805.7500000000002, "height": 180, @@ -566,8 +406,8 @@ "type": 3 }, "seed": 2020, - "version": 157, - "versionNonce": 1329962270, + "version": 158, + "versionNonce": 669244814, "isDeleted": false, "boundElements": [ { @@ -587,7 +427,7 @@ "type": "arrow" } ], - "updated": 1772922173968, + "updated": 1783315633911, "link": null, "locked": false }, @@ -596,7 +436,7 @@ "type": "text", "index": "aO", "frameId": null, - "x": 299.0245821173464, + "x": 298.7589571173464, "y": 586.1563288303669, "width": 82.86396789550781, "height": 20.363636363636363, @@ -611,11 +451,11 @@ "groupIds": [], "roundness": null, "seed": 2021, - "version": 112, - "versionNonce": 1845296165, + "version": 113, + "versionNonce": 1992402126, "isDeleted": false, "boundElements": [], - "updated": 1773271004645, + "updated": 1783315633913, "link": null, "locked": false, "text": "Data Tier", @@ -634,7 +474,7 @@ "type": "rectangle", "index": "aP", "frameId": null, - "x": 299.5011446173464, + "x": 299.2355196173464, "y": 613.556897012185, "width": 740.7695312500001, "height": 110, @@ -651,11 +491,11 @@ "type": 3 }, "seed": 2022, - "version": 188, - "versionNonce": 769468162, + "version": 189, + "versionNonce": 957340430, "isDeleted": false, "boundElements": [], - "updated": 1772921997951, + "updated": 1783315633913, "link": null, "locked": false }, @@ -664,7 +504,7 @@ "type": "text", "index": "aQ", "frameId": null, - "x": 646.5879189714748, + "x": 646.3222939714748, "y": 648.9307025913365, "width": 91.40794372558594, "height": 40.72727272727273, @@ -679,11 +519,11 @@ "groupIds": [], "roundness": null, "seed": 2023, - "version": 194, - "versionNonce": 1938392391, + "version": 195, + "versionNonce": 2025670990, "isDeleted": false, "boundElements": [], - "updated": 1773127250366, + "updated": 1783315633913, "link": null, "locked": false, "text": "PostgreSQL\nPrimary DB", @@ -697,114 +537,15 @@ "autoResize": true, "lineHeight": 1.2727272727272727 }, - { - "id": "zIf3jPvo0-VCxKVrFzgjh", - "type": "arrow", - "index": "aYO", - "frameId": null, - "x": 535.0888338799892, - "y": 168.0578972925508, - "width": 150.20814387074972, - "height": 0.06178191097407648, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 469727783, - "version": 860, - "versionNonce": 536006709, - "isDeleted": false, - "boundElements": [ - { - "id": "_ko5bRs8jfc2pZJlV_7nB", - "type": "text" - } - ], - "updated": 1774395429495, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 150.20814387074972, - 0.06178191097407648 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "lb", - "focus": -0.11095609035478331, - "gap": 3.256802629989238 - }, - "endBinding": { - "elementId": "nginx-label", - "focus": 0.7332931113680785, - "gap": 2.558796109914283 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - } - }, - { - "id": "_ko5bRs8jfc2pZJlV_7nB", - "type": "text", - "x": 514.5335585338321, - "y": -31.826248529170094, - "width": 40.511962890625, - "height": 20, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYS", - "roundness": null, - "seed": 1782574505, - "version": 122, - "versionNonce": 99872423, - "isDeleted": false, - "boundElements": [], - "updated": 1772920784826, - "link": null, - "locked": false, - "text": "Write", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "zIf3jPvo0-VCxKVrFzgjh", - "originalText": "Write", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "s5tJdSVnNVQCYvd5G_Mke", "type": "arrow", "index": "aYe", "frameId": null, - "x": 766.7545440185462, - "y": 236.49825245108954, - "width": 3.200128374480755, - "height": 61.796158558025496, + "x": 767.1386379616014, + "y": 236.83218749267266, + "width": 2.894430579527125, + "height": 61.46222351644238, "angle": 0, "strokeColor": "#1971c2", "backgroundColor": "transparent", @@ -816,8 +557,8 @@ "groupIds": [], "roundness": null, "seed": 137760798, - "version": 572, - "versionNonce": 1126923434, + "version": 639, + "versionNonce": 1289397586, "isDeleted": false, "boundElements": [ { @@ -825,7 +566,7 @@ "type": "text" } ], - "updated": 1773362634204, + "updated": 1783315633985, "link": null, "locked": false, "points": [ @@ -834,20 +575,16 @@ 0 ], [ - 3.200128374480755, - 61.796158558025496 + 2.894430579527125, + 61.46222351644238 ] ], "lastCommittedPoint": null, - "startBinding": { - "elementId": "nginx", - "focus": 0.3955800939063361, - "gap": 2.1740674637514985 - }, + "startBinding": null, "endBinding": { "elementId": "app", - "focus": -0.33241402492339367, - "gap": 1.783087191888967 + "focus": -0.3324378675549369, + "gap": 1 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -900,7 +637,7 @@ "type": "arrow", "index": "aYf8", "frameId": null, - "x": 707.983677967105, + "x": 707.718052967105, "y": 411.2846466869674, "width": 367.16764436812997, "height": 158.26429737114364, @@ -915,8 +652,8 @@ "groupIds": [], "roundness": null, "seed": 891063682, - "version": 1001, - "versionNonce": 1528235242, + "version": 1004, + "versionNonce": 1708844046, "isDeleted": false, "boundElements": [ { @@ -924,7 +661,7 @@ "type": "text" } ], - "updated": 1773362634206, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1031,7 +768,7 @@ "type": "arrow", "index": "aYfGG", "frameId": null, - "x": 383.18481658698215, + "x": 382.91919158698215, "y": 568.9368659494141, "width": 353.901620800695, "height": 155.52842348566253, @@ -1046,8 +783,8 @@ "groupIds": [], "roundness": null, "seed": 245149122, - "version": 1532, - "versionNonce": 1551554474, + "version": 1535, + "versionNonce": 1228878414, "isDeleted": false, "boundElements": [ { @@ -1055,7 +792,7 @@ "type": "text" } ], - "updated": 1773362634207, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1192,7 +929,7 @@ "type": "arrow", "index": "aYfGd", "frameId": null, - "x": 863.5206758673465, + "x": 863.2550508673465, "y": 413.408459512185, "width": 186.56467414911992, "height": 162.2610584528777, @@ -1207,8 +944,8 @@ "groupIds": [], "roundness": null, "seed": 133503554, - "version": 2241, - "versionNonce": 913601834, + "version": 2244, + "versionNonce": 1333770382, "isDeleted": false, "boundElements": [ { @@ -1216,7 +953,7 @@ "type": "text" } ], - "updated": 1773362634208, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1342,7 +1079,7 @@ "type": "arrow", "index": "aYfGlG", "frameId": null, - "x": 978.6767970442601, + "x": 978.4111720442601, "y": 572.7727668077843, "width": 161.7767970442601, "height": 158.77276680778425, @@ -1357,8 +1094,8 @@ "groupIds": [], "roundness": null, "seed": 810605662, - "version": 1922, - "versionNonce": 257315434, + "version": 1925, + "versionNonce": 1079342798, "isDeleted": false, "boundElements": [ { @@ -1366,7 +1103,7 @@ "type": "text" } ], - "updated": 1773362634207, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1492,10 +1229,10 @@ "type": "arrow", "index": "aYfV", "frameId": null, - "x": 861.7640500208524, - "y": 236.10612799312023, - "width": 3.2156596821807852, - "height": 62.500800013525605, + "x": 862.4291741440902, + "y": 236.2619477256685, + "width": 2.6114642409901307, + "height": 62.344980280977325, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -1507,8 +1244,8 @@ "groupIds": [], "roundness": null, "seed": 1701937410, - "version": 611, - "versionNonce": 44717930, + "version": 678, + "versionNonce": 1865944722, "isDeleted": false, "boundElements": [ { @@ -1516,7 +1253,7 @@ "type": "text" } ], - "updated": 1773362634204, + "updated": 1783315633985, "link": null, "locked": false, "points": [ @@ -1525,19 +1262,15 @@ 0 ], [ - 3.2156596821807852, - 62.500800013525605 + 2.6114642409901307, + 62.344980280977325 ] ], "lastCommittedPoint": null, - "startBinding": { - "elementId": "nginx", - "focus": -0.322975115758264, - "gap": 1 - }, + "startBinding": null, "endBinding": { "elementId": "app", - "focus": 0.38266788650076083, + "focus": 0.3826536333688436, "gap": 1 }, "startArrowhead": null, @@ -1591,7 +1324,7 @@ "type": "arrow", "index": "aYh", "frameId": null, - "x": 680.5120299528818, + "x": 680.2464049528818, "y": 362.25692479847015, "width": 735.0409846803086, "height": 91.03274719949934, @@ -1606,8 +1339,8 @@ "groupIds": [], "roundness": null, "seed": 223273783, - "version": 2668, - "versionNonce": 2092487542, + "version": 2671, + "versionNonce": 1861470862, "isDeleted": false, "boundElements": [ { @@ -1615,7 +1348,7 @@ "type": "text" } ], - "updated": 1773362672712, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1722,7 +1455,7 @@ "type": "arrow", "index": "aYn", "frameId": null, - "x": 682.3806693125345, + "x": 682.1150443125345, "y": 315.388450531492, "width": 733.7623327358049, "height": 111.47581509558387, @@ -1737,8 +1470,8 @@ "groupIds": [], "roundness": null, "seed": 169942327, - "version": 1801, - "versionNonce": 1454878378, + "version": 1804, + "versionNonce": 997673166, "isDeleted": false, "boundElements": [ { @@ -1746,7 +1479,7 @@ "type": "text" } ], - "updated": 1773362674991, + "updated": 1783315633913, "link": null, "locked": false, "points": [ @@ -1836,111 +1569,12 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "KPSeq5ElsFWNwCGxW1Dk2", - "type": "arrow", - "index": "aYt", - "frameId": null, - "x": 535.9641022612054, - "y": 194.67295758527527, - "width": 149.85576680326676, - "height": 1.850855418376966, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 1014040551, - "version": 616, - "versionNonce": 1735757653, - "isDeleted": false, - "boundElements": [ - { - "id": "_xp-Vu8RBR2HV0bkVmDNJ", - "type": "text" - } - ], - "updated": 1774395425871, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 149.85576680326676, - 1.850855418376966 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "lb", - "focus": 0.2924476840263458, - "gap": 4.132071011205426 - }, - "endBinding": { - "elementId": "nginx-label", - "focus": -0.2339735062880956, - "gap": 2.0359047961810575 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - } - }, - { - "id": "_xp-Vu8RBR2HV0bkVmDNJ", - "type": "text", - "x": 496.9744607379588, - "y": -42.13218556966905, - "width": 39.26396179199219, - "height": 20, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYx", - "roundness": null, - "seed": 59480199, - "version": 115, - "versionNonce": 2124780743, - "isDeleted": false, - "boundElements": [], - "updated": 1772920698118, - "link": null, - "locked": false, - "text": "Read", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "KPSeq5ElsFWNwCGxW1Dk2", - "originalText": "Read", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "0MuCDv3LjWbbazdXTSmtA", "type": "ellipse", "index": "aZ", "frameId": null, - "x": -150.97820672314958, + "x": -151.24383172314958, "y": 414.891917582048, "width": 90, "height": 60, @@ -1957,8 +1591,8 @@ "type": 2 }, "seed": 2038078518, - "version": 1008, - "versionNonce": 1089093748, + "version": 1009, + "versionNonce": 1590392334, "isDeleted": false, "boundElements": [ { @@ -1978,7 +1612,7 @@ "type": "arrow" } ], - "updated": 1775609326266, + "updated": 1783315633913, "link": null, "locked": false }, @@ -1987,7 +1621,7 @@ "type": "text", "index": "aa", "frameId": null, - "x": -128.01799020906375, + "x": -128.28361520906375, "y": 432.17871414645157, "width": 44.43995666503906, "height": 25, @@ -2002,11 +1636,11 @@ "groupIds": [], "roundness": null, "seed": 1938138486, - "version": 958, - "versionNonce": 1532919082, + "version": 959, + "versionNonce": 978158670, "isDeleted": false, "boundElements": [], - "updated": 1773362669080, + "updated": 1783315633913, "link": null, "locked": false, "text": "User", @@ -2024,7 +1658,7 @@ "type": "rectangle", "index": "ab", "frameId": null, - "x": -260.7445252406754, + "x": -261.0101502406754, "y": -420.19633467870415, "width": 210, "height": 110, @@ -2041,8 +1675,8 @@ "type": 3 }, "seed": 568166070, - "version": 714, - "versionNonce": 1705155624, + "version": 715, + "versionNonce": 1062858062, "isDeleted": false, "boundElements": [ { @@ -2054,7 +1688,7 @@ "type": "arrow" } ], - "updated": 1774768897542, + "updated": 1783315633913, "link": null, "locked": false }, @@ -2063,7 +1697,7 @@ "type": "text", "index": "ac", "frameId": null, - "x": -250.7445252406754, + "x": -251.0101502406754, "y": -413.3781528605224, "width": 117.66392517089844, "height": 20.363636363636363, @@ -2078,11 +1712,11 @@ "groupIds": [], "roundness": null, "seed": 354893814, - "version": 722, - "versionNonce": 603435560, + "version": 723, + "versionNonce": 1351223310, "isDeleted": false, "boundElements": [], - "updated": 1774768897542, + "updated": 1783315633913, "link": null, "locked": false, "text": "Public Internet", @@ -2101,7 +1735,7 @@ "type": "ellipse", "index": "ad", "frameId": null, - "x": -242.7445252406754, + "x": -243.0101502406754, "y": -390.19633467870415, "width": 174, "height": 68, @@ -2118,8 +1752,8 @@ "type": 2 }, "seed": 23580982, - "version": 713, - "versionNonce": 563031336, + "version": 714, + "versionNonce": 1516885582, "isDeleted": false, "boundElements": [ { @@ -2127,7 +1761,7 @@ "type": "arrow" } ], - "updated": 1774768897542, + "updated": 1783315633913, "link": null, "locked": false }, @@ -2136,7 +1770,7 @@ "type": "arrow", "index": "af", "frameId": null, - "x": -220.66529003471368, + "x": -220.93091503471368, "y": -303.5077851544676, "width": 153.29535786023803, "height": 125.23422502347421, @@ -2151,8 +1785,8 @@ "groupIds": [], "roundness": null, "seed": 1036090294, - "version": 4179, - "versionNonce": 648276660, + "version": 4182, + "versionNonce": 2058216270, "isDeleted": false, "boundElements": [ { @@ -2160,7 +1794,7 @@ "type": "text" } ], - "updated": 1780887497358, + "updated": 1783315633915, "link": null, "locked": false, "points": [ @@ -2255,10 +1889,10 @@ "type": "arrow", "index": "agG", "frameId": null, - "x": -68.31548488645194, + "x": -68.58110988645194, "y": -58.347896271234035, - "width": 326.36303377401043, - "height": 179.08743718829965, + "width": 750.1554803773221, + "height": 188.66087302833137, "angle": 0, "strokeColor": "#f08c00", "backgroundColor": "transparent", @@ -2270,8 +1904,8 @@ "groupIds": [], "roundness": null, "seed": 1565690968, - "version": 4887, - "versionNonce": 1655437108, + "version": 5008, + "versionNonce": 667669650, "isDeleted": false, "boundElements": [ { @@ -2279,7 +1913,7 @@ "type": "text" } ], - "updated": 1780887497472, + "updated": 1783315676866, "link": null, "locked": false, "points": [ @@ -2297,11 +1931,11 @@ ], [ 116.15568655422243, - 179.08743718829965 + 188.66087302833137 ], [ - 326.36303377401043, - 179.08743718829965 + 750.1554803773221, + 188.66087302833137 ] ], "lastCommittedPoint": null, @@ -2315,10 +1949,10 @@ "gap": 0 }, "endBinding": { - "elementId": "pkg-entry", + "elementId": "nginx", "fixedPoint": [ - -0.042379533701236136, - 0.2451264342114695 + -0.0189447288812687, + 0.2626818982835347 ], "focus": 0, "gap": 0 @@ -2352,7 +1986,7 @@ ], "end": [ 116.15568655422243, - 179.08743718829965 + 188.66087302833137 ] } ], @@ -2401,7 +2035,7 @@ "type": "arrow", "index": "agd", "frameId": null, - "x": -198.19754093659753, + "x": -198.46316593659753, "y": -58.16019490533003, "width": 28.24434257472356, "height": 188.16026601866412, @@ -2416,8 +2050,8 @@ "groupIds": [], "roundness": null, "seed": 777376600, - "version": 5651, - "versionNonce": 330421556, + "version": 5655, + "versionNonce": 628959762, "isDeleted": false, "boundElements": [ { @@ -2425,7 +2059,7 @@ "type": "text" } ], - "updated": 1780888335799, + "updated": 1783315659746, "link": null, "locked": false, "points": [ @@ -2518,7 +2152,7 @@ { "id": "2ucAl8lxXg5KRjTLbWDaN", "type": "arrow", - "x": -30.893636335298027, + "x": -31.159261335298027, "y": 231.339180498005, "width": 80.08238122636898, "height": 43.5801848377794, @@ -2541,8 +2175,8 @@ 43.5801848377794 ] ], - "version": 3056, - "versionNonce": 453304152, + "version": 3059, + "versionNonce": 1718692878, "index": "ai", "isDeleted": false, "fillStyle": "solid", @@ -2558,7 +2192,7 @@ "id": "4hponeV5MF067RA8WQA2N" } ], - "updated": 1774769149314, + "updated": 1783315633914, "link": null, "locked": false, "startBinding": { @@ -2627,10 +2261,10 @@ { "id": "R4FiWveCwiAlLngPLjPwZ", "type": "arrow", - "x": -33.62314108521025, + "x": -33.88876608521025, "y": 156.18465581639524, - "width": 295.6453558568137, - "height": 0.3692872908414415, + "width": 715.3887660852102, + "height": 6.6938598086047705, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -2642,12 +2276,20 @@ 0 ], [ - 295.6453558568137, - 0.3692872908414415 + 357.6018109210064, + 0 + ], + [ + 357.6018109210064, + 6.6938598086047705 + ], + [ + 715.3887660852102, + 6.6938598086047705 ] ], - "version": 2884, - "versionNonce": 423881304, + "version": 2919, + "versionNonce": 445332238, "index": "ak", "isDeleted": false, "fillStyle": "solid", @@ -2663,7 +2305,7 @@ "id": "93ZGY3spN1_4IdKH46xJl" } ], - "updated": 1774769149313, + "updated": 1783315681413, "link": null, "locked": false, "startBinding": { @@ -2724,10 +2366,10 @@ { "id": "Efn73IUgAM4JpqBCJtgSW", "type": "arrow", - "x": -33.80828532840769, + "x": -34.07391032840769, "y": 192.90075889186778, - "width": 293.4898133942354, - "height": 5.991135182448062, + "width": 713.0521959241842, + "height": 3.6756831376517596, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -2739,20 +2381,20 @@ 0 ], [ - 143.24490669711773, + 357.78695516420385, 0 ], [ - 143.24490669711773, - 5.991135182448062 + 357.78695516420385, + 3.6756831376517596 ], [ - 293.4898133942354, - 5.991135182448062 + 713.0521959241842, + 3.6756831376517596 ] ], - "version": 2961, - "versionNonce": 111602008, + "version": 3026, + "versionNonce": 76495182, "index": "am", "isDeleted": false, "fillStyle": "solid", @@ -2768,7 +2410,7 @@ "type": "text" } ], - "updated": 1774769149315, + "updated": 1783315689380, "link": null, "locked": false, "startBinding": { @@ -2780,7 +2422,15 @@ "focus": 0, "gap": 0 }, - "endBinding": null, + "endBinding": { + "elementId": "nginx", + "fixedPoint": [ + -0.028929670785475117, + 0.7440919817111843 + ], + "focus": 0, + "gap": 0 + }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow", @@ -2831,7 +2481,7 @@ "type": "rectangle", "index": "ao", "frameId": null, - "x": -251.01383405159032, + "x": -251.27945905159032, "y": 135.2110086133341, "width": 212.2055487231827, "height": 115.57950055706735, @@ -2848,8 +2498,8 @@ "type": 3 }, "seed": 1674034934, - "version": 1111, - "versionNonce": 405934580, + "version": 1112, + "versionNonce": 219585038, "isDeleted": false, "boundElements": [ { @@ -2877,14 +2527,14 @@ "type": "arrow" } ], - "updated": 1775609326266, + "updated": 1783315633913, "link": null, "locked": false }, { "id": "o1WwkFWNsEshjEVMEIg_r", "type": "text", - "x": -235.1809876685146, + "x": -235.4466126685146, "y": 155.50075889186775, "width": 180.53985595703125, "height": 75, @@ -2901,11 +2551,11 @@ "index": "ap", "roundness": null, "seed": 1168142390, - "version": 1084, - "versionNonce": 1294253045, + "version": 1085, + "versionNonce": 999565390, "isDeleted": false, "boundElements": [], - "updated": 1774395476059, + "updated": 1783315633913, "link": null, "locked": false, "text": "CloudFront CDN\nEdge routing\ntinyurl.buffden.com", @@ -2923,7 +2573,7 @@ "type": "rectangle", "index": "aq", "frameId": null, - "x": -32.20177707636594, + "x": -32.46740207636594, "y": 279.91935646944705, "width": 162.94121655976784, "height": 75.05222611463006, @@ -2940,8 +2590,8 @@ "type": 3 }, "seed": 1674639734, - "version": 1086, - "versionNonce": 878262582, + "version": 1087, + "versionNonce": 1965766542, "isDeleted": false, "boundElements": [ { @@ -2953,14 +2603,14 @@ "type": "arrow" } ], - "updated": 1773362499698, + "updated": 1783315633914, "link": null, "locked": false }, { "id": "Zr1j5i4x4lrnpqWt4bWgz", "type": "text", - "x": -25.16309170907965, + "x": -25.42871670907965, "y": 297.44546952676205, "width": 148.8638458251953, "height": 40, @@ -2977,11 +2627,11 @@ "index": "ar", "roundness": null, "seed": 362699446, - "version": 546, - "versionNonce": 1921557110, + "version": 547, + "versionNonce": 1111037390, "isDeleted": false, "boundElements": [], - "updated": 1773362499698, + "updated": 1783315633914, "link": null, "locked": false, "text": "S3 Angular SPA\nstatic assets / UI", @@ -2999,7 +2649,7 @@ "type": "arrow", "index": "as", "frameId": null, - "x": -155.97820672314958, + "x": -156.24383172314958, "y": 444.791917582048, "width": 188.22948728596157, "height": 810.0882522607521, @@ -3014,8 +2664,8 @@ "groupIds": [], "roundness": null, "seed": 528812680, - "version": 7554, - "versionNonce": 38590248, + "version": 7557, + "versionNonce": 1875671630, "isDeleted": false, "boundElements": [ { @@ -3023,7 +2673,7 @@ "type": "text" } ], - "updated": 1774768897542, + "updated": 1783315633914, "link": null, "locked": false, "points": [ @@ -3149,7 +2799,7 @@ "type": "text", "index": "av", "frameId": null, - "x": -232.82645730854642, + "x": -233.09208230854642, "y": -377.3786549781124, "width": 152.1482391357422, "height": 35.919328098816536, @@ -3164,21 +2814,21 @@ "groupIds": [], "roundness": null, "seed": 1689498101, - "version": 861, - "versionNonce": 2027353804, + "version": 862, + "versionNonce": 1159987342, "isDeleted": false, "boundElements": [], - "updated": 1775609294605, + "updated": 1783315633914, "link": null, "locked": false, - "text": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "text": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "fontSize": 9.407443073499568, "fontFamily": 5, "textAlign": "center", "verticalAlign": "top", "baseline": 11, "containerId": null, - "originalText": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "originalText": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "autoResize": true, "lineHeight": 1.2727272727272727 }, @@ -3187,7 +2837,7 @@ "type": "rectangle", "index": "ay", "frameId": null, - "x": -227.329292070367, + "x": -227.594917070367, "y": -173.3545317219096, "width": 199.74500710497375, "height": 110, @@ -3204,8 +2854,8 @@ "type": 3 }, "seed": 1430654040, - "version": 1553, - "versionNonce": 968050612, + "version": 1554, + "versionNonce": 170966734, "isDeleted": false, "boundElements": [ { @@ -3225,14 +2875,14 @@ "type": "arrow" } ], - "updated": 1780887497357, + "updated": 1783315633914, "link": null, "locked": false }, { "id": "qKIIAMdmMGTIov9tNsvuj", "type": "text", - "x": -216.1467299241301, + "x": -216.4123549241301, "y": -168.3545317219096, "width": 177.3798828125, "height": 100, @@ -3249,11 +2899,11 @@ "index": "az", "roundness": null, "seed": 376619352, - "version": 338, - "versionNonce": 2027434292, + "version": 339, + "versionNonce": 59487502, "isDeleted": false, "boundElements": [], - "updated": 1780887497357, + "updated": 1783315633914, "link": null, "locked": false, "text": "Cloudflare\n(DDoS Absorption\nWAF Rate Limiting\nDNS Proxy)", diff --git a/docs/architecture/00-baseline/v1/url-shortener-v1-hld.puml b/docs/architecture/00-baseline/v1/url-shortener-v1-hld.puml index afc1a28..37f7dc9 100644 --- a/docs/architecture/00-baseline/v1/url-shortener-v1-hld.puml +++ b/docs/architecture/00-baseline/v1/url-shortener-v1-hld.puml @@ -35,10 +35,6 @@ package "Public Internet" #LightGray { ' ---------------- AWS REGION ---------------- package "AWS Region (Single Region)" #LightBlue { - package "Entry Layer" #LightGreen { - [Load Balancer\n(L4 / L7)] as LB - } - package "Application Tier" #LightYellow { [Nginx\n(TLS Termination + Reverse Proxy)] as NGINX [URL Shortener App\n(Stateless Instances)] as APP @@ -61,8 +57,7 @@ User --> DNS : Resolve \n domain ' ---------------- READ PATH (blue) ---------------- -User -[#4C9BE8]-> LB : [READ] GET /{code} -LB -[#4C9BE8]-> NGINX +User -[#4C9BE8]-> NGINX : [READ] GET /{code} NGINX -[#4C9BE8]-> APP APP -[#4C9BE8]-> DB : Lookup short_code DB -[#4C9BE8]-> APP : Return original_url @@ -70,8 +65,7 @@ APP -[#4C9BE8]-> User : HTTP 301/302 Redirect ' ---------------- WRITE PATH (red) ---------------- -User -[#FF0000]-> LB : [WRITE] POST /shorten -LB -[#FF0000]-> NGINX +User -[#FF0000]-> NGINX : [WRITE] POST /shorten NGINX -[#FF0000]-> APP APP -[#FF0000]-> DB : INSERT mapping DB -[#FF0000]-> APP : Confirm write diff --git a/docs/architecture/00-baseline/v1/url-shortener-v1.md b/docs/architecture/00-baseline/v1/url-shortener-v1.md index 3afe247..e33bbfe 100644 --- a/docs/architecture/00-baseline/v1/url-shortener-v1.md +++ b/docs/architecture/00-baseline/v1/url-shortener-v1.md @@ -96,8 +96,7 @@ Reason: keep v1 small, shippable, and focused on redirect correctness + low late ## 6) Minimum Components (What Must Exist) - Client (browser). -- DNS resolution (`go.buffden.com` → ALB for API/redirects; `tinyurl.buffden.com` → CloudFront for Angular SPA). -- AWS Application Load Balancer (ALB) — TLS termination, health checks, HTTP→HTTPS redirect. +- DNS resolution (`go.buffden.com` → EC2 for API/redirects; `tinyurl.buffden.com` → CloudFront for Angular SPA). - Nginx (TLS termination + reverse proxy). - Stateless application servers (redirect + create logic). - Storage system (SQL) for `short_code → original_url` mapping. @@ -108,7 +107,7 @@ At 5K QPS peak, horizontal scaling is expected at the application layer. ### Read Path (Redirect) — v1 1. Client sends `GET /{short_code}`. -2. Load balancer routes to an app instance via Nginx. +2. Nginx routes to an app instance. 3. App looks up `short_code` in the primary DB. 4. If found and not expired → app returns HTTP 301/302 with `Location: original_url`. 5. If not found or expired → app returns 404/410. @@ -116,7 +115,7 @@ At 5K QPS peak, horizontal scaling is expected at the application layer. ### Write Path (Create) — v1 1. Client sends `POST /api/urls` with original URL and optional expiry. -2. Load balancer routes to an app instance via Nginx. +2. Nginx routes to an app instance. 3. App validates input and generates `short_code` (DB-backed sequence + Base62). 4. App writes mapping to the primary DB. 5. App returns the short URL to the client. @@ -196,7 +195,7 @@ Reason: - Read-heavy workload (80/20). - Redirect path is stateless (lookup + redirect). -- App instances can scale independently behind the load balancer. +- App instances can scale independently behind Nginx. - DB starts as a single primary (vertical scaling + tuning); introduce read replicas later if needed. Avoid premature sharding at this stage. @@ -209,8 +208,7 @@ Avoid premature sharding at this stage. | ------------- | ------------------- | ---------------------- | ---------- | | DB | Slow query / lock | Redirect latency spike | Proper indexing, query tuning, connection pool limits | | DB | Down | All redirects fail | Automated backups, failover plan (single-region), clear 503 response | -| App | Crash | Partial 5xx errors | Multiple instances behind LB, health checks | -| Load Balancer | Misroute / unhealthy | Partial outage | Health checks + autoscaling policies | +| App | Crash | Partial 5xx errors | Multiple instances, health checks | | DNS | Resolution failure | Full outage | Correct records + sensible TTL; rely on resolver caching | | Nginx/TLS | Cert expired | Full outage (HTTPS) | Automated certificate renewal (Let’s Encrypt) + monitoring | | Cache (if any)| Miss storm | DB overload | (v2) cache-aside + TTL + stampede protection | diff --git a/docs/architecture/00-baseline/v2/security-hardening-v2.md b/docs/architecture/00-baseline/v2/security-hardening-v2.md index 93c2cb8..c0577c9 100644 --- a/docs/architecture/00-baseline/v2/security-hardening-v2.md +++ b/docs/architecture/00-baseline/v2/security-hardening-v2.md @@ -55,7 +55,7 @@ ## 4. CloudWatch Anomaly Alerting (4xx / 5xx Rate Spikes) -**Why deferred:** CloudWatch alarms for infrastructure health exist (CPU, ALB 5xx). Application-level alerting on anomalous request patterns (sudden spike in 400s suggesting scanning, or 429s suggesting a rate-limit bypass attempt) requires a CloudWatch Metric Filter on the structured log stream and an alarm with a meaningful threshold. +**Why deferred:** CloudWatch alarms for infrastructure health exist (CPU, EC2 5xx). Application-level alerting on anomalous request patterns (sudden spike in 400s suggesting scanning, or 429s suggesting a rate-limit bypass attempt) requires a CloudWatch Metric Filter on the structured log stream and an alarm with a meaningful threshold. **Why it matters in v2:** v1 has observability (structured logs, Prometheus metrics) but no automated paging when attack patterns emerge. Without this, an ongoing abuse incident may go unnoticed until it impacts latency or DB load. diff --git a/docs/architecture/00-baseline/v2/url-shortener-v2-hld.excalidraw b/docs/architecture/00-baseline/v2/url-shortener-v2-hld.excalidraw index 38ba576..457043e 100644 --- a/docs/architecture/00-baseline/v2/url-shortener-v2-hld.excalidraw +++ b/docs/architecture/00-baseline/v2/url-shortener-v2-hld.excalidraw @@ -23,8 +23,8 @@ "groupIds": [], "roundness": null, "seed": 1044940512, - "version": 3303, - "versionNonce": 753890480, + "version": 3320, + "versionNonce": 1017055554, "isDeleted": false, "boundElements": [ { @@ -32,7 +32,7 @@ "type": "text" } ], - "updated": 1773443576692, + "updated": 1783315871188, "link": null, "locked": false, "points": [ @@ -199,8 +199,8 @@ "groupIds": [], "roundness": null, "seed": 962819872, - "version": 3066, - "versionNonce": 484849328, + "version": 3068, + "versionNonce": 814496898, "isDeleted": false, "boundElements": [ { @@ -208,7 +208,7 @@ "type": "text" } ], - "updated": 1773443576691, + "updated": 1783315857039, "link": null, "locked": false, "points": [ @@ -419,8 +419,8 @@ "type": 2 }, "seed": 1002, - "version": 651, - "versionNonce": 1036957048, + "version": 653, + "versionNonce": 1277972866, "isDeleted": false, "boundElements": [ { @@ -440,7 +440,7 @@ "type": "arrow" } ], - "updated": 1775609367496, + "updated": 1783315857029, "link": null, "locked": false }, @@ -486,7 +486,7 @@ "type": "rectangle", "index": "a7", "frameId": null, - "x": 8.886760822448082, + "x": 8.653375250077943, "y": -275.6136564345842, "width": 2053.3427131910744, "height": 1381.5507288501856, @@ -503,11 +503,11 @@ "type": 3 }, "seed": 1008, - "version": 962, - "versionNonce": 600849584, + "version": 965, + "versionNonce": 1707073822, "isDeleted": false, "boundElements": [], - "updated": 1773443655216, + "updated": 1783315848213, "link": null, "locked": false }, @@ -549,146 +549,6 @@ "autoResize": true, "lineHeight": 1.2727272727272727 }, - { - "id": "pkg-entry", - "type": "rectangle", - "index": "a9", - "frameId": null, - "x": 280.34538588827627, - "y": 108.98530799832434, - "width": 250, - "height": 150, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "#d3f9d8", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 1010, - "version": 58, - "versionNonce": 500482956, - "isDeleted": false, - "boundElements": [ - { - "id": "J1OCywa5y8qTtOz_FOLZi", - "type": "arrow" - }, - { - "id": "ch7kzIhHMnf3E5fOqA9Zd", - "type": "arrow" - }, - { - "id": "OdugGG_69b0BIaWzDnsV7", - "type": "arrow" - }, - { - "id": "7ZaWBnKuXnY4D5alcvXx4", - "type": "arrow" - } - ], - "updated": 1774769199701, - "link": null, - "locked": false - }, - { - "id": "entry-label", - "type": "text", - "index": "aA", - "frameId": null, - "x": 293.6562092774344, - "y": 115.49445498834572, - "width": 91.61593627929688, - "height": 20, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 1011, - "version": 24, - "versionNonce": 468821008, - "isDeleted": false, - "boundElements": [], - "updated": 1772951916798, - "link": null, - "locked": false, - "text": "Entry Layer", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 11, - "containerId": null, - "originalText": "Entry Layer", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lb", - "type": "rectangle", - "index": "aB", - "frameId": null, - "x": 300.1718953612828, - "y": 148.1960829485196, - "width": 200, - "height": 88, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 1012, - "version": 28, - "versionNonce": 1800120053, - "isDeleted": false, - "boundElements": [ - { - "id": "VW_q5qq8d6mleomTFUvVS", - "type": "arrow" - }, - { - "id": "OPQatKZ-8ikrw4jcJJNnz", - "type": "arrow" - }, - { - "id": "8l-lVTQDA58PVAydn9tXQ", - "type": "arrow" - }, - { - "id": "VMSkF3Ci9d8aXipypNEXC", - "type": "arrow" - }, - { - "id": "ouJJ4TViZk6O8i2EPCuKT", - "type": "arrow" - }, - { - "id": "OdugGG_69b0BIaWzDnsV7", - "type": "arrow" - } - ], - "updated": 1773362856008, - "link": null, - "locked": false - }, { "id": "apptier-label", "type": "text", @@ -728,17 +588,17 @@ "lineHeight": 1.2727272727272727 }, { - "id": "nginx", + "id": "ye0xLDOr0TTlo1Fm_VObn", "type": "rectangle", - "index": "aF", + "index": "aE8", "frameId": null, - "x": 714.4854726549383, - "y": 137.75841613754432, - "width": 306.45780492996596, - "height": 88, + "x": 667.0389983114593, + "y": 92.36278233346056, + "width": 383.85039476603356, + "height": 509.70383103683974, "angle": 0, "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", + "backgroundColor": "#ffec99", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", @@ -748,9 +608,9 @@ "roundness": { "type": 3 }, - "seed": 1016, - "version": 184, - "versionNonce": 1445521936, + "seed": 629543152, + "version": 489, + "versionNonce": 15751966, "isDeleted": false, "boundElements": [ { @@ -786,22 +646,22 @@ "type": "arrow" } ], - "updated": 1772951820970, + "updated": 1783315840537, "link": null, "locked": false }, { - "id": "ye0xLDOr0TTlo1Fm_VObn", + "id": "nginx", "type": "rectangle", - "index": "aFV", + "index": "aF", "frameId": null, - "x": 667.2960772439758, - "y": 92.36155353988181, - "width": 383.85039476603356, - "height": 509.70383103683974, + "x": 714.4854726549383, + "y": 137.75841613754432, + "width": 306.45780492996596, + "height": 182.65176955607407, "angle": 0, "strokeColor": "#1e1e1e", - "backgroundColor": "#ffec99", + "backgroundColor": "transparent", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", @@ -811,88 +671,74 @@ "roundness": { "type": 3 }, - "seed": 629543152, - "version": 472, - "versionNonce": 104961712, + "seed": 1016, + "version": 319, + "versionNonce": 924878494, "isDeleted": false, "boundElements": [ { "id": "J1OCywa5y8qTtOz_FOLZi", "type": "arrow" }, - { - "id": "VW_q5qq8d6mleomTFUvVS", - "type": "arrow" - }, - { - "id": "OPQatKZ-8ikrw4jcJJNnz", - "type": "arrow" - }, { "id": "auNfcll9F2jJQacjXB4Pk", "type": "arrow" }, { - "id": "8l-lVTQDA58PVAydn9tXQ", + "id": "pbZwmHyg51c_rYp0hvzqc", "type": "arrow" }, { - "id": "VMSkF3Ci9d8aXipypNEXC", - "type": "arrow" + "type": "text", + "id": "Mm6sfUB6NSNAGZxDM2SR4" }, { - "id": "ouJJ4TViZk6O8i2EPCuKT", + "id": "ch7kzIhHMnf3E5fOqA9Zd", "type": "arrow" }, { - "id": "pbZwmHyg51c_rYp0hvzqc", + "id": "OdugGG_69b0BIaWzDnsV7", "type": "arrow" } ], - "updated": 1773443576688, + "updated": 1783315869427, "link": null, "locked": false }, { - "id": "nginx-label", + "id": "Mm6sfUB6NSNAGZxDM2SR4", "type": "text", - "index": "aG", - "frameId": null, - "x": 744.2081482118525, - "y": 156.2426183809322, - "width": 242.62376403808594, - "height": 40, + "x": 746.1344343240229, + "y": 154.08430091558137, + "width": 243.15988159179688, + "height": 150, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, + "fillStyle": "solid", + "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], + "frameId": null, + "index": "aFG", "roundness": null, - "seed": 1017, - "version": 81, - "versionNonce": 2037853264, + "seed": 1561631426, + "version": 26, + "versionNonce": 727414594, "isDeleted": false, - "boundElements": [ - { - "id": "8l-lVTQDA58PVAydn9tXQ", - "type": "arrow" - } - ], - "updated": 1773443578690, + "boundElements": null, + "updated": 1783315851206, "link": null, "locked": false, - "text": "Nginx (TLS Termination + \nReverse Proxy + Rate Limiting)", - "fontSize": 16, + "text": "Nginx (EC2)\n(TLS :443\nHTTP→HTTPS redirect)\n(TLS Termination + \nReverse Proxy + Rate\nLimiting)", + "fontSize": 20, "fontFamily": 5, "textAlign": "center", - "verticalAlign": "top", - "baseline": 12, - "containerId": null, - "originalText": "Nginx (TLS Termination + \nReverse Proxy + Rate Limiting)", + "verticalAlign": "middle", + "containerId": "nginx", + "originalText": "Nginx (EC2)\n(TLS :443\nHTTP→HTTPS redirect)\n(TLS Termination + \nReverse Proxy + Rate Limiting)", "autoResize": true, "lineHeight": 1.25 }, @@ -918,8 +764,8 @@ "type": 3 }, "seed": 1018, - "version": 344, - "versionNonce": 60449360, + "version": 346, + "versionNonce": 1371282270, "isDeleted": false, "boundElements": [ { @@ -979,7 +825,7 @@ "type": "arrow" } ], - "updated": 1773443576688, + "updated": 1783315857029, "link": null, "locked": false }, @@ -1560,14 +1406,14 @@ "lineHeight": 1.2727272727272727 }, { - "id": "VW_q5qq8d6mleomTFUvVS", + "id": "pbZwmHyg51c_rYp0hvzqc", "type": "arrow", - "index": "aYK", + "index": "aYOO8", "frameId": null, - "x": 505.1418574252759, - "y": 203.75437764483718, - "width": 203.52305310013952, - "height": 0.5789713901349671, + "x": 819.3604787456209, + "y": 330.052938230122, + "width": 1.3675134957431965, + "height": 124.33520979455295, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -1578,17 +1424,17 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 171777760, - "version": 563, - "versionNonce": 1579097840, + "seed": 1972178720, + "version": 1613, + "versionNonce": 1971926046, "isDeleted": false, "boundElements": [ { - "id": "EAeOSgsClWyffHc_wL8-n", + "id": "wxsN7HDVrjK-pu8o_EGHh", "type": "text" } ], - "updated": 1772951906600, + "updated": 1783315857038, "link": null, "locked": false, "points": [ @@ -1597,28 +1443,36 @@ 0 ], [ - 203.52305310013952, - -0.5789713901349671 + 0, + 60.30289612466993 + ], + [ + -1.3675134957431965, + 60.30289612466993 + ], + [ + -1.3675134957431965, + 124.33520979455295 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "lb", + "elementId": "nginx", "fixedPoint": [ - 1.0248498103199655, - 0.6313442579126997 + 0.34221678940319156, + 1.0527930967213721 ], "focus": 0, - "gap": 0 + "gap": 1 }, "endBinding": { - "elementId": "nginx", - "focus": -0.25127711719543405, - "gap": 3.2273093567523574, + "elementId": "app", "fixedPoint": [ - -0.018993029499944213, - 0.7433748876949761 - ] + 0.29316594523705686, + -0.053757590829913875 + ], + "focus": 0, + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -1634,10 +1488,10 @@ "endIsSpecial": null }, { - "id": "EAeOSgsClWyffHc_wL8-n", + "id": "wxsN7HDVrjK-pu8o_EGHh", "type": "text", - "x": 593.1959103903632, - "y": 190.96211944394256, + "x": 649.1654958482977, + "y": 330.5299495767549, "width": 50.63995361328125, "height": 25, "angle": 0, @@ -1650,14 +1504,14 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "aYO", + "index": "aYOOG", "roundness": null, - "seed": 449888032, - "version": 100, - "versionNonce": 2047283728, + "seed": 1088877280, + "version": 124, + "versionNonce": 750681840, "isDeleted": false, "boundElements": [], - "updated": 1772951706772, + "updated": 1772951820970, "link": null, "locked": false, "text": "Write", @@ -1665,20 +1519,20 @@ "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "VW_q5qq8d6mleomTFUvVS", + "containerId": "pbZwmHyg51c_rYp0hvzqc", "originalText": "Write", "autoResize": true, "lineHeight": 1.25 }, { - "id": "OPQatKZ-8ikrw4jcJJNnz", + "id": "hkApFxacUDwjvTTA2JqV0", "type": "arrow", - "index": "aYO8", + "index": "aYOOK", "frameId": null, - "x": 505.1418574252759, - "y": 203.75437764483718, - "width": 203.52305310013952, - "height": 0.5789713901349671, + "x": 755.5941489042532, + "y": 573.2515704029574, + "width": 361.45585669338107, + "height": 243.40842967946924, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -1689,17 +1543,17 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 269085408, - "version": 771, - "versionNonce": 2092810480, + "seed": 1574277688, + "version": 1222, + "versionNonce": 7806430, "isDeleted": false, "boundElements": [ { - "id": "b6FrUbrf87wdbrPjMcF5w", + "id": "tEYquKnHuSawkcyQ1E2yC", "type": "text" } ], - "updated": 1772951906600, + "updated": 1783315857034, "link": null, "locked": false, "points": [ @@ -1708,28 +1562,36 @@ 0 ], [ - 203.52305310013952, - -0.5789713901349671 + 0, + 124.87672677788646 + ], + [ + -361.45585669338107, + 124.87672677788646 + ], + [ + -361.45585669338107, + 243.40842967946924 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "lb", + "elementId": "app", "fixedPoint": [ - 1.0248498103199655, - 0.6313442579126997 + 0.05317049775388577, + 1.0268189762453817 ], "focus": 0, "gap": 0 }, "endBinding": { - "elementId": "nginx", - "focus": -0.25127711719543405, - "gap": 3.2273093567523574, + "elementId": "pkg-data", "fixedPoint": [ - -0.018993029499944213, - 0.7433748876949761 - ] + 0.05752684510984113, + -0.05163950701830989 + ], + "focus": 0, + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -1745,11 +1607,11 @@ "endIsSpecial": null }, { - "id": "b6FrUbrf87wdbrPjMcF5w", + "id": "tEYquKnHuSawkcyQ1E2yC", "type": "text", - "x": 593.1959103903632, - "y": 190.96211944394256, - "width": 50.63995361328125, + "x": 493.49627121674234, + "y": 685.6282971808439, + "width": 162.73989868164062, "height": 25, "angle": 0, "strokeColor": "#e03131", @@ -1761,266 +1623,28 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "aYOG", + "index": "aYOOO", "roundness": null, - "seed": 1040626464, - "version": 105, - "versionNonce": 1530320624, + "seed": 1101909320, + "version": 146, + "versionNonce": 603619138, "isDeleted": false, "boundElements": [], - "updated": 1772951706772, + "updated": 1773279413566, "link": null, "locked": false, - "text": "Write", + "text": "INSERT mapping", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "OPQatKZ-8ikrw4jcJJNnz", - "originalText": "Write", + "containerId": "hkApFxacUDwjvTTA2JqV0", + "originalText": "INSERT mapping", "autoResize": true, "lineHeight": 1.25 }, { - "id": "pbZwmHyg51c_rYp0hvzqc", - "type": "arrow", - "index": "aYOO8", - "frameId": null, - "x": 819.3604787456209, - "y": 230.40420864902507, - "width": 1.3675134957431965, - "height": 223.98393937564987, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 1972178720, - "version": 1539, - "versionNonce": 34709584, - "isDeleted": false, - "boundElements": [ - { - "id": "wxsN7HDVrjK-pu8o_EGHh", - "type": "text" - } - ], - "updated": 1773443576691, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 112.62574092772985 - ], - [ - -1.3675134957431965, - 112.62574092772985 - ], - [ - -1.3675134957431965, - 223.98393937564987 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "nginx", - "fixedPoint": [ - 0.34221678940319156, - 1.0527930967213721 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "app", - "fixedPoint": [ - 0.29316594523705686, - -0.053757590829913875 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "wxsN7HDVrjK-pu8o_EGHh", - "type": "text", - "x": 649.1654958482977, - "y": 330.5299495767549, - "width": 50.63995361328125, - "height": 25, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYOOG", - "roundness": null, - "seed": 1088877280, - "version": 124, - "versionNonce": 750681840, - "isDeleted": false, - "boundElements": [], - "updated": 1772951820970, - "link": null, - "locked": false, - "text": "Write", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "pbZwmHyg51c_rYp0hvzqc", - "originalText": "Write", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "hkApFxacUDwjvTTA2JqV0", - "type": "arrow", - "index": "aYOOK", - "frameId": null, - "x": 755.5941489042532, - "y": 573.2515704029574, - "width": 361.45585669338107, - "height": 243.40842967946924, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 1574277688, - "version": 1221, - "versionNonce": 1080998480, - "isDeleted": false, - "boundElements": [ - { - "id": "tEYquKnHuSawkcyQ1E2yC", - "type": "text" - } - ], - "updated": 1773443576689, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 124.87672677788646 - ], - [ - -361.45585669338107, - 124.87672677788646 - ], - [ - -361.45585669338107, - 243.40842967946924 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "app", - "fixedPoint": [ - 0.05317049775388577, - 1.0268189762453817 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "pkg-data", - "fixedPoint": [ - 0.05752684510984113, - -0.05163950701830989 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "tEYquKnHuSawkcyQ1E2yC", - "type": "text", - "x": 493.49627121674234, - "y": 685.6282971808439, - "width": 162.73989868164062, - "height": 25, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYOOO", - "roundness": null, - "seed": 1101909320, - "version": 146, - "versionNonce": 603619138, - "isDeleted": false, - "boundElements": [], - "updated": 1773279413566, - "link": null, - "locked": false, - "text": "INSERT mapping", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "hkApFxacUDwjvTTA2JqV0", - "originalText": "INSERT mapping", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "__zMfcAsmoGYwn7wYRIFR", + "id": "__zMfcAsmoGYwn7wYRIFR", "type": "arrow", "index": "aYOOQ", "frameId": null, @@ -2039,8 +1663,8 @@ "groupIds": [], "roundness": null, "seed": 1443721784, - "version": 1433, - "versionNonce": 1564667984, + "version": 1434, + "versionNonce": 1842590238, "isDeleted": false, "boundElements": [ { @@ -2048,7 +1672,7 @@ "type": "text" } ], - "updated": 1773443576689, + "updated": 1783315857034, "link": null, "locked": false, "points": [ @@ -2170,8 +1794,8 @@ "groupIds": [], "roundness": null, "seed": 1957531192, - "version": 2068, - "versionNonce": 2116052560, + "version": 2069, + "versionNonce": 1662238302, "isDeleted": false, "boundElements": [ { @@ -2179,7 +1803,7 @@ "type": "text" } ], - "updated": 1773443576689, + "updated": 1783315857035, "link": null, "locked": false, "points": [ @@ -2316,8 +1940,8 @@ "groupIds": [], "roundness": null, "seed": 58593055, - "version": 3082, - "versionNonce": 609686096, + "version": 3083, + "versionNonce": 912408286, "isDeleted": false, "boundElements": [ { @@ -2325,7 +1949,7 @@ "type": "text" } ], - "updated": 1773443576690, + "updated": 1783315857036, "link": null, "locked": false, "points": [ @@ -2435,8 +2059,8 @@ "groupIds": [], "roundness": null, "seed": 1118313489, - "version": 5670, - "versionNonce": 366132816, + "version": 5671, + "versionNonce": 527081310, "isDeleted": false, "boundElements": [ { @@ -2444,7 +2068,7 @@ "type": "text" } ], - "updated": 1773443576690, + "updated": 1783315857037, "link": null, "locked": false, "points": [ @@ -2570,8 +2194,8 @@ "groupIds": [], "roundness": null, "seed": 1377771903, - "version": 6010, - "versionNonce": 1871082576, + "version": 6011, + "versionNonce": 903545758, "isDeleted": false, "boundElements": [ { @@ -2579,7 +2203,7 @@ "type": "text" } ], - "updated": 1773443576691, + "updated": 1783315857038, "link": null, "locked": false, "points": [ @@ -2716,8 +2340,8 @@ "groupIds": [], "roundness": null, "seed": 841747249, - "version": 3184, - "versionNonce": 1382041680, + "version": 3185, + "versionNonce": 1538437918, "isDeleted": false, "boundElements": [ { @@ -2725,7 +2349,7 @@ "type": "text" } ], - "updated": 1773443576690, + "updated": 1783315857037, "link": null, "locked": false, "points": [ @@ -2835,8 +2459,8 @@ "groupIds": [], "roundness": null, "seed": 1315260081, - "version": 3383, - "versionNonce": 1340021840, + "version": 3384, + "versionNonce": 84427422, "isDeleted": false, "boundElements": [ { @@ -2844,7 +2468,7 @@ "type": "text" } ], - "updated": 1773443576690, + "updated": 1783315857035, "link": null, "locked": false, "points": [ @@ -2944,413 +2568,32 @@ "index": "aYOOUl", "roundness": null, "seed": 712800479, - "version": 194, - "versionNonce": 1735135551, - "isDeleted": false, - "boundElements": [], - "updated": 1772949630236, - "link": null, - "locked": false, - "text": "Cache Miss Lookup", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "VwcRCuZ6f439U3KLIpSS7", - "originalText": "Cache Miss Lookup", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "auNfcll9F2jJQacjXB4Pk", - "type": "arrow", - "index": "aYOOV", - "frameId": null, - "x": 917.4613953107904, - "y": 230.82894640706706, - "width": 2.7698382697398074, - "height": 224.16566913216843, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 650506016, - "version": 1148, - "versionNonce": 89352784, - "isDeleted": false, - "boundElements": [ - { - "id": "fu92T73B_E5_MJzNHkAVH", - "type": "text" - } - ], - "updated": 1773443576691, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 112.20100316968785 - ], - [ - -2.7698382697398074, - 112.20100316968785 - ], - [ - -2.7698382697398074, - 224.16566913216843 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "nginx", - "fixedPoint": [ - 0.66232910172491, - 1.0576196621536675 - ], - "focus": 0, - "gap": 1 - }, - "endBinding": { - "elementId": "app", - "fixedPoint": [ - 0.6650836059723374, - -0.04824424978845421 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "fu92T73B_E5_MJzNHkAVH", - "type": "text", - "x": 887.4085837174198, - "y": 330.5299495767549, - "width": 49.0799560546875, - "height": 25, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYOP", - "roundness": null, - "seed": 69997280, - "version": 135, - "versionNonce": 1847490800, - "isDeleted": false, - "boundElements": [], - "updated": 1772951706772, - "link": null, - "locked": false, - "text": "Read", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "auNfcll9F2jJQacjXB4Pk", - "originalText": "Read", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "LzfazZGq0Peff87XwRPPg", - "type": "arrow", - "index": "aYQ", - "frameId": null, - "x": 1006.7557597903625, - "y": 505.15713969586324, - "width": 409.167424630367, - "height": 477.6614601109175, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 427636040, - "version": 2493, - "versionNonce": 1484918352, - "isDeleted": false, - "boundElements": [ - { - "id": "7WRSyoQACUhNxlcpszV7J", - "type": "text" - } - ], - "updated": 1773443576689, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 305.261534349759, - 0 - ], - [ - 305.261534349759, - -477.6614601109175 - ], - [ - 409.167424630367, - -477.6614601109175 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "app", - "fixedPoint": [ - 1.0191766934696909, - 0.40777869708997977 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "logs-label", - "fixedPoint": [ - -0.0947910399827595, - 0.6976853483448712 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": [ - { - "index": 2, - "start": [ - 305.261534349759, - 0 - ], - "end": [ - 305.261534349759, - -477.6614601109175 - ] - } - ], - "startIsSpecial": false, - "endIsSpecial": false - }, - { - "id": "7WRSyoQACUhNxlcpszV7J", - "type": "text", - "x": 1281.3462577732907, - "y": 386.2538406016155, - "width": 158.2998809814453, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYS", - "roundness": null, - "seed": 806462264, - "version": 156, - "versionNonce": 1690147135, - "isDeleted": false, - "boundElements": [], - "updated": 1772949815470, - "link": null, - "locked": false, - "text": "Structured Logs", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "LzfazZGq0Peff87XwRPPg", - "originalText": "Structured Logs", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "bpDKAYvk7iagWPpi3DTmZ", - "type": "arrow", - "index": "aYT", - "frameId": null, - "x": 1007.8150202750793, - "y": 484.6605694616014, - "width": 410.34497421700723, - "height": 602.322436676582, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 106924360, - "version": 2377, - "versionNonce": 1070318672, - "isDeleted": false, - "boundElements": [ - { - "id": "0_hpGfl1whtukxhN8Dk2S", - "type": "text" - } - ], - "updated": 1773443576689, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 154.69198127817538, - 0 - ], - [ - 154.69198127817538, - -602.322436676582 - ], - [ - 410.34497421700723, - -602.322436676582 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "app", - "fixedPoint": [ - 1.0232507722570632, - 0.22144624041487243 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "metrics-label", - "fixedPoint": [ - -0.08169732894359479, - 0.6477280621190225 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": [ - { - "index": 2, - "start": [ - 154.69198127817538, - 0 - ], - "end": [ - 154.69198127817538, - -602.322436676582 - ] - } - ], - "startIsSpecial": false, - "endIsSpecial": false - }, - { - "id": "0_hpGfl1whtukxhN8Dk2S", - "type": "text", - "x": 1152.2736171720717, - "y": 345.2702620048998, - "width": 179.0598602294922, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "aYU", - "roundness": null, - "seed": 942575416, - "version": 172, - "versionNonce": 447236424, + "version": 194, + "versionNonce": 1735135551, "isDeleted": false, "boundElements": [], - "updated": 1772928650648, + "updated": 1772949630236, "link": null, "locked": false, - "text": "Emit Latency/RPS", + "text": "Cache Miss Lookup", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "bpDKAYvk7iagWPpi3DTmZ", - "originalText": "Emit Latency/RPS", + "containerId": "VwcRCuZ6f439U3KLIpSS7", + "originalText": "Cache Miss Lookup", "autoResize": true, "lineHeight": 1.25 }, { - "id": "8l-lVTQDA58PVAydn9tXQ", + "id": "auNfcll9F2jJQacjXB4Pk", "type": "arrow", - "index": "aYp", + "index": "aYOOV", "frameId": null, - "x": 505.07682581515047, - "y": 171.44171692026887, - "width": 203.53345488912436, - "height": 0.3244033574179639, + "x": 917.4613953107904, + "y": 330.9345189472089, + "width": 2.7698382697398074, + "height": 124.06009659202658, "angle": 0, "strokeColor": "#1971c2", "backgroundColor": "transparent", @@ -3361,17 +2604,17 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 1190544096, - "version": 453, - "versionNonce": 1396187888, + "seed": 650506016, + "version": 1222, + "versionNonce": 557113310, "isDeleted": false, "boundElements": [ { - "id": "AyqkZR6Gt9F9V1FwXdCwS", + "id": "fu92T73B_E5_MJzNHkAVH", "type": "text" } ], - "updated": 1772951906600, + "updated": 1783315857038, "link": null, "locked": false, "points": [ @@ -3380,28 +2623,36 @@ 0 ], [ - 203.53345488912436, - -0.3244033574179639 + 0, + 59.421315407582995 + ], + [ + -2.7698382697398074, + 59.421315407582995 + ], + [ + -2.7698382697398074, + 124.06009659202658 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "lb", + "elementId": "nginx", "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 + 0.66232910172491, + 1.0576196621536675 ], "focus": 0, - "gap": 0 + "gap": 1 }, "endBinding": { - "elementId": "nginx", + "elementId": "app", "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 + 0.6650836059723374, + -0.04824424978845421 ], "focus": 0, - "gap": 1 + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -3417,10 +2668,10 @@ "endIsSpecial": null }, { - "id": "AyqkZR6Gt9F9V1FwXdCwS", + "id": "fu92T73B_E5_MJzNHkAVH", "type": "text", - "x": 593.9202192876639, - "y": 158.77674273573274, + "x": 887.4085837174198, + "y": 330.5299495767549, "width": 49.0799560546875, "height": 25, "angle": 0, @@ -3433,11 +2684,11 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "aYt", + "index": "aYOP", "roundness": null, - "seed": 2091637536, - "version": 98, - "versionNonce": 1674956304, + "seed": 69997280, + "version": 135, + "versionNonce": 1847490800, "isDeleted": false, "boundElements": [], "updated": 1772951706772, @@ -3448,22 +2699,22 @@ "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "8l-lVTQDA58PVAydn9tXQ", + "containerId": "auNfcll9F2jJQacjXB4Pk", "originalText": "Read", "autoResize": true, "lineHeight": 1.25 }, { - "id": "VMSkF3Ci9d8aXipypNEXC", + "id": "LzfazZGq0Peff87XwRPPg", "type": "arrow", - "index": "aYtV", + "index": "aYQ", "frameId": null, - "x": 505.07682581515047, - "y": 171.44171692026887, - "width": 203.53345488912436, - "height": 0.3244033574179639, + "x": 1006.7557597903625, + "y": 505.15713969586324, + "width": 409.167424630367, + "height": 477.6614601109175, "angle": 0, - "strokeColor": "#1971c2", + "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "hachure", "strokeWidth": 2, @@ -3472,17 +2723,17 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 2133474080, - "version": 480, - "versionNonce": 1880507632, + "seed": 427636040, + "version": 2494, + "versionNonce": 1199856990, "isDeleted": false, "boundElements": [ { - "id": "oGL5RnoV-9G8P1EFSEwvc", + "id": "7WRSyoQACUhNxlcpszV7J", "type": "text" } ], - "updated": 1772951906600, + "updated": 1783315857033, "link": null, "locked": false, "points": [ @@ -3491,28 +2742,36 @@ 0 ], [ - 203.53345488912436, - -0.3244033574179639 + 305.261534349759, + 0 + ], + [ + 305.261534349759, + -477.6614601109175 + ], + [ + 409.167424630367, + -477.6614601109175 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "lb", + "elementId": "app", "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 + 1.0191766934696909, + 0.40777869708997977 ], "focus": 0, "gap": 0 }, "endBinding": { - "elementId": "nginx", + "elementId": "logs-label", "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 + -0.0947910399827595, + 0.6976853483448712 ], "focus": 0, - "gap": 1 + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -3523,19 +2782,31 @@ "fontFamily": 5 }, "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null + "fixedSegments": [ + { + "index": 2, + "start": [ + 305.261534349759, + 0 + ], + "end": [ + 305.261534349759, + -477.6614601109175 + ] + } + ], + "startIsSpecial": false, + "endIsSpecial": false }, { - "id": "oGL5RnoV-9G8P1EFSEwvc", + "id": "7WRSyoQACUhNxlcpszV7J", "type": "text", - "x": 593.9202192876639, - "y": 158.77674273573274, - "width": 49.0799560546875, + "x": 1281.3462577732907, + "y": 386.2538406016155, + "width": 158.2998809814453, "height": 25, "angle": 0, - "strokeColor": "#1971c2", + "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -3544,37 +2815,37 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "aYu", + "index": "aYS", "roundness": null, - "seed": 201492192, - "version": 101, - "versionNonce": 1203886832, + "seed": 806462264, + "version": 156, + "versionNonce": 1690147135, "isDeleted": false, "boundElements": [], - "updated": 1772951706772, + "updated": 1772949815470, "link": null, "locked": false, - "text": "Read", + "text": "Structured Logs", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "VMSkF3Ci9d8aXipypNEXC", - "originalText": "Read", + "containerId": "LzfazZGq0Peff87XwRPPg", + "originalText": "Structured Logs", "autoResize": true, "lineHeight": 1.25 }, { - "id": "ouJJ4TViZk6O8i2EPCuKT", + "id": "bpDKAYvk7iagWPpi3DTmZ", "type": "arrow", - "index": "aYuG", + "index": "aYT", "frameId": null, - "x": 505.07682581515047, - "y": 171.44171692026887, - "width": 203.53345488912436, - "height": 0.3244033574179639, + "x": 1007.8150202750793, + "y": 484.6605694616014, + "width": 410.34497421700723, + "height": 602.322436676582, "angle": 0, - "strokeColor": "#1971c2", + "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "hachure", "strokeWidth": 2, @@ -3583,17 +2854,17 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 190181152, - "version": 631, - "versionNonce": 1356867312, + "seed": 106924360, + "version": 2378, + "versionNonce": 1879900574, "isDeleted": false, "boundElements": [ { - "id": "7QOTFr2ZNUF9KYQf3hq3p", + "id": "0_hpGfl1whtukxhN8Dk2S", "type": "text" } ], - "updated": 1772951906601, + "updated": 1783315857033, "link": null, "locked": false, "points": [ @@ -3602,28 +2873,36 @@ 0 ], [ - 203.53345488912436, - -0.3244033574179639 + 154.69198127817538, + 0 + ], + [ + 154.69198127817538, + -602.322436676582 + ], + [ + 410.34497421700723, + -602.322436676582 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "lb", + "elementId": "app", "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 + 1.0232507722570632, + 0.22144624041487243 ], "focus": 0, "gap": 0 }, "endBinding": { - "elementId": "nginx", + "elementId": "metrics-label", "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 + -0.08169732894359479, + 0.6477280621190225 ], "focus": 0, - "gap": 1 + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -3634,19 +2913,31 @@ "fontFamily": 5 }, "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null + "fixedSegments": [ + { + "index": 2, + "start": [ + 154.69198127817538, + 0 + ], + "end": [ + 154.69198127817538, + -602.322436676582 + ] + } + ], + "startIsSpecial": false, + "endIsSpecial": false }, { - "id": "7QOTFr2ZNUF9KYQf3hq3p", + "id": "0_hpGfl1whtukxhN8Dk2S", "type": "text", - "x": 593.9202192876639, - "y": 158.77674273573274, - "width": 49.0799560546875, + "x": 1152.2736171720717, + "y": 345.2702620048998, + "width": 179.0598602294922, "height": 25, "angle": 0, - "strokeColor": "#1971c2", + "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -3655,23 +2946,23 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "aYuV", + "index": "aYU", "roundness": null, - "seed": 1889276640, - "version": 106, - "versionNonce": 1913752592, + "seed": 942575416, + "version": 172, + "versionNonce": 447236424, "isDeleted": false, "boundElements": [], - "updated": 1772951706772, + "updated": 1772928650648, "link": null, "locked": false, - "text": "Read", + "text": "Emit Latency/RPS", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "ouJJ4TViZk6O8i2EPCuKT", - "originalText": "Read", + "containerId": "bpDKAYvk7iagWPpi3DTmZ", + "originalText": "Emit Latency/RPS", "autoResize": true, "lineHeight": 1.25 }, @@ -3807,8 +3098,8 @@ "groupIds": [], "roundness": null, "seed": 1060648475, - "version": 7152, - "versionNonce": 575214348, + "version": 7153, + "versionNonce": 1385520194, "isDeleted": false, "boundElements": [ { @@ -3816,7 +3107,7 @@ "type": "text" } ], - "updated": 1774769172658, + "updated": 1783315857039, "link": null, "locked": false, "points": [ @@ -3957,8 +3248,8 @@ "groupIds": [], "roundness": null, "seed": 93397243, - "version": 5751, - "versionNonce": 1879617164, + "version": 5752, + "versionNonce": 1133983006, "isDeleted": false, "boundElements": [ { @@ -3966,7 +3257,7 @@ "type": "text" } ], - "updated": 1774769234504, + "updated": 1783315857040, "link": null, "locked": false, "points": [ @@ -4063,8 +3354,8 @@ "frameId": null, "x": -108.34259504864488, "y": -98.80495528454173, - "width": 420.9597066486368, - "height": 195.81098804100077, + "width": 815.7411133659275, + "height": 272.62887004272284, "angle": 0, "strokeColor": "#f08c00", "backgroundColor": "transparent", @@ -4076,8 +3367,8 @@ "groupIds": [], "roundness": null, "seed": 1087428876, - "version": 5928, - "versionNonce": 575968820, + "version": 6152, + "versionNonce": 2131697182, "isDeleted": false, "boundElements": [ { @@ -4085,7 +3376,7 @@ "type": "text" } ], - "updated": 1774769238897, + "updated": 1783315883810, "link": null, "locked": false, "points": [ @@ -4095,15 +3386,27 @@ ], [ 0, - 101.58268149799034 + 40 + ], + [ + 289.66085966877074, + 40 ], [ - 420.9597066486368, - 101.58268149799034 + 289.66085966877074, + 141.99209121265943 ], [ - 420.9597066486368, - 195.81098804100077 + 597.6241695339638, + 141.99209121265943 + ], + [ + 597.6241695339638, + 272.62887004272284 + ], + [ + 815.7411133659275, + 272.62887004272284 ] ], "lastCommittedPoint": null, @@ -4116,15 +3419,7 @@ "focus": 0, "gap": 0 }, - "endBinding": { - "elementId": "pkg-entry", - "fixedPoint": [ - 0.1290869028468628, - -0.07986183494576873 - ], - "focus": 0, - "gap": 0 - }, + "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "label": { @@ -4134,15 +3429,49 @@ "fontFamily": 5 }, "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null + "fixedSegments": [ + { + "index": 3, + "start": [ + 289.66085966877074, + 40 + ], + "end": [ + 289.66085966877074, + 141.99209121265943 + ] + }, + { + "index": 4, + "start": [ + 289.66085966877074, + 141.99209121265943 + ], + "end": [ + 597.6241695339638, + 141.99209121265943 + ] + }, + { + "index": 5, + "start": [ + 597.6241695339638, + 141.99209121265943 + ], + "end": [ + 597.6241695339638, + 272.62887004272284 + ] + } + ], + "startIsSpecial": false, + "endIsSpecial": false }, { "id": "o_YOlci_WWImkFNdEBAFz", "type": "text", - "x": 44.80563753766984, - "y": -7.042592440637463, + "x": 44.985318883583716, + "y": -7.287729504972418, "width": 114.30387878417969, "height": 20, "angle": 0, @@ -4158,11 +3487,11 @@ "index": "ael", "roundness": null, "seed": 336465332, - "version": 163, - "versionNonce": 1267603212, + "version": 164, + "versionNonce": 209484894, "isDeleted": false, "boundElements": [], - "updated": 1774769238897, + "updated": 1783315857030, "link": null, "locked": false, "text": "go.buffden.com", @@ -4195,8 +3524,8 @@ "groupIds": [], "roundness": null, "seed": 927031348, - "version": 6051, - "versionNonce": 1287617420, + "version": 6053, + "versionNonce": 833188610, "isDeleted": false, "boundElements": [ { @@ -4204,7 +3533,7 @@ "type": "text" } ], - "updated": 1774769236147, + "updated": 1783315857042, "link": null, "locked": false, "points": [ @@ -4328,8 +3657,8 @@ 83.17046804201496 ] ], - "version": 4555, - "versionNonce": 2080913072, + "version": 4556, + "versionNonce": 1074539330, "index": "af", "isDeleted": false, "fillStyle": "solid", @@ -4345,7 +3674,7 @@ "id": "Ym_Dydz-qBFkMEVjeZ_9i" } ], - "updated": 1773442147482, + "updated": 1783315857042, "link": null, "locked": false, "startBinding": { @@ -4416,8 +3745,8 @@ "type": "arrow", "x": -58.03077796830104, "y": 110.16099133721656, - "width": 331.8585747620853, - "height": 36.17521159875017, + "width": 767.045009666635, + "height": 99.50771015348789, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -4429,20 +3758,20 @@ 0 ], [ - 166.59447811524157, + 383.6645214985726, 0 ], [ - 166.59447811524157, - 36.17521159875017 + 383.6645214985726, + 99.50771015348789 ], [ - 331.8585747620853, - 36.17521159875017 + 767.045009666635, + 99.50771015348789 ] ], - "version": 4157, - "versionNonce": 1965940912, + "version": 4252, + "versionNonce": 338727170, "index": "ah", "isDeleted": false, "fillStyle": "solid", @@ -4458,7 +3787,7 @@ "id": "22Mh-BxaJGbWHZEWDSUhU" } ], - "updated": 1773442147483, + "updated": 1783315871189, "link": null, "locked": false, "startBinding": { @@ -4471,10 +3800,10 @@ "gap": 0 }, "endBinding": { - "elementId": "pkg-entry", + "elementId": "nginx", "fixedPoint": [ - -0.026070356377967983, - 0.2490059662509492 + -0.017853162388390343, + 0.3937015531135256 ], "focus": 0, "gap": 0 @@ -4490,8 +3819,8 @@ { "id": "22Mh-BxaJGbWHZEWDSUhU", "type": "text", - "x": -154.8183018957073, - "y": 133.80997912138326, + "x": 79.02372974899131, + "y": 115.68314141817064, "width": 59.07994079589844, "height": 25, "angle": 0, @@ -4507,11 +3836,11 @@ "index": "ai", "roundness": null, "seed": 2041316667, - "version": 127, - "versionNonce": 1671342555, + "version": 128, + "versionNonce": 1692303490, "isDeleted": false, "boundElements": [], - "updated": 1773362844643, + "updated": 1783315857030, "link": null, "locked": false, "text": "/api/*", @@ -4529,8 +3858,8 @@ "type": "arrow", "x": -58.092119237691634, "y": 156.4553178195462, - "width": 326.47305506783135, - "height": 72.52535908648184, + "width": 767.7296181540207, + "height": 112.52603770611037, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -4547,15 +3876,15 @@ ], [ 150.3320403755847, - 72.52535908648184 + 112.52603770611037 ], [ - 326.47305506783135, - 72.52535908648184 + 767.7296181540207, + 112.52603770611037 ] ], - "version": 4366, - "versionNonce": 122778800, + "version": 4462, + "versionNonce": 853391490, "index": "aj", "isDeleted": false, "fillStyle": "solid", @@ -4571,7 +3900,7 @@ "type": "text" } ], - "updated": 1773442147647, + "updated": 1783315871190, "link": null, "locked": false, "startBinding": { @@ -4584,10 +3913,10 @@ "gap": 0 }, "endBinding": { - "elementId": "pkg-entry", + "elementId": "nginx", "fixedPoint": [ - -0.047857800232546197, - 0.799969126051358 + -0.01581938413908958, + 0.7184323464647674 ], "focus": 0, "gap": 0 @@ -4605,7 +3934,7 @@ ], "end": [ 150.3320403755847, - 72.52535908648184 + 112.52603770611037 ] } ], @@ -4615,8 +3944,8 @@ { "id": "HOfiiJccpkl3DJ5vTJZ8T", "type": "text", - "x": -197.5207549102501, - "y": 173.3370061426591, + "x": 19.939971491896983, + "y": 180.1525416443661, "width": 144.5998992919922, "height": 25, "angle": 0, @@ -4632,11 +3961,11 @@ "index": "ak", "roundness": null, "seed": 984756859, - "version": 132, - "versionNonce": 710784341, + "version": 133, + "versionNonce": 1051279518, "isDeleted": false, "boundElements": [], - "updated": 1773362844644, + "updated": 1783315857030, "link": null, "locked": false, "text": "/{short_code}", @@ -4671,8 +4000,8 @@ "type": 3 }, "seed": 73149211, - "version": 1846, - "versionNonce": 1984513656, + "version": 1850, + "versionNonce": 741664962, "isDeleted": false, "boundElements": [ { @@ -4688,19 +4017,19 @@ "type": "arrow" }, { - "id": "ch7kzIhHMnf3E5fOqA9Zd", + "id": "9CVPfmrCU2Kc-942h7F80", "type": "arrow" }, { - "id": "OdugGG_69b0BIaWzDnsV7", + "id": "ch7kzIhHMnf3E5fOqA9Zd", "type": "arrow" }, { - "id": "9CVPfmrCU2Kc-942h7F80", + "id": "OdugGG_69b0BIaWzDnsV7", "type": "arrow" } ], - "updated": 1775609367496, + "updated": 1783315857029, "link": null, "locked": false }, @@ -4817,44 +4146,6 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "AEiKmEbqKl3JqDE2wHyAx", - "type": "text", - "index": "ar", - "frameId": null, - "x": 319.55822889441674, - "y": 158.89211909320667, - "width": 160.88494873046875, - "height": 87.0886295810665, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 826674875, - "version": 178, - "versionNonce": 396691221, - "isDeleted": false, - "boundElements": [], - "updated": 1774395527643, - "link": null, - "locked": false, - "text": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "fontSize": 13.68535607702474, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "top", - "baseline": 12, - "containerId": null, - "originalText": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "autoResize": true, - "lineHeight": 1.2727272727272727 - }, { "id": "7-fdzbsrW9pUq9JPWKN5s", "type": "text", @@ -4882,14 +4173,14 @@ "updated": 1774769172658, "link": null, "locked": false, - "text": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "text": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "fontSize": 9.407443073499568, "fontFamily": 5, "textAlign": "center", "verticalAlign": "top", "baseline": 11, "containerId": null, - "originalText": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "originalText": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "autoResize": true, "lineHeight": 1.2727272727272727 }, @@ -4915,8 +4206,8 @@ "type": 3 }, "seed": 414206732, - "version": 1542, - "versionNonce": 73975692, + "version": 1544, + "versionNonce": 191549762, "isDeleted": false, "boundElements": [ { @@ -4928,15 +4219,15 @@ "type": "arrow" }, { - "id": "7ZaWBnKuXnY4D5alcvXx4", + "id": "9CVPfmrCU2Kc-942h7F80", "type": "arrow" }, { - "id": "9CVPfmrCU2Kc-942h7F80", + "id": "7ZaWBnKuXnY4D5alcvXx4", "type": "arrow" } ], - "updated": 1774769232330, + "updated": 1783315857029, "link": null, "locked": false }, diff --git a/docs/architecture/00-baseline/v2/url-shortener-v2-hld.puml b/docs/architecture/00-baseline/v2/url-shortener-v2-hld.puml index 8b61a43..8a7b667 100644 --- a/docs/architecture/00-baseline/v2/url-shortener-v2-hld.puml +++ b/docs/architecture/00-baseline/v2/url-shortener-v2-hld.puml @@ -35,10 +35,6 @@ package "Public Internet" #LightGray { ' ---------------- AWS REGION ---------------- package "AWS Region (Single Region)" #LightBlue { - package "Entry Layer" #LightGreen { - [Load Balancer\n(L4 / L7)] as LB - } - package "Application Tier" #LightYellow { [Nginx\n(TLS Termination + Reverse Proxy)] as NGINX [URL Shortener App\n(Stateless Instances + Token Bucket RL)] as APP @@ -71,8 +67,7 @@ User --> DNS : Resolve \n domain ' ---------------- READ PATH (blue) ---------------- -User -[#4C9BE8]-> LB : [READ] GET /{code} -LB -[#4C9BE8]-> NGINX +User -[#4C9BE8]-> NGINX : [READ] GET /{code} NGINX -[#4C9BE8]-> APP APP -[#4C9BE8]-> REDIS : Lookup short_code REDIS -[#4C9BE8]-> APP : Cache hit\nor NULL (negative cache) @@ -83,8 +78,7 @@ APP -[#4C9BE8]-> User : HTTP 301/302 Redirect\nor HTTP 404 Not Found ' ---------------- WRITE PATH (red) ---------------- -User -[#FF0000]-> LB : [WRITE] POST /shorten -LB -[#FF0000]-> NGINX +User -[#FF0000]-> NGINX : [WRITE] POST /shorten NGINX -[#FF0000]-> APP APP -[#FF0000]-> DB : INSERT mapping DB -[#FF0000]-> APP : Confirm write diff --git a/docs/architecture/00-baseline/v2/url-shortener-v2.md b/docs/architecture/00-baseline/v2/url-shortener-v2.md index 7973305..75458b5 100644 --- a/docs/architecture/00-baseline/v2/url-shortener-v2.md +++ b/docs/architecture/00-baseline/v2/url-shortener-v2.md @@ -101,14 +101,14 @@ Version 2: ## 6) Architecture Overview -Deployment: **AWS `us-east-1` (Single Region)**. API + redirects at `go.buffden.com` (ALB → EC2); Angular SPA at `tinyurl.buffden.com` (S3 + CloudFront). +Deployment: **AWS `us-east-1` (Single Region)**. API + redirects at `go.buffden.com` (EC2); Angular SPA at `tinyurl.buffden.com` (S3 + CloudFront). ### Layers | Layer | Components | | --- | --- | | Public Internet | DNS — `go.buffden.com` (API/redirects) and `tinyurl.buffden.com` (SPA) | -| Entry Layer | AWS ALB (`go.buffden.com`) + CloudFront (`tinyurl.buffden.com`) | +| Edge | CloudFront (`tinyurl.buffden.com`) | | Application Tier | Nginx (TLS Termination + Reverse Proxy), URL Shortener App (Stateless Instances + Token Bucket RL) | | Cache Layer | Redis (Cache-Aside + Negative Caching) | | Data Tier | PostgreSQL Primary DB | @@ -126,7 +126,6 @@ Starting from v1 components, v2 requires additional components because of scale - Client (browser) - DNS resolution -- Load balancer - Nginx (TLS termination + reverse proxy) - Stateless application servers - **Redis cache** (mandatory in v2 for redirect path) @@ -140,8 +139,8 @@ Starting from v1 components, v2 requires additional components because of scale ### Read Path (Redirect) — v2 1. User resolves domain via DNS. -2. Client sends `GET /{code}` to Load Balancer. -3. LB forwards to Nginx; Nginx forwards to App. +2. Client sends `GET /{code}` to Nginx. +3. Nginx forwards to App. 4. App looks up `short_code` in Redis. 5. **Cache hit**: Redis returns `original_url` (or NULL for negative cache) → skip to step 8. 6. **Cache miss**: App queries PostgreSQL (`Cache miss lookup`). @@ -152,7 +151,7 @@ Starting from v1 components, v2 requires additional components because of scale ### Write Path (Create) — v2 1. Client sends `POST /api/urls` (with optional `alias` field for custom aliases) with URL and optional expiry. -2. LB forwards to Nginx; Nginx applies rate limits and forwards to App. +2. Nginx applies rate limits and forwards to App. 3. App validates input, checks alias rules, and generates `short_code` if needed. 4. App writes mapping to PostgreSQL (`INSERT mapping`). 5. PostgreSQL confirms write to App. @@ -280,7 +279,7 @@ Reason: | DB | Slow queries | Tail latency spike | Indexing, connection pool limits, query tuning | | DB | Down | Redirects fail | Backups + failover plan; return 503 with retry headers | | App | Thread exhaustion | Timeouts/5xx | Autoscale app, set timeouts, bulkheads | -| LB/Nginx | Misconfig | Partial/full outage | Health checks, config validation in CI | +| Nginx | Misconfig | Partial/full outage | Health checks, config validation in CI | | Rate limiter | Too strict | False throttling | Observe metrics, adjust thresholds, allowlists | | Cache stampede | Many expirations at once | DB spike | TTL jitter + request coalescing for same key | diff --git a/docs/deployment/PHASE_A_INFRASTRUCTURE.md b/docs/deployment/PHASE_A_INFRASTRUCTURE.md index 999c73a..12be7a9 100644 --- a/docs/deployment/PHASE_A_INFRASTRUCTURE.md +++ b/docs/deployment/PHASE_A_INFRASTRUCTURE.md @@ -5,7 +5,7 @@ **Region:** `us-east-1` (N. Virginia) for everything **Estimated time:** 2–3 hours in the console -**Deliverable:** `curl https://go.buffden.com` returns HTTP 502 — ALB is up, EC2 is registered, but no app running yet. That 502 confirms the full network path is working. +**Deliverable:** `curl https://go.buffden.com` returns a connection response — EC2 is up and reachable, but no app running yet. That confirms the full network path is working. --- @@ -18,11 +18,10 @@ - [ ] Step 5 — IAM roles - [ ] Step 6 — RDS PostgreSQL - [ ] Step 7 — EC2 instance -- [ ] Step 8 — ALB + target group -- [ ] Step 9 — S3 bucket -- [ ] Step 10 — CloudFront distribution -- [ ] Step 11 — Route 53 DNS records -- [ ] Step 12 — Cloudflare DNS migration (free DDoS + edge protection) +- [ ] Step 8 — S3 bucket +- [ ] Step 9 — CloudFront distribution +- [ ] Step 10 — Route 53 DNS records +- [ ] Step 11 — Cloudflare DNS migration (free DDoS + edge protection) --- @@ -77,7 +76,7 @@ A single wildcard cert covers both `tinyurl.buffden.com` and `go.buffden.com`. ### 3b. Create subnets -**Public subnets (ALB + EC2):** +**Public subnets (EC2):** | Name | AZ | CIDR | |---|---|---| @@ -96,7 +95,7 @@ For each subnet: 2. Select `tinyurl-prod-vpc` 3. Fill in name, AZ, CIDR as above -> ALB requires subnets in at least 2 AZs — that's why we create two public subnets even with one EC2. +> Two public subnets across AZs are created for future flexibility, though only one EC2 is used. ### 3c. Internet Gateway @@ -119,28 +118,20 @@ For each subnet: ## Step 4 — Security Groups -Create three security groups inside `tinyurl-prod-vpc`. +Create two security groups inside `tinyurl-prod-vpc`. -### tinyurl-alb (Internet-facing load balancer) +### tinyurl-ec2 (Application server) > AWS does not allow security group names starting with `sg-` — use names without that prefix. 1. **VPC → Security Groups → Create security group** -2. Name: `tinyurl-alb`, VPC: `tinyurl-prod-vpc` -3. Description: `Internet-facing load balancer` +2. Name: `tinyurl-ec2`, VPC: `tinyurl-prod-vpc` +3. Description: `Application server, accepts HTTP and HTTPS from internet` 4. Inbound rules: - - HTTP (80) from `0.0.0.0/0` + - HTTP (80) from `0.0.0.0/0` (for HTTP → HTTPS redirect) - HTTPS (443) from `0.0.0.0/0` 5. Outbound: All traffic (default) -### tinyurl-ec2 (Application server) - -1. Name: `tinyurl-ec2`, VPC: `tinyurl-prod-vpc` -2. Description: `Application server, accepts traffic from ALB only` -3. Inbound rules: - - HTTP (80) from source: `tinyurl-alb` (not `0.0.0.0/0` — only ALB can reach EC2) -4. Outbound: All traffic (default) - > No port 22. SSH is not used. EC2 access is via SSM Session Manager only. ### tinyurl-rds (Database) @@ -317,45 +308,7 @@ mkdir -p /app --- -## Step 8 — ALB + Target Group - -### 8a. Create target group - -1. Go to **EC2 → Target Groups → Create target group** -2. Target type: **Instances** -3. Name: `tg-tinyurl-api` -4. Protocol: HTTP, Port: 80 -5. VPC: `tinyurl-prod-vpc` -6. Health check: - - Protocol: HTTP - - Path: `/actuator/health` - - Healthy threshold: 2 - - Unhealthy threshold: 3 - - Interval: 30s - - Timeout: 5s - - Success codes: 200 -7. Click **Next**, select your EC2 instance, click **Include as pending**, then **Create target group** - -### 8b. Create load balancer - -1. Go to **EC2 → Load Balancers → Create load balancer → Application Load Balancer** -2. Name: `tinyurl-alb` -3. Scheme: **Internet-facing** -4. IP address type: IPv4 -5. VPC: `tinyurl-prod-vpc` -6. Subnets: select both public subnets (`tinyurl-public-1a`, `tinyurl-public-1b`) -7. Security groups: `tinyurl-alb` -8. Listeners: - - Port 80: **Add listener** → Action: Redirect to HTTPS (443), status 301 - - Port 443: **Add listener** → Action: Forward to `tg-tinyurl-api` - - For port 443, select ACM certificate: `*.buffden.com` -9. Click **Create load balancer** - -> Copy the ALB **DNS name** (e.g. `tinyurl-alb-123456789.us-east-1.elb.amazonaws.com`). You'll need it for Route 53 in Step 11. - ---- - -## Step 9 — S3 Bucket +## Step 8 — S3 Bucket 1. Go to **S3 → Create bucket** 2. Bucket name: `tinyurl-spa-prod` @@ -372,7 +325,7 @@ mkdir -p /app --- -## Step 10 — CloudFront Distribution +## Step 9 — CloudFront Distribution 1. Go to **CloudFront → Create distribution** - If a pricing plan popup appears, select **Pay-as-you-go** (no commitment, costs under $1/month for a portfolio project — AWS Shield Standard DDoS protection is included free regardless) @@ -410,7 +363,7 @@ mkdir -p /app --- -## Step 11 — Route 53 DNS Records +## Step 10 — Route 53 DNS Records 1. Go to **Route 53 → Hosted zones → buffden.com → Create record** @@ -425,17 +378,15 @@ mkdir -p /app **Record 2 — API + redirects:** - Record name: `go` - Record type: **A** -- Alias: **Yes** -- Route traffic to: **Alias to Application and Classic Load Balancer** -- Region: us-east-1 -- Select `tinyurl-alb` +- Alias: **No** +- Value: EC2 Elastic IP address (e.g. `100.56.217.158`) - Click **Create record** --- -## Step 12 — Cloudflare DNS Migration +## Step 11 — Cloudflare DNS Migration -Moves DNS from Route 53 to Cloudflare so flood attacks are absorbed at Cloudflare's edge before CloudFront or ALB ever see the traffic — eliminating the billing impact of DDoS attacks. All AWS services stay completely unchanged. +Moves DNS from Route 53 to Cloudflare so flood attacks are absorbed at Cloudflare's edge before CloudFront or EC2 ever see the traffic — eliminating the billing impact of DDoS attacks. All AWS services stay completely unchanged. ### 12a. Sign up and add domain @@ -449,13 +400,13 @@ Cloudflare's auto-import is incomplete. Manually verify and correct all records: | Type | Name | Content | Proxy | | --- | --- | --- | --- | -| CNAME | `go` | `dualstack.tinyurl-alb-xxx.us-east-1.elb.amazonaws.com` | Orange (Proxied) | +| A | `go` | `` | Orange (Proxied) | | CNAME | `tinyurl` | `d1anlbbmfo4elu.cloudfront.net` | Orange (Proxied) | | A | `ems` | `` | Orange (Proxied) | | CNAME | `portfolio` | `buffden.github.io` | Grey (DNS only) | | CNAME | `_2a3ec5a40d53220a744f6e248e46f22b` | `_bf955b229028da621bc467ed086b9ddc.jkddzztszm.acm-validations.aws` | Grey (DNS only) | -> The `go` record is likely imported as two raw A records (IPs). Delete them and add a single CNAME pointing to the ALB hostname — ALB IPs change, the hostname does not. +> The `go` record should be an A record pointing to the EC2 Elastic IP. The EIP does not change on stop/start. > > The ACM validation CNAME (`_2a3ec5a...`) **must be grey cloud (DNS only)**. If proxied, AWS cannot reach it to auto-renew your SSL certificate. @@ -536,7 +487,7 @@ Once Cloudflare has been running without issues for 24 hours, delete the Route 5 After all steps above, verify the network path is working: ```bash -# Should return HTTP 502 (ALB up, no app running yet — expected) +# Should return a TLS handshake or connection response (EC2 up, no app running yet — expected) curl -I https://go.buffden.com # Should return HTTP 301 redirect to https:// diff --git a/docs/deployment/PHASE_B_SECRETS_AND_CONFIG.md b/docs/deployment/PHASE_B_SECRETS_AND_CONFIG.md index 271f584..716cc8d 100644 --- a/docs/deployment/PHASE_B_SECRETS_AND_CONFIG.md +++ b/docs/deployment/PHASE_B_SECRETS_AND_CONFIG.md @@ -205,7 +205,7 @@ http { add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; - # Allow ALB health checks — MUST be before the blanket /actuator/ block + # Expose health check endpoint for monitoring — MUST be before the blanket /actuator/ block location = /actuator/health { proxy_pass http://app:8080; proxy_set_header Host $host; @@ -238,7 +238,7 @@ http { } ``` -> `HSTS` (`Strict-Transport-Security`) header is intentionally omitted here — the ALB handles HTTPS enforcement. Adding HSTS at the Nginx level (HTTP only internally) would have no effect and could cause confusion. +> `HSTS` (`Strict-Transport-Security`) is handled by Cloudflare's **Always Use HTTPS** setting and the HTTP→HTTPS redirect in the Nginx HTTPS server block. --- @@ -323,13 +323,10 @@ grep apiUrl src/environments/environment.prod.ts Browser │ ▼ -Route 53 (go.buffden.com) +Cloudflare DNS (go.buffden.com → EC2 EIP) │ ▼ -ALB (HTTPS:443 → HTTP:80 to EC2) - │ - ▼ -Nginx container (port 80) +Nginx container (port 443, TLS via Let's Encrypt) ├── rate limits POST /api/urls ├── blocks /actuator/ └── proxies everything else diff --git a/docs/deployment/PHASE_C_FIRST_MANUAL_DEPLOY.md b/docs/deployment/PHASE_C_FIRST_MANUAL_DEPLOY.md index 0630e09..f6008b1 100644 --- a/docs/deployment/PHASE_C_FIRST_MANUAL_DEPLOY.md +++ b/docs/deployment/PHASE_C_FIRST_MANUAL_DEPLOY.md @@ -11,10 +11,9 @@ - [ ] Step 1 — Build and push Docker image to GHCR - [ ] Step 2 — Copy files to EC2 via SSM - [ ] Step 3 — Start application on EC2 -- [ ] Step 4 — Verify ALB health check -- [ ] Step 5 — Build and upload Angular to S3 -- [ ] Step 6 — Invalidate CloudFront cache -- [ ] Step 7 — Full end-to-end smoke test +- [ ] Step 4 — Build and upload Angular to S3 +- [ ] Step 5 — Invalidate CloudFront cache +- [ ] Step 6 — Full end-to-end smoke test --- @@ -171,48 +170,7 @@ If you see SSM Parameter Store errors, check: --- -## Step 4 — Verify ALB Health Check - -1. Go to **EC2 → Target Groups → tg-tinyurl-api** -2. Click **Targets** tab -3. Wait for the registered EC2 to show **Healthy** status (can take up to 60s) - -If it stays **Unhealthy:** - -- Use SSM Session Manager to check: `curl http://localhost/actuator/health` - - If this returns `{"status":"UP"}` the app is fine — the issue is between Nginx and the health check path - - If this returns 403, Nginx is blocking `/actuator/health` (see fix below) - - If connection refused, the app hasn't started — check `docker compose -f docker-compose.prod.yml logs app` -- Confirm Nginx is running: `docker compose -f docker-compose.prod.yml ps` -- Confirm security group `sg-ec2` allows port 80 from `sg-alb` - -**Critical: ALB targets port 80 on EC2, which goes through Nginx — not directly to the app.** - -If Nginx has a blanket `location /actuator/ { return 403; }` block, health checks will return 403 and the target will stay Unhealthy. The fix is an exact-match location *before* the blanket block in `nginx.prod.conf`: - -```nginx -# Allow ALB health checks through — MUST be before the blanket /actuator/ block -location = /actuator/health { - proxy_pass http://app:8080; - proxy_set_header Host $host; -} - -# Block all other actuator endpoints -location /actuator/ { - return 403; -} -``` - -After updating the nginx config, re-upload via S3 and restart the nginx container: - -```bash -aws s3 cp s3://tinyurl-spa-prod/deploy/nginx.prod.conf /app/infra/nginx/nginx.prod.conf -docker compose -f docker-compose.prod.yml restart nginx -``` - ---- - -## Step 5 — Build and Upload Angular to S3 +## Step 4 — Build and Upload Angular to S3 Run on your local machine from the `tinyurl-gui` repo: @@ -261,7 +219,7 @@ Without the `BootstrapContext` parameter, Angular's server platform cannot initi --- -## Step 6 — Invalidate CloudFront Cache +## Step 5 — Invalidate CloudFront Cache ```bash # Get your distribution ID @@ -281,7 +239,7 @@ aws cloudfront list-invalidations --distribution-id --- -## Step 7 — Full End-to-End Smoke Test +## Step 6 — Full End-to-End Smoke Test Run these checks in order: @@ -326,7 +284,6 @@ curl -I https://tinyurl.buffden.com | `aws s3 cp` returns 403 Forbidden | EC2 role missing S3 permissions | Attach `AmazonS3ReadOnlyAccess` to `role-tinyurl-ec2` | | CloudWatch `AccessDeniedException` in Docker logs | EC2 role missing CloudWatch permissions | Attach `CloudWatchLogsFullAccess` to `role-tinyurl-ec2` | | App crash: `Could not resolve placeholder 'tinyurl.cors.allowed-origins'` | CORS config uses YAML list syntax; `@Value` requires a string | Change `allowed-origins` to comma-separated string in `application-prod.yaml` | -| ALB health check stays Unhealthy with 403 | Nginx blocks `/actuator/` before ALB can reach app | Add `location = /actuator/health` exact-match block before the blanket `location /actuator/` block in nginx config | | `ng build` fails with NG0401 | `main.server.ts` missing `BootstrapContext` parameter (Angular 19.2+) | Update bootstrap function signature — see Step 5 | | CORS error in browser | `CorsConfig.java` not applied | Verify `application-prod.yaml` has correct origin as comma-separated string | | Short URL has wrong domain | Wrong SSM `base-url` | Update `/tinyurl/prod/base-url` → `https://go.buffden.com` | diff --git a/docs/deployment/PHASE_D_CICD_AUTOMATION.md b/docs/deployment/PHASE_D_CICD_AUTOMATION.md index 84a3365..a0a87ef 100644 --- a/docs/deployment/PHASE_D_CICD_AUTOMATION.md +++ b/docs/deployment/PHASE_D_CICD_AUTOMATION.md @@ -328,7 +328,7 @@ Make a small, safe change and merge it: Verify: 1. GitHub Actions tab → `Deploy API` workflow runs 2. All three jobs complete: `build-test` → `compose-smoke` → `deploy` -3. ALB health check stays green during deploy +3. App health check stays green during deploy 4. `curl https://go.buffden.com/actuator/health` still returns 200 after deploy --- diff --git a/docs/deployment/PHASE_E_OBSERVABILITY.md b/docs/deployment/PHASE_E_OBSERVABILITY.md index 18de571..f9a364b 100644 --- a/docs/deployment/PHASE_E_OBSERVABILITY.md +++ b/docs/deployment/PHASE_E_OBSERVABILITY.md @@ -73,25 +73,16 @@ db.t3.micro supports a maximum of ~87 connections. Alert before it's full. 4. Action: Use SNS topic `tinyurl-alerts` 5. Alarm name: `tinyurl-rds-high-connections` -### Alarm 3 — ALB 5xx Errors +### Alarm 3 — App 5xx Errors -Triggers if Spring Boot is returning server errors at scale. +Triggers if Spring Boot is returning server errors. Uses a CloudWatch Metric Filter on the app log stream. -1. Metric: **ApplicationELB → Per AppELB Metrics → HTTPCode_Target_5XX_Count** → select `tinyurl-alb` -2. Statistic: Sum, Period: 1 minute -3. Condition: **Greater than 5** -4. Action: Use SNS topic `tinyurl-alerts` -5. Alarm name: `tinyurl-alb-5xx` - -### Alarm 4 — ALB Slow Responses - -Triggers if the application is responding slowly (DB bottleneck, connection pool exhaustion). - -1. Metric: **ApplicationELB → Per AppELB Metrics → TargetResponseTime** → select `tinyurl-alb` -2. Statistic: p99 (99th percentile), Period: 5 minutes -3. Condition: **Greater than 1** (1 second) -4. Action: Use SNS topic `tinyurl-alerts` -5. Alarm name: `tinyurl-alb-slow-p99` +1. Go to **CloudWatch → Log groups → /tinyurl/prod → app → Metric filters → Create metric filter** +2. Filter pattern: `{ $.status_class = "5xx" }` +3. Metric name: `App5xxCount`, Namespace: `TinyURL`, Value: `1` +4. Create alarm on this metric: Sum > 5 over 1 minute +5. Action: Use SNS topic `tinyurl-alerts` +6. Alarm name: `tinyurl-app-5xx` ### Confirm SNS email subscription @@ -133,8 +124,7 @@ Access via **RDS → Databases → tinyurl-prod → Performance Insights tab**. |---|---|---|---| | EC2 CPU | <30% | >80% sustained | App under load or runaway process | | RDS connections | <20 | >75 | HikariCP pool exhausted | -| ALB 5xx count | 0 | >5/min | Spring Boot throwing unhandled exceptions | -| ALB P99 latency | <100ms | >1s | DB slow query or connection pool wait | +| App 5xx count | 0 | >5/min | Spring Boot throwing unhandled exceptions | | Redirect success rate | 100% | — | Monitor manually via logs for now | --- diff --git a/docs/deployment/PHASE_F_HARDENING.md b/docs/deployment/PHASE_F_HARDENING.md index 2503353..d5f2f55 100644 --- a/docs/deployment/PHASE_F_HARDENING.md +++ b/docs/deployment/PHASE_F_HARDENING.md @@ -9,13 +9,12 @@ ## Checklist - [ ] Step 1 — Verify Nginx security headers -- [ ] Step 2 — Enable ALB access logs -- [ ] Step 3 — Enable AWS CloudTrail -- [ ] Step 4 — Enable EC2 termination protection -- [ ] Step 5 — Verify RDS deletion protection -- [ ] Step 6 — Take manual RDS snapshot -- [ ] Step 7 — Test rollback procedure -- [ ] Step 8 — Go-live verification checklist +- [ ] Step 2 — Enable AWS CloudTrail +- [ ] Step 3 — Enable EC2 termination protection +- [ ] Step 4 — Verify RDS deletion protection +- [ ] Step 5 — Take manual RDS snapshot +- [ ] Step 6 — Test rollback procedure +- [ ] Step 7 — Go-live verification checklist --- @@ -42,37 +41,7 @@ docker compose -f /app/docker-compose.prod.yml exec nginx nginx -T | grep add_he --- -## Step 2 — Enable ALB Access Logs - -ALB access logs record every request — useful for debugging and security audits. - -1. Create an S3 bucket for logs: `tinyurl-alb-logs-prod` (in us-east-1) -2. Add S3 bucket policy to allow ALB to write (AWS requires this): - ```json - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::127311923021:root" - }, - "Action": "s3:PutObject", - "Resource": "arn:aws:s3:::tinyurl-alb-logs-prod/alb/*" - } - ] - } - ``` - > `127311923021` is the AWS ELB service account for us-east-1 — this is correct, not a mistake. - -3. Go to **EC2 → Load Balancers → tinyurl-alb → Attributes → Edit** -4. Access logs: **Enable** -5. S3 URI: `s3://tinyurl-alb-logs-prod/alb` -6. Save - ---- - -## Step 3 — Enable AWS CloudTrail +## Step 2 — Enable AWS CloudTrail CloudTrail records all AWS API calls — who did what, when. @@ -88,7 +57,7 @@ CloudTrail records all AWS API calls — who did what, when. --- -## Step 4 — EC2 Termination Protection +## Step 3 — EC2 Termination Protection Prevents accidentally terminating the EC2 instance from the console or CLI. @@ -101,7 +70,7 @@ Prevents accidentally terminating the EC2 instance from the console or CLI. --- -## Step 5 — Verify RDS Deletion Protection +## Step 4 — Verify RDS Deletion Protection Was enabled during Phase A. Verify: @@ -113,7 +82,7 @@ If not enabled: --- -## Step 6 — Manual RDS Snapshot Before Go-Live +## Step 5 — Manual RDS Snapshot Before Go-Live Take a manual snapshot as a safety baseline before going live. @@ -126,7 +95,7 @@ Take a manual snapshot as a safety baseline before going live. --- -## Step 7 — Rollback Procedures +## Step 6 — Rollback Procedures Test these **before** going live so you know they work. @@ -178,7 +147,7 @@ docker compose -f /app/docker-compose.prod.yml restart app --- -## Step 8 — Go-Live Verification Checklist +## Step 7 — Go-Live Verification Checklist Run this full checklist before announcing the app publicly. @@ -200,7 +169,6 @@ Run this full checklist before announcing the app publicly. - [ ] Copy-to-clipboard works **Infrastructure:** -- [ ] ALB target group health check: **Healthy** - [ ] RDS status: **Available** - [ ] CloudWatch alarms: all in **OK** state - [ ] CloudWatch logs: `/tinyurl/prod/app` receiving logs diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 0026e4d..4b5575a 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -1,7 +1,7 @@ # TinyURL v1 — Deployment Overview **Frontend:** `tinyurl.buffden.com` → S3 + CloudFront (Angular SPA) -**Backend + Redirects:** `go.buffden.com` → ALB → EC2 → Spring Boot +**Backend + Redirects:** `go.buffden.com` → EC2 → Spring Boot **Short URL format:** `https://go.buffden.com/{code}` **Region:** `us-east-1` (N. Virginia) **Estimated cost:** ~$54/month @@ -28,28 +28,23 @@ │ Cloudflare (DNS + Proxy) │ │ buffden.com nameservers │ │ tinyurl.buffden.com → CloudFront (proxied)│ - │ go.buffden.com → ALB (proxied) │ + │ go.buffden.com → EC2 EIP (proxied) │ └──────────┬──────────────────────┬───────────┘ │ │ - ┌───────────────▼──────┐ ┌───────────▼──────────────┐ - │ CloudFront CDN │ │ Application Load │ - │ tinyurl.buffden.com│ │ Balancer (ALB) │ - │ Origin: S3 bucket │ │ go.buffden.com │ - │ PriceClass_100 │ │ :443 → EC2:80 │ - │ HTTPS only │ │ :80 → redirect to 443 │ - └──────────────────────┘ └───────────┬──────────────┘ - │ │ - ┌──────────▼──────────┐ ┌────────────▼──────────────────┐ - │ S3 Bucket │ │ EC2 t3.small (us-east-1a) │ - │ tinyurl-spa-prod │ │ Ubuntu 22.04 LTS │ - │ Block public │ │ Nginx + Spring Boot │ - │ access (OAC) │ │ (Docker Compose) │ - └─────────────────────┘ └────────────┬──────────────────┘ - │ - ┌─────────────▼─────────────────┐ - │ RDS PostgreSQL 16 │ - │ db.t3.micro, private subnet │ - └───────────────────────────────┘ + ┌───────────────▼──────┐ ┌───────────▼──────────────────────┐ + │ CloudFront CDN │ │ EC2 t3.small (us-east-1a) │ + │ tinyurl.buffden.com│ │ Ubuntu 22.04 LTS │ + │ Origin: S3 bucket │ │ Nginx (TLS :443) + Spring Boot │ + │ PriceClass_100 │ │ (Docker Compose) │ + │ HTTPS only │ └────────────┬─────────────────────┘ + └──────────────────────┘ │ + │ ┌────────────▼─────────────────────┐ + ┌──────────▼──────────┐ │ RDS PostgreSQL 16 │ + │ S3 Bucket │ │ db.t3.micro, private subnet │ + │ tinyurl-spa-prod │ └──────────────────────────────────┘ + │ Block public │ + │ access (OAC) │ + └─────────────────────┘ ``` ## Traffic Flows @@ -58,8 +53,8 @@ |---|---| | `https://tinyurl.buffden.com` | Cloudflare → CloudFront → S3 (Angular SPA) | | `https://tinyurl.buffden.com/*` | CloudFront → S3 → `index.html` (Angular router) | -| `POST https://go.buffden.com/api/urls` | Cloudflare → ALB → Nginx → Spring Boot → RDS | -| `GET https://go.buffden.com/{code}` | Cloudflare → ALB → Nginx → Spring Boot → 301/302 | +| `POST https://go.buffden.com/api/urls` | Cloudflare → Nginx → Spring Boot → RDS | +| `GET https://go.buffden.com/{code}` | Cloudflare → Nginx → Spring Boot → 301/302 | | `http://` any domain | Redirect to `https://` | --- @@ -69,7 +64,7 @@ | Decision | Value | |---|---| | Frontend | S3 + CloudFront at `tinyurl.buffden.com` | -| Backend | ALB → EC2 at `go.buffden.com` | +| Backend | EC2 (Nginx) at `go.buffden.com` | | Short URL format | `https://go.buffden.com/{code}` | | API called by Angular | `https://go.buffden.com/api` | | DNS | Cloudflare (nameservers set at Namecheap) | @@ -92,13 +87,12 @@ |---|---|---| | EC2 t3.small | 2 vCPU, 2 GB | ~$15 | | RDS db.t3.micro | PostgreSQL 16, 5 GB | ~$15 | -| ALB | 1 load balancer | ~$18 | | S3 | <100 MB assets | <$1 | | CloudFront (SPA) | Low traffic | ~$1 | | Cloudflare | DNS + proxy (free plan) | $0 | | CloudWatch | Alarms + logs | ~$3 | | ACM / SSM | Free tiers | $0 | -| **Total** | | **~$54/month** | +| **Total** | | **~$36/month** | --- diff --git a/docs/hld/system-design/system-design.md b/docs/hld/system-design/system-design.md index b12ab13..8a94672 100644 --- a/docs/hld/system-design/system-design.md +++ b/docs/hld/system-design/system-design.md @@ -20,10 +20,9 @@ The architecture is CloudFront-first at the edge, with backend services running | Component | Responsibility | | --- | --- | -| DNS (Cloudflare) | Resolves `go.buffden.com` → ALB (API/redirects); `tinyurl.buffden.com` → CloudFront (Angular SPA). Proxied — DDoS mitigation and WAF sit at the edge before traffic reaches AWS. | -| AWS ALB | TLS termination, HTTP→HTTPS redirect, health checks, routes traffic to EC2 instances. | +| DNS (Cloudflare) | Resolves `go.buffden.com` → EC2 EIP (API/redirects); `tinyurl.buffden.com` → CloudFront (Angular SPA). Proxied — DDoS mitigation and WAF sit at the edge before traffic reaches AWS. | | CloudFront (SPA dist) | Serves Angular SPA from S3 origin with CDN caching and SPA fallback rules. | -| Nginx | Reverse proxy inside EC2 Docker Compose. Routes requests to the Spring Boot app. | +| Nginx | TLS termination, HTTP→HTTPS redirect, reverse proxy inside EC2 Docker Compose. Routes requests to the Spring Boot app. | | Application Server | Stateless — any instance can handle any request. Horizontally scalable within the backend runtime boundary. | | PostgreSQL | Primary data store. Stores `short_code → original_url` mappings. Single primary. | @@ -52,7 +51,7 @@ The architecture is CloudFront-first at the edge, with backend services running ### Write Path (Create Short URL) 1. Client sends `POST /api/urls` with original URL and optional expiry. -2. Request passes through Cloudflare → ALB → Nginx → App. +2. Request passes through Cloudflare → Nginx → App. 3. App validates input (URL format, length). 4. App generates `short_code` via DB sequence + Base62 encoding. 5. App writes mapping to PostgreSQL. @@ -62,7 +61,7 @@ The architecture is CloudFront-first at the edge, with backend services running ### Read Path (Redirect) 1. Client accesses `GET /` at `go.buffden.com`. -2. Request passes through Cloudflare → ALB → Nginx → App. +2. Request passes through Cloudflare → Nginx → App. 3. (v2) App checks Redis cache first. 4. On cache miss: App queries PostgreSQL by `short_code`. 5. App checks expiry and deletion status. diff --git a/docs/lld/c4-deployment.excalidraw b/docs/lld/c4-deployment.excalidraw index 2fe9b09..d730bb9 100644 --- a/docs/lld/c4-deployment.excalidraw +++ b/docs/lld/c4-deployment.excalidraw @@ -2284,7 +2284,7 @@ "updated": 1773376724343, "link": null, "locked": false, - "text": "<>\nAWS ALB\ngo.buffden.com\nHTTP -> HTTPS redirect\nForwards HTTPS to Nginx :443", + "text": "<>\nNginx\ngo.buffden.com\nTLS :443 (Let's Encrypt)\nHTTP->HTTPS redirect", "fontSize": 12, "fontFamily": 5, "textAlign": "center", diff --git a/docs/lld/c4-level2-container.excalidraw b/docs/lld/c4-level2-container.excalidraw index 4b464c2..1d5ade8 100644 --- a/docs/lld/c4-level2-container.excalidraw +++ b/docs/lld/c4-level2-container.excalidraw @@ -98,8 +98,8 @@ "groupIds": [], "roundness": null, "seed": 558116570, - "version": 3520, - "versionNonce": 859509655, + "version": 3523, + "versionNonce": 515893891, "isDeleted": false, "boundElements": [ { @@ -107,7 +107,7 @@ "type": "text" } ], - "updated": 1774395820257, + "updated": 1783315959145, "link": null, "locked": false, "points": [ @@ -540,11 +540,11 @@ "type": 3 }, "seed": 449692506, - "version": 1038, - "versionNonce": 24635462, + "version": 1040, + "versionNonce": 1345910925, "isDeleted": false, "boundElements": [], - "updated": 1773363200528, + "updated": 1783315948304, "link": null, "locked": false }, @@ -586,180 +586,6 @@ "autoResize": true, "lineHeight": 1.2727272727272727 }, - { - "id": "0zQg7Q2xMAQmIK1yV_tAO", - "type": "rectangle", - "index": "b0p", - "frameId": null, - "x": 2411.536365725727, - "y": 194.4092403123529, - "width": 250, - "height": 150, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "#d3f9d8", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 56898778, - "version": 143, - "versionNonce": 1987685879, - "isDeleted": false, - "boundElements": [ - { - "id": "zVDwrC6LksoC1axNvq1jn", - "type": "arrow" - }, - { - "id": "ta42cNQePDnDYm1lqc5fp", - "type": "arrow" - }, - { - "id": "L-5TVovghix78tA8qD5Vx", - "type": "arrow" - } - ], - "updated": 1774395820255, - "link": null, - "locked": false - }, - { - "id": "ryL5gWOpXKcy64mE_5ebJ", - "type": "text", - "index": "b0q", - "frameId": null, - "x": 2424.847189114885, - "y": 200.9183873023743, - "width": 91.61593627929688, - "height": 20, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 897354138, - "version": 106, - "versionNonce": 734168410, - "isDeleted": false, - "boundElements": [], - "updated": 1773363200529, - "link": null, - "locked": false, - "text": "Entry Layer", - "fontSize": 16, - "fontFamily": 5, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 11, - "containerId": null, - "originalText": "Entry Layer", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "LmPZQ6m5Mz8UdzFTyRWVR", - "type": "rectangle", - "index": "b0r", - "frameId": null, - "x": 2431.3628751987335, - "y": 233.62001526254812, - "width": 200, - "height": 88, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 1395410522, - "version": 110, - "versionNonce": 1268159814, - "isDeleted": false, - "boundElements": [ - { - "id": "j4B7RhyYOzTLfDaJT_9At", - "type": "arrow" - }, - { - "id": "6qs-B16HmEf0jF7lKzV0q", - "type": "arrow" - }, - { - "id": "hYjVhw2LwKQ1XHn2yllP4", - "type": "arrow" - }, - { - "id": "U3Kgwt6jbQLkaMYjBuzgP", - "type": "arrow" - }, - { - "id": "yWPiIqQYcAzVmg80tDMjn", - "type": "arrow" - }, - { - "id": "L-5TVovghix78tA8qD5Vx", - "type": "arrow" - } - ], - "updated": 1773363200529, - "link": null, - "locked": false - }, - { - "id": "j8iIoqaXeAVgrU2gUqrgy", - "type": "text", - "index": "b0s", - "frameId": null, - "x": 2449.497731315363, - "y": 243.10837739154107, - "width": 167.20594464079204, - "height": 90.51024779806838, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 1162242842, - "version": 132, - "versionNonce": 105661497, - "isDeleted": false, - "boundElements": [], - "updated": 1774395852908, - "link": null, - "locked": false, - "text": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "fontSize": 14.223038939696458, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "top", - "baseline": 12, - "containerId": null, - "originalText": "AWS ALB\n(TLS termination\nHTTP→HTTPS redirect\nHealth checks)\n", - "autoResize": true, - "lineHeight": 1.2727272727272727 - }, { "id": "86lpzaDvmUjz5ruAAbg0G", "type": "text", @@ -820,44 +646,32 @@ "type": 3 }, "seed": 975563930, - "version": 266, - "versionNonce": 1416162970, + "version": 273, + "versionNonce": 487574723, "isDeleted": false, "boundElements": [ { "id": "zVDwrC6LksoC1axNvq1jn", "type": "arrow" }, - { - "id": "j4B7RhyYOzTLfDaJT_9At", - "type": "arrow" - }, - { - "id": "6qs-B16HmEf0jF7lKzV0q", - "type": "arrow" - }, { "id": "eSEyuvfhd_AxtwHdN4wY1", "type": "arrow" }, { - "id": "hYjVhw2LwKQ1XHn2yllP4", - "type": "arrow" - }, - { - "id": "U3Kgwt6jbQLkaMYjBuzgP", + "id": "9hZOqDwK1iW2Y4tsfXTtL", "type": "arrow" }, { - "id": "yWPiIqQYcAzVmg80tDMjn", + "id": "L-5TVovghix78tA8qD5Vx", "type": "arrow" }, { - "id": "9hZOqDwK1iW2Y4tsfXTtL", + "id": "ta42cNQePDnDYm1lqc5fp", "type": "arrow" } ], - "updated": 1773363200530, + "updated": 1783315968682, "link": null, "locked": false }, @@ -944,16 +758,20 @@ "groupIds": [], "roundness": null, "seed": 1127419418, - "version": 139, - "versionNonce": 2046637210, + "version": 140, + "versionNonce": 1921702317, "isDeleted": false, "boundElements": [ { "id": "hYjVhw2LwKQ1XHn2yllP4", "type": "arrow" + }, + { + "id": "ta42cNQePDnDYm1lqc5fp", + "type": "arrow" } ], - "updated": 1773363200532, + "updated": 1783315954500, "link": null, "locked": false, "text": "Nginx\n(TLS Termination + Reverse Proxy)", @@ -1631,14 +1449,14 @@ "lineHeight": 1.2727272727272727 }, { - "id": "j4B7RhyYOzTLfDaJT_9At", + "id": "9hZOqDwK1iW2Y4tsfXTtL", "type": "arrow", - "index": "b1D", + "index": "b1H", "frameId": null, - "x": 2636.3328372627266, - "y": 289.1783099588657, - "width": 203.52305310013952, - "height": 0.5789713901349671, + "x": 2950.5514585830715, + "y": 315.8281409630536, + "width": 1.3675134957431965, + "height": 223.98393937564987, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -1649,13 +1467,13 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 610289370, - "version": 803, - "versionNonce": 740240070, + "seed": 60885466, + "version": 1773, + "versionNonce": 1367060806, "isDeleted": false, "boundElements": [ { - "id": "rRhUNhR_e5QonoDwncY1b", + "id": "J-13WnaGnuBEWAjapyN0_", "type": "text" } ], @@ -1668,28 +1486,36 @@ 0 ], [ - 203.52305310013952, - -0.5789713901349671 + 0, + 112.62574092772985 + ], + [ + -1.3675134957431965, + 112.62574092772985 + ], + [ + -1.3675134957431965, + 223.98393937564987 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "LmPZQ6m5Mz8UdzFTyRWVR", + "elementId": "uzox0B4PzVt-zGrqooG3W", "fixedPoint": [ - 1.0248498103199655, - 0.6313442579126997 + 0.34221678940319156, + 1.0527930967213721 ], "focus": 0, "gap": 0 }, "endBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "focus": -0.25127711719543405, - "gap": 3.2273093567523574, + "elementId": "-0zfLFsbJRSDJz_vlFVVo", "fixedPoint": [ - -0.018993029499944213, - 0.7433748876949761 - ] + 0.29316594523705686, + -0.053757590829913875 + ], + "focus": 0, + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -1705,10 +1531,10 @@ "endIsSpecial": null }, { - "id": "rRhUNhR_e5QonoDwncY1b", + "id": "J-13WnaGnuBEWAjapyN0_", "type": "text", - "x": 2229.4802001090684, - "y": -152.75755483371938, + "x": 2441.253538131472, + "y": -13.192497206734174, "width": 50.63995361328125, "height": 25, "angle": 0, @@ -1721,11 +1547,11 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "b1E", + "index": "b1I", "roundness": null, - "seed": 1093032858, - "version": 111, - "versionNonce": 147999898, + "seed": 559333018, + "version": 135, + "versionNonce": 1514630682, "isDeleted": false, "boundElements": [], "updated": 1773363200537, @@ -1736,20 +1562,20 @@ "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "j4B7RhyYOzTLfDaJT_9At", + "containerId": "9hZOqDwK1iW2Y4tsfXTtL", "originalText": "Write", "autoResize": true, "lineHeight": 1.25 }, { - "id": "6qs-B16HmEf0jF7lKzV0q", + "id": "K0iz5fjQ69dxnGeBd1mtA", "type": "arrow", - "index": "b1F", + "index": "b1J", "frameId": null, - "x": 2636.3328372627266, - "y": 289.1783099588657, - "width": 203.52305310013952, - "height": 0.5789713901349671, + "x": 2886.785128741704, + "y": 658.675502716986, + "width": 361.45585669338107, + "height": 243.40842967946924, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -1760,13 +1586,13 @@ "opacity": 100, "groupIds": [], "roundness": null, - "seed": 768333914, - "version": 1011, - "versionNonce": 1572463110, + "seed": 267901786, + "version": 1455, + "versionNonce": 1656854662, "isDeleted": false, "boundElements": [ { - "id": "D4ysSAuRs7r-sp1uWniMS", + "id": "WxznIgYmRl6xYsqujthgw", "type": "text" } ], @@ -1779,28 +1605,36 @@ 0 ], [ - 203.52305310013952, - -0.5789713901349671 + 0, + 124.87672677788646 + ], + [ + -361.45585669338107, + 124.87672677788646 + ], + [ + -361.45585669338107, + 243.40842967946924 ] ], "lastCommittedPoint": null, "startBinding": { - "elementId": "LmPZQ6m5Mz8UdzFTyRWVR", + "elementId": "-0zfLFsbJRSDJz_vlFVVo", "fixedPoint": [ - 1.0248498103199655, - 0.6313442579126997 + 0.05317049775388577, + 1.0268189762453817 ], "focus": 0, "gap": 0 }, "endBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "focus": -0.25127711719543405, - "gap": 3.2273093567523574, + "elementId": "pMHUqr015xJIVyLez8diN", "fixedPoint": [ - -0.018993029499944213, - 0.7433748876949761 - ] + 0.05752684510984113, + -0.05163950701830989 + ], + "focus": 0, + "gap": 0 }, "startArrowhead": null, "endArrowhead": "arrow", @@ -1816,11 +1650,11 @@ "endIsSpecial": null }, { - "id": "D4ysSAuRs7r-sp1uWniMS", + "id": "WxznIgYmRl6xYsqujthgw", "type": "text", - "x": 2229.4802001090684, - "y": -152.75755483371938, - "width": 50.63995361328125, + "x": 2141.3930641571055, + "y": 341.9058503973548, + "width": 162.73989868164062, "height": 25, "angle": 0, "strokeColor": "#e03131", @@ -1832,273 +1666,35 @@ "opacity": 100, "groupIds": [], "frameId": null, - "index": "b1G", + "index": "b1K", "roundness": null, - "seed": 170114330, - "version": 116, - "versionNonce": 1557244250, + "seed": 1842831386, + "version": 156, + "versionNonce": 1445370586, "isDeleted": false, "boundElements": [], "updated": 1773363200537, "link": null, "locked": false, - "text": "Write", + "text": "INSERT mapping", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", - "containerId": "6qs-B16HmEf0jF7lKzV0q", - "originalText": "Write", + "containerId": "K0iz5fjQ69dxnGeBd1mtA", + "originalText": "INSERT mapping", "autoResize": true, "lineHeight": 1.25 }, { - "id": "9hZOqDwK1iW2Y4tsfXTtL", + "id": "4mUa6OoMWVkL-7PI6Hlp8", "type": "arrow", - "index": "b1H", + "index": "b1L", "frameId": null, - "x": 2950.5514585830715, - "y": 315.8281409630536, - "width": 1.3675134957431965, - "height": 223.98393937564987, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 60885466, - "version": 1773, - "versionNonce": 1367060806, - "isDeleted": false, - "boundElements": [ - { - "id": "J-13WnaGnuBEWAjapyN0_", - "type": "text" - } - ], - "updated": 1773363200537, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 112.62574092772985 - ], - [ - -1.3675134957431965, - 112.62574092772985 - ], - [ - -1.3675134957431965, - 223.98393937564987 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "fixedPoint": [ - 0.34221678940319156, - 1.0527930967213721 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "-0zfLFsbJRSDJz_vlFVVo", - "fixedPoint": [ - 0.29316594523705686, - -0.053757590829913875 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "J-13WnaGnuBEWAjapyN0_", - "type": "text", - "x": 2441.253538131472, - "y": -13.192497206734174, - "width": 50.63995361328125, - "height": 25, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1I", - "roundness": null, - "seed": 559333018, - "version": 135, - "versionNonce": 1514630682, - "isDeleted": false, - "boundElements": [], - "updated": 1773363200537, - "link": null, - "locked": false, - "text": "Write", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "9hZOqDwK1iW2Y4tsfXTtL", - "originalText": "Write", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "K0iz5fjQ69dxnGeBd1mtA", - "type": "arrow", - "index": "b1J", - "frameId": null, - "x": 2886.785128741704, - "y": 658.675502716986, - "width": 361.45585669338107, - "height": 243.40842967946924, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 267901786, - "version": 1455, - "versionNonce": 1656854662, - "isDeleted": false, - "boundElements": [ - { - "id": "WxznIgYmRl6xYsqujthgw", - "type": "text" - } - ], - "updated": 1773363200537, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 124.87672677788646 - ], - [ - -361.45585669338107, - 124.87672677788646 - ], - [ - -361.45585669338107, - 243.40842967946924 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "-0zfLFsbJRSDJz_vlFVVo", - "fixedPoint": [ - 0.05317049775388577, - 1.0268189762453817 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "pMHUqr015xJIVyLez8diN", - "fixedPoint": [ - 0.05752684510984113, - -0.05163950701830989 - ], - "focus": 0, - "gap": 0 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "WxznIgYmRl6xYsqujthgw", - "type": "text", - "x": 2141.3930641571055, - "y": 341.9058503973548, - "width": 162.73989868164062, - "height": 25, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1K", - "roundness": null, - "seed": 1842831386, - "version": 156, - "versionNonce": 1445370586, - "isDeleted": false, - "boundElements": [], - "updated": 1773363200537, - "link": null, - "locked": false, - "text": "INSERT mapping", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "K0iz5fjQ69dxnGeBd1mtA", - "originalText": "INSERT mapping", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "4mUa6OoMWVkL-7PI6Hlp8", - "type": "arrow", - "index": "b1L", - "frameId": null, - "x": 2555.3024118534026, - "y": 900.3718121760198, - "width": 352.76870641255107, - "height": 241.63879473174666, + "x": 2555.3024118534026, + "y": 900.3718121760198, + "width": 352.76870641255107, + "height": 241.63879473174666, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -3414,371 +3010,33 @@ "lineHeight": 1.25 }, { - "id": "hYjVhw2LwKQ1XHn2yllP4", - "type": "arrow", - "index": "b1f", + "id": "F1B694ZpRedHbn3PyD8lv", + "type": "rectangle", + "index": "b1l", "frameId": null, - "x": 2636.267805652601, - "y": 256.8656492342974, - "width": 203.53345488912436, - "height": 0.3244033574179639, + "x": 1856.1076775627398, + "y": -82.02766750890851, + "width": 210, + "height": 110, "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", + "strokeColor": "#868e96", + "backgroundColor": "#dee2e6", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], - "roundness": null, - "seed": 367926234, - "version": 693, - "versionNonce": 1511438406, - "isDeleted": false, - "boundElements": [ - { - "id": "kDSNEEC5Pipi_rEdztCPl", - "type": "text" - } - ], - "updated": 1773363200537, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 203.53345488912436, - -0.3244033574179639 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "LmPZQ6m5Mz8UdzFTyRWVR", - "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 - ], - "focus": 0, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "kDSNEEC5Pipi_rEdztCPl", - "type": "text", - "x": 2230.200368172732, - "y": -184.94293154192923, - "width": 49.0799560546875, - "height": 25, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1g", - "roundness": null, - "seed": 1278316698, - "version": 109, - "versionNonce": 33838874, + "roundness": { + "type": 3 + }, + "seed": 1684140122, + "version": 1434, + "versionNonce": 1202853382, "isDeleted": false, "boundElements": [], "updated": 1773363200537, "link": null, - "locked": false, - "text": "Read", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "hYjVhw2LwKQ1XHn2yllP4", - "originalText": "Read", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "U3Kgwt6jbQLkaMYjBuzgP", - "type": "arrow", - "index": "b1h", - "frameId": null, - "x": 2636.267805652601, - "y": 256.8656492342974, - "width": 203.53345488912436, - "height": 0.3244033574179639, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 2074635610, - "version": 720, - "versionNonce": 1348714374, - "isDeleted": false, - "boundElements": [ - { - "id": "IjZkvIE2CKX18r4Rk46Qg", - "type": "text" - } - ], - "updated": 1773363200537, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 203.53345488912436, - -0.3244033574179639 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "LmPZQ6m5Mz8UdzFTyRWVR", - "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 - ], - "focus": 0, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "IjZkvIE2CKX18r4Rk46Qg", - "type": "text", - "x": 2230.200368172732, - "y": -184.94293154192923, - "width": 49.0799560546875, - "height": 25, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1i", - "roundness": null, - "seed": 2040902170, - "version": 112, - "versionNonce": 677274586, - "isDeleted": false, - "boundElements": [], - "updated": 1773363200537, - "link": null, - "locked": false, - "text": "Read", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "U3Kgwt6jbQLkaMYjBuzgP", - "originalText": "Read", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "yWPiIqQYcAzVmg80tDMjn", - "type": "arrow", - "index": "b1j", - "frameId": null, - "x": 2636.267805652601, - "y": 256.8656492342974, - "width": 203.53345488912436, - "height": 0.3244033574179639, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": null, - "seed": 2031902426, - "version": 871, - "versionNonce": 1577967302, - "isDeleted": false, - "boundElements": [ - { - "id": "g5VLf2VRKocXucEnuZ72N", - "type": "text" - } - ], - "updated": 1773363200537, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 203.53345488912436, - -0.3244033574179639 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "LmPZQ6m5Mz8UdzFTyRWVR", - "fixedPoint": [ - 1.024524652269338, - 0.2641549314971508 - ], - "focus": 0, - "gap": 0 - }, - "endBinding": { - "elementId": "uzox0B4PzVt-zGrqooG3W", - "fixedPoint": [ - -0.019171291630200708, - 0.3790783798330295 - ], - "focus": 0, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "label": { - "containerId": "arr-user-lb", - "text": "HTTPS Request\n(GET /{code}\nor POST /shorten)", - "fontSize": 11, - "fontFamily": 5 - }, - "elbowed": true, - "fixedSegments": null, - "startIsSpecial": null, - "endIsSpecial": null - }, - { - "id": "g5VLf2VRKocXucEnuZ72N", - "type": "text", - "x": 2230.200368172732, - "y": -184.94293154192923, - "width": 49.0799560546875, - "height": 25, - "angle": 0, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1k", - "roundness": null, - "seed": 1453290394, - "version": 117, - "versionNonce": 962123930, - "isDeleted": false, - "boundElements": [], - "updated": 1773363200537, - "link": null, - "locked": false, - "text": "Read", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "yWPiIqQYcAzVmg80tDMjn", - "originalText": "Read", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "F1B694ZpRedHbn3PyD8lv", - "type": "rectangle", - "index": "b1l", - "frameId": null, - "x": 1856.1076775627398, - "y": -82.02766750890851, - "width": 210, - "height": 110, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "#dee2e6", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "roundness": { - "type": 3 - }, - "seed": 1684140122, - "version": 1434, - "versionNonce": 1202853382, - "isDeleted": false, - "boundElements": [ - { - "id": "he9CFo5JVGSJqld7VEWdL", - "type": "arrow" - } - ], - "updated": 1773363200537, - "link": null, "locked": false }, { @@ -3844,12 +3102,7 @@ "version": 1446, "versionNonce": 911041862, "isDeleted": false, - "boundElements": [ - { - "id": "he9CFo5JVGSJqld7VEWdL", - "type": "arrow" - } - ], + "boundElements": [], "updated": 1773363200537, "link": null, "locked": false @@ -4087,8 +3340,8 @@ "type": "arrow", "x": 2073.16410811915, "y": 195.34664240124513, - "width": 331.854668512085, - "height": 36.41349284875017, + "width": 766.5624354523029, + "height": 59.87446716082849, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -4100,20 +3353,20 @@ 0 ], [ - 166.59252499024115, - -2.842170943040401e-14 + 383.66256837357196, + 0 ], [ - 166.59252499024115, - 36.41349284875014 + 383.66256837357196, + 59.87446716082849 ], [ - 331.854668512085, - 36.41349284875014 + 766.5624354523029, + 59.87446716082849 ] ], - "version": 4399, - "versionNonce": 1728251639, + "version": 4462, + "versionNonce": 1105180675, "index": "b1v", "isDeleted": false, "fillStyle": "solid", @@ -4129,7 +3382,7 @@ "id": "4BUu94K9X9IKNG9veMN16" } ], - "updated": 1774395820260, + "updated": 1783315976542, "link": null, "locked": false, "startBinding": { @@ -4142,10 +3395,10 @@ "gap": 0 }, "endBinding": { - "elementId": "0zQg7Q2xMAQmIK1yV_tAO", + "elementId": "uzox0B4PzVt-zGrqooG3W", "fixedPoint": [ - -0.026070356377967983, - 0.2490059662509492 + -0.01941509997533089, + 0.364076830801145 ], "focus": 0, "gap": 0 @@ -4200,8 +3453,8 @@ "type": "arrow", "x": 2073.102766849759, "y": 241.64096888357477, - "width": 326.4691488178314, - "height": 72.7636403364819, + "width": 767.6196172961227, + "height": 47.13145118727408, "angle": 0, "strokeColor": "#2f9e44", "backgroundColor": "transparent", @@ -4214,19 +3467,19 @@ ], [ 150.3281341255847, - -5.684341886080802e-14 + 0 ], [ 150.3281341255847, - 72.76364033648179 + 47.13145118727408 ], [ - 326.4691488178314, - 72.76364033648184 + 767.6196172961227, + 47.13145118727408 ] ], - "version": 4607, - "versionNonce": 1137592313, + "version": 4669, + "versionNonce": 899623053, "index": "b1x", "isDeleted": false, "fillStyle": "solid", @@ -4242,7 +3495,7 @@ "type": "text" } ], - "updated": 1774395820260, + "updated": 1783315963438, "link": null, "locked": false, "startBinding": { @@ -4255,10 +3508,10 @@ "gap": 0 }, "endBinding": { - "elementId": "0zQg7Q2xMAQmIK1yV_tAO", + "elementId": "uzox0B4PzVt-zGrqooG3W", "fixedPoint": [ - -0.047857800232546197, - 0.799969126051358 + -0.01616558060134631, + 0.7453417229463181 ], "focus": 0, "gap": 0 @@ -4276,7 +3529,7 @@ ], "end": [ 150.3281341255847, - 72.76364033648184 + 47.13145118727408 ] } ], @@ -4649,14 +3902,14 @@ "updated": 1774395810450, "link": null, "locked": false, - "text": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "text": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "fontSize": 9.407443073499568, "fontFamily": 5, "textAlign": "center", "verticalAlign": "top", "baseline": 11, "containerId": null, - "originalText": "DNS Cloudflare\ngo.buffden.com → ALB\ntinyurl.buffden.com → CloudFront", + "originalText": "DNS Cloudflare\ngo.buffden.com → EC2 EIP\ntinyurl.buffden.com → CloudFront", "autoResize": true, "lineHeight": 1.2727272727272727 }