Architecture

Overview

backend-crons is a Serverless Framework v3 service (abstract-play-backend-crons) deployed to AWS us-east-1. All functions are Node.js 20 Lambdas triggered by EventBridge cron rules (prod only).

There is no API Gateway — these are batch and maintenance jobs only.

Lambda functions

Defined in serverless.yml. Schedules are daily unless noted (EventBridge cron(0 H * * ? *) = every day at hour H UTC).

Function Schedule (UTC, prod) Role
dumpdb Daily 00:00 Export prod DynamoDB to S3
records Daily 03:00 Build game records from dump
records-ttm Daily 03:00 Per-player time-to-move arrays
records-move-times Daily 03:00 Move activity histograms
records-cooccur Daily 03:00 PMI co-occurrence for recommendations
records-rec-analytics Daily 03:00 Recommendation impression funnel analytics (ops S3)
layout-feedback-analytics Daily 03:00 Game Move beta layout feedback analytics (ops S3)
tournament-data Daily 03:00 Tournament summaries from dump
records-manifest Daily 04:00 and 07:30 S3 listing + _manifest.json v2
summarize Daily 06:00 Site analytics from ALL.json
player-summary-fanout Daily 06:15 SQS fan-out for player/*-summary.json
player-summary-worker SQS-triggered Writes one player summary slice per message
starttournaments Daily 10:00 and 22:00 Start/cancel tournaments, create games
standingchallenges Daily 00:00 and 12:00 Process preset standing challenges

See Records pipeline and Functions reference for details.

Gameslib Lambda layer

Functions that call @abstractplay/gameslib attach the abstractplayGameslib layer, built by scripts/build-layers.cjs before packaging:

esbuild marks @abstractplay/gameslib and @abstractplay/recranks as external so they resolve from the layer at runtime, not from the function bundle.

Data flow

flowchart LR
    ddb[(DynamoDB abstract-play-prod)] --> dumpdb
    dumpdb --> s3dump[(S3 abstractplay-db-dump)]
    s3dump --> records
    s3dump --> cooccur[records-cooccur]
    records --> s3rec[(S3 records.abstractplay.com)]
    cooccur --> s3rec
    s3rec --> summarize
    summarize --> s3rec
    ddb --> live[Live crons]
    ddb --> recanalytics[records-rec-analytics]
    recanalytics --> s3ops[(private ops S3)]
    live --> ddb
    s3rec --> cf[CloudFront CDN]

IAM permissions

The service role grants:

Environment

Variable Source Purpose
ABSTRACT_PLAY_TABLE serverless.yml abstract-play-{stage} — used by summarize (geo stats), live crons, and records-rec-analytics

Dependencies

Package Used by Purpose
@abstractplay/gameslib records, records-ttm, records-move-times, summarize, starttournaments GameFactory, gameinfo, genRecord, addResource
@abstractplay/recranks records, summarize APGameRecord, ELO/Glicko2/Trueskill raters
ion-js, fflate dump consumers Parse gzipped ION export files
i18next records, starttournaments Email copy (apback namespace)

records-cooccur uses ion-js and fflate only (no gameslib layer).

Project layout

src/functions/     Lambda handlers
src/types/         Record and StatSummary TypeScript types
src/locales/       i18n strings (en, fr, it) for tournament emails
src/utils/         Shared utilities (e.g. isoToCountryCode, cooccurPmi)
scripts/           build-layers.cjs
serverless.yml     Infrastructure and schedules

Related