Skip to content

EventStats: compiled queries endpoint for server-side aggregations #116

Open

Description

@oto-macenauer-absa

Feature Description

Extend the EventStats Lambda with a compiled queries endpoint — a curated list of predefined, server-executed queries (e.g. "failed jobs in the last 7 days", "job count aggregated by catalog") that consumers can invoke by name. Rather than pushing aggregation logic into the client or BI tool, the heavy lifting runs inside PostgreSQL and only the final result set is returned over the wire.

Problem / Opportunity

The current POST /stats/{topic_name} endpoint returns raw paginated rows. Consumers (dashboards, reports, downstream tools) re-derive the same aggregations repeatedly on the client side — wasting bandwidth, duplicating logic, and making results inconsistent across consumers. Predefined server-side queries centralise that logic, reduce payload sizes significantly, and allow PostgreSQL query planning to optimise repeated patterns.

Acceptance Criteria

  • A new route (e.g. POST /stats/{topic_name}/query/{query_name}) accepts a query identifier and an optional parameter bag (e.g. time window, tenant filter).
  • A SUPPORTED_QUERIES registry maps each query_name to its SQL template and accepted parameters — unknown names return 400.
  • Queries are executed server-side; only the aggregated result is returned (no raw row streaming).
  • The endpoint is protected by the same JWT auth and per-topic ACL as the rest of EventStats.
  • Unknown or unsupported query_name values produce a clear 400 error, not a 500.
  • Unit tests cover routing, registry lookup, and each query's result shaping.
  • Integration tests validate at least one aggregation query end-to-end against a seeded testcontainer database.
  • All quality gates pass (./ci_local.sh).

Note for implementer: the concrete set of queries and their SQL definitions must be identified and agreed upon as part of this issue's implementation. The examples below are starting points only — validate with stakeholders which aggregations are actually needed before writing SQL.

Proposed Solution

Introduce a CompiledQueryRegistry (or extend ReaderPostgres) that maps query names to parameterised SQL templates (using psycopg2 %s / sql.SQL composition — never string interpolation). Each entry declares its accepted input parameters and output schema.

Example candidate queries to evaluate with stakeholders:

  • failed_jobs_last_7d — count and list of jobs with a failure status in the last N days, grouped by pipeline/tenant.
  • aggregation_by_catalog — job count, success rate, and average elapsed time grouped by catalog/source.
  • run_status_summary — distribution of run statuses (running, completed, failed) over a configurable time window.

Route dispatch follows the existing ROUTE_HANDLERS pattern in event_stats_lambda.py. A new HandlerCompiledQuery (or an extension of HandlerStats) handles validation and delegates to the registry.

Dependencies / Related

Activity

  1. self-assigned this
    on Apr 13, 2026
  2. tmikula-dev commented on Jun 2, 2026

    @tmikula-dev
    Collaborator

    The issue is currently waiting for the input from the business, which will help us define, which compiled queries should be set.

  3. lukasmatta commented on Sep 30, 2026

    @lukasmatta

    As a starting point, I would recommend adding simple aggregations for a given time window. So the endpoint query/runs_jobs_detail would, in addition to the currently returned paginated job runs, also return aggregated data for the given time window:

    "aggregations": {
        "window": {
          "timestamp_start": 1704067200000,
          "timestamp_end": 1706745600000
        },
        "total_runs": 1840,
        "total_tenants": 12,
        "total_catalogs": 44,
        "succeeded": 1600,
        "failed": 210,
        "success_rate": 86.9,
        "avg_duration_ms": 41000,
        "timeline": {
          "bucket": "hour",
          "entries": [
            {
              "bucket_start": 1704067200000,
              "total": 12,
              "succeeded": 10,
              "failed": 2
            },
            {
              "bucket_start": 1704070800000,
              "total": 3,
              "succeeded": 3,
              "failed": 0
            },
            ...
          ]
        }
      }
    • total_runs - count across the full window
    • total_tenants - number of distinct tenants
    • total_catalogs - number of distinct catalogs
    • succeeded - count of succeeded runs
    • failed - count of failed runs
    • success_rate - percentage (this could also be calculated on the FE side if it's only succeeded/total_runs)
    • avg_duration_ms - average run duration
    • timeline - a bit more tricky, explained below:

    Timeline

    Timeline breaks the selected window into equal time buckets so we can see when runs happened, not just the totals. It should be the source for a heatmap chart.

    bucket - how wide each slot is ("hour" or "day"). We use hourly for short ranges (last day/week) and daily for longer ones (last month).
    entries - one per bucket that actually had runs (empty ones can be skipped):
    bucket_start - when the bucket starts (epoch ms); we read the date and hour from it.
    total, succeeded, failed - how many runs happened in that slot, and how they turned out.

    Entries are ordered oldest-first, and all the totals add up to total_runs.


    From there, we can add additional data/aggregations based on specific business needs.

  4. self-assigned this
    on Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions