Skip to content

rmconstantin/dsql-party

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serverless Event Platform — Aurora DSQL Edition

An alternative implementation of JimmyDqv's Serverless Event Platform that replaces MongoDB Atlas with Amazon Aurora DSQL as the database backend.

The original platform manages event guest lists with RSVP tracking, group relationships, dietary preferences, and an admin dashboard. This version preserves the same serverless architecture (Lambda, API Gateway, SQS, SAM) while swapping the data layer to Aurora DSQL.

Why Aurora DSQL?

MongoDB Atlas Aurora DSQL
Pricing model Fixed-cost M10+ cluster required for workload federation Fully pay-per-use, no minimum
Authentication OIDC federation via STS — requires Atlas Identity Provider setup, IAM role mapping, and database user creation Native IAM auth tokens — generate a token with boto3, connect with psycopg
Query language MongoDB aggregation pipelines Standard PostgreSQL SQL
Consistency Tunable (eventual by default for reads) Strong consistency with ACID transactions and snapshot isolation
Availability Depends on cluster tier 99.99% single-region, 99.999% multi-region
Serverless fit Requires always-on cluster Designed for serverless and event-driven architectures — scales to zero
Schema Schemaless documents Relational tables with enforced types

Architecture

Client → API Gateway (HTTP API) → Lambda Functions → Aurora DSQL
                                       ↓
                                   SQS Queue → Activity Consumer Lambda → Aurora DSQL

Four SAM stacks from the original are consolidated into a single stack:

  • 10 Lambda functions — one per API endpoint for clean routing
  • HTTP API Gateway — routes for guests, groups, admin, and settings
  • SQS + DLQ — async activity tracking with partial batch failure support
  • Aurora DSQL — three tables: guests, settings, activities

API Endpoints

Method Path Description
POST /guests Create a guest
GET /guests/{guest_id} Get a guest by ID
PUT /guests/{guest_id} Update a guest
DELETE /guests/{guest_id} Delete a guest
POST /guests/{guest_id}/rsvp Submit RSVP
GET /groups/{group_id} Get all guests in a group
GET /groups Get group summary with member counts
GET /admin/stats Dashboard statistics
GET /admin/settings Get event settings
PUT /admin/settings Update event settings

Project Structure

├── src/
│   ├── models.py              # Dataclasses and conversion utilities
│   ├── auth.py                # IAM token generation for DSQL
│   ├── connection.py          # Module-scope connection manager with retry
│   ├── retry.py               # OCC retry decorator (exponential backoff)
│   ├── schema.py              # DDL for tables and async indexes
│   ├── guest_handlers.py      # Guest CRUD, RSVP, and group handlers
│   ├── admin_handlers.py      # Dashboard stats and settings handlers
│   └── activity_consumer.py   # SQS consumer for async activity tracking
├── template.yaml              # SAM template
└── requirements.txt           # Python dependencies

Prerequisites

Deployment

1. Create an Aurora DSQL cluster

Create a single-region cluster in the DSQL console. Note the cluster endpoint (e.g., abc123.dsql.us-east-1.on.aws).

2. Initialize the database schema

Connect to your cluster using psql or the DSQL query editor and run:

CREATE TABLE IF NOT EXISTS guests (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    group_id    UUID,
    first_name  TEXT NOT NULL,
    last_name   TEXT NOT NULL,
    email       TEXT,
    attending   BOOLEAN,
    dietary     TEXT,
    rsvp_at     TIMESTAMPTZ,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS settings (
    id             UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_name     TEXT,
    event_date     TIMESTAMPTZ,
    location       TEXT,
    rsvp_deadline  TIMESTAMPTZ,
    max_guests     INTEGER,
    settings_data  TEXT,
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS activities (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    guest_id    UUID,
    action      TEXT,
    timestamp   TIMESTAMPTZ,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Async indexes (non-blocking)
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_group_id  ON guests (group_id);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_email     ON guests (email);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_attending ON guests (attending);
CREATE INDEX ASYNC IF NOT EXISTS idx_guests_rsvp_at   ON guests (rsvp_at);
CREATE INDEX ASYNC IF NOT EXISTS idx_activities_guest_id ON activities (guest_id);

3. Deploy the stack

sam build && sam deploy --guided

You'll be prompted for:

  • DsqlClusterEndpoint — your cluster endpoint
  • DsqlClusterArnarn:aws:dsql:<region>:<account-id>:cluster/<cluster-id>

4. Test it

# Create a guest
curl -X POST https://<api-endpoint>/prod/guests \
  -H "Content-Type: application/json" \
  -d '{"first_name": "Jane", "last_name": "Doe", "email": "jane@example.com"}'

# Submit RSVP
curl -X POST https://<api-endpoint>/prod/guests/<guest-id>/rsvp \
  -H "Content-Type: application/json" \
  -d '{"attending": true, "dietary": "vegetarian"}'

# Check dashboard
curl https://<api-endpoint>/prod/admin/stats

Key Implementation Details

IAM Authentication (no credentials stored)

Lambda functions authenticate to DSQL using short-lived IAM tokens — no database passwords, no secrets, no OIDC federation setup:

client = boto3.client("dsql", region_name=region)
token = client.generate_db_connect_admin_auth_token(
    Hostname=cluster_endpoint, Region=region
)
conn = psycopg.connect(host=endpoint, user="admin", password=token, sslmode="require")

Connection Reuse

Database connections are stored at module scope and reused across Lambda warm starts, avoiding reconnection overhead.

Optimistic Concurrency Control

DSQL uses OCC instead of locks. A decorator retries transactions on serialization conflicts with exponential backoff (50ms → 100ms → 200ms):

@occ_retry(max_retries=3, base_delay_ms=50)
def update_rsvp():
    ...

DSQL Compatibility Notes

  • No JSONBsettings_data uses TEXT with JSON serialized/deserialized in the application layer
  • Async indexesCREATE INDEX ASYNC is required; synchronous index creation is not supported
  • PostgreSQL 16 compatible — standard psycopg driver works out of the box

Cleanup

sam delete --stack-name dsql-event-platform

Delete the Aurora DSQL cluster separately from the DSQL console.

Credits

Based on the Serverless Event Platform by JimmyDqv. Original architecture and design patterns adapted for Aurora DSQL.

About

Jimmy Dahlqvist's Serverless event platform with Aurora DSQL

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages